注:文章转载自 《透视前端工程化》
这个系列是我自己购买的课程内容,把比较干货的试读章节放在我的上,作为个人Mark。但其他篇目因为版权问题不好直接放,所以感兴趣的小伙伴们自取吧。
1 项目模板
我们的项目框架都是基于项目模板生成的。学过 JS 的知道:
function Person(name = 'ant') {
this.name = name;
}
let man = new Person();
这里 Person
是 man
的原型对象。同样项目模板相当于对象 Person
,具体的项目相当于 man
。通过项目模板,我们可以事先将项目所需要的功能点定义好,使用的时候只需要像实例化一个对象一样,简单地 new 一下就搞定了。大大地减少重复劳动,屏蔽项目配置的复杂度。
在动手搭建之前,有必要对我们的项目模板进行一个整体规划,然后按照规划逐个实现。
2 支持哪些功能
2.1 代码检查
JavaScript 是一个动态的弱类型语言,在开发中比较容易出错。同时由于每个人的编码习惯和风格不尽相同,写出的代码风格迥异,时间久了很难维护。因为没有编译程序, JavaScript 代码错误通常需要在执行过程中不断调试,效率很低。
但是,我们不可能人肉进行代码检查,因为当代码量很大的时候,需要耗费很大的精力。所以代码检查我们借助 ESLint 来实现。ESLint 可以让程序员在编码的过程中就发现问题,避免将问题带到执行时。
为什么使用 ESLint?
- ESLint 的所有规则都是可拔插的。ESLint 不限制开发人员的编码风格。因为其设计的初衷就是为了让开发人员创建符合自己编码风格的规则。当然 ESLint 有一套内置的默认规则。但是可以修改甚至自定义自己的规则一切都是可拔插的。
- 每条规则可以自由地开关。开发人员可以根据自己项目的需要决定是否开启某条规则。也可以修改检查结果的告警等级,决定抛错还是只是警告。
一个简单的示例:
let foo = bar;
1:5 - 'foo' is assigned a value but never used. (no-unused-vars)
如果我们在 ESLint 配置文件中启用了 no-unused-vars 规则。那么当代码中出现定义未使用的变量时,就会给出错误提示。
至于 CSS 的检查,这里我们使用 stylelint 来处理。stylelint 功能非常强大,可以支持 scss、sass、less 以及 sugarss 的语法检查。此外,stylelint 和 ESlint 类似,也是提供了插件机制,开发者可以自己来定义规则,根据自己的团队定制一套统一的规范,避免样式错误。
我们看一个简单的示例。
有下面这样一段 css 代码:
/* */
a { color: #FFF; }
stylelint 规则如下:
"rules": {
"comment-no-empty": true,
"color-hex-case": "lower"
}
stylelint 提示注释内容不能为空,字体颜色的 16 进制表示字母不允许大写。
1:1 error Unexpected empty comment (comment-no-empty)
3:12 error Expected "#FFF" to be "#fff" (color-hex-case)
2.2 本地开发环境
在日常开发中,前端开发经常做的是完成部分功能和页面的开发后,去刷新一下浏览器,看一下效果是否符合预期。所以能有一个本地 Web 服务器加载我们的页面,并且当代码变动的时候自动刷新页面,可以减少开发同学的切换成本,提升开发效率。
我们在模板中使用 webpack-dev-server 作为 一个简单的 Web 服务器,由于 webpack-dev-server 内置了模块热替换(Hot Module Replacement 或 HMR),页面被修改后自动重新加载。
// package.json
"scripts": {
"dev": "webpack-dev-server --open"
}
在 Webpack 配置文件中完成 dev-server 的配置后,我们只需要在 package.json 文件中添加一个 script 命令。在进行开发时,只需要一个简单的 npm run dev
命令就可以启动本地服务器。
2.3 mock server
前后端分离后,双方有清晰的边界,后端负责业务逻辑的编写,前端负责视图逻辑的开发,双方的数据传输通过 API 实现。前后端共同约定一份接口文档,接口文档中规定了接口名称、入参、出参以及数据结构等。
但仅仅有一份静态的接口文档还远远不够,我们需要的是在开发的阶段就能像在线上一样去调用服务端接口获取数据。
所以,Mock 是前后端分离模式下一项必备的功能。
我们采取的做法是在项目模板中实现一个 mock server,来响应本地请求。同时假数据通过 mockjs 来生成。
由于我们的本地开发环境与 mock server 运行在不同的端口上,因此我们还需要在 Webpack 中提供的 proxy 功能将我们的请求转发到 mock server 上去。
graph LR
A(dev server)-->B(proxy server)
B(proxy server)-->C(mock server)
2.4 构建
现在的前端开发基本上都是采用了模块化的开发方式,而且在代码中使用了大量的 es6+ 语言特性。但宿主浏览器中对新特性的支持不一。我们的代码无法直接运行在浏览器中,所以需要对代码进行构建打包,将代码编译成浏览器都可运行的代码。
// webpack.config.js
...
output: {
path: path.join(__dirname, './build'),
publicPath: opt.publicPath,
// 给线上环境的资源添加hash
filename: 'js/[name]' + hash(needHash, 'chunk') + '.js',
chunkFilename: 'js/[name]' + hash(needHash, 'chunk') + '.js',
}
...
plugins: [
...
new webpack.optimize.UglifyJsPlugin({
compress: {
// 删除console和警告
drop_console: true,
warnings: false
}
})
...
]
由于目标代码的应用场景不同,我们需要针对不同的环境来进行构建。比如,针对测试环境运行的代码,由于需要不断进行调试,我们一般不清除代码中的注释和 console。由于代码部署无需考虑备份,不给构建出的文件添加 hash。线上环境构建的时候,我们需要尽可能减小文件的体积,同时需要考虑部署后不覆盖线上资源,所以需要在构建时剔除代码中的注释和调试语句,对代码进行混淆压缩,给文件添加 hash。
现在绝大多数现代浏览器都已经支持了原生的 ES2015,编译后的包通常都比原生的 ES2015+ 代码会更冗长,运行更慢。所以因为要支持更老的浏览器而为它们交付笨重的代码是一种浪费。我们在模板中可以针对现代浏览器打出体积更小的包。
- 现代版的包在被支持的浏览器中通过
进行加载,使用
进行预加载。
- 旧版本的包会通过
加载,支持 ES modules 的浏览器会忽略该引用。
2.5 部署
构建完成后,最后一步就是把前端资源发布到服务器了。假定我们的前端项目都是完全的前后端分离的,这意味着前端的资源需要和服务端分开部署。
比如,我们为专门准备一个 OSS 服务器用来部署入口文件,准备另一个 OSS 服务器部署 js、css 和图片等资源。
// package.json
"scripts": {
// 假定我们的部署逻辑在deploy.js中
"deploy": "node deploy.js"
}
部署我们会用到两个 npm 包,一个是 vinyl-ftp
,专门用以登录我们的 OSS 服务器,另一个包是 vinyl-fs
,用来将本地的资源发布到远程服务器。最后我们在 package.json 文件中配置 deploy
命令。部署操作只需要执行 npm run deploy
即可。
2.6 Qdebug
相信大家在测试过程中,有一个问题经常会遇到。总是有测试同学把一些本属于服务端的 bug 提给前端。前端同学看到 bug 后,按照复现步骤排查一遍,“咦!这个问题是接口中数据有问题导致的!”赶紧转给服务端同学,并且心中有一丝不爽。
所以,很有必要开发一个小工具辅助测试同学,为他们分析 bug 产生的原因,准确指派 bug 提供事实依据。
因此,我们在项目模板中要实现一个 Qdebug 小工具。
Qdebug 基于 vconsole 进行扩展,可以将接口请求中的详细接口数据打印出来,同时会生成一个当前前端代码的版本号。测试同学通过版本号就可以知道前端的资源是不是已经更新,通过查看详细的接口数据就可以准确判断 bug 的归属。
2.7 自动化测试
自动化测试在 Web 工程中扮演重要的角色。自动化测试还可以与持续集成进行结合,通过机器来保证工程的质量,提升团队整体的效能。因此在前端工程化的建设过程中,自动化测试也是很重要的一环。
自动化测试根据粒度的不同,基本可以分为:单元测试、接口测试、端到端测试(也叫功能测试)。在前端工程中得到应用的主要是接口测试和端到端测试。
单元测试,是站在开发人员的视角,把代码划分成一个个的代码单元逐个进行测试,看返回的结果是否符合预期。
我们通过 Karma+Mocha+Chai 来实现单元测试功能。执行单元测试很简单也是通过 npm script 来调用,例如:
npm run test:uinit
端到端测试是站在用户的视角,把 Web 应用当成一个黑盒,模拟用户的真实使用场景,比如在页面中输入文字,点击搜索。看测试结果是不是符合预期。端到端测试框架有不少,比如 Nightwatch、TestCafe、CasperJS。在本模板中,我们选用 Nightwatch 来实现端到端测试,主要是因为使用 Nigthwatch 编写的代码非常简洁。
执行端到端测试很简单通过 npm script 来调用,例如:
npm run test:e2e
3 目录结构
说完了模板要实现的主要功能后,我们来看一下项目模板的整体目录结构:
boilerplate-vue
├── package.json
├── README.md
├── .gitignore
├── .babelrc
├── config
├── build
├── mock
├── test
| ├── unit
| └── e2e
├── src
| ├── assets
│ ├── components
│ ├── router
│ ├── pages
│ ├── app.js
│ └── App.vue
└── public
├── index.html
└── favicon.ico
主要的一些目录和文件功能如下:
- src 中存放业务代码。其中
src/app.js
是项目构建的入口文件,src/App.vue
是视图层的入口文件,src/pages
中放置不同的页面,src/components
中放置 Vue 组件,src/router
中放置 router 相关的文件,src/assets
中放置静态资源如图片、字体等。 - mock 存放 Mock 的假数据。
- test 用于放置测试相关的文件。
- build 用于放置构建相关的文件。
- config 用于放置配置相关的文件。
- public 用于放置公用的静态资源,如 HTML 入口模板文件、站点的小图标。
- package.json npm 模块的配置文件。
- .babelrc babel 的配置文件。
- .gitignore 用于指定哪些文件不提交到 Git 仓库。
- README.md 项目的介绍和使用文档。
总结
本节我们对模板的功能进行了系统的规划和目录结构设计。模板功能覆盖了开发阶段、测试阶段、构建阶段、部署阶段。文中对各个功能的实现所需要用到的技术和工具也进行了简单的介绍,大家对每个部分有个大概的印象即可。在后面的章节中,我们会有更为详尽的讲解。
点击了解《透视前端工程化》。
作者简介:
王超,现任快狗打车(原58速运)前端负责人。
先后任职于人人网、奇虎360,8 年知名互联网工作经验。
从 0 到 1 组建了快狗前端团队,负责团队技术体系的搭建,形成了以 Webpack 和 Vue 为基础、 Node.js 中间层为补充的,自动化、工程化、组件化的快狗前端技术体系。