为了升高首屏代码大小,对于一些大的第三方库或者团队的根底工具库,须要按需导入模块。如:

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等工具反对一些配置定制,但还是存在上面毛病:

  1. 每个插件都是针对特定的组件库,须要合乎特定的目录和文件维护标准。如babel-plugin-import导入的模块须要反对目录为:

    |--component|----index.js|----*.js|----style|------index.js|------*.css
  2. 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能够参照计划本人实现下。

你有什么更好的想法,欢送探讨。