Element-UI中的按需加载用的就是babel-plugin-component这个插件,它的转换如下:

如果在.babelrc中配置了styleLibraryName:'theme_package',那么转换如下:

从上面我们可以大致了解到babel-plugin-component可以将一个import拆分成一个包含具体路径的import以及一个css(或者多个css)的import。那么我们深入源码看看babel-plugin-component的主要思路。

(这个需要了解什么是AST,如果还不清楚,可以先看看 babel插件开发入门)。

目录结构

首先需要下载babel-plugin-component这个插件,然后去node_module下找到这个插件:

其中的core.js就是AST实现的代码。

收集specifiers

core.js中,先通过ImportDeclaration将所有的specifiers收集起来:

例如import {Button} from 'antd',在AST中的结构如下:

我们可以发现节点中的specifiers代表的是from前面的部分,也就是Button(可能多个);source代表的是from后面的部分,这里是'antd'。

需要在ImportDeclaration的时候,得到source和specifiers,然后判断这个source是否是我们需要的(.babelrc中配置的libraryName),如果是的话就开始收集specifiers(这里就是Button)。

同时这里还有一个小细节:

这个表示如果不是全局引用的,就要删除该import,为啥要删除呢?因为如果不是全局引用的import,它将会变成引用具体路径的import(开头提到的),它的具体操作过程就是先删除旧的import,然后添加新的具体引用的import。

识别是否被调用

上面我们收集了specifiers(这里是Button),但是插件不会一股脑的去增加import,而是要先去看specifiers(这里是Button)是否被调用:

像上面的Vue.use(Button),我们可以知道Button是被使用了,如何在AST中识别呢?我们先看看Vue.use(Button)转成的AST:

我们可以看到Vue.vue(Button)在AST中属于CallExpression,那么就拦截CallExpression(code.js中):

CallExpression中先判断callee(方法调用者),我们这里的callee属于MemberExpression(属性成员表达式),需要看arguments是否属于specifiers,刚好Button属于参数,因此需要添加新的import(importMethod)。

CallExpression中的callee为isIdentifier的情况,如Button('ok')之类的,此时会检查方法名是否属于specifiers,而此时Button刚好作为方法名,因此添加新的import(importMethod)。

我们可以注意到CallExpression中只是识别了特定调用姿势,例如Hello(Button)这种调用是不会被识别为调用了,因为此时callee为isIdentifier,但是只会检查callee是否为specifiers(Hello不属于specifiers),并不会检查arguments是否为specifiers(此时的Button属于arguments)。

更多的识别姿势

1.识别Button['ok'],这种属于MemberExpression(属性成员表达式),先来看看AST结构:

code.js中如何识别MemberExpression这种AST:

我们可以看到识别的是object.name是否属于specifiers。

2.识别a=Button;这种属于AssignmentExpression(赋值表达式),看看AST结构:

code.js中识别AssignmentExpression代码:

可以看到识别的是赋值号右边的内容。

3.识别[Button],这种属于ArrayExpression,看看AST结构:

code.js中识别ArrayExpression:

可以发现这里是遍历elements,elements就是数组中的内容。

4.识别{a:Button},这种属于Property,看看AST结构

code.js中识别Property:

可以看到识别的是value部分。

5.识别let a=Button;这种属于VariableDeclarator,看看AST结构:

code.js中识别VariableDeclarator:

可以看到识别的是init部分。

6.识别Button&&1;这种属于LogicalExpression,看看AST结构:

code.js识别LogicalExpression:

识别的是left和right部分。

7.识别b==3?Button:'b',这种属于ConditionalExpression,看看AST结构:

code.js识别ConditionalExpression:

这是识别test,consequent,alternate三部分,但是注意这里的test包含left和right,无法直接获取到value,因此要将BUtton写在consequent,alternate才可以识别到。

8.识别if(Button==1){},这种属于IfStatement,看看AST结构:

code.js识别IfStatement:

这里识别的是test中的left和right。

生成新的import

调用importMethod,就会生成一个新的import和一个css的import:

这里有两个方法,一个是addDefault,这种是新增如import {Button} from 'antd/lib/Button'的import;addSideEffect是新增一个单独的import,无from,一般用于css,例如import 'antd/lib/style.css'。

方法内部更多的是关于配置,如果配置了styleLibraryName,就会多生成一个css。

其他文章

0
我要评论

评论

返回
×

我要评论

回复:

昵称:(昵称不超过20个字)

图片:

提交
还可以输入500个字