位置: IT常识 - 正文

实现扩展 external 能力的 Vite 插件(实现扩展功能的快捷键)

编辑:rootadmin
实现扩展 external 能力的 Vite 插件 前言

推荐整理分享实现扩展 external 能力的 Vite 插件(实现扩展功能的快捷键),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:如何使用扩展,进行扩展,如何使用扩展,进行扩展,扩展方式,进行扩展,数据选择器如何实现扩展,译码器如何实现扩展,内容如对您有帮助,希望把文章链接给更多的朋友!

大家对构建工具中的 external 的属性一定不会陌生吧。在优化构建产物体积需求中可能会引入 CDN 来取代一些基础的模块或工具包,如 React、Vue、lodash 等。 最近没什么事情,闲余时间研究了下 Vite 和 Rollup 的内部实现,借此机会来探究下构建工具是如何处理 external 这一类外部链接的,并对 external 的能力做一些扩展。

如何解析 External

因为 external 的能力主要体现在构建流程,那么我们就从构建的时机开始看起吧。构建的时候 Vite 是依赖于 Rollup 的能力,也就是说 external 的属性主要是在 Rollup 构建上体现出来的。从 Vite 传递给 Rollup 的参数上也可以看出来。

const userExternal = options.rollupOptions?.externallet external = userExternalconst rollupOptions: RollupOptions = { context: 'globalThis', preserveEntrySignatures: ssr ? 'allow-extension' : libOptions ? 'strict' : false, ...options.rollupOptions, input, plugins, external, onwarn(warning, warn) { onRollupWarning(warning, warn, config) }}const { rollup } = await import('rollup')const bundle = await rollup(rollupOptions)

那么 Rollup 是如何处理 external 的路径信息呢?

在构建准备阶段我们可以看到有一个初始化的操作:

function normalizeInputOptions(config) { const options = { // ... external: getIdMatcher(config.external) }; return { options, unsetOptions };}

在 getIdMatcher 函数中有对 config.external 进行初始化流程

const getIdMatcher = (option) => { if (option === true) { return () => true; } if (typeof option === 'function') { return (id, ...args) => (!id.startsWith('\0') && option(id, ...args)) || false; } if (option) { const ids = new Set(); const matchers = []; for (const value of ensureArray(option)) { if (value instanceof RegExp) { matchers.push(value); } else { ids.add(value); } } return (id, ..._args) => ids.has(id) || matchers.some(matcher => matcher.test(id)); } return () => false;};

简单的说就是当用户在 vite.config.* 配置模块中配置了 build.rollupOptions.external 的话,那么 rollup 会收集配置的路径用来进行判断解析的路径是否为外部链接。

options.external = (id, ..._args) => ids.has(id) || matchers.some(matcher => matcher.test(id));

在此大家一定会好奇检测是在什么时机下发生的呢?

Rollup 检测时机

带着这个好奇我们继续往下看吧,Rollup 在初始化配置信息之后就会通过入口来生成 模块依赖图

async generateModuleGraph() { ( { entryModules: this.entryModules, implicitEntryModules: this.implicitEntryModules } = await this.moduleLoader.addEntryModules(normalizeEntryModules(this.options.input), true) );}async build() { timeStart('generate module graph', 2); await this.generateModuleGraph(); // ...}

大家对于模块依赖图一定不会陌生吧,说简单点就是确定模块与模块之间的关系。我们就不一一照着源码来解释生成模块依赖图的流程,那么多无聊呀!

我们可以静下来好好想想,如果让我们来构建项目的模块依赖图,我们应该怎么做呢?

首先,我们肯定需要从配置的 入口(默认 index.html) 开始分析起来,我们可以分析 index.html 模块依赖哪些模块吧,可能 依赖 这个词有些同学不是很懂,那么就用更通俗的话来说 “【我们可以分析 index.html 模块 import 、 script、link 了哪些模块吧】”。分析完 index.html 模块后我们不就又可以分析它依赖的模块嘛,在分析依赖模块之前我们应该要先找一下依赖模块在哪,毕竟依赖模块通常也是文件资源嘛。找到依赖模块的位置后很轻易的就能联想到肯定是要读取依赖模块信息吧,那么熟悉 node 的同学就直接 fs 开干! 那我们读取完了嘞,这还要想嘛,不就也像 index.html 模块一样进行解析然后分析依赖情况嘛。细心的同学就可以发现其实就是一个递归检索依赖模块的过程,好像也没什么难度嘛。

真棒! Rollup 还真就是这么处理的,只不过处理流程的时候会引入插件来协助分析。

实现扩展 external 能力的 Vite 插件(实现扩展功能的快捷键)

那么有同学可能就会说:好像讲离题了唉,这个文章不是要分析 external,怎么讲到了依赖构建的流程呢!

别急嘛,心急吃不了热豆腐。将上面的内容是为了让大家对于 Rollup 执行流程有一个大体的认识,在后续会有关联的。

前面有提到 「找到依赖模块的位置」,Rollup 在处理依赖模块路径的时候会判断当前模块路径是否是 external。

this.resolveId =async (source, importer, customOptions, isEntry, skip = null) => { return this.getResolvedIdWithDefaults( this.getNormalizedResolvedIdWithoutDefaults( this.options.external(source, importer, false) ? false : await resolveId(source, importer, this.options.preserveSymlinks, this.pluginDriver, this.resolveId, skip, customOptions, typeof isEntry === 'boolean' ? isEntry : !importer), importer, source ) );};

源码上可以了解到当路径被判定为外部链接的情况下是不会执行 await resolveId(...),也就意味着对于外部链接 Rollup 不会对其做处理。

对 external 的扩展目标与思路

好嘞,前文先介绍到这里。我们接下来先确定下对 external 扩展的目标吧

对 ESM 和 UMD 产物链接做支持且自动引入 CDN 链接。解释: 伴随着高版本浏览器支持 ESM 规范。各大包开发者也意识到了 ESM 的重要性,也提供了ESM 规范的产物且同时也有类似 [skypack](Skypack: search millions of open source JavaScript packages) 的 CDN 厂商对ESM 产物的链接做支持。因此插件对于 ESM 外链的支持也是势在必行。支持 Vite 2.0及其以上版本。解释: 可能大家有疑惑为什么不支持 2.0 之前的版本呢,原因在于 2.0 其实是 Vite 的第一个稳定版本。大家可以在 NPM Version 上看出 2.0 之前的版本基本没什么受众,耗费时间去兼容意义并不是很大。同时在 awesome-vite 库中在申请优秀插件的时候官方也是要求: The plugin/tool is working with Vite 2.x and onward 即对于 Vite 2.x 版本及以上的支持。

好嘞,现在我们已经确认好目标。那么接下来我们就要分析下如何需要如何进行实现。

对 ESM 和 UMD 产物链接做支持且自动引入 CDN 链接 目标的实现

ESM规范产物链接的支持:

由于高版本浏览器支持对 ESM 链接的支持,那么我们只需要将原先的包模块名称换成 CDN 链接就可以了。就好比将 import react from 'react' 替换为 import react from 'https://cdn.skypack.dev/react'。按照我们先前说的,当我们引入 react 模块,那么在构建阶段 Rollup 就会去找 react 模块究竟在哪里,也就是说寻找 react 模块的具体路径。那么我们只需要告诉 Rollup,react 的具体路径为 https://cdn.skypack.dev/react 不就好了嘛。的确就是这么处理的,不过这里需要提一嘴的是 Rollup 在获取路径后还会通过 external 配置项来确定下获取的链接是否是外链,如果不是外链的话会进一步进行解析,继续解析按照我们先前说的就是去加载资源,我们引入 CDN,不就是想让包体积变小嘛,如果让 Rollup 加载资源,那打包体积不久没优化嘛,我们目标就想要替换 react 为 https://cdn.skypack.dev/react。因此我们需要告诉 Rollup,https://cdn.skypack.dev/react是一个外链,你不要继续解析了。

UMD规范产物链接的支持:

在早期 JQuery 盛行的年代,我们通常会使用 CDN 的方式来引入 JQuery,然后 CDN 引入的产物执行后会在全局(浏览器环境在 window 下)注入特定的属性,开发者就可以通过这个特定属性来获取 JQuery 的能力。大家可能会有想法,浏览器环境中我们只需要帮忙获取一下 JQuery 在 window 下注入的属性然后将导入语句改一下就好了嘛,就也是将 import jQuery from 'jquery' 替换成 const jQuery = window['jQuery']。这样做也可以,不过需要对不同的导入方式做特定的处理。在这里,我们可以使用更为简单的方式来进行实现,借助 虚拟模块 来进行实现。可能有部分同学对于 虚拟模块 的定义有点陌生,其实顾名思义就是虚假的模块,是一个不存在磁盘空间上的模块。那么我们可以在 虚拟模块 中将我们定义的内容导出就好了,即将 import jQuery from 'jquery' 转换为

// virtual: jqueryconst jQuery = window['jQuery'];export default jQuery;

那么执行流程就是 Rollup 加载 jquery 内容其实就转换为我们上述的代码了。

自动引入 CDN 链接:

按照以往流程的话,最后一步我们肯定会手动创建 script 或 link 标签插入到 index.html 模块的 head 标签内。由于这个流程过于简单但有可能存在开发人员不用 CDN 后当没将 CDN 链接去除,那就存在一定的开销了,那么我们一并把这个能力做掉吧! 其实这个流程就是对于 html 模块做操作,按照正常想法实现的话肯定是先找到一个能解析 html 模块的工具,将 html 文本转换为 ast 结构的对象,然后递归分析 ast,针对 head 节点做插入处理。这里 也实现了相类似的能力,感兴趣的同学可以看看。

不过通过借助 Vite 内置 html 插件 的能力,我们可以用更简单的方式来做到。感兴趣的同学可以点击了解一下具体的实现,这里我就简单的说明一下作用吧。插件 会调用 transformIndexHtml,返回值若携带特定的标识符会将信息注入到 html 特定的地方,使用方式可见 官方文章。那么我们只需要在 transformIndexHtml 钩子中返回信息,Vite 的内置 插件 就会帮我们处理。

支持 Vite 2.0及其以上版本 目标的实现:

在上文 【ESM规范产物链接的支持】 中有提到我们需要告诉 Rollup 对于 ESM 规范的 CDN 链接不需要继续做解析,因此需要在 Rollup 配置上新增 external 属性。由于 ESM 外链支持异步化(配置外链的流程很大程度会接入平台),而对于在 2.0 ~ 2.2 版本之间 Vite 并不支持异步配置。只能在 vite.config.* 模块中手动添加 external。

结束语

好嘞,到此实现思路就已经聊完了。相信大部分的同学已经可以写出 external 这一类的插件了,感觉还是蛮有意思的。我已经将实现的思路代码化了,感兴趣的同学可以将 项目 clone 下来。在此我也很期待大家能给 仓库 带来好的 想法 和 贡献,当然能留下您的 Star 再好不过了,感谢大家的阅读!

本文链接地址:https://www.jiuchutong.com/zhishi/299761.html 转载请保留说明!

上一篇:React获取DOM和获取组件实例的方式(react useref获取dom元素)

下一篇:「部署全流程」 中科院学术专业版chatGPT来啦(部署fabric)

  • 新科移动空调需要接排水管吗(新科移动电源)(新科移动空调需要加氟吗)

    新科移动空调需要接排水管吗(新科移动电源)(新科移动空调需要加氟吗)

  • 华为荣耀20青春版机身多厚(华为荣耀20青春版图片)

    华为荣耀20青春版机身多厚(华为荣耀20青春版图片)

  • 华为手机的通话记录在哪里能查到(华为手机的通话录音储存在哪里)

    华为手机的通话记录在哪里能查到(华为手机的通话录音储存在哪里)

  • 电脑息屏之后很久才能唤醒(电脑息屏之后很卡怎么办)

    电脑息屏之后很久才能唤醒(电脑息屏之后很卡怎么办)

  • 抖音发的视频播放量为0(抖音发的视频播放量不过百)

    抖音发的视频播放量为0(抖音发的视频播放量不过百)

  • 一体机怎么强制开机(一体机怎么强制关机)

    一体机怎么强制开机(一体机怎么强制关机)

  • 探探不显示活跃时间(探探有些人没有显示活跃时间)

    探探不显示活跃时间(探探有些人没有显示活跃时间)

  • 耳机总是掉出来怎么办(耳机总是掉出来怎么回事)

    耳机总是掉出来怎么办(耳机总是掉出来怎么回事)

  • oppo手机微信步数为什么总是0(oppo手机微信步数为什么走了还是0)

    oppo手机微信步数为什么总是0(oppo手机微信步数为什么走了还是0)

  • 红米note5呼吸灯亮无法开机(红米note5呼吸灯颜色设置)

    红米note5呼吸灯亮无法开机(红米note5呼吸灯颜色设置)

  • 文件夹照片怎么按自己要求顺序排序(文件夹照片怎么弄)

    文件夹照片怎么按自己要求顺序排序(文件夹照片怎么弄)

  • 抖音刷到的视频是随机的吗(抖音刷到的视频找不到了怎么办)

    抖音刷到的视频是随机的吗(抖音刷到的视频找不到了怎么办)

  • windows和mac的区别(windows和mac的区别很大)

    windows和mac的区别(windows和mac的区别很大)

  • 启用gpu调试层有必要开吗(启用 gpu 调试层)

    启用gpu调试层有必要开吗(启用 gpu 调试层)

  • matepad pro可以打电话吗(华为matepadpro可以打电脑游戏吗)

    matepad pro可以打电话吗(华为matepadpro可以打电脑游戏吗)

  • vivoNEX3s支持光学防抖吗(vivonex3拍照有光学防抖吗)

    vivoNEX3s支持光学防抖吗(vivonex3拍照有光学防抖吗)

  • 微信群邀请链接多久失效(微信群邀请链接怎么弄)

    微信群邀请链接多久失效(微信群邀请链接怎么弄)

  • 所谓的裸机是指(所谓裸机 是指)

    所谓的裸机是指(所谓裸机 是指)

  • 未接电话详单能查到吗(未接电话打电话记录详单能查到吗)

    未接电话详单能查到吗(未接电话打电话记录详单能查到吗)

  • 序号怎么自动生成(序号怎么自动生成排序)

    序号怎么自动生成(序号怎么自动生成排序)

  • 华为p30怎么删除本地音乐(华为p30怎么删除自带软件)

    华为p30怎么删除本地音乐(华为p30怎么删除自带软件)

  • 自动确认收货后还可以退款吗(自动确认收货后钱什么时候到账)

    自动确认收货后还可以退款吗(自动确认收货后钱什么时候到账)

  • 小天才通话记录在哪看(小天才通话记录查询)

    小天才通话记录在哪看(小天才通话记录查询)

  • vivo云相册怎么登录(vivo云相册怎么恢复到手机相册)

    vivo云相册怎么登录(vivo云相册怎么恢复到手机相册)

  • 苹果x单手模式怎么开(iphone x单手模式怎么用)

    苹果x单手模式怎么开(iphone x单手模式怎么用)

  • 华为手环3怎么开启微信信息提醒(华为手环3怎么测心率)

    华为手环3怎么开启微信信息提醒(华为手环3怎么测心率)

  • 微信怎么取消听筒模式(微信怎么取消听筒播放)

    微信怎么取消听筒模式(微信怎么取消听筒播放)

  • 美团乘车码怎么没有了(美团乘车码怎么添加到桌面)

    美团乘车码怎么没有了(美团乘车码怎么添加到桌面)

  • MOM.exe是什么进程?是MOM.exe病毒吗?为什么运行?(mode exe)

    MOM.exe是什么进程?是MOM.exe病毒吗?为什么运行?(mode exe)

  • 公司首次申报个税怎么填
  • 退税现金流量表做哪里
  • 间接税种有哪些
  • 工资是当月计提下月发放吗
  • 财务汽车折旧年限的最新规定2020
  • 收回次月作废的发票需要做账吗
  • 自建厂房出售如何计算所得税
  • 进料深加工境外付汇
  • 公关费用明细表
  • 酒店代金券使用规则
  • 产品不合格重新加工叫什么
  • 变电站是否缴纳房产税
  • 税控盘维护费是普票能全额抵扣吗
  • 饭店取得的烟酒是什么
  • 金税盘是固定资产还是无形资产
  • 退役士兵创业就业政策
  • 库存成本与实际成本不符
  • 注册资本需要缴清吗
  • 工会账户销户
  • 公益事业捐赠
  • 企业所得税汇算清缴表
  • 小规模纳税人的专票可以抵税吗
  • 个税申报晚了会退税费吗
  • 广告业进项都能开什么票
  • PHP:session_commit()的用法_Session函数
  • PHP:parse_ini_file()的用法_Filesystem函数
  • 收取包装物押金税率
  • 房地产开发公式
  • oss使用场景
  • 终身职业技能培训
  • 新的担保法是怎么规定的
  • 企业发生的现金折扣应计入什么费用
  • 愚公全名
  • 关于猿猴的作文
  • 小规模纳税人房产税优惠政策2023
  • 拨出专款年末结转
  • 商品出入库台账
  • 毛利的计算公式为
  • 增值税纳税申报类型怎么填
  • SQLserver2014(ForAlwaysOn)安装图文教程
  • 全年一次性奖金税率表2023最新
  • 建筑施工企业质量管理规范
  • 印花税根据不同的税目,分别采用
  • 事业单位破产清算开户流程
  • 购买商标权需要缴税吗
  • 制造费用的明细账应当按照什么设置
  • 库存商品的出售
  • 待转销项税额对应科目
  • 私企干不长久
  • sql server数据库版本
  • win7开机报错0xc0000098
  • win8系统运行慢怎么办
  • win10有没有语音输入
  • 播放winner
  • windowsxp
  • Linux磁盘分区的作用
  • windows注销登录会怎样
  • linux系统批量任务
  • console打开
  • win7切换输入法不见了怎么弄出来
  • windows8.1升级到windows10
  • linux curl命令使用
  • 2015-04-04---CCAction详解(欠了大家好几天了)
  • 自动释放池原理,本质
  • android开发最全教程
  • Android 获取网络时间
  • etmvc+jQuery EasyUI+combobox多值操作实现角色授权实例
  • 怎么把两个文件中的内容合并
  • 详解各种汽油一吨等于多少升
  • Linux bash Shell中的变量类型详解
  • javascript给表单赋值
  • python并发和并行
  • python画足球
  • 陷阱类过关游戏
  • 北京增值税普通发票图片
  • 如何强化理论武装
  • 211学生占全国比例
  • 12366纳税服务热线存在的问题
  • 福利费是否计入成本费用
  • 国家税务总局关于税务机构改革有关事项的公告
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设