docs: 新增插件静态文件引用规范 + 经验条目18($public_host最佳实践)
- EXPERIENCES.md: 新增第18条(P2)— 插件模板静态文件引用规范
- 根因一:cdn.jsdelivr.net 大陆阻断
- 根因二:$public_host 在插件视图不可用
- 规范:控制器显式传递 public_host,模板用 {{}}
- 优先级:本地文件 > 国内CDN > 国际CDN
- EXPERIENCES.md: 检查清单新增2项(本地文件优先 + $public_host 显式传递)
- DEVELOPMENT_GUIDELINES.md: 新增第8条 — 插件模板静态文件引用规范(完整代码示例)
- README.md: 实现参考新增 DEVELOPMENT_GUIDELINES.md(置顶)
feat/phase-b-verification
parent
a673c09746
commit
5c433ea20e
|
|
@ -23,13 +23,14 @@
|
|||
|------|------|
|
||||
| **[docs/VR_GOODS_CONFIG_SPEC.md](docs/VR_GOODS_CONFIG_SPEC.md)** | ⚠️ **vr_goods_config JSON 格式 v3.0 完整规格**(商品配置核心) |
|
||||
| **[docs/PHASE2_PLAN.md](docs/PHASE2_PLAN.md)** | Phase 2 当前状态 + 下一步工作计划 |
|
||||
| **[docs/EXPERIENCES.md](docs/EXPERIENCES.md)** | ⚠️ **踩坑经验(必读)** — 16条核心教训 |
|
||||
| **[docs/EXPERIENCES.md](docs/EXPERIENCES.md)** | ⚠️ **踩坑经验(必读)** — 18条核心教训 |
|
||||
| **[docs/DEVELOPMENT_LOG.md](docs/DEVELOPMENT_LOG.md)** | 开发日志(完整变更记录) |
|
||||
|
||||
### 🔧 实现参考
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [docs/DEVELOPMENT_GUIDELINES.md](docs/DEVELOPMENT_GUIDELINES.md) | ⚠️ **插件开发规范**(静态文件引用、$public_host、铁律、pre-commit 自检) |
|
||||
| [docs/GOODS_PHP_MODIFICATION.md](docs/GOODS_PHP_MODIFICATION.md) | Goods.php 1行改动说明 |
|
||||
| [docs/09_SHOPXO_HOOKS_REFERENCE.md](docs/09_SHOPXO_HOOKS_REFERENCE.md) | ShopXO 全部钩子清单(从源码提取) |
|
||||
| [docs/07_SHOPXO_PLUGIN_MECHANISM.md](docs/07_SHOPXO_PLUGIN_MECHANISM.md) | 插件开发机制 |
|
||||
|
|
|
|||
|
|
@ -85,6 +85,56 @@ Council agents 和 Claude Code subagents 的 commit 行为**必须约束在 feat
|
|||
- 所有敏感配置(数据库密码、API Key、frp token 等)必须写入 `docs/PRIVATE_CONFIG.md`(不 commit)或 `.env`(已 gitignore)
|
||||
- 禁止将真实密码写入代码或 commit message
|
||||
|
||||
### 8. 插件模板静态文件引用规范
|
||||
|
||||
ShopXO 插件模板引用 JS/CSS 时,必须遵循以下规范:
|
||||
|
||||
**背景**:ShopXO 官方模板用 `{{$public_host}}` 引用静态文件,但插件控制器不继承 `index/Common.php`,`$public_host` 不会自动赋值给插件视图。
|
||||
|
||||
**正确写法**:
|
||||
|
||||
```php
|
||||
// 控制器:显式传递 public_host
|
||||
class Index
|
||||
{
|
||||
public function wallet()
|
||||
{
|
||||
$user = UserService::LoginUserInfo();
|
||||
return MyView('../../../plugins/vr_ticket/view/goods/ticket_wallet', [
|
||||
'user' => $user,
|
||||
// 关键:插件不继承 Common 基类,必须显式传递
|
||||
'public_host' => \think\facade\Config::get('shopxo.host_url'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- 模板:使用 {{$public_host}} -->
|
||||
<!-- ✅ 引用 ShopXO 自带库(优先) -->
|
||||
<script src="{{$public_host}}static/common/lib/JsBarcode/JsBarcode.all.min.js"></script>
|
||||
|
||||
<!-- ✅ 引用插件自己的静态文件 -->
|
||||
<link rel="stylesheet" href="{{$public_host}}plugins/vr_ticket/static/css/ticket.css">
|
||||
|
||||
<!-- ❌ 错误:插件模板里直接用 Config() 散落在模板各处 -->
|
||||
<link href="<?php echo Config('shopxo.host_url'); ?>plugins/vr_ticket/static/css/ticket.css">
|
||||
```
|
||||
|
||||
**静态文件来源优先级**:
|
||||
1. **ShopXO 自带**:`{{$public_host}}static/common/lib/` 下已有 JsBarcode、jQuery、AmazeUI 等
|
||||
2. **国内 CDN**:`cdn.staticfile.net`(ShopXO 无自带时)
|
||||
3. **禁止**:国际 CDN(`unpkg.com`、`jsdelivr.net`、`cdnjs.cloudflare.com`)— 大陆阻断
|
||||
|
||||
**验证方法**:容器内文件是否存在:
|
||||
```bash
|
||||
# ShopXO 自带 JsBarcode
|
||||
docker exec shopxo-php ls /var/www/html/public/static/common/lib/JsBarcode/
|
||||
# 应输出:JsBarcode.all.min.js
|
||||
```
|
||||
|
||||
**双目录陷阱**:插件 JS/CSS 文件在 `app/plugins/`(PHP runtime)和 `public/plugins/`(Nginx webroot)各有一份副本。修改后必须同步两边。详见 [docs/DEBUG_STATIC_FILE_SYNC.md](docs/DEBUG_STATIC_FILE_SYNC.md)。
|
||||
|
||||
---
|
||||
|
||||
## 三、已知特殊情况记录
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ return [
|
|||
|
||||
**教训**:国内项目 CDN 必须用 `cdn.staticfile.net` 或 `cdn.bootcdn.net`,禁止 `unpkg.com`/`cdnjs.cloudflare.com`。
|
||||
|
||||
**更优方案**:优先使用 ShopXO 源码自带的本地文件(见第 18 条)。
|
||||
|
||||
---
|
||||
|
||||
### 7. PHP 注释块污染:未闭合 `/*` 导致语法错误
|
||||
|
|
@ -289,13 +291,65 @@ const zone = zones.find(z => z.char === seatChar);
|
|||
|
||||
---
|
||||
|
||||
## 🟢 P2 — 重要经验
|
||||
|
||||
### 18. 插件模板静态文件引用:`$public_host` 最佳实践
|
||||
|
||||
**背景**:`ticket_wallet.html` 的 JsBarcode 条形码不显示,原因是 `cdn.jsdelivr.net` 在大陆阻断。
|
||||
|
||||
**案例**:票夹页面用 `<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.0/...">` → JsBarcode 加载失败 → `typeof JsBarcode === 'undefined'` → 条形码静默不渲染。
|
||||
|
||||
**ShopXO 源码已自带 JsBarcode v3.11.5**:
|
||||
```
|
||||
public/static/common/lib/JsBarcode/JsBarcode.all.min.js
|
||||
```
|
||||
|
||||
**根本原因一(CDN 阻断)**:jsdelivr.net 在大陆不可用。应优先使用 ShopXO 自带库或国内 CDN(`cdn.staticfile.net`)。
|
||||
|
||||
**根本原因二(`$public_host` 不可用)**:ShopXO 官方模板用 `{{$public_host}}` 引用静态文件,但插件控制器不继承 `index/Common.php`,`$public_host` 不会自动赋值。
|
||||
|
||||
```php
|
||||
// ❌ 错误:插件控制器不继承 Common,模板里 $public_host 是空的
|
||||
// 模板中用 {{$public_host}}static/... → 变成 /static/...(丢域名)
|
||||
|
||||
// ✅ 正确:在控制器显式传递 public_host
|
||||
class Index
|
||||
{
|
||||
public function wallet()
|
||||
{
|
||||
return MyView('../../../plugins/vr_ticket/view/goods/ticket_wallet', [
|
||||
'user' => $user,
|
||||
'public_host' => \think\facade\Config::get('shopxo.host_url'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 模板中用 {{$public_host}}static/...
|
||||
```
|
||||
|
||||
**静态文件引用优先级**:
|
||||
1. **ShopXO 自带文件**(如 JsBarcode):直接引用 `{{$public_host}}static/common/lib/...`,无需下载
|
||||
2. **国内 CDN**:`cdn.staticfile.net` / `cdn.bootcdn.net`(ShopXO 无自带时)
|
||||
3. **国际 CDN**:`unpkg.com` / `jsdelivr.net` — 禁止在国内项目使用
|
||||
|
||||
**教训**:引用 JS/CSS 前先查 ShopXO 源码是否已自带(`public/static/common/lib/`),优先使用本地文件。
|
||||
|
||||
**规范写法(插件模板)**:
|
||||
- 控制器:`MyView('path', ['public_host' => \think\facade\Config::get('shopxo.host_url'), ...])`
|
||||
- 模板:`{{$public_host}}static/...`
|
||||
|
||||
> ⚠️ **高危**:插件 JS/CSS 文件在 `app/`(PHP runtime)和 `public/`(Nginx webroot)各有一份副本。修改后必须同步两边并验证 MD5。详情见专项文档。
|
||||
|
||||
---
|
||||
|
||||
## 📌 开发前检查清单
|
||||
|
||||
接手本插件时,逐项确认以下内容:
|
||||
|
||||
- [ ] `save.html` 有完整的 `{{:ModuleInclude('public/header')}}` 和 `{{:ModuleInclude('public/footer')}}`
|
||||
- [ ] Vue 3 的 `<textarea>` 没有使用 `[[ ]]` 插值绑定 value
|
||||
- [ ] Vue CDN 使用 `cdn.staticfile.net` 而非 `unpkg.com`
|
||||
- [ ] 引用 JS/CSS 前先查 ShopXO 是否已自带(`public/static/common/lib/`),优先本地文件而非 CDN
|
||||
- [ ] 插件模板使用 `$public_host` 时,控制器已显式传递(不依赖框架自动赋值)
|
||||
- [ ] Hook.php 返回数组包含 `id`、`url`、`name`、`is_show`
|
||||
- [ ] Admin.php 有缓存锁机制保护 `initialize()` 免于每次请求检查表
|
||||
- [ ] 改字段名之前查过 Service 层源码或实际表结构
|
||||
|
|
|
|||
Loading…
Reference in New Issue