stencil 不能直接编译 html 文件,只处理 src/components/ 下带 @component 装饰器的 tsx/ts 文件;html 片段需重构为符合 web components 规范的组件,包括短横线标签名、jsx 渲染、类方法事件、shadow dom 样式隔离及正确构建引入。

Stencil 不能直接编译 HTML 文件
Stencil 编译器根本不处理纯 index.html 或 card.html 这类文件——它只扫描 src/components/**/*.{tsx,ts},且必须包含 @Component 装饰器的类。你把 HTML 片段扔进 src/components/ 目录下,npm run build 会静默跳过,不会报错,也不会生成任何组件。
常见错误现象:dist/ 目录里找不到 my-card.js,浏览器控制台也无报错,但自定义标签不渲染。
- 根本原因:Stencil 是编译器,不是 HTML 转换器或模板引擎
- HTML 中的
class="btn primary"、onclick="doX()"、id="modal-root"等写法,在 Stencil 组件中全部失效,必须重构为 JSX + 类成员 + Shadow DOM 查询 - 原生
<style></style>块必须抽离为独立 CSS 文件,并通过styleUrl: 'my-component.css'引入
如何把现有 HTML 正确转成 Stencil 组件
关键不是“转换”,而是按 Web Components 规范重新封装:明确影子 DOM 边界、用属性驱动状态、生命周期可控。
例如一段商品卡片 HTML:
<div class="card">
<h3 id="title">{name}</h3>
<button onclick="addToCart()">加入购物车</button>
</div>
需重构为:
- 标签名改为短横线格式:
<product-card></product-card>→ 对应类名ProductCard -
render()返回 JSX:<h3>{this.name}</h3>,而非字符串插值 - 事件绑定改写为:
onClick={() => this.addToCart()},且addToCart必须是类方法 - 移除全局
id,改用this.el.shadowRoot.querySelector('.title')(如需 DOM 操作) - 样式移入
product-card.css,并在@Component中声明styleUrl: 'product-card.css'
构建后如何在 HTML 中使用组件
编译产物是标准 Custom Element,可直接在任意 HTML 页面中引入,无需框架。
步骤如下:
- 运行
npm run build,默认输出到dist/,含my-component.esm.js(ESM)和my-component.js(UMD) - 在 HTML 中引入 loader:
<script type="module" src="/build/my-component.esm.js"></script> - 或兼容旧浏览器:
<script nomodule src="/build/my-component.js"></script> - 然后直接写标签:
<my-component name="Stencil"></my-component>,注意属性名全小写,不加on前缀(onclick→click)
若组件启用了 shadow: true,外部 CSS 无法穿透影响内部元素,必须用 ::part() 或 :host 显式暴露样式钩子。
跨框架使用的实际限制点
虽然 Stencil 宣称“一次编写,多框架运行”,但真实集成时仍有几个容易被忽略的细节:
- React 中需用
react-jsx配置或createCustomElement包裹,否则事件如click不会触发onXxx回调 - Angular 需在
AppModule中调用defineCustomElements()手动注册,否则标签不识别 - Vue 3 中
v-model绑定需配合modelValueprop 和update:modelValue事件,不能直接用v-model原生属性 - 所有框架中,传入对象或数组 props 必须 JSON 序列化再反序列化,因为 DOM attribute 只支持字符串
真正跨框架可用,不等于“零适配”——loader 和注册逻辑仍需按目标环境补足,这点常被文档弱化,但实操中绕不开。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











