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按需引入去探索的更多相关文章

  1. 在Vue项目中使用Element UI:按需引入和完整引入

    下面操作在main.js文件中进行 完整引入: import Element from 'element-ui'; //样式文件,需单独引入 import 'element-ui/lib/theme- ...

  2. 针对elementUI 中InfiniteScroll按需引入的一点注意事项

    大家为了节省空间,常常进行按需引入来节省空间,这里我给大家来介绍一下element中按需引入无限滚动指令注意的事项. 针对前面element 按需引入的一些配置这里就不再详细介绍了. 那么这里讲的是在 ...

  3. vue Cli 按需引入Element UI 和全局引用Element UI

    全局引用: 一.安装 Element UI npm i element-ui -S 二.在main.js 中引入 element UI import ElementUI from 'element-u ...

  4. vue-cli按需引入Element UI组件

    一.环境 使用vue-cli搭建的环境 二.安装 babel-plugin-component npm install babel-plugin-component -D 三.修改.babelrc文件 ...

  5. vue按需引入Element UI的方法

    在我们的实际项目开发中,多数是采用按需引入的模式来进行开发的,那么具体应该如何操作呢,可能会有许多新人傻傻分不清楚,具体将在下文讲到. 首先我们先vue create demo创建一个项目出来此时我们 ...

  6. Element UI——本地引入iconfont不显示

    前言 前面因为本地引入Element UI导致了iconfont不显示,所以只好再去Element UI官网去扒下iconfot 步骤 进入官网 组件 | Element UI F12进入控制台,找到 ...

  7. vue-cli脚手架引入element UI的正确打开方式

    element UI官网教程:http://element-cn.eleme.io/#/zh-CN/component/quickstart 1.完整引入,直接了当,但是组件文件不是按需加载,造成多余 ...

  8. Html | Vue | Element UI——引入使用

    前言 做个项目,需要一个效果刚好Element UI有,就想配合Vue和Element UI,放在tp5.1下使用,但是引入在线的地址各种报错,本地引入就完美的解决了问题! 代码 __STATIC_J ...

  9. UI组件--element-ui--全部引入和按需引入

    主要就是一句话, 如果用到的组件少, 要按需引入, 如果用到的组件很多,就全部引入, 因为按需引入全部的, 和全部引入效果一样(我这是废话, 大家都知道...) 完整引入 在 main.js 中写入以 ...

随机推荐

  1. vue全家桶(3.2)

    4.5.创建实例 可以创建一个自定义实例应用于多个请求接口 <template> <div class="page"> </div> </ ...

  2. RS232/485通信方式 保存和加载时数据的处理

    RS232/485通信方式 数据以RS232/485方式通信时,以0xA5作为开始码,以0xAE作为结束码.在开始码和结束码之间的0xA5, 0xAA, 0xAE数据需要进行转码. PC端发送数据时将 ...

  3. cp5200的一般步骤

    cp5200的一般步骤: 1.创建数据对象 hObj = CP5200_CommData_Create(nCommType, id, GetIDCode()); 2.生成所需要的数据,如 :生成设置亮 ...

  4. max depth exceeded when dereferencing c0-param0问题的解决

    在做项目的时候,用到了dwr,有一次居然报错,错误是 max depth exceeded when dereferencing c0-param0 上网查了一下,我居然传参数的时候传的是object ...

  5. 宿主机ping不通虚拟机,虚拟机能ping通宿主机问题

    打开虚拟机管理器,点开设置=>网络,网络选的是NAT,所以宿主机不能直接ping能虚拟机!!! 问题描述 查看虚拟机ip,  #ifconfig如下图: 宿主机ping虚拟机ip,无法通信,如下 ...

  6. Linux中快捷生成自签名ssl证书_113资讯网

    一.生成CA私钥 mkdir ca cd ca #创建私钥 (建议设置密码) openssl genrsa -des3 -out myCA.key 2048 生成CA证书 # 20 年有效期 open ...

  7. vim常用指令参考

    (完)

  8. Oracle中truncate表不更新last_ddl_time列

    Oracle中truncate表不更新last_ddl_time列 问题描述 最近发现数据库中定时job的某张表,每天都有truncate动作,由于调整了job的interval时间,想查看last_ ...

  9. Oracle Solaris 10下gdb安装(附安装包)

    文章目录 1. 背景说明 2. gdb相关包 3. gdb安装 3.1 上传资源 3.2 解压 3.3 安装 3.4 环境变量 4. 位数确认 5. 验证可用性 1. 背景说明 本文承接Oracle ...

  10. 每日一题 - 剑指 Offer 49. 丑数

    题目信息 时间: 2019-07-03 题目链接:Leetcode tag:动态规划 小根堆 难易程度:中等 题目描述: 我们把只包含质因子 2.3 和 5 的数称作丑数(Ugly Number).求 ...