什么是按需打包
Webpack 在打包的过程中会经过一个 tree shaking 的阶段,有两个作用
- 模块的按需打包
- 删除死代码(dead code elimination)
那么按需打包,它的原理是什么?
答:利用 ES6 模块语法的特点:编译时加载模块的内容,而不是像 CommonJs 是运行时加载模块内容。
在编译打包模块时,将非默认引用都转换为默认引用。
// 默认引用import _ from 'lodash-es'// 非默认引用import { cloneDeep } from 'lodash-es'
// 打包前引入模块import { cloneDeep, debounce } from 'lodash-es'// 打包后引入模块import cloneDeep from 'lodash-es/cloneDeep.js'import debounce from 'lodash-es/debounce.js'
:::info
如果在开发组件库或工具库时,我们可以尽量按功能进行分包,并且采用 ES6 的模块语法。
用户使用工具库开发业务项目时,构建工具在打包时,才可以利用这种特点,对库的功能进行按需引入,减少项目的体积。
:::
如何实现按需打包
利用 AST 对 import语句进行内容修改,将非默认引用,都改为默认引用。
从下面例子可以看出,设置文件引入路径,这个和库的文件路径设置有关。 在开发组件库或工具库时,可以在 package.json 下设置文件模块的入口。
const babel = require('@babel/core')const t = require('@babel/types')// 模块的按需加载const source = `import { cloneDeep, debounce } from 'lodash-es'`const importPlugin = {visitor: {ImportDeclaration: function (path) {const { node } = pathconst pathName = node.source.valuelet specifiers = node.specifiersif (!t.isImportDefaultSpecifier(specifiers[0])) {specifiers = specifiers.map(specifier => {return t.importDeclaration([t.importDefaultSpecifier(specifier.local)],t.stringLiteral(`${pathName}/${specifier.local.name}`) // 设置文件引入路径)})path.replaceWithMultiple(specifiers)}}}}const result = babel.transform(source, {plugins: [importPlugin]})console.log(result.code)
