☰
OrchardCore 前端资源管理器(Asset Manager)实战指南:Assets.json、构建工具链与源码级原理
2026/9/26 7:52:50 网站建设 项目流程
  • CMS
  • 后端
  • Web框架

【免费下载链接】OrchardCore

Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.

项目地址:https://gitcode.com/gh_mirrors/or/OrchardCore
点击查看免费下载

本篇指南围绕 OrchardCore 仓库中负责前端资源编译的核心工具——Asset Manager(资源管理器)展开,系统讲解其依赖环境要求、构建/监听/清理命令、三级 package.json 结构,以及Assets.json中 8 种动作(vite、sass、min、copy、concat、parcel、webpack、run)的完整配置与实战示例,并结合.scripts/assets-manager/下的实际源码揭示底层执行原理。读完本文,你将能够独立为任意模块或主题新增、构建、调试前端资源,并掌握yarn build/yarn watch/yarn host/yarn clean/yarn check/yarn lint/yarn dry-run等命令的用法与适用场景。

一、Asset Manager 是什么:模块与主题前端资源的统一编译入口

OrchardCore 是一个基于 ASP.NET Core 的模块化、多租户应用框架与 CMS。除了 C# 后端,大量模块(如 OrchardCore.Media、OrchardCore.Resources)和主题(如 TheAdmin、TheBlogTheme)还携带 SCSS、JavaScript、TypeScript、Vue 等前端资源。Asset Manager 正是负责编译这些资源的统一工具链:它以Assets.json文件为清单,在src/{OrchardCore.Modules,OrchardCore.Themes}/*/下自动发现各模块/主题的资源定义,再驱动 Vite、Parcel、Webpack、Sass 等打包器完成构建。

从源码结构看,整套工具链实现位于 .scripts/assets-manager 目录,其 package.json 声明为@orchardcore/assets-manager,入口为./build.mjs;根目录 package.json 通过"build": "assets-manager build"、"watch": "assets-manager watch"、"dry-run": "assets-manager dry-run"、"clean": "assets-manager clean"等脚本将其桥接为仓库级yarn命令。

资源发现机制

资产清单的定位逻辑在 assetGroups.mjs 中实现:

  • getAllAssetGroups()通过buildConfig("assetsLookupGlob")获取 glob,默认值为src/{Modules,Themes}/*/Assets.json(见 config.mjs),遍历仓库下所有模块/主题的Assets.json;
  • 每个清单用 JSON5 解析(支持注释与尾逗号),逐条展开为 "asset group"(资源组);
  • resolveAssetGroupPaths()会把source与dest解析为绝对路径,其中以node_modules开头的路径统一从**仓库根目录(workspace root)**解析,其余路径则相对于该Assets.json所在目录解析——这正是后面copy/concat/min动作中node_modules路径语义的由来。

默认 glob 与parcelBundleOutput等关键配置都可以通过仓库根目录的build.config.mjs覆写(该文件未提交到仓库,由各开发者按需创建),见 config.mjs 的实现。

二、环境准备:Node.js 24.x 与版本检测机制

Node.js 版本要求

仓库通过根目录的 .node-version 文件将 Node.js 版本固定为24.x(当前仓库内容为24.20.0)。构建脚本会在启动时读取该文件并与当前process.versions.node比对,版本不匹配时弹出交互式选择:

1) Continue anyway 2) Abort 3) Install via fnm, Node.js 24.x and build 4) Install via Volta, Node.js 24.x and build

推荐始终选择选项 1(继续)或选项 3(通过 fnm 安装)。该提示逻辑、fnm/Volta 的安装与 PATH 处理、以及 CI 环境(检测到CI/TF_BUILD/GITHUB_ACTIONS环境变量时直接报错退出)的完整实现见 build.mjs 开头的版本检测段落。构建脚本甚至会在磁盘上查找~/.local/share/fnm、~/.fnm、~/.volta/bin等已知安装目录并动态扩展 PATH,避免"装了但当前终端找不到"的经典问题。

在自动化/无交互环境下,可以用管道输入预置答案:

echo "1" | yarn build # 使用当前 Node.js 继续构建 echo "3" | yarn build # 通过 fnm 安装 Node 24 并构建

安装依赖

若包缺失(如首次克隆仓库、或修改了任一package.json),使用 fnm 指定版本执行安装:

fnm exec --using 24.14.1 -- corepack yarn install

注意:修改任何package.json(根目录、.scripts/assets-manager/、或某个模块的Assets/目录)之后都必须重新执行 install,否则新增依赖不会被解析。

三、构建命令速查:build / watch / host / clean

所有命令均从仓库根目录执行。CLI 的核心参数为:

  • -n/--name/--names:按Assets.json中的"name"字段精确筛选要构建的资源(支持逗号分隔多个);
  • -t/--tag/--tags:按"tags"数组筛选(支持逗号分隔多个);
  • -b/--bundle:仅构建带 bundle 入口的资源(内部会聚合为orchardcore-bundle)。
# 构建全部资源 echo "1" | yarn build # 按名称构建单个资源 echo "1" | yarn build -n media-app # 按名称构建多个资源 echo "1" | yarn build -n media-app,media-field # 按标签构建(例如所有带 admin 标签的资源) echo "1" | yarn build -t admin # 监听模式:保存即自动重新构建(开发模式) echo "1" | yarn watch -n media-app # 以打包器开发服务器托管(支持 HMR 热更新) echo "1" | yarn host -n media-app # 清理全部构建产物 echo "1" | yarn clean

从 build.mjs 的源码可以看到这些参数的解析方式:minimist解析后,parseFilterValues会把-n/-t的值按空白与逗号拆分成数组,再分别对资源组做name精确匹配或tags交集匹配;watch与host任务强制要求通过-n指定资源(不允许按标签监听)。随后各组按action分派到vite.mjs/parcel.mjs/webpack.mjs/sass.mjs/copy.mjs/min.mjs/concat.mjs等子进程,组配置以 Base64 编码后通过ASSETS_MANAGER_ENCODED_GROUP环境变量传入,最后用concurrently并行执行并按order字段排序。

构建产物与提交约定

构建输出写入模块/主题自身的wwwroot/目录,并且必须提交到仓库。修改任何资源后,需要同时提交源文件与生成的wwwroot/文件——这是 OrchardCore 资源发布机制的一部分:运行中的站点直接静态托管wwwroot下的产物,无需在部署时重新构建前端。例如 OrchardCore.Media 的产物可见于 src/OrchardCore.Modules/OrchardCore.Media/wwwroot/Scripts(media2.js、media2.min.js、media-picker2.js等)。

四、三级 package.json 结构与依赖管理

整个仓库采用 Yarn workspaces 组织依赖,分为三个层级(见根目录 package.json 的workspaces字段与.scripts/assets-manager/package.json):

位置作用
根目录package.jsonWorkspace 编排、版本解析、顶层脚本(build/watch/clean/lint/check)
.scripts/assets-manager/package.json构建工具链本体(Vite、Parcel、Sass、Webpack、esbuild、lightningcss 等)
src/.../Assets/package.json被捆绑进该模块/主题的运行时依赖
  • 为模块添加运行时依赖:在模块的Assets/目录下执行yarn add some-library@1.2.3,然后重新构建对应资源(echo "1" | yarn build -n asset-name)。运行时依赖会被打包进该模块产出的 bundle 中。
  • 添加构建工具(影响所有资源):编辑 .scripts/assets-manager/package.json,然后运行fnm exec --using 24.14.1 -- corepack yarn install。该文件目前内置的构建依赖包括vite@^8.1.5、parcel@2.13.3、sass@^1.85.1、webpack@5.94.0、esbuild@^0.28.1、lightningcss@1.29.1、postcss-rtlcss@5.6.0、@vitejs/plugin-vue@^6.0.8等,是理解各 action 能力边界的直接依据。

workspaces 还包含了src/Frontend/与.scripts/bloom/(Media 模块 Vue 应用共享的组件库,见 .scripts/bloom),说明部分前端工程是跨模块共享的。

五、Assets.json 清单格式与 8 种动作详解

每个带资源的模块/主题在其根目录都有一个Assets.json(例如 src/OrchardCore.Modules/OrchardCore.Media/Assets.json、src/OrchardCore.Themes/TheAdmin/Assets.json)。它是一个 JSON 数组,每个元素描述一个资源组:

  • action:构建动作类型(必填);
  • name:唯一标识,也是-n命令行筛选用的名称(必填);
  • source:源文件/目录/glob(字符串或字符串数组);
  • dest:输出目录(可选,各动作有默认值);
  • tags:字符串数组,供-t筛选(可选);
  • order:构建顺序(可选,越小越先执行,见 build.mjs 中的排序逻辑);
  • generateRTL:是否额外生成 RTL 样式(部分动作支持);
  • options:透传给具体打包器的附加选项(如 parcel 的shouldContentHash)。

一个最简示例:

[ { "action": "vite", "name": "media-app", "source": "Assets/media-app/", "tags": ["admin", "js"] }, { "action": "sass", "name": "media-styles", "source": "Assets/scss/media.scss", "tags": ["admin", "css"] } ]

以下按 .agents/skills/orchardcore-asset-manager/references/actions.md 与源码逐一展开 8 种动作。

5.1 vite —— Vue 应用与 TypeScript 项目

使用 Vite 打包,读取source目录下的vite.config.ts。适用于 Vue 应用与 TS 工程。

{ "action": "vite", "name": "media-app", "source": "Assets/media-app/", "tags": ["admin", "js"] }

关键约束与行为:

  • source为包含vite.config.ts的根目录;输出位置取决于配置中的build.outDir;
  • Asset Manager 会自动注入orchard-minify插件(见 vite.mjs 中plugins: [minifyPlugin()]),因此不要在vite.config.ts中设置build.minify;
  • 不要在 Vite 应用目录内放 workspace 级package.json,应使用模块的Assets/package.json。

真实示例可见 media-picker/vite.config.ts:它设置了build.outDir指向模块wwwroot、build.minify: false(注释明确说明"Minification is handled by the asset-manager pipeline"),并通过lib模式输出Scripts/media-picker2.js。同目录的 media-gallery/vite.config.ts 展示了 Vue + Tailwind + RTL 的完整配置形态。

orchard-minify 插件的产物约定(见 plugins/vite-plugin-minify.mjs):对每个 JS chunk 用 esbuild、对 CSS 用 lightningcss 压缩,生成三件套:

  • file.js—— 压缩且带//# sourceMappingURL引用;
  • file.min.js—— 压缩且不带sourcemap 引用;
  • file.map—— source map。

资源清单(Resource Manifest)衔接:构建产物需要在 C# 侧注册为资源。以 ResourceManifestOptionsConfiguration.cs 为范本:

s_manifest .DefineScript("media-picker") .SetUrl("~/OrchardCore.Media/Scripts/media-picker2.min.js", "~/OrchardCore.Media/Scripts/media-picker2.js") .SetVersion("2.0.0") .SetAttribute("type", "module");

SetUrl的第一参数为生产环境使用的压缩版(.min.js),第二参数为调试/开发使用的非压缩版(.js),OrchardCore 资源管理系统会按环境自动选择——这也解释了为何构建必须同时产出.js与.min.js两个文件。

5.2 sass —— SCSS 转译

将 SCSS 转译为 CSS,默认输出到wwwroot/Styles/,可用dest覆盖。

{ "action": "sass", "name": "admin-dashboard", "source": "Assets/scss/dashboard.scss", "tags": ["admin", "css"] }
  • source:单个.scss文件或 glob(如Assets/trumbowyg/plugins/*/ui/sass/*.scss,见 OrchardCore.Resources 的 Assets.json);
  • dest:可选输出目录(默认wwwroot/Styles/);
  • 支持generateRTL: true额外生成 RTL 版本。

真实案例:TheAdmin 主题在 src/OrchardCore.Themes/TheAdmin/Assets.json 中定义了theme-theadmin、theme-theadmin-layout、theme-theadmin-login三个 sass 组,均设置generateRTL: true并输出到wwwroot/css/。

5.3 min —— JS/CSS 压缩

对 JS 或 CSS 文件做压缩,产出.min.js/.min.css与.map文件。

{ "action": "min", "name": "media-field", "source": "Assets/js/media-field.js", "dest": "wwwroot/Scripts/", "tags": ["admin", "js"] }
  • source:文件或 glob;
  • dest:可选输出目录。

这是 OrchardCore.Resources 中使用最频繁的动作之一(src/OrchardCore.Modules/OrchardCore.Resources/Assets.json),例如对node_modules/jquery/dist/jquery.js、node_modules/codemirror/...等数十个第三方库做压缩并输出到wwwroot/Scripts/codemirror/等路径。注意这里node_modules/从 workspace 根解析,与copy动作一致。

5.4 copy —— 原样拷贝(常用于 node_modules 依赖)

将文件(常来自node_modules)复制到wwwroot/。不监听文件变化。

{ "action": "copy", "name": "bootstrap-5", "source": [ "node_modules/bootstrap/dist/css/bootstrap.min.css", "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" ], "dest": "wwwroot/Vendor/bootstrap-5/", "tags": ["resources"] }
  • source:字符串或字符串数组/glob;
  • dest:输出目录;若省略,则根据tags与第一个源文件扩展名推断;
  • node_modules/路径从workspace 根解析(实现见 assetGroups.mjs 对src.startsWith("node_modules")的处理)。

真实案例:OrchardCore.SignalR 通过 src/OrchardCore.Modules/OrchardCore.SignalR/Assets.json 将../../../node_modules/@microsoft/signalr/dist/browser/*.js拷入wwwroot/Scripts/;OrchardCore.Resources 则用 copy 把 Bootstrap、FontAwesome、jQuery 各历史版本、Monaco 编辑器等资源分发到wwwroot/Vendor/下的独立版本目录。

5.5 concat —— 多文件拼接

将多个文件按顺序物理拼接为一个文件,不经过打包器。

{ "action": "concat", "name": "media", "source": [ "node_modules/blueimp-file-upload/js/jquery.fileupload.js", "Assets/js/app/Shared/uploadComponent.js" ], "dest": "wwwroot/Scripts" }
  • source:文件数组(不支持 glob);
  • 所有node_modules/路径从 workspace 根解析——因此所有模块必须就同一依赖版本达成一致,或者使用NPM 别名(aliasing),例如"bootstrap-4.6.1": "npm:bootstrap@4.6.1"(仓库中正是这样为不同模块并存的旧版本依赖命名的)。

真实案例:OrchardCore.Resources 用 concat 将 trumbowyg 的 20 余个插件 JS 文件拼接为单个wwwroot/Scripts/trumbowyg/产物。

5.6 parcel —— 零配置打包

运行 Parcel 打包器,零配置,从Assets/package.json读取入口。

{ "action": "parcel", "name": "datasource-wrapper", "source": "Assets/Scripts/datasource-wrapper.js", "dest": "wwwroot/datasource-wrapper", "tags": ["js"] }
  • source:入口文件;
  • dest:输出目录(必填,Parcel 会产出多个文件);
  • 若删除输出后缓存过期,先运行yarn clean再构建。

真实案例:OrchardCore.Media 的 Assets.json 中media-editors、media-profiles-index、media-profile-editor均为 parcel 组,输出到wwwroot/Scripts/media-editors/等目录;OrchardCore.Resources 则用 parcel 构建 Monaco 编辑器的 ESM 版本及其 5 个 worker 入口(monaco-esm),并通过options.shouldContentHash: false关闭内容哈希。

5.7 webpack —— Webpack 打包

使用配置文件运行 Webpack。

{ "action": "webpack", "name": "graphiql", "config": "/Assets/webpack.config.js", "tags": ["admin", "js"] }
  • config:webpack.config.js的路径(相对于模块根目录)。

该动作是保留给仍依赖 Webpack 的既有前端工程的过渡通道,当前仓库中 GraphQL 相关前端即采用此方式。

5.8 run —— 运行任意命令

通过 Asset Manager 调度任意 shell 命令,用于自定义构建脚本。

{ "action": "run", "name": "custom-app", "source": "Assets/custom-app", "scripts": { "build": "yarn build", "watch": "yarn start" } }
  • source:命令的工作目录;
  • scripts:把管道命令(build/watch/host)映射为要执行的 shell 命令。

在 build.mjs 中,run 动作的实现是cd ${group.source} && ${script},且只在scripts中恰好存在当前任务名对应的脚本时才执行,否则跳过并给出提示。

六、各动作与任务的兼容矩阵

从 build.mjs 的分派逻辑可以整理出动作与任务的兼容关系(构建时并行执行、按order排序,见代码中concurrently部分):

动作buildwatchhostdry-runcopyclean
vite✅✅(build.watch模式)✅(dev server + HMR)——✅
parcel✅✅✅——✅
webpack✅✅✅——✅
sass✅✅———✅
copy✅—(提示使用 copy/build)—✅✅✅
min✅——✅—✅
concat✅——✅—✅
run视scripts[task]是否存在视scripts.watch视scripts.host———

七、代码质量命令:check / lint / dry-run

这三个命令不经过 Node.js 版本提示,可直接执行:

# 对所有 Vue/TS 文件做类型检查(vue-tsc --noEmit) yarn check # 对整个仓库执行 ESLint yarn lint # 只对某个目录执行 ESLint yarn lint src/OrchardCore.Modules/OrchardCore.Media/Assets/ # 预览将要构建/拷贝的文件清单(不写入任何文件) echo "1" | yarn dry-run echo "1" | yarn dry-run -n media-app # 限定单个资源
  • yarn check:底层是vue-tsc --noEmit(根 package.json 中定义为yarn workspaces foreach -A -v -v run check),用于在提交 TS/Vue 改动前捕获类型错误。不支持-n;若要检查特定模块,直接指向该模块的 tsconfig:yarn vue-tsc --noEmit -p src/OrchardCore.Modules/OrchardCore.Media/Assets/media-app/tsconfig.json。
  • yarn lint:仓库级 ESLint(根目录 eslint.config.mjs),可传文件/目录参数限定范围。
  • yarn dry-run:仅打印构建计划(拷贝目标、输出路径)而不触碰文件,非常适合在新增Assets.json条目后先验证路径正确性再执行首次真实构建。

八、常见问题排查

问题解决方案
Cannot find package '@tailwindcss/vite'运行fnm exec --using 24.14.1 -- corepack yarn install重新安装依赖
Parcel 缓存过期(删除输出后仍报错)先echo "1" | yarn clean,再重新构建
改动未生效确认生成的wwwroot/文件确实更新;未更新则重新构建
误跑了全仓库构建(-n值写错,例如用了 package.json 的 name 而不是Assets.json的"name")立即执行git status --short检查;用git checkout --还原目标模块/主题之外的所有改动路径,再提交
改了源码但运行中的应用仍复现旧 bug忘记重新构建了。对比时间戳:stat -c '%y' <source>.ts与stat -c '%y' wwwroot/Scripts/<bundle>.js,若 bundle 更旧,运行yarn build -n <asset-name>

关于-n的常见误解

-n匹配的是Assets.json中的"name"字段(如media-gallery、theme-theadmin),不是package.json 中的包名。以 OrchardCore.Media 为例,其 Assets.json 中的合法-n取值是media-gallery、media-picker、media-editors、media-profiles-index、media-profile-editor。若误用了 package.json 的 name,筛选结果为空,会导致意外构建全仓库资源,务必留意。

九、为模块新增一个前端资源的完整流程

综合以上内容,给模块(或主题)新增前端资源的标准步骤为:

  1. 编写源码:在模块的Assets/目录下创建源文件(如Assets/js/my-widget.js或Assets/scss/my-widget.scss)。若需要运行时依赖,先在Assets/下yarn add <package>@<version>;
  2. 登记Assets.json:在模块根目录的Assets.json中追加一个条目,按需选择action(Vue 应用用vite、TS 入口用parcel、样式用sass、第三方库分发用copy/min等),并填写唯一的name与tags;
  3. 验证路径:echo "1" | yarn dry-run -n my-widget预览构建计划,确认 source/dest 无误;
  4. 构建:echo "1" | yarn build -n my-widget,检查wwwroot/下的产物(.js/.min.js/.map或.css/.min.css);
  5. 在 C# 侧注册资源:参照 ResourceManifestOptionsConfiguration.cs,用DefineScript/DefineStyle+SetUrl("...min.js", "...js")注册,供 Razor/Liquid 视图按环境引用压缩版或调试版;
  6. 质量检查:yarn check(类型)与yarn lint <assets-dir>(代码规范);
  7. 提交:将源码与生成的wwwroot/文件一并提交到仓库。

十、总结:Asset Manager 的定位与边界

Asset Manager 将 OrchardCore 的前端构建收敛为"一份Assets.json清单 + 一组 yarn 命令"的简单模型:开发者不需要逐模块配置 Vite/Parcel/Webpack,也不需要维护各自的构建脚本;构建工具链集中在 .scripts/assets-manager 一处(vite.mjs、parcel.mjs、webpack.mjs、sass.mjs、copy.mjs、min.mjs、concat.mjs、clean.mjs、output.mjs、assetGroups.mjs、config.mjs),产物统一落入各模块/主题的wwwroot/并随仓库提交。需要强调的是,本文描述的版本、命令与配置均以当前仓库内容为准:Node.js 版本由 .node-version 固定,构建依赖版本见 .scripts/assets-manager/package.json,各动作的权威行为则分别体现在 build.mjs 与动作参考文档 actions.md 中——当两者冲突时,以源码为准。

  • CMS
  • 后端
  • Web框架

【免费下载链接】OrchardCore

Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.

项目地址:https://gitcode.com/gh_mirrors/or/OrchardCore
点击查看免费下载

相关推荐

上一篇:coordTransform_py核心函数解析:从源码角度理解坐标转换原理
下一篇:缠论算法实战:C++可视化插件的深度解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询