Skip to content

1.3 工程的时代

2009 年推出的 AngularJS 加速了前后端分离。2009 年还诞生了另一项对前端具有革命性意义的技术,即 Node.js。Node.js 把客户端脚本语言 JavaScript 应用到了服务端,使其拥有操作文件和操作数据库的能力。

Node.js 是一个单线程的、基于事件循环的异步 I/O 框架,基于 Chrome 8 引擎,性能卓越,可以代替传统的 MVC 框架编写后端。Node.js 的 API 使用起来非常简单,只需要几行代码就能运行一个 Web 服务器。

Node.js 的一大亮点是 Npm 包管理。Npm 可以通过命令快捷地添加和删除第三方包,有数百万的第三方包可以免费使用。另外,Node.js 本身只保留了基础功能,其他功能都作为 Npm 包存在,这也使 Node.js 代码非常简单干净,维护起来非常简单。

“JavaScript 语法、可开发后端、包管理、代码极简”这几项特点使 Node.js 成为前端最耀眼的“明星”,开发者纷纷尝试用 Node.js 做一些更酷的事情。

因为 JavaScript 运行在浏览器沙盒环境下,所以没有主动操作文件的权限。Node.js 提供了操作文件的能力后,意味着让代码发生“变化”成为可能。

让代码发生“变化”是“编译”的前提,这正是前端工程化的萌芽。

1.3.1 Node.js 开启了前端工程

AngularJS 诞生后,其创始团队继续探索先进的前端技术,并于 2015 年推出了 Angular CLI。这是一款命令行工具,也被叫作脚手架工具,可以通过命令选择模板,快速创建并运行项目,使前端可以运行在像 http://localhost:8080 这样的本地服务之下。

Angular CLI 通过命令行创建和运行项目,这是前端工程化的开端。但是 Angular CLI 创建和运行项目的基础功能是 Node.js 提供的。Node.js 通过文件 API 生成项目,同时创建本地服务器挂载前端项目,使前端代码始终处在 Node.js 的运行环境之下。

有了 Node.js 的运行环境,前端开发者就可以充分发挥想象力,将服务端领域的先进理念迁移到前端。例如,用脚手架创建的项目自带热更新功能。在修改代码并保存后,不需要手动刷新,页面会自动检测到修改并更新。

这是什么原理呢?其实是 Node.js 启动了一个 WebSocket 连接,在修改代码时一旦检测到文件发生变化,就会主动触发编译更新。

很多前端开发者都有过这样一个疑问 —— 什么是前端工程化?
笔者认为主流的前端工程化是将 Node.js 的功能投射到前端项目上,为前端安装各种“装备”,从而彻底提高前端开发者的开发效率。

1.3.2 Webpack 带来了编译

Node.js 提供的服务器能力和文件操作能力开启了前端的工程之路,Webpack 则将操作文件的能力发挥到了极致。

Webpack 是一个现代 JavaScript 应用程序的打包器。它有两方面核心作用,分别是打包和转换。

  • 打包:意味着 Webpack 可以将任意文件模块化,从而在项目中可以通过 import/export 实现文件的导入/导出。
  • 转换:意味着对于 Webpack 不认识的非 JavaScript 文件,通过自定义的 Loader 让 Webpack 认识并处理这些文件。

例如,Vue.js 中的.vue 文件本不属于 HTML 或 JavaScript,Webpack 自然也就不认识它。但是,Vue.js 提供的 vue-loader 可以告诉 Webpack 如何处理和解析.vue 文件,并且最终将它转换成 HTML、CSS 和 JavaScript,于是这个.vue 文件被打包成模块。

这种转换功能在 Webpack 中被叫作编译。

正因为出现了编译,前端才有了“源码”和“可执行代码”的区别。这在 jQuery 时代是没有的,那时开发编写的代码就是源码,交给浏览器就能直接运行。

1.3.3 工程化体系持续完善

Node.js 提供了工程的基础能力,Webpack 提供了灵活的编译能力,两者强强联合,带动了大量的优秀工具集成到了工程化体系当中。这其中最具代表性的有:

  • Babel
  • Less/Sass
  • ESLint
  • Jest

Babel 是一款优秀的语法转换工具,它面向 JavaScript,可以将最新的 JavaScript 语法转换成 ES5 的标准语法,让绝大部分的浏览器支持。可以说有了 Babel 我们再也不用担心万恶的兼容性了,可以放心大胆的使用最先进的语法。同时 Babel 还可以指定最低兼容的浏览器版本,版本越高转码效率越高。

Less/Sass 被称为 CSS 的预处理器,是一种更高级的类 CSS 语法,它解决了大部分纯 CSS 不够友好的问题。比如支持了嵌套语法,定义变量,让开发效率和编码体验成倍提升。预处理器经过编译之后会转换为普通的 CSS 样式,在 Webpack 中它们都有各自的 loader 来实现代码的转换。

ESLint 是一款有名的代码规范工具,他有定义代码规范和检测代码规范两大功能。定义代码规范就包括用单引号还是双引号,代码末尾要不要加号,什么时候换行等代码风格类的规范。在多人团队协作中代码规范非常重要,但往往各自编码风格不同导致规范难以统一,有了 ESLint 的规范检测和格式化就能很容易的解决这个问题。

Jest 是一款单元测试框架。特别是对一些做基础建设的同学,单元测试必不可少。我们知道,代码在升级迭代中最头疼的问题是原本正常的功能,因为新加了逻辑导致出现异常,这往往难以预料,而单元测试的作用就是在更新代码之后对每一个模块自动执行一遍测试,尤其是基础的公共函数,确保每一个引用它的模块都能通过测试,这样我们才有底气向线上发布新版。

除此之外,前端在后期衍生出了更多的工程化技术,比如 持续集成,CI/CD,自动化部署 等等,这些都表示着工程化体系在持续完善,前端也越来越高效和成熟。

1.3.4 “工程+框架”成为现代前端格局

三大框架出现早期,很多人使用框架的方式是通过 CDN 引入。比如直接在 HTML 文件中引入一个 vue.min.js 的文件,然后实例化一个 Vue 对象,就可以在当前页使用双向绑定了。

随着前端工程逐渐成熟,特别是 Webpack 流行以后,三大框架借助 Webpack 的能力开发出了自己的模块。比如 Vue 创建了以 .vue 结尾的单文件组件,在这个文件内可以同时书写 HTML、CSS 和 JavaScript,这使组件分离非常直观。

React 则创建了代表性的 JSX,直接在 JavaScript 中书写 HTML(类 HTML 语法),这使得声明一个元素像声明一个变量一样简单。

像 .vue 和 JSX 这类在 HTML 中本不存在的语法,就是借助 Webpack 创造出来的。当然仅有这些还不够,各家都集成了自己的路由方案和状态管理方案。因为在工程化的前端之中,只有一个 HTML 文件,前端变成了单页面应用,任何页面切换都是通过 AJAX 实现,因此我们需要路由变化来追踪页面变化。

在前后端分离那节我们说过,完全使用 AJAX 异步加载页面会有 URL 不变化和全局状态难以维护的问题,这不在工程化之下这个问题就解决了嘛。

框架+路由+状态管理这套完整的解决方案前端称之为 全家桶。目前绝大多数同学新建前端项目时,首先会选择某个框架的全家桶,从而快速进入开发。

像 Vue 全家桶、React 全家桶已经是前端领域非常成熟的解决方案。在全家桶之下我们又可以根据项目需求选择性的接入工程化的周边能力,比如 ESLint,比如单元测试,这就形成了“工程+框架”的现代前端格局。