# 前端模块化背景
- 前端模块化是一种标准,不是实现
- 理解模块化是理解前端工程化的前提
- 前端模块化是前端项目规范化的必然结果
# 什么是前端模块化?
- 将复杂程序根据规范拆分成若干模块,一个模块包括输入和输出
- 模块的内部实现是私有的,对外暴露接口与其他模块通信
- 一个 HTML 页面可以引用的 script 包括:脚本和模块
# 前端模块化的进化过程
第一阶段:全局 function 模式,将不同功能封装成不同函数
- 缺陷:容易引发全局命名空间冲突
- 示例:
function api() {} function handle() {}
1
2
第二阶段:全局 namespace 模式,通过对象封装模块
- 缺陷:外部能够修改模块内部数据
- 示例:
function api() {} function handle() {} var __Module = { api, handle }
1
2
3
4
5
6
第三阶段:IIFE 模式,通过自执行函数创建闭包
- 缺陷:无法解决模块间相互依赖问题
- 示例:
;(function (global) { var a = 1 function api() { return a } function handle() {} global.__Module = { api, handle } })(window)
1
2
3
4
5
6
7
8
9
10
11
第四阶段:IIFE 模式增强,支持传入自定义依赖
- 缺陷:
- 多依赖传入时,代码阅读困难
- 无法支持大规模的模块开发
- 无特定语法支持,代码简陋
- 示例:
;(function (global, moduleAPI) { function sum() {} global.__Module = { api: moduleAPI.api, handle: moduleAPI.handle } })(window, window.__Module_API)
1
2
3
4
5
6
7
- 缺陷:
第五阶段:CJS、ESM、AMD、CMD
# CJS
规范介绍
- Node.js 默认模块化规范,每个文件就是一个模块,有自己的作用域
- Node 中 CJS 模块加载采用同步加载方式
- 通过 require 加载模块,通过 exports 或 module.exports 输出模块
规范特点
- 所有代码都运行在模块作用域,不会污染全局作用域
- 模块可以多次加载,第一个加载时会运行模块,模块输出结果会被缓存,再次加载时,会从缓存结果中直接读取模块输出结果
- 模块加载的顺序,按照其在代码中出现的顺序
原理
- CJS 会在模块外层包一层 IIFE 的函数,并传入 require、module、exports、**filename、 **dirname
- CJS 如果使用了 module.exports 进行输出,其他的 exports 输出就会失效
- CJS 在浏览器中不可使用,可以通过
browserify
这个进行打包,打包后可以在浏览器中运行
# AMD
- 规范介绍
- AMD 规范采用非同步加载模块,允许指定回调函数,对比 CJS,AMD 是异步的,CJS 是同步的
- Node 模块通常都位于本地,加载速度快,所有适用于同步加载
- 浏览器环境下,模块需要请求获取,所有适用于异步加载
- require.js 是 AMD 的一个具体实现库
- 注意:用的少
# CMD
- 规范介绍
- CMD 整合了 CJS 和 AMD 的优点,模块加载时异步的
- CMD 专门用于浏览器端,sea.js 是 CMD 规范的一个实现
- 注意:用的少
# AMD 和 CMD 的问题
- AMD 和 CMD 最大的问题是没有通过语法升级解决模块化
# ESM
- 规范介绍
- ESM 设计理念是希望在编译时就确定模块依赖关系及输入输出
- CJS 和 AMD 必须在运行时才能确定依赖和输入输出
- ESM 通过 import 加载模块,通过 export 输出模块
# CJS 和 ESM 对比
CJS 模块输出的是值的拷贝,ESM 模块输出的是值的引用(重要!!!)
CJS 模块是运行时加载,ESM 模块是编译时输出接口
CJS 是单个值导出,ESM 可以导出多个
- 在 ESM 中 export 和 export default 可以同时生效
- 在 CJS 中 exports 和 module.exports 不能同时生效
CJS 模块为同步加载,ESM 支持异步加载
CJS 的 this 是当前模块,ESM 的 this 是 undefined
CJS 和 ESM 语法不同
Node.js 默认采用的模块化规范是 CJS,Node 14 后默认支持 ESM
浏览器默认采用的模块化规范是 ESM
# HTML 中脚本和模块对比
- 模块具备更高的开发效率(可读性强、复用高效)
- 脚本具有更高的页面性能(模块文件多,加载速度慢)
- 模块在浏览器中运行会存在兼容性问题,要特别注意
# 浏览器模块化的局限
- 缺乏模块管理能力,模块分散在各个项目中
- 性能加载慢,无法在大型项目中直接使用
- 这两个问题是 Npm 和 Webpack 核心解决的问题
# 前端工程化关键技术之 Npm + Webpack
# Npm
诞生背景
- Npm 由程序员 Isaac 发明
- 初步思路:
- 集中管理所有模块,所有模块都上传到仓库(registry)
- 模块内创建 package.json 标注模块的基本信息
- 通过 npm publish 发布模块,上传到仓库(registry)
- 通过 npm install 安装模块,模块安装到 node_modules 目录
Npm 于 2014 年商业化,2020 年被 Github 收购
Npm 介绍
- npm 解决的核心问题是模块管理问题
- npm 包含 cli、模块仓库、官网三大部分
- npm 仓库分为 public 和 private
- public 分为普通仓库和组织仓库(一般以 @ 开头)
- private 主要的商业模式
Npm 原理
- npm init 创建模块
- npm install 安装模块
- npm publish 发布模块
- npm link 本地开发
- npm config 调整配置
- npm run 调用 script
- npm 规范:package.json 管理模块信息,node_modules 保存依赖
Npm 的局限
- npm 只能解决模块的高效管理和获取问题
- npm 无法解决性能加载问题
- 模块化发明后,制约其广泛应用的因素就是性能问题
- 需要 Webpack
# Webpack
- 诞生背景
- webpack 2012 年 3 月 10 号诞生,作者是 Tobias(德国)
- 移植了 GWT(Google Web Toolkit)的功能
code splitting
- 2014 年
Instagram
团队分享性能优化时,提到使用 webpack 的code slitting
特性 - webpack 的出现模糊了任务和构建的边界,使之融为一体
- 原理
- 最初的 webpack 核心解决的问题就是代码合并与拆分
- webpack 的核心理念是将资源都视为模块,统一进行打包和处理
- webpack 提供了 loader 和 plugins 完成功能扩展
作者:chenjie
链接:https://webchenjie.cn
来源:ChenJieBlog