Web Components
面试速答(30 秒版 TL;DR)
- Web Components 是浏览器原生的组件化方案,目标是让你不用依赖特定框架,也能定义可复用、可封装的组件。
- 核心通常答三件套:
- Custom Elements:定义自定义标签
- Shadow DOM:做 DOM 和样式隔离
- HTML Template / Slot:做模板复用和内容分发
- 优点:
- 原生标准
- 跨框架复用
- 封装性强
- 缺点:
- 生态、状态管理、开发体验通常不如成熟框架
- 与现有框架协作时有一定心智成本
- 一句话:Web Components 适合做“通用组件资产”,未必适合直接替代整套业务框架。
心智模型:它解决的是“组件标准化”,不是“框架全替代”
很多人一听 Web Components,就会问“那是不是 React/Vue 没用了?”这个理解不对。
更准确的说法是:
- React / Vue 更像一整套应用开发方案
- Web Components 更像浏览器层面的组件标准
1)Custom Elements:定义自己的 HTML 标签
最基本的能力是注册新标签:
class UserCard extends HTMLElement {
connectedCallback() {
this.innerHTML = '<div>用户卡片</div>'
}
}
customElements.define('user-card', UserCard)
然后就能在 HTML 里这样用:
<user-card></user-card>
高频考点:
- 标签名必须包含连字符
- - 类通常继承
HTMLElement - 注册通过
customElements.define()
2)Shadow DOM:做真正的封装隔离
如果只用 Custom Elements,不做 Shadow DOM,组件内部仍然可能被外部 CSS 污染。
class FancyButton extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({ mode: 'open' })
shadow.innerHTML = `
<style>
button {
border: none;
padding: 8px 14px;
border-radius: 999px;
background: #2563eb;
color: white;
}
</style>
<button><slot>默认按钮</slot></button>
`
}
}
customElements.define('fancy-button', FancyButton)
价值:
- 外部样式不容易直接污染内部
- 内部结构不会轻易暴露给页面脚本和选择器
- 非常适合封装组件库和嵌入式组件
3)Template / Slot:做模板和插槽
template
template 里的内容默认不会立即渲染,可以当组件模板:
<template id="card-tpl">
<style>
.card {
border: 1px solid #ddd;
padding: 12px;
border-radius: 12px;
}
</style>
<div class="card">
<slot name="title"></slot>
<slot></slot>
</div>
</template>
slot
slot 负责内容分发:
<user-panel>
<h3 slot="title">前端面经</h3>
<p>这是正文内容。</p>
</user-panel>
这样组件既能封装内部结构,又能把局部内容开放给外部传入。
生命周期是高频追问
常见生命周期:
constructorconnectedCallbackdisconnectedCallbackattributeChangedCallback
一个很稳的面试表述:
constructor做初始化connectedCallback在挂载到 DOM 后执行,适合订阅和渲染disconnectedCallback做清理attributeChangedCallback响应属性变化
属性和数据传递怎么做?
最基础的方式有两类:
- HTML attributes
- DOM properties
示例:
<user-badge level="senior"></user-badge>
class UserBadge extends HTMLElement {
static get observedAttributes() {
return ['level']
}
attributeChangedCallback() {
this.render()
}
render() {
const level = this.getAttribute('level') || 'junior'
this.textContent = `级别:${level}`
}
}
工程上常见经验:
- 简单字符串配置用 attribute
- 复杂对象数据更适合 property
- 对外通信常结合
CustomEvent
this.dispatchEvent(
new CustomEvent('change-level', {
detail: { level: 'senior' },
bubbles: true,
composed: true,
}),
)
Web Components 的优点
1)标准化、跨框架
组件最终就是浏览器认识的 DOM 元素,不天然绑定 React、Vue 或其他生态。
2)封装性强
Shadow DOM 对样式和结构隔离很有帮助。
3)适合设计系统和组件分发
比如公司要给多个业务线、多个框架、多个技术栈共用一套组件,Web Components 是很自然的候选方案。
Web Components 的缺点
1)不等于应用框架
它没有天然提供:
- 路由
- 全局状态管理
- 完整模板编译体系
- 成熟的生态工作流
2)开发体验要自己补
例如:
- 响应式更新要自己设计
- 样式主题化要自己规划
- SSR、测试、调试体验要自己打磨
3)和框架协作有边界
虽然能协作,但属性传递、事件命名、受控组件模式、插槽语义等,都需要团队有明确约定。
什么时候适合用?
适合:
- 通用 UI 组件库
- 跨框架设计系统
- 微前端中的公共组件资产
- 第三方可嵌入组件
不一定适合:
- 大型业务应用整体直接“只用 Web Components”
- 强依赖框架生态能力的复杂前台项目
典型题 & 标准答法
Q1:什么是 Web Components?
答法建议:
- 浏览器原生组件化标准
- 目标是让开发者定义可复用、自封装的组件
- 核心是 Custom Elements、Shadow DOM、Template/Slot
Q2:Shadow DOM 有什么作用?
答法建议:
- 隔离 DOM 结构和样式作用域
- 降低外部样式污染内部实现的风险
Q3:Web Components 和 React/Vue 有什么区别?
答法建议:
- Web Components 是底层标准,更偏组件分发与封装
- React/Vue 是上层应用开发框架,生态更完整
- 两者不是非此即彼,可以配合使用
常见追问
- 自定义元素为什么必须带连字符?
- 为了和原生标签区分,避免未来命名冲突。
- 为什么很多组件库没有完全基于 Web Components?
- 因为业务开发往往更依赖完整框架生态,而不只是组件注册能力。
- Web Components 能做服务端渲染吗?
- 可以讨论,但整体工程复杂度通常会明显上升,不能把它想成“天然像框架那样开箱即用”。
易错点 / 坑
- 把 Web Components 当成“React/Vue 完全替代品”。
- 只会 Custom Elements,不理解 Shadow DOM 和插槽。
- 复杂数据只靠 attribute 传字符串,导致组件接口设计很别扭。
- 忽略事件透传和宿主协作,导致组件难接入外部系统。
速记要点(可背诵)
- Web Components = 浏览器原生组件化标准。
- 三件套:Custom Elements、Shadow DOM、Template/Slot。
- 优点是标准化和封装,缺点是生态和工程能力不如成熟框架完整。
- 适合做跨框架通用组件,不一定适合独立承载整套复杂业务。