
* chore: refine plugin development documentation Signed-off-by: Ryan Wang <i@ryanc.cc> * chore: refine plugin development documentation Signed-off-by: Ryan Wang <i@ryanc.cc> * chore: refine plugin development documentation Signed-off-by: Ryan Wang <i@ryanc.cc> --------- Signed-off-by: Ryan Wang <i@ryanc.cc>
12 KiB
title, description
title | description |
---|---|
构建 | UI 部分的构建说明 |
在 halo-dev/plugin-starter 模板中,我们已经配置好了 UI 的构建工具和流程,此文档主要说明一些构建细节以及其他可能的构建选项。
原理
Halo 插件的 UI 部分(Console / UC)的实现方式其实很简单,本质上就是构建一个结构固定的大对象,交给 Halo 去解析,其中包括全局注册的组件、路由定义、扩展点等。在 halo-dev/plugin-starter 模板中,我们使用 index.ts
作为入口文件,并在构建之后将 main.js
和 style.css
放到插件项目的 src/main/resources/console
目录中,后续 Halo 在内部会自动合并所有插件的 main.js
和 style.css
文件,并生成最终的 bundle.js
和 bundle.css
文件,然后在 Console 和 UC 中加载这两个资源并解析。
所以本质上,我们只需要使用支持将 index.ts
编译为 main.js
和 style.css
的工具,然后输出到插件项目的 src/main/resources/console
目录中即可,在 halo-dev/plugin-starter 模板中可以看到,我们提供了一个名为 @halo-dev/ui-plugin-bundler-kit
的库,这个库包含了 Vite 和 Rsbuild 的预配置,插件项目只需要通过简单的配置即可使用。
@halo-dev/ui-plugin-bundler-kit
在这个库中,我们提供了三个预配置,分别是:
viteConfig
: Vite 的预配置,halo-dev/plugin-starter 中默认使用的配置rsbuildConfig
: Rsbuild 的预配置HaloUIPluginBundlerKit
:已过时,迁移方式可以参考下面的文档
viteConfig
使用
如果你想要使用 Vite 构建 UI 部分,那么使用 viteConfig
即可,并且已经在 halo-dev/plugin-starter 中配置,直接使用即可。
配置
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
export default viteConfig({
vite: {
// 自定义 Vite 配置
plugins: [
// 额外的插件(Vue 插件已预配置)
],
// 其他配置...
},
});
示例:
-
添加路径别名
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; import path from "path"; export default viteConfig({ vite: { resolve: { alias: { "@": path.resolve(__dirname, "src"), "@components": path.resolve(__dirname, "src/components"), }, }, }, });
-
添加额外的 Vite 插件
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; import { defineConfig } from "vite"; import UnoCSS from "unocss/vite"; export default viteConfig({ vite: { plugins: [ UnoCSS(), // 添加 UnoCSS 插件 ], }, });
rsbuildConfig
Rsbuild 是基于 Rspack 开发的上层构建工具,其优势在于兼容 Webpack 生态并且性能优异。我们为什么要选择 Rsbuild 可以查阅 Vite vs Rsbuild。
使用
因为在 halo-dev/plugin-starter 中,默认采用 Vite 构建,所以如果想要使用 Rsbuild 构建,需要手动配置,以下是切换到 Rsbuild 的过程:
安装依赖:
pnpm install @halo-dev/ui-plugin-bundler-kit@2.21.1 @rsbuild/core -D
创建 rsbuild.config.mjs:
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
export default rsbuildConfig()
更新 package.json:
{
"type": "module",
"scripts": {
"dev": "rsbuild build --env-mode development --watch",
"build": "rsbuild build"
}
}
配置
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
export default rsbuildConfig({
rsbuild: {
// 自定义 Rsbuild 配置
plugins: [
// 额外的插件(Vue 插件已预配置)
],
// 其他配置...
},
});
示例:
-
添加路径别名
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; export default rsbuildConfig({ rsbuild: { source: { alias: { "@": "./src", "@components": "./src/components", }, }, }, });
-
添加额外的 Rsbuild 插件
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; import { pluginSass } from "@rsbuild/plugin-sass"; export default rsbuildConfig({ rsbuild: { plugins: [ pluginSass(), // 添加 Sass 插件 ], }, });
HaloUIPluginBundlerKit
旧版本 plugin-starter 使用的方式,目前已经不再推荐。
构建输出
在 viteConfig
和 rsbuildConfig
中,已经配置好了开发环境和生产构建的输出目录,分别是:
- 开发环境:
build/resources/main/console
,在开发 UI 的过程中,可以使用pnpm dev
来实时查看效果 - 生产环境:
ui/build/dist
需要注意的是,生产构建的目录仅仅是临时目录,最终在使用 Gradle 构建插件时会自动构建 UI 并复制到
src/main/resources/console
目录中。
Vite vs Rsbuild
Vite 和 Rsbuild 都是优秀的构建工具,但它们在不同的使用场景下有各自的优势:
何时使用 Rsbuild
- ✅ 代码分割支持 - Rsbuild 为代码分割和懒加载提供了优秀的支持
- ✅ 更好的性能 - 对于复杂应用,通常有更快的构建时间和更小的包体积
- ✅ 动态导入 - 非常适合有重度前端组件的插件
动态导入示例:
import { definePlugin } from '@halo-dev/console-shared';
import { defineAsyncComponent } from 'vue';
import { VLoading } from '@halo-dev/components';
export default definePlugin({
routes: [
{
parentName: 'Root',
route: {
path: 'demo',
name: 'DemoPage',
// 懒加载重型组件
component: defineAsyncComponent({
loader: () => import('./views/DemoPage.vue'),
loadingComponent: VLoading,
}),
},
},
],
extensionPoints: {},
});
何时使用 Vite
- ✅ Vue 生态友好 - 与 Vue 生态系统工具和插件有更好的集成
- ✅ 丰富的插件生态 - 有大量可用的 Vite 插件
- ✅ 简单配置 - 对于直接的使用场景更容易配置
总结
特性 | Vite | Rsbuild |
---|---|---|
代码分割 | ❌ 有限 | ✅ 优秀 |
Vue 生态 | ✅ 优秀 | ✅ 良好 |
构建性能 | ✅ 良好 | ✅ 优秀 |
开发体验 | ✅ 优秀 | ✅ 优秀 |
插件生态 | ✅ 丰富 | ✅ 增长中 |
配置复杂度 | ✅ 简单 | ⚖️ 中等 |
建议:对于有大型前端代码库的复杂插件使用 Rsbuild,对于简单插件或需要广泛 Vue 生态系统集成时使用 Vite。
迁移
如果你当前的插件使用的是旧版本的 plugin-starter,并且想使用新的 viteConfig
和 rsbuildConfig
,可以参考以下步骤:
-
更新
@halo-dev/ui-plugin-bundler-kit
至2.21.1
或更高版本pnpm install @halo-dev/ui-plugin-bundler-kit@2.21.1 -D
-
更新
vite.config.ts
文件import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; export default viteConfig({ vite: { // Vite 配置需要按照原有的配置进行修改,但需要移除 Vue 插件,因为已经内置 plugins: [ ], }, });
-
更新项目根目录的
build.gradle
文件plugins { id 'java' - id "com.github.node-gradle.node" version "7.0.2" - id "io.freefair.lombok" version "8.0.1" - id "run.halo.plugin.devtools" version "0.2.0" + id "io.freefair.lombok" version "8.13" + id "run.halo.plugin.devtools" version "0.6.0" } group 'run.halo.starter' -sourceCompatibility = JavaVersion.VERSION_17 repositories { mavenCentral() - maven { url 'https://s01.oss.sonatype.org/content/repositories/releases' } - maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } - maven { url 'https://repo.spring.io/milestone' } } dependencies { - implementation platform('run.halo.tools.platform:plugin:2.20.0-SNAPSHOT') + implementation platform('run.halo.tools.platform:plugin:2.21.0') compileOnly 'run.halo.app:api' testImplementation 'run.halo.app:api' testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } test { useJUnitPlatform() } -tasks.withType(JavaCompile).configureEach { - options.encoding = "UTF-8" -} - -node { - nodeProjectDir = file("${project.projectDir}/ui") +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } } -tasks.register('buildFrontend', PnpmTask) { - args = ['build'] - dependsOn('installDepsForUI') +tasks.withType(JavaCompile).configureEach { + options.encoding = "UTF-8" + options.release = 21 } -tasks.register('installDepsForUI', PnpmTask) { - args = ['install'] +tasks.register('processUiResources', Copy) { + from project(':ui').tasks.named('buildFrontend') + into layout.buildDirectory.dir('resources/main/console') } -build { - // build frontend before build - tasks.named('compileJava').configure { - dependsOn('buildFrontend') - } +tasks.named('processResources', ProcessResources) { + dependsOn tasks.named('processUiResources') }
-
在 ui 或者 console 目录新建
build.gradle
文件,内容如下:plugins { id 'base' id "com.github.node-gradle.node" version "7.1.0" } group 'run.halo.starter.ui' tasks.register('buildFrontend', PnpmTask) { args = ['build'] dependsOn tasks.named('pnpmInstall') inputs.dir(layout.projectDirectory.dir('src')) inputs.files(fileTree( dir: layout.projectDirectory, includes: ['*.cjs', '*.ts', '*.js', '*.json', '*.yaml'])) outputs.dir(layout.buildDirectory.dir('dist')) shouldRunAfter(tasks.named('check')) } tasks.register('checkFrontend', PnpmTask) { args = ['test:unit'] dependsOn tasks.named('pnpmInstall') } tasks.named('check') { dependsOn tasks.named('checkFrontend') } tasks.named('build') { dependsOn tasks.named('buildFrontend') }
进行此变更的主要目的是保证 UI 构建的产物不直接输出到源码目录的 resources 目录中,而是通过 Gradle 构建插件时复制到 src/main/resources/console
目录中。
完整变更过程可参考:halo-dev/plugin-starter#52
如果你不想使用新的 Gradle 构建配置,也可以修改 viteConfig 或 rsbuildConfig 的输出目录,和旧版本保持一致:
viteConfig:
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
const OUT_DIR_PROD = "../src/main/resources/console";
const OUT_DIR_DEV = "../build/resources/main/console";
export default viteConfig({
vite: ({ mode }) => {
const isProduction = mode === "production";
const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV;
return {
build: {
outDir,
},
};
},
});
rsbuildConfig:
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
const OUT_DIR_PROD = "../src/main/resources/console";
const OUT_DIR_DEV = "../build/resources/main/console";
export default rsbuildConfig({
rsbuild: ({ envMode }) => {
const isProduction = envMode === "production";
const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV;
return {
output: {
distPath: {
root: outDir,
},
},
};
},
});