共计 3680 个字符,预计需要花费 10 分钟才能阅读完成。
为了升高首屏代码大小,对于一些大的第三方库或者团队的根底工具库,须要按需导入模块。如:
import Button from 'antd/lib/button';
但这在须要导入十分多的组件场景时,开发繁琐,体验不敌对。在这些组件库的官网文档或者社区会举荐一些 babel 插件,帮忙达到良好的开发体验和性能优化。
本文将具体探索这些工具的原理。
antd 等 UI 组件库按需加载
在应用 antd 的老版本时,会举荐应用 babel-plugin-import 工具按需导入组件。工具能够做到如下的转换:
import {Button} from 'antd';
ReactDOM.render(<Button>xxxx</Button>);
↓ ↓ ↓ ↓ ↓ ↓
var _button = require('antd/lib/button');
require('antd/lib/button/style');
ReactDOM.render(<_button>xxxx</_button>);
antd 和 element-ui 都叫做按需加载,但我感觉叫做按需导入更加贴合意境。
eleemnt-ui 配套按需导入工具为 babel-plugin-component。
babel-plugin-import 工具,会在编译时会剖析模块导入语句。当为须要导入的指标库组件时,会将原导入删除,生成多个新的导入 (如果你配置了 style,会额定导入款式)。因为从新生成的导入语句会导致模块变量发生变化(如上文案例演示中Button
会被转换为_button
),这导致插件程序还会剖析以后模块的所有变量,对应用原变量的语句中,将变量名修复。
最新的 antd 曾经举荐应用 webpack 的 tree shaking 机制来按需加载。babel-plugin-import 也根本没什么更新。对于 vue 生态来说,很多组件库为了反对全局注册的形式,无奈应用 tree shaking。
尽管 babel-plugin-import 等工具反对一些配置定制,但还是存在上面毛病:
-
每个插件都是针对特定的组件库,须要合乎特定的目录和文件维护标准。如 babel-plugin-import 导入的模块须要反对目录为:
|--component |----index.js |----*.js |----style |------index.js |------*.css
-
babel-plugin-import 因为底层实现扭转了导入的模块变量,而后再全模块枚举语句类型中找到应用变量将其修复,再某一些十分不常见的语句中,会呈现没有转变模块变量导致语法错误问题。
对任意库反对按需导入
如果也想对我的项目中公共根底模块(公共组件,公共工具文件等)反对源码中全量导入但理论按需加载的成果,除了能够 fork babel-plugin-import 等工具调整逻辑来实现,还能够应用工具 babel-plugin-transform-imports 来反对。
babel-plugin-transform-imports 是本人配置转换格局,如能够达到上面成果:
import {Row, Grid as MyGrid} from 'react-bootstrap';
import {merge} from 'lodash';
↓ ↓ ↓ ↓ ↓ ↓
import Row from 'react-bootstrap/lib/Row';
import MyGrid from 'react-bootstrap/lib/Grid';
import merge from 'lodash/merge';
此时须要的配置为:
{
"plugins": [
["transform-imports", {
"react-bootstrap": {"transform": "react-bootstrap/lib/${member}",
"preventFullImport": true
},
}]
]
}
transform 是能够反对函数的,实现高级定制,扩展性十分高。
且 babel-plugin-transform-imports 实现的形式是间接对导入语句进行了修复,比 babel-plugin-import 更加优雅和适用性更广,浏览他的源码也能够发现比 babel-plugin-import 简洁的多。
babel-plugin-import 底层是将原来导入删除,而后生成新的指标导入,导致了导入模块的变量产生了变更。
但 babel-plugin-transform-imports 同样有一些缺点:它无奈由一个导入生成多个导入。这也就意味着应用 babel-plugin-transform-imports 无奈对 antd,element-ui 这样的 UI 组件库进行模块按需导入:这些 UI 组件库,除了 js 模块的导入外,往往还有一个款式模块。
能够 fork babel-plugin-transform-imports 扩大其逻辑,如这个库 babel-plugin-transform-module-imports。我参照他的实现原理,实现了一个工具 babel-plugin-transform-import-module,能够反对更多的配置,根本能够应用它满足所有的模块在源码中全量导入但理论按需导入的成果。
像 lodash 一样依据调用按需导入
在应用 lodash 是,能够应用 babel-plugin-lodash 插件进行导入优化。可能将:
import _ from 'lodash'
import {add} from 'lodash/fp'
const addOne = add(1)
_.map([1, 2, 3], addOne)
在编译时转换为:
import _add from 'lodash/fp/add'
import _map from 'lodash/map'
const addOne = _add(1)
_map([1, 2, 3], addOne)
利用 babel-plugin-lodash 能够晋升 lodash 应用时的开发体验:代码中是全量导入应用,无需关注其内部结构,将按需导入对应的工具函数模块交给底层 babel 插件解决。使用者除了不须要操心是否导入额定的代码外,还能够配合 typescipt 应用,领有更好的代码提醒,从而升高认知。
以后这个插件只反对 lodash 库应用。借鉴其理念,能够实现:
import utils from 'utils@'
utils.downloadFile('path/to/file')
↓ ↓ ↓ ↓ ↓ ↓
import _downloadFile from 'utils@/downloadFile'
_downloadFile('path/to/file')
如果革新更加深刻一点,能够对一些市面上风行库的优化,如 antd 库的应用:
import * as Antd from 'antd';
ReactDOM.render(<Antd.Button>xxxx</Antd.Button>);
↓ ↓ ↓ ↓ ↓ ↓
import Button from 'antd/lib/button';
import('antd/lib/button');
ReactDOM.render(<Button>xxxx</Button>);
下面的成果在我实现的一个 babel 插件 babel-plugin-module-call-import 都能够反对。如果你须要齐全像 babel-plugin-lodash
一样依据文件目录构造主动转换加载器,能够间接 fork 源码上调整。
我的团队外面多个我的项目根本应用这种形式。
在 vue 代码中依据标签按需导入
在 vue 利用注册组件时,有两种形式:全局注册和部分注册。全局注册开发体验式最好的,但会导入 UI 组件库的所有代码,减少很多无用代码从而首屏激增。如何做到像全局注册组件那样开发敌对,又反对按需导入组件进步性能呢?
这里提供一个计划:在 vue 文件的模板在解析的时候,记录模板文件应用的标签,而后在文件对应的 js 代码编译的时候,依据标签主动导入 UI 组件,而后在组件配置对象的 components
属性中注册下来。
如果代码:
<template>
<div>
<el-button> 按钮 </el-button>
</div>
</template>
<script>
export default {created() {},};
</script>
在编译时,将会转换相似于:
<template>
<div>
<el-button> 按钮 </el-button>
</div>
</template>
<script>
import ElButton from 'element-ui/lib/form-item';
export default {
components: {ElButton,},
created() {},
};
</script>
我开发了两个工具实现持这个计划:
- babel 插件:babel-plugin-vue-import-component-by-tag
- webpack loader: vue-record-tags-loader
只反对与 vue2 版本,vue3 能够参照计划本人实现下。
你有什么更好的想法,欢送探讨。