跳到主要内容

Web Components

面试速答(30 秒版 TL;DR)

  • Web Components 是浏览器原生的组件化方案,目标是让你不用依赖特定框架,也能定义可复用、可封装的组件。
  • 核心通常答三件套:
    1. Custom Elements:定义自定义标签
    2. Shadow DOM:做 DOM 和样式隔离
    3. 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>

这样组件既能封装内部结构,又能把局部内容开放给外部传入。


生命周期是高频追问

常见生命周期:

  • constructor
  • connectedCallback
  • disconnectedCallback
  • attributeChangedCallback

一个很稳的面试表述:

  • constructor 做初始化
  • connectedCallback 在挂载到 DOM 后执行,适合订阅和渲染
  • disconnectedCallback 做清理
  • attributeChangedCallback 响应属性变化

属性和数据传递怎么做?

最基础的方式有两类:

  1. HTML attributes
  2. 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。
  • 优点是标准化和封装,缺点是生态和工程能力不如成熟框架完整。
  • 适合做跨框架通用组件,不一定适合独立承载整套复杂业务。