使用babel-plugin-import 实现自动按需引入方式

 更新时间:2022年12月01日 10:13:36   作者:swx980  
这篇文章主要介绍了使用babel-plugin-import 实现自动按需引入方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

babel-plugin-import 实现自动按需引入

Vant 支持一次性导入所有组件,引入所有组件会增加代码包体积,因此不推荐这种做法

babel-plugin-import 是一款 babel 插件,它会在编译过程中将 import 的写法自动转换为按需引入的方式。

1、下载

npm i babel-plugin-import -D

2、

(1)在.babelrc 中添加配置

注意:webpack 1 无需设置 libraryDirectory

{
  "plugins": [
    ["import", {
      "libraryName": "vant",
      "libraryDirectory": "es",
      "style": true
    }]
  ]
}

(2)对于使用 babel7 的用户,可以在 babel.config.js 中配置

module.exports = {
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
};

3、接着你可以在代码中直接引入 Vant 组件,插件会自动将代码转化为方式二中的按需引入形式

import { Button } from 'vant';

babel-plugin-import 的组件按需加载原理

对比webpack懒加载

webpack 懒加载是将源码中的 import、require 引入的文件编译之后再根据动态加载语法配置(通常以页面路由为基本单位)将较大的代码拆分并构建出较小的 chunk 包,运行时执行到相应业务逻辑时才去加载执行对应 chunk 代码。

webpack 懒加载主要发生在 JS 拆分出不同的 Chunk 这一过程中。

babel-plugin-import 按需加载是以组件为基本单位产出 js、css、less 文件,借助插件或者部分引入的写法,使得项目代码或 babel 编译后的代码中只包含使用到的组件的 js、css、less 等。

首先是执行时机不同,babel-plugin-import 按需加载是在源码编写阶段或者 babel 编译 js 阶段,而 webpack 懒加载则是在构建生成打包产物阶段。

其次是原理不同,babel-plugin-import 按需加载是在源码阶段就去掉了无关代码,而 webpack 懒加载则是将经过 tree-shaking 优化过后的大文件包进行拆分在适当的运行时进行按需加载。两者并不冲突,可以一前一后共同作用。

实现原理

babel-plugin-import 按需加载目的是减少项目构建打包产物的大小,提高项目线上首屏渲染速度,减少白屏时间,减少流量消耗

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

import Button from 'lib/button';
import 'lib/lib/button/style';

若是自动引入:

npm i babel-plugin-import -D
 
module.exports = {
  plugins: [
    ['import', {
      libraryName,
      libraryDirectory: 'es',
      style: true
    }, libraryName]
  ]
};
 
import { Button } from libraryName;

组件其实就是对一堆 js、css 以及 less 等文件的总称,自动引入的本质是将引入组件的写法通过插件来转换成手动引入组件对应的代码以及样式文件的写法。核心原理依然是对源码的 import 导入写法进行转换——词法语法分析,AST转换,代码生成。

该插件主要参数:

 "libraryName": "", // 组件库名称,对应 import 语法中的包名
  "libraryDirectory": "lib", // 编译之后各个组件单元所在文件夹名称
  "style": true, // 是否引入组件对应样式文件,也可以传入 less 来引入 less 文件
  "styleLibraryDirectory": "", // 编译之后引入的组件样式文件所在文件夹名称
  "camel2DashComponentName": false, // 是否将驼峰命名的导入变量转换为对应的横线连接命名的文件名
  "customName": (name, file) => { return `/lib/${name}` }, // 自定义编译之后引入的组件名
  "customStyleName": (name, file) => { return `/lib/css/${name}` }, // 自定义编译之后引入样式文件的名称

插件中使用到的钩子函数有:

const methods = [
  'ImportDeclaration', 				// import 导入声明
  'CallExpression',				    // 函数调用
  'MemberExpression',
  'Property',
  'VariableDeclarator',
  'ArrayExpression',
  'LogicalExpression',
  'ConditionalExpression',
  'IfStatement',
  'ExpressionStatement',
  'ReturnStatement',
  'ExportDefaultDeclaration',
  'BinaryExpression',
  'NewExpression',
  'ClassDeclaration',
  'SwitchStatement',
  'SwitchCase',
];

 Visitor 对象上还配置了 Program 钩子,该钩子是在 babel 处理一个独立文件(或者叫做模块更合适,node 规范定义一个文件就是一个模块)时执行,其中若不按此方式具体指定则默认为 enter 钩子。

const Program = {
  // 进入钩子
  enter(path, options) {
  	// 1. 根据插件接受到的配置参数初始化插件 Plugin 数组
    // 2. 遍历插件 Plugin 数组,依次执行各个插件的初始化方法 ProgramEnter
  },
  // 退出钩子
  exit() {
  	// ...
  }
}

转换 import 语法需要识别 ES6 模块规范的默认导入、部分导入以及整体导入等语法,主要逻辑包括鉴别是否是部分导入,只有部分导入才表示导入具体组件,转换导入变量名等。

// 1. 部分导入
import { Button } from '';
console.log(Button);
 
// 2. 默认导入
import default from '';
console.log(default.Button);
 
// 3. 全部导入
import * as D from '';
console.log(D.Button);

整体处理逻辑如下:

  • ImportDeclaration 钩子中将部分导入、默认导入和整体导入的语句记录到插件全局状态对象上,同时将节点的 path 对象记录至插件全局状态对象上;
  • 插件全局状态对象上存储的 path 对象会在 Program 退出时遍历执行 remove 方法,从而移除了所有原始的导入语句;
  • 在 MemberExpression、CallExpression、buildExpressionHandler、buildDeclaratorHandler等钩子函数中执行 importMethod 函数;
  • importMethod 函数会根据插件的配置参数计算出真实文件导入路径、是否导入样式文件、样式文件名、是否转换默认导入等配置,从而使用 @babel/helper-module-imports 提供的 addSideEffect 方法添加对应的部分导入语句。
  • 重命名导入模块的变量描述符 Identifier。

以钩子函数为入口,根据不同的节点类型取找到不同节点与变量相关的属性;

校验变量的 name 是否存在于插件全局状态的 specfied 中,即变量是否是导入组件指向的变量;

通过 path.scope.hasBinding、path.scope.getBinding 排除掉其他作用域的变量;

借助 importMethod 方法计算转换后模块对应的变量名然后修改节点对应的变量名。

变量描述符 Identifier可能指向的钩子有:

Property
VariableDeclarator
ArrayExpression
LogicalExpression
ConditionalExpression
IfStatement
ExpressionStatement
ReturnStatement
ExportDefaultDeclaration
BinaryExpression
NewExpression
SwitchStatement
SwitchCase
ClassDeclaration

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • vue指令以及dom操作详解

    vue指令以及dom操作详解

    本篇文章主要介绍了vue指令以及dom操作详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • Vue3中内置组件Teleport的基本使用与典型案例

    Vue3中内置组件Teleport的基本使用与典型案例

    Teleport是一种能够将我们的模板移动到DOM中Vue app之外的其他位置的技术,下面这篇文章主要给大家介绍了关于Vue3中内置组件Teleport的基本使用与典型案例的相关资料,需要的朋友可以参考下
    2023-04-04
  • vue管理系统项目中的一些核心技能汇总

    vue管理系统项目中的一些核心技能汇总

    Vue是当今增长最快的前端框架,Vue 平易近人、用途广泛且性能卓越,它的语法非常直观,并且具有友好的学习曲线,是开发人员最想学习的顶级前端库之一,下面这篇文章主要给大家介绍了关于vue管理系统项目中的一些核心技能,需要的朋友可以参考下
    2022-05-05
  • 解析Vue.use()是干什么的

    解析Vue.use()是干什么的

    今天通过本文给大家分享Vue.use是什么,主要包括vueEsign 插件的install是什么,element-ui的install是什么,为什么有的库就不需要使用Vue.use,对vue.use()相关知识感兴趣的朋友一起看看吧
    2022-06-06
  • 使用keep-alive时,数据无法刷新的问题及解决

    使用keep-alive时,数据无法刷新的问题及解决

    这篇文章主要介绍了使用keep-alive时,数据无法刷新的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • vue动态生成dom并且自动绑定事件

    vue动态生成dom并且自动绑定事件

    本篇文章主要介绍了vue动态生成dom并且自动绑定事件,具有一定的参考价值,有兴趣的可以了解一下。
    2017-04-04
  • 利用Vue实现移动端图片轮播组件的方法实例

    利用Vue实现移动端图片轮播组件的方法实例

    轮播图是前端很常用的一个网页特效,几乎所有的网站或多或少都会用到这个特效。下面这篇文章主要给大家介绍了关于利用Vue实现移动端图片轮播组件的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下。
    2017-08-08
  • Vue调用后端java接口的实例代码

    Vue调用后端java接口的实例代码

    今天小编就为大家分享一篇Vue调用后端java接口的实例代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-10-10
  • Vuex实现数据共享的方法

    Vuex实现数据共享的方法

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。这篇文章主要介绍了Vuex实现数据共享的方法,需要的朋友可以参考下
    2019-12-12
  • webpack+vue-cli项目中引入外部非模块格式js的方法

    webpack+vue-cli项目中引入外部非模块格式js的方法

    今天小编就为大家分享一篇webpack+vue-cli项目中引入外部非模块格式js的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-09-09

最新评论