无构建方案的红线。踩中任何一条,症状往往隐蔽(白屏 / 404 / 路由丢失), 排查时先按本表逐条对照。
@customElement 等)症状:整个模块 SyntaxError: Invalid or unexpected token / Unexpected character '@',白屏。
原因:装饰器是 TS/Babel 编译期特性,浏览器原生 JS 不执行。Lit 官方文档默认「装饰器 + 构建」,
无构建下照抄即挂。
对策:一律用 static properties + customElements.define() + ContextConsumer 编程式 API。
详见 components.md 第〇节。本项目所有示例均无装饰器。
Failed to fetch dynamically imported module、控制台 CORS 报错。原因:FMODE Repose 未放行业务域名。
对策:Repose 的 CORS 必须允许所有业务域名;跨域模块请求带 Origin,响应需 Access-Control-Allow-Origin。
ESM 模块请求是 CORS 请求(比普通脚本严格),勿用「能下载就能跑」判断。
text/javascript症状:Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of ...。
原因:依赖仓把 .js 当 text/plain / application/octet-stream 返回。
对策:Repose 上传时显式设 Content-Type: text/javascript; charset=utf-8。
症状:自定义元素重复注册报错 Failed to execute 'define' ... has already been used;或响应式失效。
原因:importmap / 直接路径引到了两个不同版本的 lit(如 lit@3.1 与 lit@3.2 各被加载一次,LitElement 类不同)。
对策:全业务统一 importmap,只允许一个 lit 版本;所有包都从同一 Repose 版本段解析。除 lit 本身,注意 lit-html / lit-element / @lit/reactive-element 也要单实例(importmap 里补全子路径映射)。
症状:深链接刷新后地址栏变成 /index.html,路由参数丢失;微信 OAuth 回调失败。
原因:CDN 回退用 301/302 重定向。
对策:回退必须 200 返回 index.html,地址栏不变。见 deployment.md。
new URL(path, baseURI) 丢部署前缀(子路径部署最高频)症状:子路径部署下(/dev/myapp/)导航跳到域名根 https://cdn/students,刷新 404;
根部署时正常。
原因:new URL 的相对解析:input 以 / 开头时是绝对路径,会整体替换 base 的
path,不保留 base 的目录前缀。
对策:把应用内路径拼到 base 的 pathname 上再解析;导航一律走 utils/router.js。
见 routing-best-practices.md 坑 1。
@lit-labs/router 污染子路径部署的路由状态症状:引入后路由偶发跳错/停错页/点内部链接整页刷新,根部署正常、二级路径异常。
原因:它装全局 <a> click 拦截器自行 pushState;且用未剥前缀的 location.pathname
匹配(路由表是应用内路径)。
对策:子路径部署自写 utils/router.js(~60 行);根部署可选。
见 routing-best-practices.md 坑 2。
<base href>(或三处前缀不一致)症状:./app.js、相对 import 全部 404(被解析到域名根)。
原因:子目录部署(/admin/)未写 <base href="/admin/">;或 <base href>、
OBS 上传前缀、importmap 路径前缀三处不一致。
对策:三处由同一变量派生,部署脚本里 grep 硬校验;navigate() 基于 base 拼 URL。
见 routing-best-practices.md 坑 4。
.js 后缀症状:Failed to resolve module specifier './foo' / 404。
原因:浏览器原生 ESM 不做扩展名补全。
对策:相对导入一律带 .js(import x from './foo.js')。裸标识符(lit)走 importmap。
症状:更新时机错乱、该刷不刷、或刷两次。
原因:把「组件本地 UI 状态」也塞进全局 signal,或把「跨组件业务状态」放在 @property 里手动同步。
对策:划清界限 —— 组件内 UI 用 @property/@state;跨组件业务用 signal + SignalWatcher。
症状:首屏依赖瀑布、加载慢。
原因:无打包 = 每个模块一次请求。
对策:Repose 开 HTTP/2/3 多路复用;合理目录深度(别过深);Cache-Control 长缓存版本化依赖。
症状:低版本浏览器无法解析裸标识符。 原因:Import Map 是现代浏览器原生特性。 对策:内部后台一般可接受现代浏览器基线;如需兼容,引入 import-map polyfill(es-module-shims)放在所有 module 之前。
症状:某模块拿到 undefined(如 http 顶层解构了尚未初始化的 authService)。
原因:ESM 循环引用 + 顶层立即求值。
对策:跨模块引用延迟到函数内取值(别在模块顶层解构相互引用的单例)。
| 坑 | 说明 |
|---|---|
| 引入需要打包器转译的依赖(CJS/TS) | 浏览器无法直接跑 CJS;只选 原生 ESM 依赖 |
用 .ts 源码直发 OBS |
浏览器不认 TS;无构建下源码即 JS |
直接改 src/ 后忘记刷 CDN |
看到旧版本;发布流程要含 CDN 刷新 |
index.html 被 CDN 长缓存 |
回退/更新拿到旧壳;index.html 设 no-cache |
index.html 被 sync 跳过 |
sync 按 size+mtime 判 Skip;先删后传 |
| CDN 刷新「看似成功」 | refresh_task 是字符串 ID,读 .id 恒 undefined → 假成功;须非空断言 + 失败退出非 0 |
| CDN 刷新只刷了一个域名 | domain_id/凭证对号入座;未知域名直接报错 |
只 curl 首页算验收 |
首页 200 ≠ 能跑;须模块图爬取 + 真实浏览器 |
| 自定义元素标签忘带连字符 | define 抛错,如 approot 应 app-root |
在卡片内嵌链接未 stopPropagation |
点内层链接触发外层跳转;内层 @click 断冒泡 |
路由 / base / CDN 的完整肺坑见 routing-best-practices.md。
<base href> 与部署前缀是否一致。new URL(path, baseURI)。@lit-labs/router(子路径部署)或裸 <a>。refresh_task 返回值(别信「假成功」日志)。