按需加载
背景
js
// common.js
export const a = 'a'js
// index.js
import { a } from './common.js'
// 无用代码
export const b = 'b'
console.log(a)问题:变量 b 虽然没有用到,但是打包时依然会保留。 解决:通过 Tree Shaking,对没有用到的代码进行标记清除,达到减小打包体积的目的。
Tree Shaking 原理
实际应用:组件库按需加载
ES module
- 组件库导出需要有
es模块。如dist/es/组件内容 - 外部引入时,需要可以指向
es,通过package.json里的module字段制定。 package.json中需要指定sideEffects,标识副作用。- 项目中使用时,需要保证编译后都是
es模块。比如tsconfig.json里的module、@babel/preset-env里的module属性, 都有可能更改模块形式。 - 例如:
lodash=>lodash-es
babel-plugin-import
将 import { Button } from 'xxx' 改写为 import { Button } from 'xxx/button'
- 类型可以不考虑,因为经过
ts-loader后,类型被剔除了。 - 对于不规则的组件名称,需要通过
customName方法来自定义相应的引入路径。 - 例如:
loadash引入路径转换
side effects
- package.json side effects
- 引入外部模块
import 'test'会查找外部模块的package.json看是否有sideEffects。 - 引入没有导出:
import './index.js'会查找自身package.json看是否有sideEffects。 - 导出了但是没有使用:
export { xx } from 'xxx'会查找自身package.json看是否有sideEffects。 - 如果导出被使用或者有
sideEffects的文件会被计算分析。
- 引入外部模块
cjs 与 es 区别
- cjs 为值拷贝(浅拷贝),es 为引用
todo
- commonjs 是如何也支持 tree shaking 的?
- https://webpack.docschina.org/blog/2020-10-10-webpack-5-release/#commonjs-tree-shaking
- 对于部分语法,会同 es module 一样进行标记清除。
- 支持 require('xxx').xxx,对于动态路径、动态名称,动态属性无法进行标记清除,如 require['xxx']('' + 'xxx')
- const { a } = require('xxx') 会全量引入,不支持 tree shaking。
- if 语句内部,同样遵循上述两条规则。
- require('xxx').xxx 有一定的局限性。一旦导入了,就意味着使用了,比如引入。
- https://github.com/webpack/webpack/projects/5#card-30291446
- webpack 尚未处理部分 cjs 语法的 tree-shaking,证明这些是可以处理的,这意味着 commonjs 也可以先进行语法分析?
