为了利用可能快速访问, 须要对构建代码进行 ” 减肥 ”, 将无用代码剔除掉. 以后得支流构建框架 webpack
和rollup
等都提供了 tree shaking
机制, 利用 es6
得申明式模块零碎语法和语句依赖剖析, 进行高精度得代码剔除. 但 tree shaking
也存在一些限度, 个别的第三方库都采纳 es5
语法, 不应用 es6
的模块语法, 导致 tree shaking
生效. 对于这些第三方库, 个别采纳一些转换导入语句的 babel
插件, 如 babel-plugin-import), babel-plugin-component), babel-plugin-transform-imports)等.
本文中的案例中很多采纳了
antd
的例子, 但其实antd
是反对tree shaking
的, 不须要应用这些插件也能按需导入.
导入语句转换插件
不采纳按需导入的插件, 导入一个库如loadash
, 就会这样写:
import {trim, isEqual} from 'loadash';
trim(str);
isEqual(1, 2);
这会将整个 lodash
代码都给导入, 如果不像导入不须要的代码, 且以后库反对按需导入, 手动按需导入的代码应该为:
import isEqual from 'lodash/isequal';
import trim from 'lodash/trim';
trim(str);
isEqual(1, 2);
但以后模块中大量应用了这个库的模块 (函数) 时, 手动按需导入就会十分繁琐, 代码整洁度大大降低了.
如果可能将全量导入代码:
import {trim, isEqual} from 'loadash';
利用工具转换成按需导入代码:
import isEqual from 'lodash/isequal';
import trim from 'lodash/trim';
这样既能享受全量导入的简洁, 又能够不必放心导入过多的无用代码.
而 babel-plugin-import
, babel-plugin-component
, babel-plugin-transform-imports
等就是这种提供转换的工具.
babel-plugin-import
是阿里为了 antd
组件库量身定做的一套转换工作, 不过在后续的更新中, 实用的范畴越来越广. 它除了会导入指标组件外, 还反对导入组件从属款式文件. 转化示意:
import {Button} from 'antd';
ReactDOM.render(<Button>xxxx</Button>);
↓ ↓ ↓ ↓ ↓ ↓
var _button = require('antd/lib/button');
require('antd/lib/button/style/css');
ReactDOM.render(<_button>xxxx</_button>);
babel-plugin-component
是 element-ui
团队对 babel-plugin-import
一个低版本的 fork, 不倡议应用, 因为可配置化基地, 根本只能对 element-ui
这个 UI 库按需导入应用.
babel-plugin-transform-imports
是一个十分轻量级的转换插件, 可定制化水平十分高, 能够定制转换后的导入语句, 适应不同的目录构造. 但它只能为全量导入的每一项转换为一个独自的导入, 这不实用的对 UI 组件库按需导入. 在 babel-plugin-component
还很轻便时,babel-plugin-transform-imports
是十分好用的. 它的导入示意:
import {MdCheck, FaCheck} from 'react-icons'
->
import MdCheck from 'react-icons/lib/md/check'
import FaCheck from 'react-icons/lib/fa/check'
从原理来说, 这些插件都是剖析具名导入的 import
语句, 依据导入项, 转换为按需导入语句. babel-plugin-import
跟 babel-plugin-transform-imports
在源码实现的细节上有所不同.
在 babel-plugin-import
如对于代码:
import {Button} from 'element-ui';
console.log(Button);
插件解析进去所须要按需导入的模块后, 会在新的一行中增加按需导入语句 (应用babel@/helper-module-imports
库中的工具), 此时的代码会变为:
import _Button from 'element-ui/lib/button';
import 'components/lib/button/style.css';
import {Button} from 'element-ui';
console.log(Button);
此时还须要须要将所有应用变量 Button
的中央改为_Button
:
import _Button from 'element-ui/lib/button';
import 'components/lib/button/style.css';
import {Button} from 'element-ui';
console.log(_Button);
而后删除原有导入:
import _Button from 'element-ui/lib/button';
import 'components/lib/button/style.css';
console.log(_Button);
为了将 Button
变量转为 _Button
, 须要对可能应用变量语句转换, 在babel-plugin-import
以后最新代码中, 检测的语句 (表达式) 类型有:CallExpression
, MemberExpression
, Property
, VariableDeclarator
, ArrayExpression
, LogicalExpression
, ConditionalExpression
, IfStatement
, ExpressionStatement
, ReturnStatement
, ExportDefaultDeclaration
, BinaryExpression
, NewExpression
, ClassDeclaration
等 (具体能够参照源码). 这种采纳枚举可能存在的语句可能会有脱漏. 从库的变更来看, babel-plugin-component
对应的 babel-plugin-import
的版本到最新的版本, 多了几个表达式. 其实这里能够像 babel-plugin-lodash
的实现外面一样, 通过作用域查问到所有应用变量的语句, 更加精确和简洁.
从
babel-plugin-import
的最新的代码来看, 应用了通过作用域来解决变量重名导致的问题)
而 babel-plugin-transform-imports
非常简单, 通过剖析指标 import
语句, 将具名导入语句替换为多条按需导入语句, 且变量名维持跟原样.
就算最新版本的 babel-plugin-import
适用范围曾经十分广, 然而还是倡议应用 babel-plugin-transform-imports
, 它的实现轻量, 简洁. 对于babel-plugin-transform-imports
不反对按需导入额定的资源, 能够 fork
源码, 进行扩大.
依据调用进行按需导入
还有一种 babel
转换导入语句的按需导入的机制, 原理来自 babel-plugin-loadash
插件, 它能够将上面的代码:
import _ from 'loadash';
_.trim(str);
_.isEqual(1, 2);
转换为:
import isEqual from 'lodash/isequal';
import trim from 'lodash/trim';
trim(str);
isEqual(1, 2);
原理是依据全量导入的变量的调用链, 剖析所须要的模块, 而后按需导入. 这样的不便之处在于编码时应用模块的全量导入或者默认导入, 防止具名导入那样须要保护每一项. 比方利用 antd
组件库在开发表单时, 须要导入大量的表单组件, 随着业务的变更, 组件也须要变更, 每一次保护都须要从新在导入语句中导入增加须要的模块.
一开始控件中只应用了 input
和button
:
import {Button, Input, Form, FormItem} from 'antd';
后续扩大了业务时, 须要应用下拉框, 须要改写:
import {Button, Input, Form, FormItem, Select} from 'antd';
这在以后文件代码量非常大是, 每一次应用新的组件, 都须要滚动文件顶部保护好新的导入, 而后在回到开发点持续开发, 打断晦涩的开发快感. 且如果没有配置相应的 eslint
规定的话, 还会导致一些没有应用的组件仍旧被导入, 导致无用代码被加载.
而一大早应用全量导入就没有这种懊恼:
import * as Ad from 'antd';
return (<Ad.Form>
<Ad.FormItem>
<Ad.Input />
// ...
这里只提供这样一个的思路, 如果感觉这样能够进步开发效率, 能够参考 babel-plugin-lodash
实现相应的插件. 笔者所在的团队是这样应用的, 但带来一个苦恼是可能组件前缀过多 (Ad.
为前缀), 不简洁.
写在最初
在开发 vue 我的项目时, 组件注册有全局注册和部分注册之分. 全局注册后的组件在每一个其余组件中都能够应用, 无需再次导入注册, 具备良好的开发体验. 但也会导致首屏包过大, 升高用户体验.
能够不能够有一种形式, 让开发人员开发时像全局注册一样, 理论打包又跟部分注册一样反对按需导入呢?