使用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

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

相关文章

  • Javascript vue.js表格分页,ajax异步加载数据

    Javascript vue.js表格分页,ajax异步加载数据

    这篇文章主要介绍了Javascript vue.js表格分页,ajax异步加载数据的相关资料,需要的朋友可以参考下
    2016-10-10
  • vue弹出框组件封装实例代码

    vue弹出框组件封装实例代码

    这篇文章主要介绍了vue弹出框组件封装,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-10-10
  • 使用idea创建第一个Vue项目

    使用idea创建第一个Vue项目

    最近在学习vue,本文主要介绍了使用idea创建第一个Vue项目,文中根据图文介绍的十分详尽,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • 一篇文章告诉你Vue3指令是如何实现的

    一篇文章告诉你Vue3指令是如何实现的

    在计算机技术中,指令是由指令集架构定义的单个的CPU操作,在更广泛的意义上,“指令”可以是任何可执行程序的元素的表述,例如字节码,下面这篇文章主要给大家介绍了关于Vue3指令是如何实现的相关资料,需要的朋友可以参考下
    2022-01-01
  • vue2中vue-router引入使用详解

    vue2中vue-router引入使用详解

    Vue Router 是 Vue 的官方路由,它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举,下面就跟随小编一起学习一下vue-router的具体用法吧
    2023-12-12
  • vue静态配置文件不进行编译的处理过程(在public中引入js)

    vue静态配置文件不进行编译的处理过程(在public中引入js)

    这篇文章主要介绍了vue静态配置文件不进行编译的处理过程(在public中引入js),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • Vue3中Watch、Watcheffect、Computed的使用和区别解析

    Vue3中Watch、Watcheffect、Computed的使用和区别解析

    Watch、Watcheffect、Computed各有优劣,选择使用哪种方法取决于应用场景和需求,watch 适合副作用操作,watchEffect适合简单的自动副作用管理,computed 适合声明式的派生状态计算,本文通过场景分析Vue3中Watch、Watcheffect、Computed的使用和区别,感兴趣的朋友一起看看吧
    2024-07-07
  • vue.js,ajax渲染页面的实例

    vue.js,ajax渲染页面的实例

    下面小编就为大家分享一篇vue.js,ajax渲染页面的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-02-02
  • vite配置别名并处理报错:找不到模块“xxx”或其相应的类型声明方法详解

    vite配置别名并处理报错:找不到模块“xxx”或其相应的类型声明方法详解

    我在学习vue3+vite+ts的时候,在配置别名这一步的时候遇到了一个问题,这篇文章主要给大家介绍了关于vite配置别名并处理报错:找不到模块“xxx”或其相应的类型声明的相关资料,需要的朋友可以参考下
    2022-11-11
  • vue菜单栏联动内容页面tab的实现示例

    vue菜单栏联动内容页面tab的实现示例

    本文主要介绍了vue菜单栏联动内容页面tab的实现示例,左侧菜单栏与右侧内容部分联动,当点击左侧的菜单,右侧会展示对应的tab,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01

最新评论