组件库按需加载原理分析&& Vue3中提到的Tree-shaking

在使用 vant、element-ui、ant-design 等 UI 组件库时候会用到按需加载,通过 babel-plugin-import 插件可以快速配置好自动按需加载组件,还可以通过直接手动引入对应组件和样式文件的方式来实现。同时,在开发中使用 webpack 构建项目时也常使用懒加载技术,本文所述的组件库动态加载和 webpack 构建项目的懒加载是不同的。本文将以 babel-plugin-import 插件为主,讲解组件库按需加载方案的实现原理。


对比 webpack 懒加载

组件库按需加载: 组件库以组件为基本单位产出 js、css、less 文件,借助插件或者部分引入的写法,使得项目代码或 babel 编译后的代码中只包含使用到的组件的 js、css、less 等。
组件库
webpack 懒加载: webpack 将源码中的 import、require 引入的文件编译之后再根据动态加载语法配置(通常以页面路由为基本单位)将较大的代码拆分并构建出较小的 chunk 包,应用在运行时执行到相应业务逻辑时才去加载执行对应 chunk 代码。 webpack 懒加载主要发生在下图的 JS 拆分出不同的 Chunk 这一过程中。
组件库
可见,两者的差别主要在于:

两者执行时机不同,组件库按需加载是在源码编写阶段或者 babel 编译 js 阶段,而 webpack 懒加载则是在构建生成打包产物时,组件库按需加载在前,webpack 懒加载在后;

两者原理不同,组件库按需加载是在源码阶段就去掉了无关代码,而 webpack 懒加载则是将经过 tree-shaking 优化过后的大文件包进行拆分在适当的运行时进行按需加载。

为何需要组件库按需加载

组件库按需加载主要目的就是为了减少项目构建打包产物的大小,提高项目线上首屏渲染速度,减少白屏时间,减少流量消耗。
一般组件库会提供一种引入全部组件和 css 文件的写法,例如:

1
2
3
4
import Vue from 'vue';
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);

这种写法经过 webpack 构建之后会将组件库产出的 vant.min.js、index.css 引入并打包至构建产物中,而引入的 vant.min.js 文件是包含组件库全部组件的 js 部分,index.css 包含全部组件的 css 部分。因此,这会导致构建打包产物增大。

组件库动态加载用法

Vant 官方文档中推荐使用如下两种方式让 Vant 组件库支持按需加载。

方式一:手动加载

手动引入需要使用到的组件以及其对应的样式文件即可,在 webpack 构件时组件库中其他未被引入的文件不会被打包。

1
2
import Button from 'vant/lib/button';
import 'vant/lib/button/style';
方式二:自动加载

安装 babel-plugin-import 插件

1
npm i babel-plugin-import -D

修改 babel 插件配置

1
2
3
4
5
6
7
8
9
module.exports = {
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
};

在项目代码中按需引入要用到的组件

1
2
import { Button } from 'vant';
Vue.use(Button);

组件库按需加载的本质

从上文中手动配置按需加载需要用到的组件中就可以看出,所谓的按需加载就如字面意思一样,指的就是按需引入需要的组件,用专业术语来讲就是:在代码中手动引入需要用到的组件。组件其实就是对一堆 js、css 以及 less 等文件的总称,所以上文中需要手动引入组件对应的样式文件 vant/lib/button/style 。
即,本质就是对源代码进行如下转换

1
import { Button } from 'vant';

转换为

1
2
import "vant/es/button/style";
import _Button from "vant/es/button";

可以想到,如果每次需要用到新的组件都像这样时都同时手动引入 js、css 或 less 文件岂不是很麻烦,所以为了免去引入写法的繁杂,产生了两种方案:引入全部组件和使用插件自动引入。使用插件自动引入就是插件帮我们把引入组件的写法进行了转换,最后转换成了上文中手动加载方式的写法。

babel-plugin-import 插件

上文已经介绍了使用插件自动按需加载的本质,下面开始进一步地分析该插件底层是如何实现的。

参考文章

组件库按需加载原理分析

Vue3中提到的Tree-shaking

按照作者的原话解释,Tree-shaking其实就是:把无用的模块进行“剪枝”,很多没有用到的API就不会打包到最后的包里
vue3一个比较大的显著的区别就是,当你用一个bundler的时候,比如webpack或者rollup,webpack和rollup都是有tree shaking功能,但是tree shaking的前提是所有的东西都必须用ES6 module的import来写

而vue3 在浏览器里的时候依然会由一个全局的Vue对象,但是当你用了一个bundler时(比如webpack),它就没有default export,你就不能import xxx from vue,然后把vue本身当一个对象去操作。那所有的这些API全部要用import的方式import进来,这样的结果就是使得一些可能不会用到的一些功能就可以被tree shaking掉。比如说 v-model、<transition>这些功能,如果你不用的话,就不会引用到最后的包里。

Tree-shaking某种程度上来讲,也是通过编译器去实现的(记住这句话)

之前让大家记住的一句话,为什么尤大说某种程度上来讲,Tree-shaking是通过编译器去实现的
其实说白了,Tree-shaking本质并不是Vue3的东西,而是那些打包工具的功能。只是Vue3代码结构调整,当用webpack等打包工具打包项目时,webpack会将那些没用用到的代码不打包到最后的项目中,这样使得项目体积更小
主要原理:依赖es6的模块化的语法,将无用的代码(dead-code)进行剔除!

看尤老师解释Vue3中提到的Tree-shaking