前端工程化之路

时至今日,前端也在飞速发展,各种框架以及因框架产生的生态圈也拔地而起,满天都是概念,如数据绑定、虚拟DOM、前端构建系统、前端工程化、大前端等等。很多前端同学被困在前端框架里,学的是心力交瘁,但有些同学早已学会了透过现象看本质的本事,不管是什么样的框架都能运用自如。
下面就带大家先看看前端的发展历史。

前端发展史

  • 1990年,Tim Berners Lee 发明了世界上第一个网页浏览器。

浏览器是被发明出来了,但问题来了,当时的网页都是静态的,如表单验证就需要后端完成,在那个年代带宽很差,发一次请求带代价是极其昂贵的,因此为了解决这一问题,急需要开发一种在浏览器执行的脚本语言,因此发生了下面的事情。

  • 1995年,Brendan Eich 只用了不到半个月时间完成了第一版网页脚本语言的设计。

  • 1996年,样式表标准CSS第一版发布。

至此 htmljavascriptcss 催生了前端的雏形。

阅读更多

不可思议的BUG

莫看江面平如镜,要看水底万丈深——记一次由表象引起的不是BUG的BUG

起因

先上图
image
一位同学碰到了这样一个问题:明明配置的请求地址是ip,为什么变成域名了。

上图,第一行是实际请求报错的接口,第二行才是打印出的真正配置的接口地址,由于项目上会根据项目环境,会切换接口地址,方便开发、测试、生产。我的第一反应就是切换地址的逻辑有问题。仔细跟了他写的代码,确实在接口请求前,地址是开发地址,但请求之后变成了生产地址。

自己折腾了半天,尝试了各种方案:

  • 把地址换成固定的字符串,如aaa,没问题;
  • 将接口定义文件内容简化;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 简化前
    export default {
    dev: {
    DEV_URL: ''
    },
    test: {
    TEST_URL: ''
    },
    prod: {
    PROD_URL: ''
    }
    }
    // 简化后
    export default {
    dev: '',
    test: '',
    prod: ''
    }
  • 我把生产地址注释掉,发现还调用的是生产地址
阅读更多

前端自动化构建及部署

前言

自动化这个字眼神圣而又高大上,对后端开发的同学来说也许再熟悉不过了,可对于前端开发来说,确实有些遥不可及,接下来分享下,我在前端项目的自动化实践。

实现方式

  • 一套是工作中经常使用的 Docker + Jenkins。Jenkins 是持续集成的关键。
  • 使用 DaoClound

什么是持续集成?

jenkins 的使用成本还是比较高的,因为需要搭建 Jenkins 的基本环境,但使用率想到高。我主要介绍的是第二套方案,成本低,但能实现自动化的功能。

阅读更多

理解HTTP之Content-Type

About

查看 Restful API 报头插件:Chrome 插件 REST Console,以及发送 Restful API 工具:Chrome 插件 POST Man

在 HTTP 1.1 规范中,HTTP 请求方式有 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT

通常我们用的只有 GET、POST,然而对于 Restful API 规范来说,请求资源要用 PUT 方法,删除资源要用 DELETE 方法。

例如发送个 DELETE 包:

http://example.com/my/resource?id=12345

阅读更多

Node.js module.exports与exports

折腾Node.js有些日子了,下面将陆陆续续记录下使用Node.js的一些细节。

熟悉Node.js的童鞋都知道,Node.js作为服务器端的javascript运行环境,它使用npm作为通用的包管理工具,npm遵循CommonJS规范定义了一套用于Node.js模块的约定,关于npm实现Node.js模块的更多细节请细读深入Node.js的模块机制,这里简单讲下书写Node.js代码时module.exportsexports的区别。

在浏览器端js里面,为了解决各模块变量冲突等问题,往往借助于js的闭包把所有模块相关的代码都包装在一个匿名函数里。而Node.js编写模块相当的自由,开发者只需要关注requireexportsmodule等几个变量就足够,而为了保持模块的可读性,很推荐把不同功能的代码块都写成独立模块,减少各模块耦合。开发者可以在“全局”环境下任意使用var申明变量(不用写到闭包里了),通过exports暴露接口给调用者。
我们经常看到类似export.xxx = yyy或者module.exports = xx这样的代码,可实际在通过require函数引入模块时会出现报错的情况,这是什么原因导致的呢?

Node.js在模块编译的过程中会对模块进行包装,最终会返回类似下面的代码:

1
2
3
(function (exports, require, module, __filename, __dirname) {
// module code...
});

其中,module就是这个模块本身,require是对Node.js实现查找模块的模块Module._load实例的引用,filename和dirname是Node.js在查找该模块后找到的模块名称和模块绝对路径,这就是官方API里头这两个全局变量的来历。
关于module.exportsexports的区别,了解了下面几点之后应该就完全明白:

阅读更多

git 使用规范流程

团队开发,需要遵循一个 git 使用规范,方便项目的协调和维护。

git 操作需遵循以下步骤:

  • 新建分支
  • 提交分支 commit
  • 编写提交信息
  • 与主干同步
  • 合并 commit
  • 推送到远程仓库
  • 发出 Pull Request

新建分支

每次开发新功能都应该新建一个单独的分支。

1
2
3
4
5
6
# 获取主干最新代码
$ git checkout master
$ git pull

# 新建一个开发分支myfeature
$ git checkout -b myfeature
阅读更多

nginx-proxy 使用说明

这是使用 docker-gen 自动化的 Docker 容器的 nginx 代理,减少手动配置 nginx。

GitHub 地址里面有具体的使用教程。

我的项目使用 docker 部署并使用 nginx-proxy 做的反向代理,在使用过程中碰到两个问题:

  • 如何在 nginx-proxy 中使用 SSL
  • 在代理多个 nginx 项目的情况下,如何对指定的项目进行 https 访问

我在阿里云 CA 证书中心给我的域名 申请了免费版的 SSL https://rnode.me,该证书针对单域名有效,因此我只希望我的主域名通过 https 访问,其他二级域名通过 http 访问。

现在针对以上两个问题做个总结。

阅读更多

package.json中^符号的含义

版本号 x.y.z :其中z 表示一些小的bugfix, 更改z的号,

y表示一些大的版本更改,比如一些API的变化

x表示一些设计的变动及模块的重构之类的,会升级x版本号

在package.json里面dependencies依赖包的版本号前面的符号有两种,一种是~,一种是^。

~,^的区别是:
~的意思是匹配最近的小版本 比如~1.2.3将会匹配所有的1.2.x版本,但不匹配1.3.0, 1.2.0 <= ~1.2.3 <1.3.0
^的意思是最近的一个大版本 比如^1.2.3 将会匹配 所有 1.x.x 包括1.3.0 但不包括2.0 1.0.0 <= ^1.2.3 < 1.x.x

angular4 运行 ng build -pord 出错

  1. npm, angular cli 安装成功后;
  2. 安装依赖时用 npm install ,由于需要 FQ,一直安装不下去;
  3. 用了 taobao 镜像 用 cnpm install 依赖安装成功;
  4. 在用 ng build 时成功,但用 ng build -prod 时出错。错误如下:

大致意思是找不到 package.json,之前用 angular4.0 没问题,装到最新的就有问题了,可能是使用 cnpm 装依赖引起的,因为 npm install 装不成功,不能确定是不是用 npm install 就可以了,

目前暂时用 ng build –prod –no-extract-license 解决,能成功运行,如果有碰到此问题的,可以用此命令试试。

转载地址

angular模块库开发

随着前端框架的诞生,也会随之出现一些组件库,方便日常业务开发。今天就聊聊 angular4 组件库开发流程。

下图是 button 组件的基础文件。

image

nk-button.component.ts 为该组件的核心文件,看看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import {Component, Renderer2, ElementRef, AfterContentInit, ViewEncapsulation, Input} from '@angular/core';

@Component({
selector: '[nk-button]',
templateUrl: './nk-button.component.html',
encapsulation: ViewEncapsulation.None,
styleUrls: ['./nk-button.component.scss']
})
export class NkButtonComponent implements AfterContentInit {
_el: HTMLElement;
_prefixCls = 'ky-btn';
_type: string;
_size: string;
_shape: string;
_classList: Array<string> = [];

@Input()
get nkType() {
return this._type;
}

set nkType(value) {
this._type = value;
this._setClass();
}

@Input()
get nkSize() {
return this._size;
}

set nkSize(value: string) {
this._size = value;
this._setClass();
}

@Input()
get nkShape() {
return this._shape;
}

set nkShape(value: string) {
this._shape = value;
this._setClass();
}

constructor(private _elementRef: ElementRef, private _renderer: Renderer2) {
this._el = this._elementRef.nativeElement;
this._renderer.addClass(this._el, this._prefixCls);
}

ngAfterContentInit() {
}

/**
*设置class属性
*/
_setClass(): void {
this._classList = [
this.nkType && `${this._prefixCls}-${this.nkType}`,
this.nkSize && `${this._prefixCls}-${this.nkSize}`,
this.nkShape && `${this._prefixCls}-${this.nkShape}`
].filter(item => {
return item;
});
this._classList.forEach(_className => {
this._renderer.addClass(this._el, _className);
});
}
}

针对核心概念 ElementRef、Renderer2、ViewEncapsulation 做简要说明:

阅读更多