5656
5757感谢所有支持本项目的用户和开发者,也特别感谢 Halo CMS 团队为插件生态提供的优秀平台。
5858
59- ## 版本说明
60-
61- 插件提供两个版本:
62-
63- - ** 全量版** :包含所有功能,包括代码高亮等依赖 JS 的相关功能。
64- - ** 轻量版** :轻量级版本,不包含 JS 相关功能和相关依赖。
65-
66- ### 轻量版的优势
67-
68- - 更小的插件体积
69- - 更快的启动速度
70- - 更低的内存占用
71- - 更低的系统性能要求
72- - 支持全平台(全量版仅支持以下平台:Linux ARM64、Linux x86_64、macOS ARM64、macOS x86_64、Windows x86_64)
73-
74- ### 轻量版本缺少的功能
75-
76- - 代码高亮(Shiki.js 渲染)
77-
78- <!-- - 图表渲染(Mermaid)
79- - 公式渲染(KaTeX) -->
80-
81- - 其他 JS 运行时相关功能
82-
83- 如果您需要上述功能,请使用全量版。
84-
85- ## 全量版使用须知
86-
87- ⏱️ ** 首次使用提示** : 全量版的 JavaScript 功能(如 Shiki 代码高亮)在首次调用时需要 1~ 3 秒进行环境初始化。这是正常现象,之后的调用速度会非常快。
88-
89- > ⚠️ ** 重要** : 全量版依赖 Javet 加载 Node.js 原生库(基于 JNI),受 Halo 插件架构限制,存在[ 已知问题] ( #全量版已知问题 ) 。
90-
91- ### 基本使用要求
92-
93- 1 . ** 请勿热重载更新** : 请勿直接覆盖更新(如使用应用商店/插件列表的快捷更新操作)
94- 2 . ** 正确的更新流程** :
95- - 方法一:停止插件 → 卸载插件 → ** 重启 Halo** → 安装新版本 → 启动插件
96- - 方法二:停止插件 → 卸载插件 → 安装新版本 → ** 重启 Halo** → 启动插件
97- 3 . ** 卸载推荐做法** : 在卸载全量版后** 重启 Halo** 确保原生库资源完全释放。
98-
99- ### 全量版已知问题
100-
101- - 问题一:卸载后重新安装全量版插件,不重启就启用。调用 JS 相关 API 时会出现错误:
102- - ** 首次安装并启动** : 正常工作
103- - ** 禁用后重新启用** : 正常工作
104- - ** 卸载后重新安装** : ❌ 会出现 ` JavetException: Javet library is not loaded ` 错误
105- - ** 重启 Halo 后** : 恢复正常工作
106-
107- #### 问题一解决方案
108-
109- 安装好新版本插件后** 先别启用** !
110- 在启用新版本插件之前,请** 先重启 Halo CMS** 。
111-
112- 未来版本会将引擎作为前置依赖插件,更新本插件不再需要重启。
113-
114- <details >
115- <summary >📖 技术原因分析(展开查看详细说明)</summary >
116-
117- 根据错误日志和 Halo 架构分析:
118-
119- ** 问题根源** :
120-
121- 1 . ** Halo 插件架构** : 根据 [ Halo 开发文档 - 插件架构] ( https://docs.halo.run/developer-guide/core/framework ) , Halo 使用
122- Spring Plugin Framework 实现插件隔离
123- - 每个插件拥有独立的 Spring ApplicationContext
124- - 每个插件使用独立的 classloader 加载资源
125- - 插件间通过 ExtensionPoint 机制通信
126- - classloader 完全隔离,无法跨插件共享类或资源
127-
128- 2 . ** Javet 原生库加载机制** :
129- - Javet 需要从 JAR 中提取原生库文件(` .dll ` /` .so ` /` .dylib ` )到临时目录
130- - JVM 通过 JNI 加载这些原生库文件
131- - 原生库文件一旦加载,会被 JVM 锁定
132-
133- 3 . ** 冲突发生过程** :
134- - 安装插件时,Javet 提取原生库文件到 ` C:\Users\用户名\AppData\Local\Temp\javet\进程ID\ ` (以 Windows 举例)
135- - JVM 加载这些文件并锁定
136- - 卸载插件时,虽然 classloader 被销毁,但原生库文件仍被 JVM 锁定,无法删除
137- - 重新安装时,Javet 尝试提取新文件到同一位置
138- - 因为文件被锁定,提取失败
139- - Javet 初始化失败,报错 ` Javet library is not loaded because <null> `
140-
141- ** 典型错误日志解读** :
142-
143- ```
144- WARN - Failed to write to ...libjavet-node-windows-x86_64.v.4.1.7.dll because it is locked
145- ERROR - Native Library already loaded in another classloader
146- ERROR - JavetException: Javet library is not loaded because <null>
147- ```
148-
149- 这三条日志清晰地展示了整个失败过程:
150-
151- 1 . 尝试写入文件失败(文件被锁定)
152- 2 . 检测到库已在其他 classloader 中加载
153- 3 . 初始化失败,因为无法提取必要的库文件
154-
155- ** 为什么重启有效** :
156-
157- - 重启 Halo 会终止 JVM 进程
158- - JVM 终止时会释放所有文件锁
159- - 临时目录中的原生库文件会被清理
160- - 新的 Halo 进程启动后,Javet 可以正常提取和加载原生库
161-
162- ** 为什么 Javet 文档中提到的 JVM 参数无效** :
163-
164- ` -Djavet.lib.loading.suppress.error=true ` 这个参数的作用是:
165-
166- - 抑制 Javet 在检测到"already loaded in another classloader"时的错误日志
167- - ** 但无法解决文件锁定问题**
168- - 当库文件无法提取时,Javet 根本无法完成初始化
169- - 因此该参数在这个场景下无效
170-
171- ** 架构层面的限制** :
172-
173- 这个问题是 JVM/JNI + 插件 classloader 隔离的固有矛盾:
174-
175- - Halo 的插件隔离设计保证了安全性和稳定性
176- - 但也导致原生库这类 JVM 级别资源难以管理
177- - 类似问题在所有使用 classloader 隔离的插件系统中都存在
178- - 这不是 Javet 或本插件的 bug,而是架构层面的限制
179-
180- ** 相关技术文档** :
181-
182- - [ Javet - Load and Unload] ( https://www.caoccao.com/Javet/reference/resource_management/load_and_unload.html )
183- - [ Javet Issue #124 - Classloader Reload] ( https://github.com/caoccao/Javet/issues/124 )
184- - [ Halo - Plugin Framework] ( https://docs.halo.run/developer-guide/core/framework )
185-
186- </details >
187-
188- ## TODO
189-
190- <details ><summary >展开折叠内容</summary >
191-
192- - [ ] 提供随机文章 API
193- - [ ] 提供预计阅读时间 API,及相关配置项
194- - [ ] 提供图表渲染 API
195- - [ ] 提供公式渲染 API
196- - [ ] 分离 Node.js 环境支持为可选前置插件(预计 3.0 版本实现)
197-
198- </details >
199-
20059## 文档目录
20160
20261- [ halo-plugin-extra-api] ( #halo-plugin-extra-api )
20362 - [ 简介] ( #简介 )
20463 - [ 核心理念] ( #核心理念 )
20564 - [ 功能介绍] ( #功能介绍 )
206- - [ 版本说明] ( #版本说明 )
207- - [ 轻量版的优势] ( #轻量版的优势 )
208- - [ 轻量版本缺少的功能] ( #轻量版本缺少的功能 )
209- - [ 全量版使用须知] ( #全量版使用须知 )
210- - [ 基本使用要求] ( #基本使用要求 )
211- - [ 全量版已知问题] ( #全量版已知问题 )
212- - [ 问题一解决方案] ( #问题一解决方案 )
213- - [ TODO] ( #todo )
21465 - [ 文档目录] ( #文档目录 )
21566 - [ Finder API 文档] ( #finder-api-文档 )
21667 - [ 文档类型定义] ( #文档类型定义 )
@@ -228,11 +79,19 @@ ERROR - JavetException: Javet library is not loaded because <null>
22879 - [ 配置选项] ( #配置选项 )
22980 - [ 支持的主题] ( #支持的主题 )
23081 - [ 补充说明] ( #补充说明 )
82+ - [ 版本说明] ( #版本说明 )
83+ - [ 轻量版的优势] ( #轻量版的优势 )
84+ - [ 轻量版本缺少的功能] ( #轻量版本缺少的功能 )
85+ - [ 全量版使用须知] ( #全量版使用须知 )
86+ - [ 基本使用要求] ( #基本使用要求 )
87+ - [ 全量版已知问题] ( #全量版已知问题 )
88+ - [ 问题一解决方案] ( #问题一解决方案 )
23189 - [ 下载和安装] ( #下载和安装 )
23290 - [ 稳定版] ( #稳定版 )
23391 - [ 开发版] ( #开发版 )
23492 - [ 下载步骤] ( #下载步骤 )
23593 - [ 开发指南/贡献指南] ( #开发指南贡献指南 )
94+ - [ TODO] ( #todo )
23695 - [ 许可证] ( #许可证 )
23796
23897## Finder API 文档
@@ -657,8 +516,140 @@ extraApiRenderFinder.renderCodeHtml(htmlContent)
657516- 补充说明:
658517 - 双主题模式会生成两个并列的 div 元素
659518
519+ ## 版本说明
520+
521+ 插件提供两个版本:
522+
523+ - **全量版**:包含所有功能,包括代码高亮等依赖 JS 的相关功能。
524+ - **轻量版**:轻量级版本,不包含 JS 相关功能和相关依赖。
525+
526+ ### 轻量版的优势
527+
528+ - 更小的插件体积
529+ - 更快的启动速度
530+ - 更低的内存占用
531+ - 更低的系统性能要求
532+ - 支持全平台(全量版仅支持以下平台:Linux ARM64、Linux x86_64、macOS ARM64、macOS x86_64、Windows x86_64)
533+
534+ ### 轻量版本缺少的功能
535+
536+ - 代码高亮(Shiki.js 渲染)
537+
538+ <!-- - 图表渲染(Mermaid)
539+ - 公式渲染(KaTeX) -->
540+
541+ - 其他 JS 运行时相关功能
542+
543+ 如果您需要上述功能,请使用全量版。
544+
545+ ## 全量版使用须知
546+
547+ ⏱️ **首次使用提示**: 全量版的 JavaScript 功能(如 Shiki 代码高亮)在首次调用时需要 1~3 秒进行环境初始化。这是正常现象,之后的调用速度会非常快。
548+
549+ > ⚠️ **重要**: 全量版依赖 Javet 加载 Node.js 原生库(基于 JNI),受 Halo 插件架构限制,存在[已知问题](#全量版已知问题)。
550+
551+ ### 基本使用要求
552+
553+ 1. **请勿热重载更新**: 请勿直接覆盖更新(如使用应用商店/插件列表的快捷更新操作)
554+ 2. **正确的更新流程**:
555+ - 方法一:停止插件 → 卸载插件 → **重启 Halo** → 安装新版本 → 启动插件
556+ - 方法二:停止插件 → 卸载插件 → 安装新版本 → **重启 Halo** → 启动插件
557+ 3. **卸载推荐做法**: 在卸载全量版后**重启 Halo** 确保原生库资源完全释放。
558+
559+ ### 全量版已知问题
560+
561+ - 问题一:卸载后重新安装全量版插件,不重启就启用。调用 JS 相关 API 时会出现错误:
562+ - **首次安装并启动**: 正常工作
563+ - **禁用后重新启用**: 正常工作
564+ - **卸载后重新安装**: ❌ 会出现 `JavetException: Javet library is not loaded` 错误
565+ - **重启 Halo 后**: 恢复正常工作
566+
567+ #### 问题一解决方案
568+
569+ 安装好新版本插件后**先别启用**!
570+ 在启用新版本插件之前,请**先重启 Halo CMS**。
571+
572+ 未来版本会将引擎作为前置依赖插件,更新本插件不再需要重启。
573+
574+ <details >
575+ <summary >📖 技术原因分析(展开查看详细说明)</summary >
576+
577+ 根据错误日志和 Halo 架构分析:
578+
579+ **问题根源**:
580+
581+ 1. **Halo 插件架构**: 根据 [Halo 开发文档 - 插件架构](https://docs.halo.run/developer-guide/core/framework), Halo 使用
582+ Spring Plugin Framework 实现插件隔离
583+ - 每个插件拥有独立的 Spring ApplicationContext
584+ - 每个插件使用独立的 classloader 加载资源
585+ - 插件间通过 ExtensionPoint 机制通信
586+ - classloader 完全隔离,无法跨插件共享类或资源
587+
588+ 2. **Javet 原生库加载机制**:
589+ - Javet 需要从 JAR 中提取原生库文件(`.dll`/`.so`/`.dylib`)到临时目录
590+ - JVM 通过 JNI 加载这些原生库文件
591+ - 原生库文件一旦加载,会被 JVM 锁定
592+
593+ 3. **冲突发生过程**:
594+ - 安装插件时,Javet 提取原生库文件到 `C:\Users\用户名\AppData\Local\Temp\javet\进程ID\`(以 Windows 举例)
595+ - JVM 加载这些文件并锁定
596+ - 卸载插件时,虽然 classloader 被销毁,但原生库文件仍被 JVM 锁定,无法删除
597+ - 重新安装时,Javet 尝试提取新文件到同一位置
598+ - 因为文件被锁定,提取失败
599+ - Javet 初始化失败,报错 `Javet library is not loaded because <null >`
600+
601+ **典型错误日志解读**:
602+
603+ ```
604+ WARN - Failed to write to ...libjavet-node-windows-x86_64.v.4.1.7.dll because it is locked
605+ ERROR - Native Library already loaded in another classloader
606+ ERROR - JavetException: Javet library is not loaded because <null >
607+ ```
608+
609+ 这三条日志清晰地展示了整个失败过程:
610+
611+ 1. 尝试写入文件失败(文件被锁定)
612+ 2. 检测到库已在其他 classloader 中加载
613+ 3. 初始化失败,因为无法提取必要的库文件
614+
615+ **为什么重启有效**:
616+
617+ - 重启 Halo 会终止 JVM 进程
618+ - JVM 终止时会释放所有文件锁
619+ - 临时目录中的原生库文件会被清理
620+ - 新的 Halo 进程启动后,Javet 可以正常提取和加载原生库
621+
622+ **为什么 Javet 文档中提到的 JVM 参数无效**:
623+
624+ `-Djavet.lib.loading.suppress.error=true` 这个参数的作用是:
625+
626+ - 抑制 Javet 在检测到"already loaded in another classloader"时的错误日志
627+ - **但无法解决文件锁定问题**
628+ - 当库文件无法提取时,Javet 根本无法完成初始化
629+ - 因此该参数在这个场景下无效
630+
631+ **架构层面的限制**:
632+
633+ 这个问题是 JVM/JNI + 插件 classloader 隔离的固有矛盾:
634+
635+ - Halo 的插件隔离设计保证了安全性和稳定性
636+ - 但也导致原生库这类 JVM 级别资源难以管理
637+ - 类似问题在所有使用 classloader 隔离的插件系统中都存在
638+ - 这不是 Javet 或本插件的 bug,而是架构层面的限制
639+
640+ **相关技术文档**:
641+
642+ - [Javet - Load and Unload](https://www.caoccao.com/Javet/reference/resource_management/load_and_unload.html)
643+ - [Javet Issue #124 - Classloader Reload](https://github.com/caoccao/Javet/issues/124)
644+ - [Halo - Plugin Framework](https://docs.halo.run/developer-guide/core/framework)
645+
646+ </details>
647+
660648## 下载和安装
661649
650+ 全量版和轻量版的区别请看:[版本说明](#版本说明)
651+ 使用全量版前请看:[全量版使用须知](#全量版使用须知)
652+
662653### 稳定版
663654
664655稳定版通过 GitHub Releases 发布,建议生产环境使用。
@@ -700,6 +691,18 @@ extraApiRenderFinder.renderCodeHtml(htmlContent)
700691
701692参见 [CONTRIBUTING.md](./CONTRIBUTING.md)
702693
694+ ## TODO
695+
696+ <details><summary>展开折叠内容</summary>
697+
698+ - [ ] 提供随机文章 API
699+ - [ ] 提供预计阅读时间 API,及相关配置项
700+ - [ ] 提供图表渲染 API
701+ - [ ] 提供公式渲染 API
702+ - [ ] 分离 Node.js 环境支持为可选前置插件(预计 3.0 版本实现)
703+
704+ </details>
705+
703706## 许可证
704707
705708[AGPL-3.0](./LICENSE) © HowieHz
0 commit comments