功能与用法
组件 scoped(child-root 继承)
自定义组件不会被直接注入 data-v-*(那只是普通 props),而是注入
scopedId="data-v-{hash}"。子组件如果需要继承父级 scope(让父组件 scoped
样式能命中子组件根元素),自行读取并把该属性绑到根元素上:
tsx
// Child.tsx
export default function Child({ scopedId }: { scopedId?: string }) {
return <div className="child-root" {...(scopedId ? { [scopedId]: '' } : {})}>…</div>
}scss
/* demo.scoped.scss —— 选择器被追加 [data-v-{hash}],可命中上面的 child-root */
.child-root { border-left: 4px solid #4f46e5; }- 默认开启;
componentScoped: false整体关闭(组件标签不再注入任何属性); - 属性名可用
scopedIdAttributeName配置; - 已存在同名属性时自动覆盖,不会重复添加。
Vue JSX:可以零改造继承(attrs 透传)
Vue 会把子组件未声明的 attrs 合并到子组件的唯一根元素上,所以 Vue JSX 下
连 scopedId 都不用手动绑定:给子组件加 direct-scoped marker,插件注入的
data-v-{hash} 会被 Vue 自动透传下去。
tsx
// 父组件
<Child direct-scoped label="子组件零改造" />
// 子组件根元素最终带 data-v-{父hash} → 父组件 .child-root[data-v-{父hash}] 命中- 仅单根组件生效(多根 / Fragment / 文本根会触发 Vue 的 “could not be automatically inherited” 警告);
- 子组件需是
defineComponent(或声明了props的函数组件):Vue 对裸函数组件 只透传class/style/事件(getFunctionalFallthrough),其余 attrs 静默丢弃; - 反向注意:有 props 声明的子组件若没有声明
scopedId,Vue 不会像 React 那样 忽略未知 prop,而是透传成根元素上的scopedid="data-v-{hash}"属性(仅噪声, 不影响样式命中);不想要就让子组件声明该 prop,或componentScoped: false。
变量当标签:<Comp direct-scoped />
当大写组件在运行时其实是原生 DOM 标签时(如变量持有 'a'/'button'),
scopedId 语义不适用。加一个 marker 让插件按普通 DOM 元素处理——直接注入
data-v-{hash}=""、不再注入 scopedId;marker 是编译期指令,会从产物中移除。
tsx
const Comp: any = tag || (href ? 'a' : 'button')
export default function Demo() {
return <Comp direct-scoped className="vp-button">按钮</Comp>
}
// 产物 ≈ <Comp data-v-xxxxxxxx="" className="vp-button">…</Comp>(无 direct-scoped)- 大写组件、成员表达式组件(
<UI.Button direct-scoped />)均可; - 原生标签上写 marker 无意义,静默忽略并移除;
- 属性名可用
directScopedAttributeName配置; - 默认名带连字符,TS 对这类属性名不做类型检查(与
data-*同理),因此在强类型 组件上也不会报未知 prop;若把 marker 配成合法标识符(如directScoped), 强类型组件需自行放行该属性。
样式来源一:外部 *.scoped.* 导入
tsx
import './a.scoped.css'
import './b.scoped.scss'- 仅
*.scoped.{css,scss,sass,less}参与 scope(其余样式文件按 Vite 常规处理); - 一个组件可导入多份,与内联块共享同一把 hash;多份外部文件会触发
warnMultiScopedImport警告(默认开),提示覆盖风险; - 同一份文件被两个不同组件导入 → 构建报错(组件私有资源)。
样式来源二:内联 <style scoped>
tsx
export default function Demo() {
return (
<div className="wrap">
<style scoped>{`.wrap { padding: 1rem }`}</style>
<style scoped lang="scss">{`.btn { &:hover { opacity: .9 } }`}</style>
</div>
)
}- 属性存在即生效(布尔语义,同 Vue);
lang只认字符串字面量(默认 css); - 内容只支持静态文本;含 JSX 表达式会报错;
- 组件内可写多个内联块;所有块共享组件 hash。
CSS 追加规则与边界
- 规则选择器末尾追加属性(逗号列表逐段追加);
@media/@supports/@layer/@container内正常生效; - 伪元素保持在属性之后:
.btn::before→.btn[data-v-x]::before; @keyframes帧选择器、@page不追加;@import原样保留;- 选择器已含同一属性时跳过(幂等);
- scss/less 的
@import由预处理器内联展开后再追加,同样命中 scope。
注意
- 隔离只作用于「本文件书写的 JSX 元素」;跨文件组件内部 DOM 默认不带父级 scope,
需要时用
scopedId手动继承; - 全局样式(普通
<style>/ 非.scoped.*文件)不受本工具影响。
配置项与完整 API 见 API 参考。