电子商务网站体系结构有哪些,陈木胜导演拍完怒火重案走的吗,家装设计培训班哪里有,摄影手机网站模板组件基础 {#components-basics}
组件允许我们将 UI 划分为独立的、可重用的部分#xff0c;并且可以对每个部分进行单独的思考。在实际应用中#xff0c;组件常常被组织成层层嵌套的树状结构#xff1a; 这和我们嵌套 HTML 元素的方式类似#xff0c;Vue 实现了自己的组件…组件基础 {#components-basics}
组件允许我们将 UI 划分为独立的、可重用的部分并且可以对每个部分进行单独的思考。在实际应用中组件常常被组织成层层嵌套的树状结构 这和我们嵌套 HTML 元素的方式类似Vue 实现了自己的组件模型使我们可以在每个组件内封装自定义内容与逻辑。Vue 同样也能很好地配合原生 Web Component。如果你想知道 Vue 组件与原生 Web Components 之间的关系可以阅读此章节。
定义一个组件 {#defining-a-component}
当使用构建步骤时我们一般会将 Vue 组件定义在一个单独的 .vue 文件中这被叫做单文件组件 (简称 SFC) script
export default {data() {return {count: 0}}
}
/scripttemplatebutton clickcountYou clicked me {{ count }} times./button
/templatescript setup
import { ref } from vueconst count ref(0)
/scripttemplatebutton clickcountYou clicked me {{ count }} times./button
/template当不使用构建步骤时一个 Vue 组件以一个包含 Vue 特定选项的 JavaScript 对象来定义 export default {data() {return {count: 0}},template: button clickcountYou clicked me {{ count }} times./button
}import { ref } from vueexport default {setup() {const count ref(0)return { count }},template: button clickcountYou clicked me {{ count }} times./button// 也可以针对一个 DOM 内联模板// template: #my-template-element
}这里的模板是一个内联的 JavaScript 字符串Vue 将会在运行时编译它。你也可以使用 ID 选择器来指向一个元素 (通常是原生的 template 元素)Vue 将会使用其内容作为模板来源。
上面的例子中定义了一个组件并在一个 .js 文件里默认导出了它自己但你也可以通过具名导出在一个文件中导出多个组件。
使用组件 {#using-a-component}
:::tip 我们会在接下来的指引中使用 SFC 语法无论你是否使用构建步骤组件相关的概念都是相同的。示例一节中展示了两种场景中的组件使用情况。 :::
要使用一个子组件我们需要在父组件中导入它。假设我们把计数器组件放在了一个叫做 ButtonCounter.vue 的文件中这个组件将会以默认导出的形式被暴露给外部。 script
import ButtonCounter from ./ButtonCounter.vueexport default {components: {ButtonCounter}
}
/scripttemplateh1Here is a child component!/h1ButtonCounter /
/template若要将导入的组件暴露给模板我们需要在 components 选项上注册它。这个组件将会以其注册时的名字作为模板中的标签名。 script setup
import ButtonCounter from ./ButtonCounter.vue
/scripttemplateh1Here is a child component!/h1ButtonCounter /
/template通过 script setup导入的组件都在模板中直接可用。
当然你也可以全局地注册一个组件使得它在当前应用中的任何组件上都可以使用而不需要额外再导入。关于组件的全局注册和局部注册两种方式的利弊我们放在了组件注册这一章节中专门讨论。
组件可以被重用任意多次
h1Here is a child component!/h1
ButtonCounter /
ButtonCounter /
ButtonCounter /在演练场中尝试一下 在演练场中尝试一下
你会注意到每当点击这些按钮时每一个组件都维护着自己的状态是不同的 count。这是因为每当你使用一个组件就创建了一个新的实例。
在单文件组件中推荐为子组件使用 PascalCase 的标签名以此来和原生的 HTML 元素作区分。虽然原生 HTML 标签名是不区分大小写的但 Vue 单文件组件是可以在编译中区分大小写的。我们也可以使用 / 来关闭一个标签。
如果你是直接在 DOM 中书写模板 (例如原生 template 元素的内容)模板的编译需要遵从浏览器中 HTML 的解析行为。在这种情况下你应该需要使用 kebab-case 形式并显式地关闭这些组件的标签。
!-- 如果是在 DOM 中书写该模板 --
button-counter/button-counter
button-counter/button-counter
button-counter/button-counter请看 DOM 模板解析注意事项了解更多细节。
传递 props {#passing-props}
如果我们正在构建一个博客我们可能需要一个表示博客文章的组件。我们希望所有的博客文章分享相同的视觉布局但有不同的内容。要实现这样的效果自然必须向组件中传递数据例如每篇文章标题和内容这就会使用到 props。
Props 是一种特别的 attributes你可以在组件上声明注册。要传递给博客文章组件一个标题我们必须在组件的 props 列表上声明它。这里要用到 props 选项defineProps 宏 !-- BlogPost.vue --
script
export default {props: [title]
}
/scripttemplateh4{{ title }}/h4
/template当一个值被传递给 prop 时它将成为该组件实例上的一个属性。该属性的值可以像其他组件属性一样在模板和组件的 this 上下文中访问。 !-- BlogPost.vue --
script setup
defineProps([title])
/scripttemplateh4{{ title }}/h4
/templatedefineProps 是一个仅 script setup 中可用的编译宏命令并不需要显式地导入。声明的 props 会自动暴露给模板。defineProps 会返回一个对象其中包含了可以传递给组件的所有 props
const props defineProps([title])
console.log(props.title)TypeScript 用户请参考为组件 props 标注类型
如果你没有使用 script setupprops 必须以 props 选项的方式声明props 对象会作为 setup() 函数的第一个参数被传入
export default {props: [title],setup(props) {console.log(props.title)}
}一个组件可以有任意多的 props默认情况下所有 prop 都接受任意类型的值。
当一个 prop 被注册后可以像这样以自定义 attribute 的形式传递数据给它
BlogPost titleMy journey with Vue /
BlogPost titleBlogging with Vue /
BlogPost titleWhy Vue is so fun /在实际应用中我们可能在父组件中会有如下的一个博客文章数组 export default {// ...data() {return {posts: [{ id: 1, title: My journey with Vue },{ id: 2, title: Blogging with Vue },{ id: 3, title: Why Vue is so fun }]}}
}const posts ref([{ id: 1, title: My journey with Vue },{ id: 2, title: Blogging with Vue },{ id: 3, title: Why Vue is so fun }
])这种情况下我们可以使用 v-for 来渲染它们
BlogPostv-forpost in posts:keypost.id:titlepost.title/在演练场中尝试一下 在演练场中尝试一下
留意我们是如何使用 v-bind 来传递动态 prop 值的。当事先不知道要渲染的确切内容时这一点特别有用。
以上就是目前你需要了解的关于 props 的全部了。如果你看完本章节后还想知道更多细节我们推荐你深入阅读关于 props 的完整指引。
监听事件 {#listening-to-events}
让我们继续关注我们的 BlogPost 组件。我们会发现有时候它需要与父组件进行交互。例如要在此处实现无障碍访问的需求将博客文章的文字能够放大而页面的其余部分仍使用默认字号。
在父组件中我们可以添加一个 postFontSize 数据属性ref 来实现这个效果 data() {return {posts: [/* ... */],postFontSize: 1}
}const posts ref([/* ... */
])const postFontSize ref(1)在模板中用它来控制所有博客文章的字体大小
div :style{ fontSize: postFontSize em }BlogPostv-forpost in posts:keypost.id:titlepost.title/
/div然后给 BlogPost 组件添加一个按钮
!-- BlogPost.vue, 省略了 script --
templatediv classblog-posth4{{ title }}/h4buttonEnlarge text/button/div
/template这个按钮目前还没有做任何事情我们想要点击这个按钮来告诉父组件它应该放大所有博客文章的文字。要解决这个问题组件实例提供了一个自定义事件系统。父组件可以通过 v-on 或 来选择性地监听子组件上抛的事件就像监听原生 DOM 事件那样
BlogPost...enlarge-textpostFontSize 0.1/子组件可以通过调用内置的 $emit 方法通过传入事件名称来抛出一个事件
!-- BlogPost.vue, 省略了 script --
templatediv classblog-posth4{{ title }}/h4button click$emit(enlarge-text)Enlarge text/button/div
/template因为有了 enlarge-textpostFontSize 0.1 的监听父组件会接收这一事件从而更新 postFontSize 的值。 在演练场中尝试一下 在演练场中尝试一下
我们可以通过 emits 选项defineEmits 宏来声明需要抛出的事件 !-- BlogPost.vue --
script
export default {props: [title],emits: [enlarge-text]
}
/script!-- BlogPost.vue --
script setup
defineProps([title])
defineEmits([enlarge-text])
/script这声明了一个组件可能触发的所有事件还可以对事件的参数进行验证。同时这还可以让 Vue 避免将它们作为原生事件监听器隐式地应用于子组件的根元素。 和 defineProps 类似defineEmits 仅可用于 script setup 之中并且不需要导入它返回一个等同于 $emit 方法的 emit 函数。它可以被用于在组件的 script setup 中抛出事件因为此处无法直接访问 $emit
script setup
const emit defineEmits([enlarge-text])emit(enlarge-text)
/scriptTypeScript 用户请参考为组件 emits 标注类型
如果你没有在使用 script setup你可以通过 emits 选项定义组件会抛出的事件。你可以从 setup() 函数的第二个参数即 setup 上下文对象上访问到 emit 函数
export default {emits: [enlarge-text],setup(props, ctx) {ctx.emit(enlarge-text)}
}以上就是目前你需要了解的关于组件自定义事件的所有知识了。如果你看完本章节后还想知道更多细节请深入阅读组件事件章节。
通过插槽来分配内容 {#content-distribution-with-slots}
一些情况下我们会希望能和 HTML 元素一样向组件中传递内容
AlertBoxSomething bad happened.
/AlertBox我们期望能渲染成这样
:::danger This is an Error for Demo Purposes Something bad happened. :::
这可以通过 Vue 的自定义 slot 元素来实现
templatediv classalert-boxstrongThis is an Error for Demo Purposes/strongslot //div
/templatestyle scoped
.alert-box {/* ... */
}
/style如上所示我们使用 slot 作为一个占位符父组件传递进来的内容就会渲染在这里。 在演练场中尝试一下 在演练场中尝试一下
以上就是目前你需要了解的关于插槽的所有知识了。如果你看完本章节后还想知道更多细节请深入阅读组件插槽章节。
动态组件 {#dynamic-components}
有些场景会需要在两个组件间来回切换比如 Tab 界面 在演练场中查看示例 在演练场中查看示例
上面的例子是通过 Vue 的 component 元素和特殊的 is attribute 实现的 !-- currentTab 改变时组件也改变 --
component :iscurrentTab/component!-- currentTab 改变时组件也改变 --
component :istabs[currentTab]/component在上面的例子中被传给 :is 的值可以是以下几种
被注册的组件名导入的组件对象
你也可以使用 is attribute 来创建一般的 HTML 元素。
当使用 component :is... 来在多个组件间作切换时被切换掉的组件会被卸载。我们可以通过 KeepAlive 组件强制被切换掉的组件仍然保持“存活”的状态。
DOM 模板解析注意事项 {#dom-template-parsing-caveats}
如果你想在 DOM 中直接书写 Vue 模板Vue 则必须从 DOM 中获取模板字符串。由于浏览器的原生 HTML 解析行为限制有一些需要注意的事项。
:::tip 请注意下面讨论只适用于直接在 DOM 中编写模板的情况。如果你使用来自以下来源的字符串模板就不需要顾虑这些限制了
单文件组件内联模板字符串 (例如 template: ...)script typetext/x-template :::
大小写区分 {#case-insensitivity}
HTML 标签和属性名称是不分大小写的所以浏览器会把任何大写的字符解释为小写。这意味着当你使用 DOM 内的模板时无论是 PascalCase 形式的组件名称、camelCase 形式的 prop 名称还是 v-on 的事件名称都需要转换为相应等价的 kebab-case (短横线连字符) 形式
// JavaScript 中的 camelCase
const BlogPost {props: [postTitle],emits: [updatePost],template: h3{{ postTitle }}/h3
}!-- HTML 中的 kebab-case --
blog-post post-titlehello! update-postonUpdatePost/blog-post闭合标签 {#self-closing-tags}
我们在上面的例子中已经使用过了闭合标签 (self-closing tag)
MyComponent /这是因为 Vue 的模板解析器支持任意标签使用 / 作为标签关闭的标志。
然而在 DOM 模板中我们必须显式地写出关闭标签
my-component/my-component这是由于 HTML 只允许一小部分特殊的元素省略其关闭标签最常见的就是 input 和 img。对于其他的元素来说如果你省略了关闭标签原生的 HTML 解析器会认为开启的标签永远没有结束用下面这个代码片段举例来说
my-component / !-- 我们想要在这里关闭标签... --
spanhello/span将被解析为
my-componentspanhello/span
/my-component !-- 但浏览器会在这里关闭标签 --元素位置限制 {#element-placement-restrictions}
某些 HTML 元素对于放在其中的元素类型有限制例如 uloltable 和 select相应的某些元素仅在放置于特定元素中时才会显示例如 litr 和 option。
这将导致在使用带有此类限制元素的组件时出现问题。例如
tableblog-post-row/blog-post-row
/table自定义的组件 blog-post-row 将作为无效的内容被忽略因而在最终呈现的输出中造成错误。我们可以使用特殊的 is attribute 作为一种解决方案
tabletr isvue:blog-post-row/tr
/table:::tip 当使用在原生 HTML 元素上时is 的值必须加上前缀 vue: 才可以被解析为一个 Vue 组件。这一点是必要的为了避免和原生的自定义内置元素相混淆。 :::
以上就是你需要了解的关于 DOM 模板解析的所有注意事项同时也是 Vue 基础部分的所有内容。祝贺你虽然还有很多需要学习的但你可以先暂停一下去用 Vue 做一些有趣的东西或者研究一些示例。
完成了本页的阅读后回顾一下你刚才所学到的知识如果还想知道更多细节我们推荐你继续阅读关于组件的完整指引。
插槽 Slots {#slots} 此章节假设你已经看过了组件基础。若你还不了解组件是什么请先阅读该章节。 插槽内容与出口 {#slot-content-and-outlet}
在之前的章节中我们已经了解到组件能够接收任意类型的 JavaScript 值作为 props但组件要如何接收模板内容呢在某些场景中我们可能想要为子组件传递一些模板片段让子组件在它们的组件中渲染这些片段。
举例来说这里有一个 FancyButton 组件可以像这样使用
FancyButtonClick me! !-- 插槽内容 --
/FancyButton而 FancyButton 的模板是这样的
button classfancy-btnslot/slot !-- 插槽出口 --
/buttonslot 元素是一个插槽出口 (slot outlet)标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。 最终渲染出的 DOM 是这样
button classfancy-btnClick me!/button在演练场中尝试一下 在演练场中尝试一下
通过使用插槽FancyButton 仅负责渲染外层的 button (以及相应的样式)而其内部的内容由父组件提供。
理解插槽的另一种方式是和下面的 JavaScript 函数作类比其概念是类似的
// 父元素传入插槽内容
FancyButton(Click me!)// FancyButton 在自己的模板中渲染插槽内容
function FancyButton(slotContent) {return button classfancy-btn${slotContent}/button
}插槽内容可以是任意合法的模板内容不局限于文本。例如我们可以传入多个元素甚至是组件
FancyButtonspan stylecolor:redClick me!/spanAwesomeIcon nameplus /
/FancyButton在演练场中尝试一下 在演练场中尝试一下
通过使用插槽FancyButton 组件更加灵活和具有可复用性。现在组件可以用在不同的地方渲染各异的内容但同时还保证都具有相同的样式。
Vue 组件的插槽机制是受原生 Web Component slot 元素的启发而诞生同时还做了一些功能拓展这些拓展的功能我们后面会学习到。
渲染作用域 {#render-scope}
插槽内容可以访问到父组件的数据作用域因为插槽内容本身是在父组件模板中定义的。举例来说
span{{ message }}/span
FancyButton{{ message }}/FancyButton这里的两个 {{ message }} 插值表达式渲染的内容都是一样的。
插槽内容无法访问子组件的数据。Vue 模板中的表达式只能访问其定义时所处的作用域这和 JavaScript 的词法作用域规则是一致的。换言之 父组件模板中的表达式只能访问父组件的作用域子组件模板中的表达式只能访问子组件的作用域。 默认内容 {#fallback-content}
在外部没有提供任何内容的情况下可以为插槽指定默认内容。比如有这样一个 SubmitButton 组件
button typesubmitslot/slot
/button如果我们想在父组件没有提供任何插槽内容时在 button 内渲染“Submit”只需要将“Submit”写在 slot 标签之间来作为默认内容
button typesubmitslotSubmit !-- 默认内容 --/slot
/button现在当我们在父组件中使用 SubmitButton 且没有提供任何插槽内容时
SubmitButton /“Submit”将会被作为默认内容渲染
button typesubmitSubmit/button但如果我们提供了插槽内容
SubmitButtonSave/SubmitButton那么被显式提供的内容会取代默认内容
button typesubmitSave/button在演练场中尝试一下 在演练场中尝试一下
具名插槽 {#named-slots}
有时在一个组件中包含多个插槽出口是很有用的。举例来说在一个 BaseLayout 组件中有如下模板
div classcontainerheader!-- 标题内容放这里 --/headermain!-- 主要内容放这里 --/mainfooter!-- 底部内容放这里 --/footer
/div对于这种场景slot 元素可以有一个特殊的 attribute name用来给各个插槽分配唯一的 ID以确定每一处要渲染的内容
div classcontainerheaderslot nameheader/slot/headermainslot/slot/mainfooterslot namefooter/slot/footer
/div这类带 name 的插槽被称为具名插槽 (named slots)。没有提供 name 的 slot 出口会隐式地命名为“default”。
在父组件中使用 BaseLayout 时我们需要一种方式将多个插槽内容传入到各自目标插槽的出口。此时就需要用到具名插槽了
要为具名插槽传入内容我们需要使用一个含 v-slot 指令的 template 元素并将目标插槽的名字传给该指令
BaseLayouttemplate v-slot:header!-- header 插槽的内容放这里 --/template
/BaseLayoutv-slot 有对应的简写 #因此 template v-slot:header 可以简写为 template #header。其意思就是“将这部分模板片段传入子组件的 header 插槽中”。 下面我们给出完整的、向 BaseLayout 传递插槽内容的代码指令均使用的是缩写形式
BaseLayouttemplate #headerh1Here might be a page title/h1/templatetemplate #defaultpA paragraph for the main content./ppAnd another one./p/templatetemplate #footerpHeres some contact info/p/template
/BaseLayout当一个组件同时接收默认插槽和具名插槽时所有位于顶级的非 template 节点都被隐式地视为默认插槽的内容。所以上面也可以写成
BaseLayouttemplate #headerh1Here might be a page title/h1/template!-- 隐式的默认插槽 --pA paragraph for the main content./ppAnd another one./ptemplate #footerpHeres some contact info/p/template
/BaseLayout现在 template 元素中的所有内容都将被传递到相应的插槽。最终渲染出的 HTML 如下
div classcontainerheaderh1Here might be a page title/h1/headermainpA paragraph for the main content./ppAnd another one./p/mainfooterpHeres some contact info/p/footer
/div在演练场中尝试一下 在演练场中尝试一下
使用 JavaScript 函数来类比可能更有助于你来理解具名插槽
// 传入不同的内容给不同名字的插槽
BaseLayout({header: ...,default: ...,footer: ...
})// BaseLayout 渲染插槽内容到对应位置
function BaseLayout(slots) {return div classcontainerheader${slots.header}/headermain${slots.default}/mainfooter${slots.footer}/footer/div
}动态插槽名 {#dynamic-slot-names}
动态指令参数在 v-slot 上也是有效的即可以定义下面这样的动态插槽名
base-layouttemplate v-slot:[dynamicSlotName].../template!-- 缩写为 --template #[dynamicSlotName].../template
/base-layout注意这里的表达式和动态指令参数受相同的语法限制。
作用域插槽 {#scoped-slots}
在上面的渲染作用域中我们讨论到插槽的内容无法访问到子组件的状态。
然而在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。
我们也确实有办法这么做可以像对组件传递 props 那样向一个插槽的出口上传递 attributes
!-- MyComponent 的模板 --
divslot :textgreetingMessage :count1/slot
/div当需要接收插槽 props 时默认插槽和具名插槽的使用方式有一些小区别。下面我们将先展示默认插槽如何接受 props通过子组件标签上的 v-slot 指令直接接收到了一个插槽 props 对象
MyComponent v-slotslotProps{{ slotProps.text }} {{ slotProps.count }}
/MyComponent[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lq0fLQB4-1691855652867)(https://cn.vuejs.org/assets/scoped-slots.1c6d5876.svg)] 在演练场中尝试一下 在演练场中尝试一下
子组件传入插槽的 props 作为了 v-slot 指令的值可以在插槽内的表达式中访问。
你可以将作用域插槽类比为一个传入子组件的函数。子组件会将相应的 props 作为参数传给它
MyComponent({// 类比默认插槽将其想成一个函数default: (slotProps) {return ${slotProps.text} ${slotProps.count}}
})function MyComponent(slots) {const greetingMessage helloreturn div${// 在插槽函数调用时传入 propsslots.default({ text: greetingMessage, count: 1 })}/div
}实际上这已经和作用域插槽的最终代码编译结果、以及手动编写渲染函数时使用作用域插槽的方式非常类似了。
v-slotslotProps 可以类比这里的函数签名和函数的参数类似我们也可以在 v-slot 中使用解构
MyComponent v-slot{ text, count }{{ text }} {{ count }}
/MyComponent具名作用域插槽 {#named-scoped-slots}
具名作用域插槽的工作方式也是类似的插槽 props 可以作为 v-slot 指令的值被访问到v-slot:nameslotProps。当使用缩写时是这样
MyComponenttemplate #headerheaderProps{{ headerProps }}/templatetemplate #defaultdefaultProps{{ defaultProps }}/templatetemplate #footerfooterProps{{ footerProps }}/template
/MyComponent向具名插槽中传入 props
slot nameheader messagehello/slot注意插槽上的 name 是一个 Vue 特别保留的 attribute不会作为 props 传递给插槽。因此最终 headerProps 的结果是 { message: hello }。
如果你同时使用了具名插槽与默认插槽则需要为默认插槽使用显式的 template 标签。尝试直接为组件添加 v-slot 指令将导致编译错误。这是为了避免因默认插槽的 props 的作用域而困惑。举例
!-- 该模板无法编译 --
templateMyComponent v-slot{ message }p{{ message }}/ptemplate #footer!-- message 属于默认插槽此处不可用 --p{{ message }}/p/template/MyComponent
/template为默认插槽使用显式的 template 标签有助于更清晰地指出 message 属性在其他插槽中不可用
templateMyComponent!-- 使用显式的默认插槽 --template #default{ message }p{{ message }}/p/templatetemplate #footerpHeres some contact info/p/template/MyComponent
/template高级列表组件示例 {#fancy-list-example}
你可能想问什么样的场景才适合用到作用域插槽这里我们来看一个 FancyList 组件的例子。它会渲染一个列表并同时会封装一些加载远端数据的逻辑、使用数据进行列表渲染、或者是像分页或无限滚动这样更进阶的功能。然而我们希望它能够保留足够的灵活性将对单个列表元素内容和样式的控制权留给使用它的父组件。我们期望的用法可能是这样的
FancyList :api-urlurl :per-page10template #item{ body, username, likes }div classitemp{{ body }}/ppby {{ username }} | {{ likes }} likes/p/div/template
/FancyList在 FancyList 之中我们可以多次渲染 slot 并每次都提供不同的数据 (注意我们这里使用了 v-bind 来传递插槽的 props)
ulli v-foritem in itemsslot nameitem v-binditem/slot/li
/ul在演练场中尝试一下 在演练场中尝试一下
无渲染组件 {#renderless-components}
上面的 FancyList 案例同时封装了可重用的逻辑 (数据获取、分页等) 和视图输出但也将部分视图输出通过作用域插槽交给了消费者组件来管理。
如果我们将这个概念拓展一下可以想象的是一些组件可能只包括了逻辑而不需要自己渲染内容视图输出通过作用域插槽全权交给了消费者组件。我们将这种类型的组件称为无渲染组件。
这里有一个无渲染组件的例子一个封装了追踪当前鼠标位置逻辑的组件
MouseTracker v-slot{ x, y }Mouse is at: {{ x }}, {{ y }}
/MouseTracker在演练场中尝试一下 在演练场中尝试一下
虽然这个模式很有趣但大部分能用无渲染组件实现的功能都可以通过组合式 API 以另一种更高效的方式实现并且还不会带来额外组件嵌套的开销。之后我们会在组合式函数一章中介绍如何更高效地实现追踪鼠标位置的功能。
尽管如此作用域插槽在需要同时封装逻辑、组合视图界面时还是很有用就像上面的 FancyList 组件那样。