NocoBase RunJS 模块导入完全指南:ctx.libs 内置库、importAsync/requireAsync 动态加载与 ESM CDN 配置

发布时间:2026/9/18 0:58:15
NocoBase RunJS 模块导入完全指南:ctx.libs 内置库、importAsync/requireAsync 动态加载与 ESM CDN 配置 NocoBase RunJS 模块导入完全指南ctx.libs 内置库、importAsync/requireAsync 动态加载与 ESM CDN 配置【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobaseRunJS 是 NocoBase 中 JS 区块、JS 字段、JS 操作等场景的 JavaScript 执行环境支持顶层await、ctx上下文 API、容器内渲染与模块导入。本文聚焦 RunJS 的模块导入体系如何使用零成本的内置库ctx.libs、如何通过ctx.importAsync()/ctx.requireAsync()按需加载 ESM 与 UMD/AMD 第三方模块以及如何通过环境变量切换默认 CDN如自建 esm.sh 服务或 jsDelivr。读完本文你将能够在 RunJS 中安全、高效地引入任意第三方库并理解其底层 URL 解析与模块加载原理。一、总览RunJS 的两类模块RunJS 中可使用的模块分为两类使用方式完全不同类别访问方式是否需要 import内置模块通过ctx.libs直接使用不需要外部模块ctx.importAsync()ESM或ctx.requireAsync()UMD/AMD按需加载需要异步加载内置模块零成本、开箱即用外部模块则按 URL 动态加载覆盖任意第三方库场景。这一设计在 RunJS 概述 中也被列为 RunJS 的四大核心能力之一顶层异步、导入外部模块、容器内渲染、全局变量。二、内置模块ctx.libs无需 importRunJS 内置了常用库可直接通过ctx.libs访问无需import或异步加载属性说明ctx.libs.ReactReact 本体用于 JSX 与 Hooksctx.libs.ReactDOMReactDOM如需 createRoot 等可配合使用ctx.libs.antdAnt Design 组件库ctx.libs.antdIconsAnt Design 图标ctx.libs.dayjs日期时间处理库dayjsctx.libs.lodashLodash 工具库ctx.libs.mathMath.js数学表达式、矩阵运算等ctx.libs.formulaFormula.js类 Excel 公式SUM、AVERAGE 等源码依据内置库的注册与懒加载从源码看ctx.libs并非一次性加载全部库而是通过注册表 懒加载 缓存机制实现的。在 packages/core/flow-engine/src/runjsLibs.ts 中DEFAULT_RUNJS_LIBS数组声明了全部默认内置库其中React、ReactDOM、antd、dayjs为context级缓存从当前 RunJS 上下文取值而antdIcons、lodash、formula、math为global级缓存使用import(lodash)、import(formulajs/formulajs)、import(mathjs)等动态导入ctx.libs的每个属性通过 getter首次访问时才真正解析resolveRegisteredLibSync并把结果物化为可写数据属性任何库的解析结果会缓存在__runjsLibResolvedCacheglobal 级或按上下文缓存的 Map 中避免重复加载。也就是说ctx.libs.math只有在你的代码真正访问它的那一刻才会被加载未使用到的内置库不会产生额外开销。示例React 与 antdconst { Button } ctx.libs.antd; ctx.render(Button点击/Button);示例ctx.libs.mathconst result ctx.libs.math.evaluate(2 3 * 4); // result 14mathjs的evaluate支持任意合法数学表达式包括函数调用与常量例如ctx.libs.math.evaluate(round(sqrt(16), 2))。示例ctx.libs.formulaconst values [1, 2, 3, 4]; const sum ctx.libs.formula.SUM(values); const avg ctx.libs.formula.AVERAGE(values);formula提供类 Excel 的公式集合SUM、AVERAGE、IF、VLOOKUP 等适合在 JS 字段中做表格风格的数据计算。提示在 RunJS 编辑器中ctx.libs下各库的属性和典型用法如ctx.libs.lodash.get(obj, a.b)、ctx.libs.formula.SUM(1, 2, 3)均有代码补全提示实现见 packages/core/flow-engine/src/runjs-context/contexts/base.ts。三、外部模块按需加载第三方库需要第三方库时根据模块格式选择加载方式ESM 模块→ 使用ctx.importAsync()UMD/AMD 模块→ 使用ctx.requireAsync()两者的核心区别在于模块格式与解析方式importAsync使用浏览器原生 dynamic import 加载真正的 ESM 产物requireAsync则借助 NocoBase 前端已有的 requirejsAMD加载 UMD/AMD 或全局脚本。注意若库同时提供 ESM 版本优先使用ctx.importAsync()以获得更好的模块语义与 Tree-shaking 支持。四、导入 ESM 模块ctx.importAsync()使用ctx.importAsync()按 URL 动态加载 ESM 模块适用于 JS 区块、JS 字段、JS 操作等场景。importAsyncT any(url: string): PromiseT;参数说明urlESM 模块地址。支持简写格式包名版本或带子路径包名版本/文件路径如vue3.4.0、lodash4/lodash.js会按配置拼接 CDN 前缀也支持完整 URLhttp:///https://开头原样使用。返回解析后的模块命名空间对象。若模块只有default一个导出会直接返回default值无需再写.default见下方源码分析。源码依据importAsync 的实现链路ctx.importAsync定义在 packages/core/flow-engine/src/flowContext.tsthis.defineMethod(importAsync, async function (this: any, url: string) { // 判断是否为 CSS 文件支持 example.css?v123 等形式 if (isCssFile(url)) { return this.loadCSS(url); } return await runjsImportModule(this, url, { importer: runjsImportAsync }); });这里有两个值得注意的设计CSS 直通如果 URL 是.css文件支持 query/hash如style.css?v123importAsync会转而调用loadCSS注入link relstylesheet而不是做 JS 导入——判断逻辑见 packages/core/flow-engine/src/utils/resolveModuleUrl.ts 中的isCssFile()。归一化导出runjsImportModule在拿到模块对象后调用normalizeModule()——许多经由 esm.sh / esbuild 转换的模块会把主导出挂在default上若模块只有default一个导出键则直接返回default提升易用性。runjsImportModule的完整实现位于 packages/core/flow-engine/src/utils/runjsModuleLoader.ts它还负责antd 特判重写当简写为antdx.y.z不带子路径时会自动追加bundle1查询参数将依赖内联解决 antd 在 esm.sh 上命名导出缺失问题并在检测到外部 React 已加载时追加depsreact版本,react-dom版本避免同一页面出现多个 React 实例全局缓存以解析后的完整 URL 为 key缓存在globalThis.__nocobaseImportAsyncCache同一 URL 只加载一次内置库覆盖当导入react、react-dom/client、antd、ant-design/icons时会自动覆盖ctx.libs.React、ctx.libs.ReactDOM、ctx.libs.antd、ctx.libs.antdIcons及顶层ctx.React等别名保证ctx.render使用的 React 与后续导入的库版本一致setRunJSLibOverride见 packages/core/flow-engine/src/runjsLibs.ts。测试 packages/core/flow-engine/src/tests/runjsExternalLibs.test.ts 验证了上述行为例如await ctx.importAsync(react18.2.0); // runjsImportAsync 被调用为 https://esm.sh/react18.2.0 与 https://esm.sh/react-dom18.2.0/client // 且 ctx.React / ctx.libs.React / ctx.ReactDOM / ctx.libs.ReactDOM 均被覆盖为外部导入的实例 await ctx.importAsync(antd5.29.3); // 实际请求 https://esm.sh/antd5.29.3?bundle1动态导入的兼容性处理runjsImportAsyncrunjsModuleLoader.ts解决了 RunJS 与 NocoBase 前端 AMD 体系requirejs共存时的一个典型问题许多 UMD/CJS 库如 lodash在运行时探测define.amd若存在则优先走 AMD 分支导致 esm.sh 等 CDN 的“CJS/UMD → ESM 包装”无法从module.exports提取导出最终 ESM 导出为undefined因此importAsync在真正执行import()的瞬间会临时屏蔽define.amd通过属性描述符恢复原状并先用link relmodulepreload预取模块、等待 requirejs 空闲尽量缩小全局副作用窗口导入语句带有vite-ignore/webpackIgnore: true标记以避免被打包器重写若仍被拦截再用eval(u import(u))兜底。这些都属于 best-effort 处理即使内部结构变化或浏览器能力差异导致某一步失败也不会阻断正常流程。默认为 https://esm.sh未配置时简写形式会使用https://esm.sh作为 CDN 前缀。例如const Vue await ctx.importAsync(vue3.4.0); // 等价于从 https://esm.sh/vue3.4.0 加载自建 esm.sh 服务 / 自定义 CDN若需内网或自建 CDN可部署兼容 esm.sh 协议的服务并通过环境变量指定ESM_CDN_BASE_URLESM CDN 基础地址默认https://esm.shESM_CDN_SUFFIX可选后缀如 jsDelivr 的/esm环境变量如何生效构建时这两个环境变量会被注入为浏览器全局变量实现见 packages/core/app/client-v2/rsbuild.config.tswindow[__esm_cdn_base_url__] window[__esm_cdn_base_url__] || process.env.ESM_CDN_BASE_URL || https://esm.sh; window[__esm_cdn_suffix__] window[__esm_cdn_suffix__] || process.env.ESM_CDN_SUFFIX || ;而resolveModuleUrlresolveModuleUrl.ts在运行时读取window.__esm_cdn_base_url__与window.__esm_cdn_suffix__完成拼接// 相对路径会被拼接上 CDN 前缀和后缀默认添加后缀 resolveModuleUrl(vue3.4.0) // https://esm.sh/vue3.4.0 // 如果使用 jsdelivr需要配置 ESM_CDN_SUFFIX/esm // resolveModuleUrl(vue3.4.0) https://cdn.jsdelivr.net/npm/vue3.4.0/esm // 不添加后缀适用于 UMD 库或 CSS 文件 resolveModuleUrl(vue3.4.0, { addSuffix: false }) // https://esm.sh/vue3.4.0 // 原始 URL适用于 UMD 库 resolveModuleUrl(lodash4.17.21/lodash.js, { raw: true }) // https://esm.sh/lodash4.17.21/lodash.js?raw // 完整 URL 保持不变 resolveModuleUrl(https://cdn.jsdelivr.net/npm/vue3.4.0) // https://cdn.jsdelivr.net/npm/vue3.4.0切换到 jsDelivr 的配置示例以 jsDelivr 的 ESM 服务为例其路径格式为https://cdn.jsdelivr.net/npm/包名版本/文件/esm# 构建/启动 NocoBase 前端时注入 export ESM_CDN_BASE_URLhttps://cdn.jsdelivr.net/npm export ESM_CDN_SUFFIX/esm配置后await ctx.importAsync(vue3.4.0)将被解析为https://cdn.jsdelivr.net/npm/vue3.4.0/esm。自建兼容 esm.sh 协议的服务可参考官方开源的 esm-server 项目搜索 nocobase esm-server 即可找到仓库。五、导入 UMD/AMD 模块ctx.requireAsync()使用ctx.requireAsync()按 URL 异步加载 UMD/AMD 或挂载到全局的脚本。requireAsyncT any(url: string): PromiseT;url支持两种形式简写路径包名版本/文件路径与ctx.importAsync()相同会按当前 ESM CDN 配置解析解析时会加上?raw直接请求该路径的原始文件多为 UMD 构建。例如echarts5/dist/echarts.min.js实际请求https://esm.sh/echarts5/dist/echarts.min.js?raw当默认使用 esm.sh 时。完整 URL任意 CDN 的完整地址如https://cdn.jsdelivr.net/npm/xxx。返回加载后的库对象具体形式取决于该库的导出方式加载后许多 UMD 库会挂到全局对象如window.xxx使用时按该库文档即可。源码依据requireAsync 的实现ctx.requireAsync定义在 packages/core/flow-engine/src/flowContext.tsthis.defineMethod(requireAsync, async (url: string) { // 判断是否为 CSS 文件支持 example.css?v123 等形式 if (isCssFile(url)) { return this.loadCSS(url); } const u resolveModuleUrl(url, { raw: true }); return await runjsRequireAsync(this.requirejs, u); });与importAsync相同CSS 文件同样会走loadCSS分支。非 CSS 时调用resolveModuleUrl(url, { raw: true })解析出...?raw的原始文件地址再交给runjsRequireAsyncrunjsModuleLoader.ts通过 requirejs 加载requirejs([url], (mod) resolve(mod), reject);runjsRequireAsync与runjsImportAsync共用同一把全局串行锁withRunjsModuleLoadLock避免并发加载期间对全局对象如define.amd的临时改动互相干扰。示例// 简写路径经 esm.sh 解析为 ...?raw const echarts await ctx.requireAsync(echarts5/dist/echarts.min.js); // 完整 URL const dayjs await ctx.requireAsync(https://cdn.jsdelivr.net/npm/dayjs1/dayjs.min.js);说明?raw模式返回的是 CDN 上的原始构建文件后缀配置如/esm不会追加这一点与 ESM 导入不同——raw: true时resolveModuleUrl直接拼接?raw并跳过addSuffix逻辑。六、选择建议与最佳实践场景推荐方式理由React、antd、dayjs、lodash、mathjs、formulajs 等内置库ctx.libs.xxx零加载成本有类型/补全提示有 ESM 产物的第三方库ctx.importAsync()更好的模块语义与 Tree-shaking导出归一化仅提供 UMD/AMD 或全局脚本的库如部分图表库、老式插件ctx.requireAsync()与 NocoBase 前端 requirejs 体系一致样式文件importAsync/requireAsync传.cssURL自动走loadCSS注入link实践要点优先ctx.libs内置库已经过验证并与 RunJS 环境React 渲染、antd 主题深度集成非必要不重复加载外部版本版本对齐当importAsync导入外部react时运行时会自动顺带加载匹配的react-dom同版本/client并覆盖ctx.libs.ReactDOM见runjsImportModule的 override 逻辑保证ctx.render渲染一致若导入 antd 而ctx.libs.React仍是内置版本运行时会给出[RunJS Hint]提示建议同时导入外部 React 与 antd复用缓存importAsync对同一 URL 有全局缓存__nocobaseImportAsyncCache重复调用不会重复请求网络避免多次 React 实例若渲染组件时出现 Invalid hook call 或Cannot read properties of null (reading useState)类错误通常是多个 React 实例共存导致可先await ctx.importAsync(react与报错栈一致的版本)再读取ctx.libs.React/ 调用 hooks——运行时会基于错误栈自动生成修复提示。七、进阶阅读模块导入是 RunJS 上下文能力的一部分与之紧密相关的还有RunJS 概述顶层 await、容器内渲染、全局变量等整体能力JSX 渲染如何在 RunJS 中编写 JSX容器渲染ctx.render()的三种渲染形式上下文 APIctx完整方法说明Window 全局浏览器全局对象的使用若想深入源码可从 packages/core/flow-engine/src/utils/runjsModuleLoader.ts加载器核心、packages/core/flow-engine/src/utils/resolveModuleUrl.tsURL 解析、packages/core/flow-engine/src/runjsLibs.ts内置库注册与 override以及 packages/core/flow-engine/src/tests/runjsExternalLibs.test.ts外部库行为测试入手。【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考