Rollup 插件机制深入学习

插件系统的核心

Rollup 的插件系统是其强大功能的一部分,能够让开发者通过插件定制打包过程。插件的核心包括:

  • Graph:Rollup 的全局图形表示,用于管理入口点及其依赖关系。
  • PluginDriver:插件驱动器,负责调用插件并提供插件环境上下文。

插件系统由各种钩子函数组成,这些函数在构建的不同阶段被触发,允许插件在构建过程中插入自定义逻辑。

插件的结构

一个 Rollup 插件是一个对象,包含多个属性和钩子函数。这些钩子函数分为两类:

  1. 构建钩子函数:在构建阶段执行,影响构建过程的各个方面。
  2. 输出生成钩子函数:在生成输出文件时执行,处理和修改生成的包。

插件应该作为一个包发布,并符合以下官方约定:

  • 插件名称应以 rollup-plugin- 前缀开头。
  • package.json 中包含 rollup-plugin 关键字。
  • 插件应提供清晰的文档和测试,使用英文编写,并尽可能提供 sourcemap 支持。

插件的安装与使用

1. 安装 Rollup

首先,确保你已经安装了 Rollup。你可以通过 npm 或 yarn 来安装:

npm install --save rollup

yarn add  rollup

2. 安装 Rollup 插件

Rollup 插件可以通过 npm 或 yarn 安装。例如,以下是安装一些常见插件的命令:

npm install --save-dev @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup-plugin-terser

yarn add --dev @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup-plugin-terser

3. 配置插件

在项目根目录下创建一个名为 rollup.config.js 的配置文件。在这个文件中,你可以配置和使用各种插件。以下是一个示例配置,展示了如何使用 @rollup/plugin-node-resolve@rollup/plugin-commonjsrollup-plugin-terser 插件:

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';export default {input: 'src/index.js', // 入口文件output: {file: 'dist/bundle.js',format: 'iife', // 立即调用函数表达式(适用于浏览器)sourcemap: true // 启用 source maps},plugins: [resolve(), // 解析 node_modules 中的模块commonjs(), // 转换 CommonJS 模块为 ES6 模块terser() // 压缩代码]
};

4. 运行构建

package.json 文件中添加构建脚本,以便通过 npm 或 yarn 执行 Rollup 构建过程:

json复制代码{"scripts": {"build": "rollup -c"}
}

然后,你可以使用以下命令运行构建:

npm run build

yarn build

插件开发最佳实践

开发 Rollup 插件时,有一些最佳实践可以帮助你创建高质量、稳定的插件。

1. 插件命名和文档

  • 命名规范:插件名称应具有描述性,并以 rollup-plugin- 开头。
  • 文档编写:在 package.json 中添加 rollup-plugin 关键字,并编写清晰的文档,包括安装、用法和配置示例。

2. 异步编程

  • 异步方法:使用 asyncawait 来处理异步钩子函数,避免回调地狱。
  • Promise:确保插件在处理异步操作时返回 Promise 对象。

3. 提供 Sourcemap 支持

  • Sourcemap:如果插件涉及代码转换,确保生成正确的 sourcemap,以便于调试和错误定位。

4. 虚拟模块命名

  • 虚拟模块:使用 \0 前缀来标识虚拟模块,这样可以避免其他插件处理这些模块。

5. 插件测试

  • 编写测试用例:使用 Mocha 或 AVA 等测试框架编写测试用例,确保插件在各种场景下的正确性。

实际应用案例

以下是两个实际应用案例,展示如何利用 Rollup 插件系统解决实际问题。

自定义模块解析插件

以下插件用于解析自定义模块路径,并返回相应的内容:

export default function customResolvePlugin() {return {name: 'custom-resolve',resolveId(source) {if (source === 'my-custom-module') {return source;}return null;},load(id) {if (id === 'my-custom-module') {return 'export default "Hello from custom module!"';}return null;}};
}

代码转换和优化插件

以下插件用于在构建过程中替换代码中的 console.logconsole.warn,并在构建结束时压缩代码:

import { terser } from 'rollup-plugin-terser';export default function transformAndMinifyPlugin() {return {name: 'transform-and-minify',transform(code, id) {if (id.endsWith('.js')) {return {code: code.replace(/console\.log/g, 'console.warn'),map: null};}return null;},writeBundle(options, bundle) {console.log('Build completed and output files have been written.');},generateBundle(options, bundle) {this.emitFile({type: 'asset',fileName: 'additional.txt',source: 'Extra content in build'});},plugins: [terser() // 使用 terser 插件压缩代码]};
}

插件机制分析

钩子函数

Rollup 的插件机制核心在于钩子函数。这些函数允许插件在构建的不同阶段执行自定义逻辑。钩子函数可以分为两类:

  1. 构建钩子函数:处理构建阶段的各种任务。
    • options: 配置选项。
    • resolveId: 解析模块 ID。
    • load: 加载模块内容。
    • transform: 转换代码。
    • buildStart: 构建开始。
    • buildEnd: 构建结束。
    • closeBundle: 关闭构建。
  2. 输出生成钩子函数:处理输出生成阶段的任务。
    • outputOptions: 输出选项。
    • generateBundle: 生成包。
    • writeBundle: 写入包。
    • renderError: 渲染错误。

构建钩子函数的执行顺序和执行机制对于插件的功能实现至关重要。以下是常见钩子函数的详细说明及其实现方式:

1. options

options 钩子函数允许插件修改 Rollup 的配置选项。这是插件在构建过程开始时可以进行的一项设置调整。

export default function myPlugin() {return {name: 'my-plugin',options(options) {// 修改 Rollup 配置选项options.output.format = 'cjs';return options;}};
}
2. resolveId

resolveId 钩子函数用于解析模块 ID。在模块解析过程中,插件可以决定如何处理模块路径。

export default function myPlugin() {return {name: 'my-plugin',resolveId(source) {if (source === 'virtual-module') {return source; // 返回虚拟模块 ID}return null; // 交由其他插件处理}};
}
3. load

load 钩子函数用于加载模块的代码。当 resolveId 钩子函数返回的 ID 被请求时,load 钩子函数将会被调用。

export default function myPlugin() {return {name: 'my-plugin',load(id) {if (id === 'virtual-module') {return 'export default "This is virtual!"';}return null;}};
}
4. transform

transform 钩子函数用于转换模块的代码。它在代码被处理时执行,可以用于代码的转换或修改。

export default function myPlugin() {return {name: 'my-plugin',transform(code, id) {if (id.endsWith('.js')) {// 对所有 JavaScript 文件进行处理return {code: code.replace(/console\.log/g, 'console.warn'),map: null};}return null;}};
}
输出生成钩子函数

输出生成钩子函数用于在构建完成后处理和优化输出文件。主要包括:

  • generateBundle:在生成包之后触发,允许对生成的输出文件进行处理。
  • writeBundle:在输出文件写入磁盘之后触发,用于处理文件的最终写入。
  • renderError:处理构建过程中发生的错误。
1. generateBundle

generateBundle 钩子函数允许插件在生成包之后对包进行处理。这通常用于添加自定义的输出逻辑,例如生成额外的文件或注释。

export default function myPlugin() {return {name: 'my-plugin',generateBundle(options, bundle) {// 在生成的 bundle 中添加自定义文件this.emitFile({type: 'asset',fileName: 'extra.txt',source: 'This is an extra file'});}};
}
2. writeBundle

writeBundle 钩子函数在输出文件写入磁盘之后触发,可以用于执行额外的文件处理或日志记录操作。

export default function myPlugin() {return {name: 'my-plugin',writeBundle(options, bundle) {console.log('Bundle written to disk');}};
}
3. renderError

renderError 钩子函数用于处理构建过程中发生的错误。它可以捕捉和处理构建过程中出现的异常。

export default function myPlugin() {return {name: 'my-plugin',renderError(error) {console.error('Build error:', error);}};
}

钩子函数加载实现

Rollup 的插件系统通过 PluginDriver 类中的不同方法来加载钩子函数,确保插件能够在构建过程中插入自定义逻辑。这些方法包括:

  • hookFirst: 加载 first 类型的钩子函数,支持异步处理。
  • hookSeq: 加载 sequential 类型的钩子函数,按顺序执行。
  • hookParallel: 并行执行钩子函数,不等待当前钩子完成。
  • hookReduceArg0: 对第一个参数进行 reduce 操作。
  • hookReduceArg0Sync: 同步版本,处理同步钩子函数。
  • hookReduceValue: 对钩子函数的返回值进行 reduce 操作。
  • hookReduceValueSync: 同步版本,处理同步钩子函数的返回值。
  • hookFirstSync: first 类型的同步钩子函数加载。
  • hookSeqSync: sequential 类型的同步钩子函数加载。
  • hookParallelSync: 并行执行同步钩子函数。
hookFirst

hookFirst 方法用于加载 first 类型的钩子函数,这些钩子函数会按照插件列表中的顺序依次执行,直到其中一个返回非 null 或非 undefined 的结果。它支持异步处理,并确保异步操作按照顺序完成。

function hookFirst<H extends keyof PluginHooks, R = ReturnType<PluginHooks[H]>>(hookName: H,args: Args<PluginHooks[H]>,replaceContext?: ReplaceContext | null,skip?: number | null
): EnsurePromise<R> {let promise: Promise<any> = Promise.resolve();for (let i = 0; i < this.plugins.length; i++) {if (skip === i) continue;promise = promise.then((result: any) => {if (result != null) return result;return this.runHook(hookName, args as any[], i, false, replaceContext);});}return promise;
}
hookFirstSync

hookFirstSync 方法是 hookFirst 的同步版本。它按顺序执行 first 类型的同步钩子函数,并在找到非 null 或非 undefined 的结果时立即返回。

function hookFirstSync<H extends keyof PluginHooks, R = ReturnType<PluginHooks[H]>>(hookName: H,args: Args<PluginHooks[H]>,replaceContext?: ReplaceContext
): R {for (let i = 0; i < this.plugins.length; i++) {const result = this.runHookSync(hookName, args, i, replaceContext);if (result != null) return result as any;}return null as any;
}
hookSeq

hookSeq 方法用于加载 sequential 类型的钩子函数,这些钩子函数会按照插件列表中的顺序依次执行。无论钩子函数是否是异步的,hookSeq 方法都会等待前一个钩子函数完成后再执行下一个。

async function hookSeq<H extends keyof PluginHooks>(hookName: H,args: Args<PluginHooks[H]>,replaceContext?: ReplaceContext
): Promise<void> {let promise: Promise<void> = Promise.resolve();for (let i = 0; i < this.plugins.length; i++)promise = promise.then(() =>this.runHook<void>(hookName, args as any[], i, false, replaceContext),);return promise;
}
hookSeqSync

hookSeqSync 方法是 hookSeq 的同步版本。它按顺序执行 sequential 类型的同步钩子函数,并确保所有钩子函数都在前一个钩子函数完成后执行。

hookSeqSync<H extends SyncPluginHooks & SequentialPluginHooks>(hookName: H,args: Parameters<PluginHooks[H]>,replaceContext?: ReplaceContext
): void {for (const plugin of this.plugins) {this.runHookSync(hookName, args, plugin, replaceContext);}
}
hookParallel

hookParallel 方法用于并行执行 parallel 类型的钩子函数。它会同时执行所有的钩子函数,不会等待当前钩子函数的完成。

hookParallel<H extends AsyncPluginHooks & ParallelPluginHooks>(hookName: H,args: Parameters<PluginHooks[H]>,replaceContext?: ReplaceContext
): Promise<void> {const promises: Promise<void>[] = [];for (const plugin of this.plugins) {const hookPromise = this.runHook(hookName, args, plugin, false, replaceContext);if (!hookPromise) continue;promises.push(hookPromise);}return Promise.all(promises).then(() => {});
}
hookReduceArg0

hookReduceArg0 方法对第一个参数进行 reduce 操作。它会顺序执行钩子函数,并对第一个参数进行累积操作。

function hookReduceArg0<H extends keyof PluginHooks, V, R = ReturnType<PluginHooks[H]>>(hookName: H,[arg0, ...args]: any[],reduce: Reduce<V, R>,replaceContext?: ReplaceContext
) {let promise = Promise.resolve(arg0);for (let i = 0; i < this.plugins.length; i++) {promise = promise.then(arg0 => {const hookPromise = this.runHook(hookName, [arg0, ...args], i, false, replaceContext);if (!hookPromise) return arg0;return hookPromise.then((result: any) =>reduce.call(this.pluginContexts[i], arg0, result, this.plugins[i]));});}return promise;
}
hookReduceArg0Sync

hookReduceArg0Sync 方法是 hookReduceArg0 的同步版本,用于同步处理钩子函数的累积操作。

hookReduceArg0Sync<H extends SyncPluginHooks & SequentialPluginHooks>(hookName: H,[arg0, ...args]: any[],reduce: Reduce<V, R>,replaceContext?: ReplaceContext
): void {for (const plugin of this.plugins) {const result = this.runHookSync(hookName, [arg0, ...args], plugin, replaceContext);if (result != null) {reduce.call(this.pluginContexts[i], arg0, result, this.plugins[i]);}}
}
runHook 方法

runHook 方法是上述钩子函数加载方法的核心。它负责调用插件中的钩子函数,并处理函数的执行结果。runHook 方法能够处理异步操作和自定义上下文,从而提供了高度的灵活性。

function runHook<T>(hookName: string,args: any[],pluginIndex: number,permitValues: boolean,hookContext?: ReplaceContext | null,
): Promise<T> {this.previousHooks.add(hookName);const plugin = this.plugins[pluginIndex];const hook = (plugin as any)[hookName];if (!hook) return undefined as any;let context = this.pluginContexts[pluginIndex];if (hookContext) {context = hookContext(context, plugin);}return Promise.resolve().then(() => {if (typeof hook !== 'function') {if (permitValues) return hook;return error({code: 'INVALID_PLUGIN_HOOK',message: `Error running plugin hook ${hookName} for ${plugin.name}, expected a function hook.`,});}return hook.apply(context, args);}).catch(err => throwPluginError(err, plugin.name, { hook: hookName }));
}

runHook 方法中:

  1. 查找钩子函数: 通过 pluginIndexhookName 获取插件对象及其钩子函数。

  2. 处理上下文: 根据 hookContext 修改钩子函数的执行上下文。

  3. 执行钩子函数: 使用 Promise.resolve() 确保异步处理,并调用钩子函数。

  4. 错误处理: 捕获并处理钩子函数执行中的错误。

插件应用实例

以下是一些实际应用的插件实例,展示了如何利用 Rollup 插件系统来解决实际问题:

1. 自定义模块解析

创建一个插件,用于解析自定义的模块路径,并返回特定的虚拟模块内容。

export default function customResolvePlugin() {return {name: 'custom-resolve',resolveId(source) {if (source === 'my-custom-module') {return source;}return null;},load(id) {if (id === 'my-custom-module') {return 'export default "Hello from custom module!"';}return null;}};
}
2. 代码转换与优化

创建一个插件,用于将所有 JavaScript 代码中的 console.log 替换为 console.warn,并在构建输出时压缩代码。

import { terser } from '@rollup/plugin-terser';
export default function transformAndMinifyPlugin() {return {name: 'transform-and-minify',transform(code, id) {if (id.endsWith('.js')) {return {code: code.replace(/console\.log/g, 'console.warn'),map: null};}return null;},writeBundle(options, bundle) {console.log('Build completed');},generateBundle(options, bundle) {this.emitFile({type: 'asset',fileName: 'extra.txt',source: 'This is an extra file'});}};
}

核心依赖

  • yargs-parser:用于解析命令行选项。
  • source-map-support:这个模块通过 V8 堆栈追踪 API 支持 堆栈 sourcemap 支持

最后

rollup 的源码全都糅杂在一个库中,阅读起来着实头大,模块、工具函数管理的看起来很随意。而且我们无法直接移植它的任何工具到我们的项目中,相比起来,webpack 的插件系统封装成了一个插件 tapable 就很利于我们学习和使用。

总结

Rollup 的插件和其他大型框架大同小异,都是提供统一的接口并贯彻了约定优于配置的思想。
和 webpack 相比,rollup 的插件系统自称一派且没有区分 plugin 和 loader。
Rollup 的插件系统通过钩子函数和插件机制提供了极大的灵活性,允许开发者在构建过程中插入自定义逻辑。
通过理解插件的安装、配置、使用以及开发最佳实践,开发者可以充分利用 Rollup 的插件系统满足各种构建需求。
Rollup 的钩子函数加载实现提供了多种方法来处理插件中的钩子函数,包括顺序执行、并行执行和参数累积操作等。这些方法的设计使得 Rollup 的插件系统具有高度的灵活性和扩展性。
通过 runHook 方法,插件能够在构建过程中插入自定义逻辑,并处理异步操作和上下文。

参考:
Rollup 插件机制源码解析
rollup/plugins
rollup/awesome
tapable

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/53952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ArcGISProSDK】初识

ArcGIS Pro SDK 提供四种主要的可扩展性模式&#xff1a;加载项、托管配置、插件数据源和 CoreHost 应用程序。 各模块文件对比 API 核心 核心程序集位于 {ArcGIS Pro 安装文件夹}\bin 中。 程序集描述ArcGIS.Core.dll 提供 CIM、地理数据库、几何图形和公共设施网络 API。 …

Prometheus+grafana监控spring boot 3运行情况

使用Prometheus和Grafana来监控Spring Boot 3应用的运行情况是一种强大的监控策略&#xff0c;可以帮助你了解应用的性能、健康状况以及潜在的问题。以下是如何配置这种监控系统的基本步骤&#xff1a; 1. Spring Boot 应用配置 首先&#xff0c;确保你的Spring Boot 3应用已…

Notepad++插件:TextFX 去除重复行

目录 一、下载插件 TextFX Characters 二、去重实操 2.1 选中需要去重的文本 2.2 操作插件 2.3 结果展示 2.3.1 点击 Sort lines case sensitive (at column) 2.3.2 点击 Sort lines case insensitive (at column) 一、下载插件 TextFX Characters 点【插件】-【插件管理…

从头开始学MyBatis—02基于xml和注解分别实现的增删改查

首先介绍此次使用的数据库结构&#xff0c;然后引出注意事项。 通过基于xml和基于注解的方式分别实现了增删改查&#xff0c;还有获取参数值、返回值的不同类型对比&#xff0c;帮助大家一次性掌握两种代码编写能力。 目录 数据库 数据库表 实体类 对应的实体类如下&#x…

JS 扩展运算符有哪些使用场景?

你好&#xff0c;我是沐爸&#xff0c;欢迎点赞、收藏、评论和关注。 扩展运算符有哪些使用场景&#xff1f;直接进入正题 一、复制数组 const a1 [1, 2];// 写法一 const a2 [...a1]; // 写法二 const [...a2] a1;二、合并数组 const part1 [1, 2, 3]; const part2 …

golang学习笔记16——golang部署与运维全攻略

推荐学习文档 golang应用级os框架&#xff0c;欢迎star基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总golang学习笔记01——基本数据类型golang学习笔记02——gin框架及基本原理golang学习笔记03——gin框架的核心数据结构golang学…

【C++基础概念理解——类的继承和嵌套】

基本概念 在 C 中&#xff0c;类的继承和嵌套类的定义是两种不同的概念。 class InitialSetupProcedure : public StateMachine //类的继承(符号是":")class InitialSetupProcedure::StateMachine //类的嵌套(符号是"::"&#xff0c;意思是类StateMach…

深度学习和计算机视觉:实现图像分类

深度学习在计算机视觉领域的应用已经取得了革命性的进展。从图像分类到对象检测&#xff0c;再到图像分割和生成&#xff0c;深度学习模型在这些任务中都展现出了卓越的性能。本篇文章将介绍如何使用深度学习进行图像分类&#xff0c;这是计算机视觉中的一个基础任务。 计算机…

什么是幂等

什么是幂等&#xff1f; 幂等简单来说就是“重复做同一件事&#xff0c;结果还是一样的”。比如&#xff0c;你按电视遥控器的开关按钮&#xff0c;按一次电视开了&#xff0c;再按一次电视关了。这个操作不是幂等的&#xff0c;因为你每按一次&#xff0c;结果都不一样。 再…

KTM580030bit 绝对角度细分器支持最多 4096 对极与一键非线性自校准集成双 16bit 2M SAR ADC

KTM5800 是一款 30bit 绝对角度细分 4096 对极编码细分器&#xff0c;可以与磁电阻传感器&#xff08; AM R/TMR &#xff09;搭配&#xff0c;构成一个高速高精度的非接触磁性编码器模块。它具有以非常高的采样速率 读取传感器上的差分模拟正弦和余弦信号的能力&#xf…

pytest 生成allure测试报告

allure的安装 github地址 allure资产列表 windows下载.zip&#xff0c;解压并配置环境变量PATH&#xff1b;linux下载安装allure&#xff0c;可以使用windows下解压的.zip文件&#xff08;通用&#xff09;&#xff0c;或者下载.rpm/.deb 文件进行安装并配置PATH&#xff1b;…

通信工程学习:什么是MRF多媒体资源功能、MRFC多媒体资源功能控制、MRFP多媒体资源功能处理

一、MRF多媒体资源功能 MRF&#xff08;Multimedia Resource Function&#xff0c;多媒体资源功能&#xff09;是3G/IMS网络中定义的提供多媒体资源功能的网络实体&#xff0c;它为3G/IMS网络的业务和承载提供媒体能力支持。MRF通过提供丰富的媒体处理功能&#xff0c;如播放声…

分块总结:时髦之裤

说白了就是南外分块题做的差不多了&#xff0c;来写一篇总结。 简要题意&#xff1a; 给一序列 a&#xff0c;初始时 a i i a_ii ai​i&#xff0c;有如下两个操作&#xff1a; 1.将[l,r]每个数改为x&#xff0c;该点增加贡献 ∣ a i − x ∣ |a_i-x| ∣ai​−x∣. 2.询问[l…

SQLite的入门级项目学习记录(二)

再补充一些基础知识&#xff1a; 并行操作的问题 1、可以多游标同时运行 SQLite&#xff0c;对于同一个连接sqlite3.connect(db_file)&#xff0c;可以同时创建多个游标&#xff0c;每个游标都是独立的&#xff0c;可以执行各自的SQL命令序列。 import sqlite3# 创建数据库连…

FAT32文件系统详细分析 (格式化SD nandSD卡)

FAT32 文件系统详细分析 (格式化 SD nand/SD 卡) 目录 FAT32 文件系统详细分析 (格式化 SD nand/SD 卡)1. 前言2.格式化 SD nand/SD 卡3.FAT32 文件系统分析3.1 保留区分析3.1.1 BPB(BIOS Parameter Block) 及 BS 区分析3.1.2 FSInfo 结构扇区分析3.1.3 引导扇区剩余扇区3.1.4 …

vue在一个组件引用其他组件

在vue一个组件中引用另一个组件的步骤 必须在script中导入要引用的组件需要在export default的components引用导入的组件(这一步经常忘记)在template使用导入的组件<script><!-- 第一步,导入--> import Vue01 from "@/components/Vue01.vue";

828华为云征文 | Flexus X 实例服务器网络性能深度评测

引言 随着互联网应用的快速发展&#xff0c;网络带宽和性能对云服务器的表现至关重要。在不同的云服务平台上&#xff0c;即便配置相同的带宽&#xff0c;实际的网络表现也可能有所差异。因此&#xff0c;了解并测试服务器的网络性能变得尤为重要。本文将以华为云X实例服务器为…

【vue-media-upload】一个好用的上传图片的组件,注意事项

一、问题 media 的saved 数组中的图片使用的是location 相对路径&#xff0c;但是我的业务需要直接根据图片链接展示图片&#xff0c;而且用的也不是location 相关源代码 <div v-for"(image, index) in savedMedia" :key"index" class"mu-image-…

Hadoop林子雨安装

文章目录 hadoop安装教程注意事项&#xff1a; hadoop安装教程 链接: 安装教程 注意事项&#xff1a; 可以先安装ububtu增强功能&#xff0c;完成共享粘贴板和共享文件夹 ubuntu增强功能 2.这里就可以使用共享文件夹 或者在虚拟机浏览器&#xff0c;用 微信文件传输助手 传文…

uniapp vite3 require导入commonJS 的js文件方法

vite3 导入commonJS 方式导出 在Vite 3中&#xff0c;你可以通过配置vite.config.js来实现导入CommonJS&#xff08;CJS&#xff09;风格的模块。Vite 默认支持ES模块导入&#xff0c;但如果你需要导入CJS模块&#xff0c;可以使用特定的插件&#xff0c;比如originjs/vite-pl…