首先来思考一个问题:是否有一种方法可以从子组件填充父组件的插槽?
最近一位同事问我这个问题,答案很简单:可以的。但我的解决方案可能和你想的完全不一样,这是涉及一个棘手的Vue架构问题,但也是一个非常有趣的问题。
为什么会有这个问题
在我们的应用程序中,我们有一个顶部栏,其中包含不同的按钮、搜索栏和其他一些控件。根据每个人所在的页面,它可能略有不同,因此我们需要一种基于每个页面配置它的方法。
为此,我们希望每个页面都能够配置操作栏。看起来很简单,但这里有个问题
这个顶部栏(我们称之为ActionBar
)实际上是我们的主布局的一部分,结构如下:
<template> <div> <FullPageError /> <ActionBar /> <App /> </div> </template>
根据你所在的页面/路线动态注入App
的位置。
我们可以使用ActionBar
上的一些插槽来配置它。 但是,我们如何从App
组件中控制这些插槽?
定义问题
首先,最好是尽可能清楚地知道我们要解决的问题。
我们来看一个具有一个子组件和一个插槽的组件:
// Parent.vue <template> <div> <Child /> <slot /> </div> </template>
我们可以这样填充Parent
的插槽:
// App.vue <template> <Parent> <p>This content goes into the slot</p> </Parent> </template>
这里没什么特别的。。。
填充子组件的插槽很容易,这也是使用插槽的最常见方式。
但是,有没有一种方法可以控制从Child
组件内部进入Parent
组件slot
的内容呢?
换种说法:我们可以让子组件填充父组件的插槽吗?来看看我想到的第一个解决方案。
向下使用 props,向上使用 event
数据流经组件树的唯一途径是使用props
。 而向上通信的方法是使用事件。这意味着,如果要让子组件与父组件进行通信,我们需要使用事件来实现。
因此,我们将使用事件来将内容传递到ActionBars
槽中
import SlotContent from './SlotContent'; export default { name: 'Application', created() { // As soon as this component is created we'll emit our events this.$emit('slot-content', SlotContent); } };
我们将要放入插槽中的所有内容打包到SlotContent
组件中。 一旦创建了应用程序组件,我们就会发出slot-content
事件,并传递我们要使用的组件。
我们的组件结构如下:
<template> <div> <FullPageError /> <ActionBar> <Component :is="slotContent" /> </ActionBar> <App @slot-content="component => slotContent = component" /> </div> </template>
监听该事件,并将slotContent
设置为我们的App
组件发送给我们的任何内容。 然后,使用内置的Component
,就可以动态地渲染该组件。
但是,通过事件传递组件感觉很奇怪,并非是主流的做法。幸运的是,还有一种方法可以完全避免使用事件。
使用 $options
由于Vue组件只是 JS 对象,因此我们可以向它们添加所需的任何属性。无需使用事件传递插槽内容,我们只需将其作为字段添加到组件中即可:
// App.vue import SlotContent from './SlotContent'; export default { name: 'Application', slotContent: SlotContent, props: { /***/ }, computed: { /***/ }, };
在主页中通过 App.slotContent
获取对应的组件
<template> <div> <FullPageError /> <ActionBar> <Component :is="slotContent" /> </ActionBar> <App /> </div> </template> import App from './App'; import FullPageError from './FullPageError'; import ActionBar from './ActionBar'; export default { name: 'Scaffold', components: { App, FullPageError, ActionBar, } data() { return { slotContent: App.slotContent, } }, };
这更像是静态配置,更美观、更简洁,但这仍然是不对的。
理想情况下,我们不会在代码中混合使用范式,所有操作应该都是以声明方式完成。
但是在这里,我们没有将我们的组件组合在一起,而是将它们作为 JS 对象传递。如果我们能以正常的Vue方式把我们想要的写在插槽里就好了。
考虑 Portal(传送门)
Vue 中的 Portal 技术 在 Vue 项目中,我们使用模板来声明 dom嵌套关系,然而有时候一些组件需要脱离固定的层级关系,不再受制与层叠上下文,比如说 Modal 和 Dialog
这种组件就希望能够脱离当前模板所在的层叠上下文。在 Vue 中有两种方式来实现这种效果,一种是使用指令,操作真实 dom,使用熟知的 dom 操作方法将指令所在的元素 append
到另外一个 dom 节点上去。另一种方式就是定义一套组件,将组件内的 vnode 转移到另外一个组件中去,然后各自渲染。
它们的工作方式和你想象的完全一样。你可以把任何东西从一个地方传送到另一个地方。在我们的例子中,我们将元素从DOM中的一个位置“传送”到另一个位置。
无论组件树如何显示,我们都可以控制组件在DOM中的显示位置。
例如,假设我们想要填充一个modal
。但是我们的modal
必须在根页面处渲染,这样我们才能正确地覆盖它。首先,我们要在modal
中指定我们想要的:
<template> <div> <!-- Other components --> <Portal to="modal"> Rendered in the modal. </Portal> </div> </template>
然后,在我们的modal
组件中,我们将拥有另一个将内容渲染出来的 portal:
<template> <div> <h1>Modal</h1> <Portal from="modal" /> </div> </template>
这是一项改进,因为现在我们实际上是在编写HTML,而不仅仅是传递对象。 它更具声明性,更容易查看应用程序中发生的事情。
由于 portal 在背后执行一些操作以在不同位置渲染元素,因此它完全打破了DOM渲染在Vue中工作方式的模型。 看起来您正在正常渲染元素,但根本无法正常工作,这可能会引起很多混乱和沮丧。
还有一个很大的问题,稍后我们会讲到。
提升状态
“提升状态”是指将状态从子组件移动到父组件或祖父组件,将它向上移动到组件树中。
这可能对应用程序的体系结构产生较大的影响。对于我们的目的,这会是更简单的解决方案。
这里的“状态”是我们试图传递到ActionBar
组件插槽中的内容。但是该状态包含在Page
组件中,我们不能真正将 page
特定的逻辑移到layout
组件中。 我们的状态必须保留在我们正在动态渲染的Page
组件内。
因此,我们必须提升整个Page
组件才能提升状态。当前,我们的Page
组件是Layout
组件的子组件:
<template> <div> <FullPageError /> <ActionBar /> <Page /> </div> </template>
解除它需要我们将其翻转,并使Layout
组件成为Page
组件的子组件。 我们的Page
组件看起来像这样:
<template> <Layout> <!-- Page-specific content --> </Layout> </template>
现在,我们的Layout
组件将看起来像这样,我们可以在其中使用插槽插入页面内容:
<template> <div> <FullPageError /> <ActionBar /> <slot /> </div> </template>
但这还不能让我们自定义任何内容。 我们必须在Layout
组件中添加一些命名的插槽,以便我们可以传递应放置在ActionBar
中的内容。
最简单的方法是使用一个插槽来完全替代ActionBar
组件:
<template> <div> <FullPageError /> <slot name="actionbar"> <ActionBar /> </slot> <slot /> </div> </template>
这样,如果你不指定“actionbar”
插槽,默认使用ActionBar
组件。 但我们可以使用自己的自定义ActionBar
配置覆盖此插槽:
<template> <Layout> <template #actionbar> <ActionBar> <!-- Custom content that goes into the action bar --> </ActionBar> </template> <!-- Page-specific content --> </Layout> </template>
对我来说,这是一种理想的处理方式,但是它确实需要我们重构页面的布局方式。 对于界面复杂点的,这可能是一项艰巨的任务。
简化一下
当我们第一次定义问题时:
我们可以让子组件填充父组件的插槽吗"_blank" href="https://dev.to/michaelthiessen/advanced-vue-controlling-parent-slots-case-study-c3k/comments">https://dev.to/michaelthiesse...以上就是Vue 技巧之控制父类的 slot的详细内容,更多关于Vue控制父类的 slot的资料请关注其它相关文章!
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]