从element-ui按需引入去探索
element-ui的按需引入的配置:文档地址
npm install babel-plugin-component -D
{
"presets": [["es2015", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
三步下来就能方便的使用按需引入的功能了。
其中的原理是什么?babel-plugin-component在其中做了什么?
探究处理过程
首先新建一个demo,使用最简化的配置,demo地址。
demo中只用了四种钩子:
Program:第一个访问的节点,初始化数据。
ImportDeclaration:处理import import { Button, Select } from 'element-ui';
CallExpression:函数执行会访问到,处理Vue.component(Button.name, Button);
MemberExpression:处理对象访问,Select.name。
总结一下处理的过程:
第一步
在Program初始化specified等数据,在处理当前文件的过程中这些数据作为全局使用。
第二步
在 ImportDeclaration 里将收集import的变量,比如Button,Select等
import { Button, Select } from 'element-ui'
将变量存储到specified中,这个specified会作为后面处理AST的判断条件
specified[spec.local.name] = spec.imported.name
第三步
在CallExpression中,根据是否使用到Button等会在AST添加节点,这些节点会转换为下面的代码:
import button form "element-ui/lib/button"
添加节点这个环节使用到@babel/helper-module-imports中的helper方法addSideEffect,addDefault,简化了手动操作。
简单介绍一下helper-module-imports:文档链接
调用addSideEffect方法能够生成类似 import "source"的代码,适合添加css等资源。
调用addDefault方法能够生成类似import _default from "source"的代码,适合添加js。
上面三步之后,想要的AST就构建完成了。以demo为例,源代码:
import { Button } from 'element-ui';
Vue.component(Button.name,Button)
执行npm run build ,babel处理之后的代码是:
var _button = _interopRequireDefault(require("element-ui/lib/theme-chalk/button.css"));
require("element-ui/lib/theme-chalk/base.css");
var _button2 = _interopRequireDefault(require("element-ui/lib/button"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
Vue.component(_button2["default"].name, _Button);
可以看到自动引入了css require("element-ui/lib/theme-chalk/base.css"),引入element-ui不见了,增加了require("element-ui/lib/button")
需要解释一下,上面的import变成了require是因为babel中presets-env的影响;同理_interopRequireDefault也是。
如果在babel.config.json设置modules:false结果将是下面的样子:
import _Button2 from "element-ui/lib/theme-chalk/button.css";
import "element-ui/lib/theme-chalk/base.css";
import _Button from "element-ui/lib/button";
Vue.component(_Button.name, _Button);
// 看起来顺眼多了
版本问题
在自己检查代码时发现第一个demo的结果Vue.component(_button2["default"].name, _Button);中的_Button是一个错误,代码中没有这个引用,执行起来肯定是要报错的;仔细查看了plugin.js并没有发现问题。当换成直接引入babel-plugin-component的时候就没有了问题,通过对比终于发现@babel/helper-module-imports的版本不同,
- babel-plugin-component 内部node_modules中依赖的 @babel/helper-module-imports 版本7.0.0
- 跟随helper-module-transforms一起安装的是7.10.4
切换到版本7.0.0就可以了。
解决方案 一
版本问题能够通过修改plugin.js来解决么?看下面的代码:
function importMethod(methodName, file, opts) {
if (!selectedMethods[methodName]) {
....
selectedMethods[methodName] = addDefault(file.path, path, { nameHint: methodName });
....
}
// ....
return selectedMethods[methodName];
}
在对Vue.component(Button.name, Button)的访问中需要对参数Button做两次处理,都需要执行到importMethod方法,methodName的值就是"Button",按照执行逻辑两次执行返回的是同一的对象:
{
type:"Identifier",
name:"_Button"
}
生成代码的时候应该是 Vue.component(_button2["default"].name, _button2["default"]),这里却好像把第二个_Button给忘了,猜测难道此处的引用传值导致的么?
考虑到通过一个简单的对象能生成_button2["default"],说明自己也可以创建一个对象生成对应的代码,于是就简单的deepClone一下selectedMethods[methodName],试过之后果然可以,此处并没有查找到真正的原因,只作为探索,代码如下:
function importMethod(methodName, file, opts) {
if (!selectedMethods[methodName]) {
....
selectedMethods[methodName] = addDefault(file.path, path, { nameHint: methodName });
....
}
// ....
// 此处的t是types,带有一个cloneDeep的方法
return t.cloneDeep(selectedMethods[methodName]);
}
解决方案二:
其实在打断点的时候发现,最终生成生成的AST是正确的,错在代码生成的阶段,经过尝试发现直接把modules:false就可以避免问题。一般来说我们都要把babel的模块处理取消掉,由webpack来处理模块打包,所以这个方案更加合适。
结束
查看有哪些钩子 :地址
babel中插件的执行顺序:插件执行顺序:
本文只介绍了四个钩子,原插件还使用了IfStatement,ConditionalExpression,LogicalExpression,VariableDeclarator,Property,ArrayExpression,AssignmentExpression七个钩子,这几个钩子主要是处理特殊的情况,暂时还未遇到。
最后如有错误之处,望指正
从element-ui按需引入去探索的更多相关文章
- 在Vue项目中使用Element UI:按需引入和完整引入
下面操作在main.js文件中进行 完整引入: import Element from 'element-ui'; //样式文件,需单独引入 import 'element-ui/lib/theme- ...
- 针对elementUI 中InfiniteScroll按需引入的一点注意事项
大家为了节省空间,常常进行按需引入来节省空间,这里我给大家来介绍一下element中按需引入无限滚动指令注意的事项. 针对前面element 按需引入的一些配置这里就不再详细介绍了. 那么这里讲的是在 ...
- vue Cli 按需引入Element UI 和全局引用Element UI
全局引用: 一.安装 Element UI npm i element-ui -S 二.在main.js 中引入 element UI import ElementUI from 'element-u ...
- vue-cli按需引入Element UI组件
一.环境 使用vue-cli搭建的环境 二.安装 babel-plugin-component npm install babel-plugin-component -D 三.修改.babelrc文件 ...
- vue按需引入Element UI的方法
在我们的实际项目开发中,多数是采用按需引入的模式来进行开发的,那么具体应该如何操作呢,可能会有许多新人傻傻分不清楚,具体将在下文讲到. 首先我们先vue create demo创建一个项目出来此时我们 ...
- Element UI——本地引入iconfont不显示
前言 前面因为本地引入Element UI导致了iconfont不显示,所以只好再去Element UI官网去扒下iconfot 步骤 进入官网 组件 | Element UI F12进入控制台,找到 ...
- vue-cli脚手架引入element UI的正确打开方式
element UI官网教程:http://element-cn.eleme.io/#/zh-CN/component/quickstart 1.完整引入,直接了当,但是组件文件不是按需加载,造成多余 ...
- Html | Vue | Element UI——引入使用
前言 做个项目,需要一个效果刚好Element UI有,就想配合Vue和Element UI,放在tp5.1下使用,但是引入在线的地址各种报错,本地引入就完美的解决了问题! 代码 __STATIC_J ...
- UI组件--element-ui--全部引入和按需引入
主要就是一句话, 如果用到的组件少, 要按需引入, 如果用到的组件很多,就全部引入, 因为按需引入全部的, 和全部引入效果一样(我这是废话, 大家都知道...) 完整引入 在 main.js 中写入以 ...
随机推荐
- dart快速入门教程 (7.4)
7.12.多态 多态字面上理解就是多种状态,通俗的说,多态表现为父类定义一个方法不去实现,子类继承这个方法后实现父类的方法,这个方法有多种表现 // import 'person.dart'; voi ...
- DOM-BOM-EVENT(5)
5.宽.高.位置相关 5.1.clientX/clientY clientX和clientY表示鼠标在浏览器可视区的坐标位置 <script> document.onclick = fun ...
- 洛谷 P1215 【[USACO1.4]母亲的牛奶 Mother's Milk】
这道题\(DFS\)就好了,六种情况,\(ab,ac,ba,bc,ca,cb\),我们直接枚举就可.什么?这样不会结束?用一个\(vis\)数组判断走过没有就可以了.最后排序输出即可. \(code: ...
- python基础知识练习2
1.请写出如下代码的结果: import copy list1 = [1,2,['a','b'],('c','d')] list2 = list1 list3 = copy.copy(list1) l ...
- Web前端开发未来的六大趋势
说起Web前端开发想必你一定不会陌生,因为现在的前端开发学习的培训机构也是层出不穷.下面济南优就业IT培训给大家总结出了未来Web前端开发的六大趋势从中可以大致看出来Web前端未来的发展前景. 趋势一 ...
- Tomcat启动流程简析
Tomcat是一款我们平时开发过程中最常用到的Servlet容器.本系列博客会记录Tomcat的整体架构.主要组件.IO线程模型.请求在Tomcat内部的流转过程以及一些Tomcat调优的相关知识. ...
- 5年前端经验小伙伴教你纯css3实现饼状图
有一些网页中,有时候会碰到饼状图的需求,比如统计图表,进度指示器,定时器等,实现方式也是各种各样,现在也有不少现成的js库,可以直接拿来使用,方便很多.这里笔者为大家演示一种纯css实现饼状图效果的方 ...
- 内嵌iframe页面在IOS下会受内部元素影响自动撑开的问题
IOS下的webview页面,内嵌iframe元素,将其样式指定为宽高100%: .iframe { width: %; height: %; } 在安卓下运行均无问题,但是在IOS下会出现异常. 具 ...
- vx小程序(1)
一.程序配置 app.json 1. pages字段——用于描述当前小程序的页面路径. 2.window字段——定义小程序所有页面的顶部背景颜色,文字颜色等. 注意:可以在pages/logs目录下的 ...
- 弹性碰撞问题:Ants+Linear world
题目一:Ants 传送门 题目描述 输入 输出 样例 样例输入 样例输出 分析 一句话题意:有n只蚂蚁在木棍上爬行,每只蚂蚁的速度都是每秒1单位长度,现在给你所有蚂蚁初始的位置(蚂蚁运动方向未定),蚂 ...