动态引入DynamicImport实现原理

目录
  • 什么是动态引入(DynamicImport)?
  • 动态引入的实现原理
  • 动态引入的限制
    • Import 路径须为相对路径
    • 引用路径需包含文件后缀
    • 导入当前目录的文件需要指定具体的文件匹配格式
    • 通配符(Glob Pattern)仅有一层深度
  • 核心流程解读

什么是动态引入(DynamicImport)?

本文介绍的动态引入实现方式基于 rollup 插件 @rollup/plugin-dynamic-import-vars

通常情况下,我们都是通过确定的字面量路径来引用文件模块的,例如:

import './a.js';
require('./a.js');
import('./a.js');

对于确定的文件路径来说,构建工具可以轻易的抓取文件并进行相关的转换。

但当import或者require的目标不是一个静态字符串,而是一个动态表达式时,构建工具其实也不确定用户到底引用了什么,所以通常这种情况只能依靠 JavaScript 的运行时来解析。

若动态表达式实际代表的路径无法被解析,则运行时会引起控制台的错误。通常是因为生成的文件路径并没有被纳入打包体系,所以找不到文件。

下面列出了一些常见的动态引入表达式:

// TemplateLiteral 模板字符串
import(`./icons/arrow-${type}.svg`);
require(`./icons/arrow-${type}.svg`);
// BinaryExpression 二元表达式
import('./icon/arrow-' + type + '.svg');
// 直接引用一个变量
import(path);
require(path)

但经过前人们的实践发现,当动态表达式满足一定的结构时,构建工具便可以通过一些特殊手段抓取并打包路径匹配的相关文件,并自动注入一些 polyfill,从而实现动态引入(DynamicImport)的效果,也就是本文的主题。

动态引入的实现原理

本节内容翻译加工自 @rollup/plugin-dynamic-import-vars README.md 部分章节

当动态导入的路径中包含变量时,经过 AST 分析可以生成对应的通配符。在构建的时候,这些通配符将被用于抓取匹配的文件。随后这些文件会被添加进构建体系中,在运行时,根据导入的实际路径返回对应的文件内容。

下面是一些通配符的转换示例:

`./locales/${locale}.js` -> './locales/*.js'
`./${folder}/${name}.js` -> './*/*.js'
`./module-${name}.js` -> './module-*.js'
`./modules-${name}/index.js` -> './modules-*/index.js'
'./locales/' + locale + '.js' -> './locales/*.js'
'./locales/' + locale + foo + bar '.js' -> './locales/*.js'
'./locales/' + `${locale}.js` -> './locales/*.js'
'./locales/' + `${foo + bar}.js` -> './locales/*.js'
'./locales/'.concat(locale, '.js') -> './locales/*.js'
'./'.concat(folder, '/').concat(name, '.js') -> './*/*.js'

待转换的代码可能是这样的:

function importLocale(locale) {
  return import(`./locales/${locale}.js`);
}

经过转换后它会变成下面这样:

function __variableDynamicImportRuntime__(path) {
  switch (path) {
    case './locales/en-GB.js':
      return import('./locales/en-GB.js');
    case './locales/en-US.js':
      return import('./locales/en-US.js');
    case './locales/nl-NL.js':
      return import('./locales/nl-NL.js');
    default:
      return new Promise(function (resolve, reject) {
        queueMicrotask(reject.bind(null, new Error('Unknown variable dynamic import: ' + path)));
      });
  }
}
function importLocale(locale) {
  return __variableDynamicImportRuntime__(`./locales/${locale}.js`);
}

可以看到,实际的 import 被替换成了注入的 __variableDynamicImportRuntime__ 函数,该函数会根据运行时拼接的具体字符串返回对应的打包文件。

动态引入的限制

本节内容翻译加工自 @rollup/plugin-dynamic-import-vars README.md 部分章节

为了知道要在代码中注入什么,我们必须能够对代码进行一些静态分析,并对可能的导入做出一些假设。例如,如果只使用一个变量,理论上可以从整个文件系统中导入任何内容。

function importModule(path) {
  return import(path); // 这根本无法推断引入了什么
}

为了能够实现静态分析,并避免可能出现的问题,动态引入的实现上限定了一些规则:

Import 路径须为相对路径

所有导入都必须相对于导入文件进行。导入不应该是纯变量、绝对路径或裸导入:

// Not allowed
import(bar); // 纯变量
import(`/foo/${bar}.js`); // 绝对路径
import(`${bar}.js`); // 裸导入
import(`some-library/${bar}.js`); // 裸导入

引用路径需包含文件后缀

文件夹中可能包含你不打算导入的文件。因此,我们要求导入的静态部分以文件扩展名结束

import(`./foo/${bar}`); // Not allowed
import(`./foo/${bar}.js`); // Allowed

导入当前目录的文件需要指定具体的文件匹配格式

如果你从当前目录导入文件,很可能会导入一些原本不打算导入的文件,包括书写代码的这个文件本身。因此这种情况下需要给出一个更具体的文件名匹配格式:

import(`./${foo}.js`); // not allowed
import(`./module-${foo}.js`); // allowed

通配符(Glob Pattern)仅有一层深度

在生成通配符时,字符串中的每个变量都会被转换为通配符中的*,每个层级的目录最多一个星号。这避免了无意中从更多的目录中添加文件到导入中。

下面的例子中,最终将会生成 ./foo/*/.js 而非 ./foo/**/.js

import(`./foo/${x}${y}/${z}.js`);

核心流程解读

插件核心转换代码仅有 100 行,且非常易懂 —— plugins/index.js at master · rollup/plugins

整体流程分为以下几步:

通过 AST 分析,拿到对应的导入路径,也就是 import 表达式括号中的源码部分。

对这部分的源码进行处理,调用 dynamicImportToGlob 函数

执行上述限制条件的判断,尝试获取一个合法的通配符。

如果通配符不合法,将会引发错误,终止进程。

执行通配符,抓取相关文件。

替换 import 表达式,并注入 __variableDynamicImportRuntime__ 函数。

附上插件核心转换代码的截图,代码本身不长且非常容易理解,感兴趣的同学可以自行跳转研究。

以上就是动态引入DynamicImport实现原理的详细内容,更多关于动态引入DynamicImport的资料请关注我们其它相关文章!

(0)

相关推荐

  • Vue3+TypeScript+Vite使用require动态引入图片等静态资源

    问题:Vue3+TypeScript+Vite的项目中如何使用require动态引入类似于图片等静态资源! 描述:今天在开发项目时(项目框架为Vue3+TypeScript+Vite)需要 动态引入静态资源,也就是img标签的src属性值为动态获取,按照以往的做法直接是require引入即可,如下代码: <img class="demo" :src="require(`../../../assets/image/${item.img}`)" /> 写上后

  • Vue3使用src动态引入本地图片的详细步骤

    目录 1. vue-cli搭建的项目 2.vite搭建的项目动态引入本地图片 补充:vue3加载动态图片 总结 1. vue-cli搭建的项目 在项目中我们想要动态引入本地图片的时候,(注意这是在cli搭建的,vite里面没有require(),vite里面需要封装个工具) 通过v-bind动态绑定 通过的require引入 require作用 用于引入模板.JSON.或本地文件 下面这种require直接包裹全部路径是可以的,但是我在想感觉不太优雅 想直接在src里用require(item.

  • Vue中动态引入图片要是require的原因解析

    目录 1.什么是静态资源 2. 为什么动态添加的src会被当做的静态的资源? 3. 没有进行编译,是指的是什么没有被编译? 4. 加上require为什么能正确的引入资源,是因为加上require就能编译了? 4.1 require是什么: 是一个node方法,用于引入模块,JSON或本地文件 4.2 调用require方法引入一张图片之后发生了什么: 5. 问题3中,静态的引入一张图片,没有使用require,为什么返回的依然是编译过后的文件地址? 6. 按照问题6中所说,那么动态添加src的

  • VUE3+vite项目中动态引入组件与异步组件的详细实例

    目录 一.全量注册,随用随取 1. 把项目中所有vue文件注册成异步组件. 2. 获取组件 3. 参考如下 二.使用@rollup/plugin-dynamic-import-vars插件 1.介绍 2.安装 3.使用 4.How it works 总结 一.全量注册,随用随取 1. 把项目中所有vue文件注册成异步组件. const app = createApp(App); function registerGlobalAsyncComponents(app: VueApp) { const

  • 动态引入DynamicImport实现原理

    目录 什么是动态引入(DynamicImport)? 动态引入的实现原理 动态引入的限制 Import 路径须为相对路径 引用路径需包含文件后缀 导入当前目录的文件需要指定具体的文件匹配格式 通配符(Glob Pattern)仅有一层深度 核心流程解读 什么是动态引入(DynamicImport)? 本文介绍的动态引入实现方式基于 rollup 插件 @rollup/plugin-dynamic-import-vars 通常情况下,我们都是通过确定的字面量路径来引用文件模块的,例如: impor

  • js动态引入的四种方法

    本文表述了关于js动态引入四种方式的实例代码.分享给大家供大家参考,具体如下: index.html <html> <head> <meta content="text/html;charset=utf-8" http-equiv="content-type"> <title> </title> <script src='' id="s1"></script> &

  • 同步异步动态引入js文件的几种方法总结

    动态加载js文件 有时候我们需要根据参数不同来引入不同的js文件,用html直接写标签满足不了我们的需求,总结几种方法,以及同步异步加载的各种需求 一.直接加载 <div id="divId"></div> <script> 二.异步加载,并发执行,但引入js内容不能直接使用 //1.1 直接document.write document.write("<script src='test.js'><\/script>

  • JavaScript动态检测密码强度原理及实现方法详解

    本文实例讲述了JavaScript动态检测密码强度原理及实现方法.分享给大家供大家参考,具体如下: 在注册账户,设置密码时,会出现密码强度动态检测,网上看了一些帖子,大多只写了具体的实现过程,而没有对原理的分析过程.下面着重讲一下其原理. 原理分析 通常实现密码强度动态判断有两种方案实现: 正则.但其效率低一点,难度也大一些. 字符串,函数和运算符. 这里用第二种方案,但是如何判断一个密码串是强还是弱呢? 一般我们的密码会设置为数字.字母(大小写).特殊符号三类. 强:密码串包含其中三种或以上

  • webpack下实现动态引入文件方法

    刚开始使用webpack时,可能很多人都会有过这样的想法,在require文件时,能不能不写静态的字符串路径,而是使用一个更灵活的方式,比如定义一个变量,根据具体的运行情况来确定需要require哪个文件! 比如,笔者就遇到了一个这样的需求. 当时是使用vue-router开发一个管理系统,管理系统自身有一个目录数组,而vue-router也需要一个route配置数组,而这两者恰恰是对应关系的.当时就想,能不能只维护一个目录数组,然后动态的生成route数组呢? 于是我实现了一个小demo,如下

  • 浅谈在vue-cli3项目中解决动态引入图片img404的问题

    博主最近手头再做一个项目,需要调用天气接口,并且还要动态显示天气相关图片icon. 本来以为没什么大问题,结果硬生生被这个动态图片路径搞得民不聊生(博主还是 too young,too simple~),给出效果图: 就是那个红框选中的那个天气状况图标icon要根据当前城市获取当前城市天气状态码,再根据当前城市状态码找到这个对应的天气图标icon~~ 按照一般的开发模式,静态图片路径是相对路径还是绝对路径都可以,因为图片路径是静态的是死的,所以webpack去找这个图片路径的时候是能找到的. 但

  • 使用Feign动态设置header和原理分析

    目录 Feign动态设置header和原理 例如 先说解决办法 设置Feign的header信息(两种形式) 1.在方法参数前面添加@RequestHeader注解 2.实现RequestInterceptor接口 Feign动态设置header和原理 项目中用到了Feign做远程调用, 有部分场景需要动态配置header 开始的做法是通过 @RequestHeader 设置参数来实现动态的header配置 例如 @GetMapping(value = "/test", consume

  • vuex 如何动态引入 store modules

    目录 动态引入store modules 我的目录结构是 使用modules时遇到的坑 动态引入store modules 主要解决的问题每次建一个module需要自己去主index.js里面去注册 为了偷懒,也为了避免团队开发时同时对index.js 进行修改引发冲突 所以在index.js中 动态的对子目录和模块进行注册 我的目录结构是 import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const dynamicModul

随机推荐