一、前端工程化解决的主要问题
JS全局作用域冲突
JS创建之初是为了快速开发网页脚本,但是未想现在发展壮大,随着项目变大,随处定义的全局变量很容易导致冲突,JS作者也承认,全局变量是JS设计的缺陷。
编码规范
首先,前端开发的前提是代码开发,而代码开发肯定离不开编码管理的规范,尤其是多人开发时,如何解决好规范问题,可以避免很多低级问题。
资源合并和压缩
当项目发布上线,受限于网络、浏览器并发请求数量的限制等,如何更高效地加载和运行依赖于资源体积的控制、加载次数的控制,这一部分需要对资源进行合并和压缩。
高版本JS降级
随着JS的升级,产生了许多新语法,但是存量浏览器不一定能很快速地进行支持,这要求我们对JS运行时进行兼容性处理,因此出现了不同的pollyfill方案。
二、工程化历史
前后端混合:1995-2005
此阶段前端页面的开发工作其实主要由后端处理,浏览器请求URL触发后端接口,后端直接渲染出HTML进行返回。
前后端分离:2005-2013
这个阶段将前端开发工作分离出来,前端可以通过AJAX进行异步网络请求,根据返回结果进行页面渲染,但该阶段存在很多开发体验的缺陷,最大的问题就是模块化开发体验差。
模块化开发:2013-2014
该阶段是对前一阶段的优化,利用webpack等打包工具将所有资源文件进行打包处理,能很好地支持模块化开发。
模块化+MVVM:2014至今
随着各种MVVM开发框架的诞生,前端开发工作从开发原生HTML、CSS、JS变化为开发Vue、React等框架的代码。通过打包工具进行转化,生成浏览器执行的原生代码。
三、模块化
模块化历史
- 全局function模式:其实就是简单地在JS脚本文件中定义全局函数,在HTML中引入使用,缺点是容易引发全局命名冲突。
- 全局namespace模式:将不同函数分类存储到不同的对象内,通过对象属性访问该方法;通过对象封装模块能一定程度解决命名冲突,但外部能够修改模块内部的数据。
- IIFE模式:通过自执行函数创建闭包,并导出执行结果;但无法解决模块间相互依赖问题。
(function (global) {
function sum(a, b) {
return a + b
}
function api() {
return {
a: 1,
b: 2
}
}
global.__Module = {
api,
sum,
}
})(window)
- IIFE模式增强,支持传入自定义依赖(参考webpack打包结果);但多依赖传入时,代码阅读困难,无法支持大规模模块化开发,无特定语法支持,代码丑陋。
(function (global, moduleAPI) {
function sum(a, b) {
return a + b
}
global.__Module = {
api: moduleAPI.api,
sun,
}
})(window, window.__Module_API)
模块化标准
CommonJS
- 代码运行在模块作用域,按照书写顺序同步加载执行
- 模块可以多次加载,第一次加载运行模块,模块内的代码会被执行,模块输出结果会被缓存,再次加载会从缓存获取
- 模块输出的是值的拷贝,类似IIFE方案中的内部变量,而非闭包引用的变量
- 原理:主模块中通过require加载模块时,会调用load方法,执行IIFE(require, module, exports, filename, dirname),将要加载的模块转为方法参数,这就是在模块中能使用require方法、__filename等变量的原因。同时执行完成后,会将执行结果存入Module Cache Map中,存储方式为 key(module path + module name): module.exports
- 在浏览器中使用可以使用browserify打包
ESModule
- ESM通过import、export导入、输出模块。
- 设计理念是希望在编译时就能确定模块关系和输入输出。
ESModule与CommonJS区别
- ESM输出的是值的引用,而CommonJS输出的是值的拷贝。
- CommonJS是单个值导出(module.exports),ESM可以导出多个。
- CommonJS是同步加载,ESM支持异步加载。
- CommonJS的this是当前模块module.exports,ESM中this为undefined。
- ESM编译时就能确定模块关系和输入输出,而CommonJS必须在运行时才能确定依赖和输入、输出。
AMD、CMD
- 非同步加载,允许指定回调函数
- 随着ESModule诞生,两者应用较少,因为没有通过语法升级解决模块化
模块化优缺点
- 更高开发效率,可读性强,复用性高。
- 浏览器中缺乏模块管理能力,模块分散在各个项目中。
- 加载性能慢,无法在大型项目中直接使用,需要npm和webpack的。
模块管理:npm

- 集中管理所有模块,所有模块上传到仓库,本地通过package.json管理模块基本信息。通过npm init初始化,npm link本地开发,npm publish/install进行发布/安装。
- 解决了模块的高效管理和获取问题,但无法解决加载性能问题。
模块打包:webpack

webpack诞生于2014年,最初是为了解决代码合并和拆分,核心理念是将所有资源视为模块,统一进行打包和处理。
将模块进行打包后,浏览器只需要请求少量个数的资源便可以进行执行,避免了HTTP1.x浏览器并发请求数量的限制。
同时,webpack通过loader和plugin进行功能扩展,在代码压缩、兼容降级等多方面均有很好的支撑。