为什么要打包
- 模块化
- 可维护性
- 可复制性
模块化定义:以功能块为单位进行程序设计,实现其求解算法的方法称为模块化。
模块化的目的是为了降低程序复杂度,使程序设计、调试和维护等操作简单化。
模块化发展史
也可谓是前端进化史
全局变量 + 命名空间
缺点:
- 依赖全局变量,污染全局作用域,不安全
- 依赖约定命名空间来避免冲突,可靠性不高
- 需要手动管理依赖并控制执行顺序,容易出错
- 需要再最终上线前手动合并所有用到的模块
common.js / AMD
common.js 之前很流行,但缺点是没法在浏览器里直接运行
js
const bar = require('./bar')
module.exports = function () {}
AMD - Asynchoronous module definition
js
define(function (require) {
// 通过相对路径获取依赖模块
const bar = require('./bar')
// 模块产出
return function () {}
})
- 采用异步方式加载模块
- 需要在全局环境定义 require 和 define ,不需要其他的全局变量
- 通过文件路径和模块自己声明的模块名定位模块
- 提供了打包工具自动分析依赖并合并
- 配合特定的 AMD 加载器使用,RequireJS
ES6 modules
js
// 通过相对路径获取依赖模块
import bar from './bar'
// 模块导出
export const foo = ''
export default function () {}
- 引入和导出的方式更多
- 支持复杂的静态分析
打包工具的功能和作用
- webpack
- vite (通过 Rollup 和 ESBuild)
学习使用 vite(Rollup)实现代码打包
- 代码入口文件
- 配置文件
- 生成多种文件类型
- 生成样式文件
- TypeScript 定义文件 d.ts
Vite
大型应用使用 webpack 等传统打包工具遇到性能瓶颈:非常慢。而 Vite
Vite 以 原生 ESM 方式提供源码。实际上是让浏览器接管了打包程序的部分工作
还是直接看文档来得快些 https://cn.vitejs.dev/guide/why.html
依赖:使用 esbuild 进行预构建(由 go 编写的,比 Node.js 快 N 倍)
- 处理 CommonJs 以及 UMD 类型文件的兼容性,转换为 ESM 及 ESM 的导入形式
- 提高性能,将多个模块合并成单个模块,因为原生 ESM 格式下,一个文件就是一次请求
- 缓存,将预构建的依赖项缓存到 node_modules/.vite 中
源码:包含一些非 JS 标准格式的文件,如 jsx/css/vue 等,时常会被编辑
发布到 npm
- 本地测试
- 注册发布到 npm
- npm hooks
打包什么类型的文件
- CommonJs, ES6 modules 需要特殊的打包工具支持
- AMD 已过时
- 浏览器直接使用 (UMD)Universal Module Definition
- 通用 JS 格式
- 兼容 Common.js、AMD、浏览器
js
;(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['b'], factory)
} else if (typeof module === 'object' && module.exports) {
// Node. Does not work with strict CommonJS, but only
// CommonJS-like environments that supportmodule.exports, like Node
module.exports = factory(require('b'))
} else {
// Browser globals (root is window)
root.returnExports = factory(root.b)
}
})(typeof self !== 'undefined' ? self : this, function (b) {
return {}
})()
但是,不是推荐格式,因为太大了,不支持 Tree Shaking
综上:
- 首要格式 - ES Module ,并且提供支持 TypeScript 的 type 文件
- 备选方案 - UMD