实现element按需加载

1,253 阅读3分钟

原理:

  • 使用babel-plugin-component实现按需引入、打包。
  • 将webpack配置成多入口,保证最终打包的目录结构符合babel-plugin-component插件的要求,实现按需加载。

按需加载的意识:

实际bable处理时直接删除当前代码,然后在对AST抽象语法树遍历时,将对引用的方法、组件进行引用处理。

babel

babel简介

Babel 是一个通用的多功能 JavaScript 编译器,但与一般编译器不同的是它只是把同种语言的高版本规则转换为低版本规则,而不是输出另一种低级机器可识别的代码,并且在依赖不同的拓展插件下可用于不同形式的静态分析。(静态分析:指在不需要执行代码的前提下对代码进行分析以及相应处理的一个过程,主要应用于语法检查、编译、代码高亮、代码转换、优化、压缩等等)

babel 转译的三个阶段
  • 解析 Parse

将代码解析生成抽象语法树( 即AST ),也就是计算机理解我们代码的方式。而 babel 则是通过 babylon 实现的 。简单来说就是一个对于 JS 代码的一个编译过程,进行了词法分析与语法分析的过程。

  • 转换 Transform

对于 AST 进行变换一系列的操作,babel 接受得到 AST 并通过 babel-traverse 对其进行遍历,在此过程中进行添加、更新及移除等操作。

  • 生成 Generate

将变换后的 AST 再转换为 JS 代码, 使用到的模块是 babel-generator。

⚠️:babel 只是转译新标准引入的语法,比如ES6箭头函数:而新标准引入的新的原生对象,部分原生对象新增的原型方法,新增的 API 等(Proxy、Set 等), 这些事不会转译的,需要引入对应的 polyfill 来解决。

babel-plugin-component.js

主要功能就是在bable遍历AST语法树时针对各个节点进行拦截处理。 其使用的AST节点也有很多,但是关键的节点其实是 ImportDeclaration 、CallExpression、 MemberExpression、 AssignmentExpression。

ImportDeclarationimport声明表达式,对引入的包进行判断将element-ui引入的成员变量进行缓存处理,然后将此节点删除。例如:

import { Button } from 'element-ui';

CallExpression函数调用表达式,在函数或者方法调用时,对函数名或者参数名进行拦截处理,当函数名或者参数名是import时缓存的成员变量时,则调用importMethod方法替换函数的AST或者参数的AST。例如下面代码中的Button将被替换:

Vue.use(Button)

MemberExpression成员变量表达式,在对象成员变量表达式中拦截是否是缓存的element-ui的包名,如果是则调用importMethod替换AST,例如下面的service将被替换:

Vue.prototype.$loading = Loading.service

AssignmentExpression赋值表达式节点,如上面的Vue.prototype.$loading = Loading.service既有MemberExpression节点也有AssignmentExpression节点,会拦截赋值的字符,对其进行AST替换。

importMethod 生成element-ui/lib/xxx.js路径找到该模块的单独文件,然后使用addDefault方法替换AST,针对css文件则先生成element-ui/lib/theme-chalk/xxx.css,然后使用addSideEffect方法进行替换AST。这边替换的路径就是多入口文件打包生成的单个组件的路径。

最终完成对AST的完整修改。即按需加载指定的组件或者方法。