|
|
@@ -1,81 +1,100 @@
|
|
|
---
|
|
|
-slug: fmode-skill-fmode-code-lit
|
|
|
-displayName: fmode-code-lit
|
|
|
-version: 1.0.0
|
|
|
name: fmode-code-lit
|
|
|
-description: "数字人开发基础规则:Lit 原生 ESM 无构建前端(零编译/零打包,浏览器直跑)。分层 core/wc/page、signals 响应式、@lit/context DI、History 路由、多标签 KeepAlive;沉淀多页面开发 light DOM 五条死线、子路径路由丢前缀、CDN 假刷新等实战教训。触发词:lit、lit 项目、lit 组件、无构建、ESM、WebComponent、importmap、signals、多页面白屏、子路径部署、keepalive、前端部署。"
|
|
|
-tags: [FmodeAgent, FmodeCode, 开发级, code-lit, 数字人基础规则]
|
|
|
-platform: Both
|
|
|
-level: 开发级
|
|
|
-category: 开发规范
|
|
|
-icon: 🧱
|
|
|
-license: MPL-2.0
|
|
|
-author: Fmode (未来飞马)
|
|
|
-homepage: https://github.com/fmodecn/skill-code-lit
|
|
|
-npm: "@fmode/skill-code-lit"
|
|
|
+description: "Lit 原生 ESM 无构建前端。触发词:lit 组件、无构建、ESM。"
|
|
|
+schema_version: 1.0.0
|
|
|
+level: system
|
|
|
+category: development
|
|
|
+version: 1.0.0
|
|
|
+author: Yuyang001 (FmodeAgent)
|
|
|
+tags: [Lit, 无构建, 前端]
|
|
|
---
|
|
|
|
|
|
# fmode-code-lit — Lit 原生 ESM 无构建前端
|
|
|
|
|
|
-> **这是方便数字人(FmodeAgent / FmodeCode)进行项目开发的基础规则技能。**
|
|
|
-> 它把踩过的坑沉淀成硬性约束,让 AI 在生成代码时不重犯同类错误。
|
|
|
-
|
|
|
-用无构建/无打包/纯原生 ESM + Lit 3.x 开发前端:源码即产物,浏览器直跑。分层(组件 / 服务 / 工具),
|
|
|
-`@lit-labs/signals` 响应式、`@lit/context` 依赖注入、自写 History 路由、多标签 KeepAlive。
|
|
|
-
|
|
|
-npm 包:`@fmode/skill-code-lit`(`npx @fmode/skill-code-lit@latest install`)。
|
|
|
-完整规则见 `references/RULES-INDEX.md` 与其索引的 `references/rules/`。
|
|
|
+用无构建/无打包/纯原生 ESM + Lit 3.x 开发前端:源码即产物,浏览器直跑;依赖走 FMODE Repose,业务码走 OBS+CDN。分层(组件/服务/工具)、signals 响应式、@lit/context DI、History 路由、多标签 KeepAlive。
|
|
|
|
|
|
## 触发
|
|
|
+新建/初始化 lit 项目 · 写 lit 组件 · 无构建前端 · 原生 ESM+importmap · WebComponent · signals · context DI · 多标签 KeepAlive · 部署 OBS+CDN。
|
|
|
|
|
|
-新建/初始化 lit 项目 · 写 lit 组件 · 无构建前端 · 原生 ESM + importmap · WebComponent · signals ·
|
|
|
-context DI · 多标签 KeepAlive · 多页面白屏排查 · 子路径部署 · 部署验收。
|
|
|
-
|
|
|
-## 三条铁律
|
|
|
-
|
|
|
-1. **源码即产物**:不引入任何构建/打包器(rollup / esbuild / vite / webpack),写好的 `.js` 直接上线。
|
|
|
-2. **依赖不落地项目**:`lit` 等运行时依赖由 importmap 从静态依赖仓解析,禁止拷进业务仓。
|
|
|
-3. **分层不可越界**:组件(渲染)→ 服务(业务与状态)→ 工具(无状态函数)。
|
|
|
+## 铁律
|
|
|
+1. 源码即产物:禁 rollup/esbuild/vite/webpack。
|
|
|
+2. 依赖不落地:lit 由 importmap 指向 FMODE Repose。
|
|
|
+3. 分层不越界:组件禁 fetch/复杂业务,服务禁 DOM。
|
|
|
|
|
|
## 无构建硬约束
|
|
|
+禁装饰器(@customElement/@property/@state/@consume/@provide),用静态字段+编程式 API。
|
|
|
|
|
|
-禁装饰器(`@customElement` / `@property` / `@state` / `@consume`),一律用
|
|
|
-`static properties` + `customElements.define()` + `ContextConsumer`。
|
|
|
+## 框架边界(用户定调,必守)
|
|
|
+- 新写项目默认可用无构建 Lit。
|
|
|
+- **项目明确要求 Angular:沿用 Angular 不改 Lit。**
|
|
|
|
|
|
-## 多页面开发铁律(light DOM 五条死线)
|
|
|
+## 用法
|
|
|
+scaffold.mjs 初始化 / install-rules.mjs 装 .claude/rules / new.mjs 生成组件服务页 / deploy.sh 部署。
|
|
|
|
|
|
-`createRenderRoot(){return this}` 是**渲染模型切换**,切完 `<slot>`、`:host`、`::slotted` 全部失效:
|
|
|
+## 关键约定
|
|
|
+组件 kebab-case.js 类 PascalCase · 页面 page-*.js · 服务 *-service.js 单例 · 相对导入带 .js · 全局 signal+SignalWatcher · DI 用 @lit/context · 子目录 <base href> · CDN History 回退 200 禁 302。
|
|
|
|
|
|
-1. light DOM 下**禁用 `<slot>`** —— 自检:`document.querySelector('main').children.length`,为 0 就是投影断了。
|
|
|
-2. 外壳**按路由自己产出页面元素**,不靠投影传内容。
|
|
|
-3. 真需传内容的组件(卡片)用 `references/light-slot.js` 的 `capture`(渲染前)/ `project`(updated 后)手动搬,`dataset.projected` 做幂等。
|
|
|
-4. 自定义元素必须 `display: block; min-width: 0`(例外元素写 `display: skip`);跑 `scripts/check-elements.mjs` 自检。
|
|
|
-5. 图表容器用 `height: 100% + min-height`,不用 `flex: 1`(父级无确定高度时算出来是 0)。
|
|
|
+## 部署
|
|
|
+依赖→FMODE Repose(.js text/javascript、CORS 放行、HTTP/2);业务码→obsutil sync src/→OBS(public-read)+刷 CDN;index.html no-cache。
|
|
|
|
|
|
-## 用法
|
|
|
+## 常见坑
|
|
|
+装饰器/CORS/MIME 非 JS/多版本混用/CDN 302/base href 缺失/漏 .js/signal 与 property 混用/老浏览器无 ImportMap。
|
|
|
|
|
|
-```bash
|
|
|
-# 安装到 ~/.claude/skills/
|
|
|
-npx @fmode/skill-code-lit@latest install
|
|
|
+## 多页面开发铁律(light DOM 五条死线)
|
|
|
|
|
|
-# 或直接用本目录脚本
|
|
|
-node scripts/scaffold.mjs ./my-admin --project my-admin # 生成项目骨架
|
|
|
-node scripts/install-rules.mjs . # 把规则装入已有项目 .claude/rules/
|
|
|
-node scripts/new.mjs component user-card # 生成组件 / 服务 / 页面 / core / wc
|
|
|
-node scripts/check-elements.mjs src # 校验自定义元素 display 声明
|
|
|
-node scripts/smoke.mjs # 自检
|
|
|
+> 来源:tongjisheji/report-review 首次交付 6 页全部错乱的复盘(2026-10-29)
|
|
|
+> **一句话**:`createRenderRoot(){return this}` 不是“让 Tailwind 生效的小开关”,是**渲染模型切换**。切完以后 `<slot>`、`:host`、`::slotted`、样式隔离全部失效,但代码看起来和普通 Lit 一样、编辑器也不报错。
|
|
|
+
|
|
|
+### 死线1:light DOM 下禁用 `<slot>`
|
|
|
+没有 shadow root 就没有投影。`<slot>` 退化成普通未知元素,`assignedSlot` 为 null,**子节点原封不动滞留在宿主元素上**——外壳和内容成了兄弟节点,谁也没包住谁,于是滚动区空、导航错、高度链断。
|
|
|
+**自检一行**:`document.querySelector('main').children.length`,为 0 就是投影断了。
|
|
|
+
|
|
|
+### 死线2:外壳接管路由渲染,不靠投影传内容
|
|
|
+页面元素由外壳**自己按路由产出**,让它天然成为 `<main>` 的子节点,父子关系由渲染函数保证:
|
|
|
+```js
|
|
|
+// app-shell.js
|
|
|
+_renderRoute() {
|
|
|
+ const { name, params } = parseRoute(this.path);
|
|
|
+ switch (name) {
|
|
|
+ case 'dashboard': return html`<page-dashboard></page-dashboard>`;
|
|
|
+ case 'project-detail': return html`<page-project-detail .code=${params.code||''}></page-project-detail>`;
|
|
|
+ }
|
|
|
+}
|
|
|
+// render 里:<main ...><div class="page-enter key-route-${this.active}">${this._renderRoute()}</div></main>
|
|
|
```
|
|
|
+`app-root` 退化成只负责挂载:`render() { return html\`<app-shell></app-shell>\` }`。
|
|
|
+
|
|
|
+### 死线3:真需传内容的组件(卡片),用 capture/project 手动搬
|
|
|
+light DOM 下只能自己搬节点。现成实现见 `references/light-slot.js`。**顺序不能错**:
|
|
|
+- `capture(host)` 在 **connectedCallback 中、渲染前** 调用,把 `childNodes` 存到 `host.__lightIncoming`
|
|
|
+- `project(host)` 在 **updated() 中、渲染后** 调用,把暂存的节点塞进 `[data-light-slot="body"]` 等容器
|
|
|
+- **必须靠 `dataset.projected` 做幂等**,否则每次重渲染都重复搬、节点来回跳
|
|
|
+- 渲染后再扫宿主子节点是致命的:会把组件自己刚生成的内容当成“外部传入”搬走,越搬越乱
|
|
|
+
|
|
|
+### 死线4:自定义元素必须显式 `display: block; min-width: 0`
|
|
|
+自定义元素是未知元素,浏览器默认按 inline 处理:不吃 height/width(`h-full`/`flex-1` 全失效)、不形成块级盒子(内部 flex/grid 失去参照)、相邻元素间出现空白间隙。
|
|
|
+```css
|
|
|
+app-shell, page-dashboard, page-analysis, data-table, kpi-card, empty-state, ... {
|
|
|
+ display: block; min-width: 0; /* min-width:0 让 flex 子项可收缩,否则长表格撑破父容器 */
|
|
|
+}
|
|
|
+```
|
|
|
+**两个例外必须排除**:`section-card`、`chart-box` 的 class 自带 `flex h-full flex-col`,写死 `display: block` 会覆盖掉、把高度链断掉。
|
|
|
|
|
|
-## 关键约定
|
|
|
-
|
|
|
-组件 `kebab-case.js` 类 `PascalCase` · 页面 `page-*.js` · 服务 `*-service.js` 单例 · 相对导入带 `.js` ·
|
|
|
-全局 signal + `SignalWatcher` · DI 用 `@lit/context` · 子目录 `<base href>` · CDN History 回退 200 禁 302。
|
|
|
+### 死线5:图表容器用 `height:100% + min-height`,不用 `flex:1`
|
|
|
+```css
|
|
|
+chart-box { height: 100%; min-height: 260px; } /* 父级无确定高度时 flex:1 算出来是 0,ECharts 拿到 0 高度直接不画 */
|
|
|
+```
|
|
|
|
|
|
-## 部署
|
|
|
+### 交付前自检清单
|
|
|
+1. **先跑通最小路径再铺量**:外壳 + 一个空白页 + 一次路由跳转,确认 DOM 父子关系正确,再往里填 6 个页面。规模越大定位成本越高。
|
|
|
+2. `node scripts/check-elements.mjs` 自动校验所有自定义元素都声明了 display —— 自动检查比文档写三遍有用。
|
|
|
+3. 长模板用 `node --check` 检查语法:**少一个闭标签,lit-html 会把后面所有兄弟节点当成它的子节点吞掉**,整页塌陷,且症状和渲染模型错误混在一起,排查方向严重跑偏。
|
|
|
+4. 症状≠根因:“样式全乱”先去查 DOM 结构,别一上来翻 Tailwind 配置(这次查错方向错了两轮)。
|
|
|
+5. **多页症状不一致时先找共同根因**:各页症状不同通常是同一个总根因的不同投影,不是 n 个独立 bug。最严重的那页反而最好的线索。
|
|
|
|
|
|
-依赖 → 静态依赖仓(`.js` 为 `text/javascript`、CORS 放行、HTTP/2);业务码 → `obsutil sync src/` + 刷 CDN;
|
|
|
-`index.html` no-cache 并先删后传。**验收不能只 curl 首页**:模块图爬取 + 深链接 + 真实浏览器。
|
|
|
+### 同批高频白屏(非 Lit 问题,但一起记)
|
|
|
+- **子路径白屏**:index.html 用绝对路径引资源,部署到 `/dev/XXX/` 子路径全 404。用 `__BASE__` 占位 + `document.write` 注入。
|
|
|
+- **改配置后白屏**:dev-server 进程还服务旧配置。**改配置必须重启,别信热更新。**
|
|
|
|
|
|
## 诚实失败
|
|
|
-
|
|
|
-生成前先探测目标目录;已存在文件不覆盖(除非 `--force`)。依赖版本无法解析时明确报错,不生成跑不起来的骨架。
|
|
|
+探测目录不覆盖已有文件;依赖解析失败明确报错。
|