Vue进阶技巧:控制父类的slot
作者:MichaelThiessen译者:前端小智来源:dev点赞阅读,养成习惯本文已收录到GitHubhttps://github。com/qq44924588...,更多的是往期好评文章的分类,也整理了很多我的文档和教程资料。欢迎来到星和完美。面试时可参考考点复习。我希望我们能在一起。让我们从一个问题开始:有没有办法从子组件填充父组件的插槽?最近有同事问我这个问题,答案很简单:是的。但是我的解决方案可能和你想的完全不一样,这涉及到一个棘手的Vue架构问题,同时也是一个非常有趣的问题。为什么会出现这个问题在我们的应用程序中,我们有一个顶部栏,其中包含不同的按钮、一个搜索栏和一些其他控件。根据每个人所在的页面,它可能会略有不同,因此我们需要一种在每个页面的基础上配置它的方法。为此,我们希望每个页面都能够配置操作栏。看起来很简单,但这就是问题所在。这个顶部栏(我们称它为ActionBar)实际上是我们主要布局的一部分,结构如下:根据您所在的页面/路线定位应用程序。我们可以使用ActionBar上的一些插槽来配置它。但是我们如何从App组件控制这些插槽呢?定义问题首先,最好尽可能清楚地了解我们要解决的问题。让我们看一个有一个子组件和一个槽的组件://Parent.vue
我们可以这样填充父槽://App.vueThiscontentgoesintotheslot
这里没什么特别的。..填充子组件的插槽很容易,这是最常用的插槽使用方式。但是有没有办法控制从子组件中进入父组件插槽的内容?换句话说:我们可以让子组件填充父组件的插槽吗?让我们看看我想到的第一个解决方案。向下使用props,向上使用事件数据流经组件树的唯一方式是使用props。向上沟通的方式是使用事件。这意味着如果我们想让子组件与父组件通信,我们需要使用事件来实现。因此,我们将使用事件向ActionBars插槽传送内容this.$emit('插槽内容',SlotContent);}};我们将要放入插槽的所有内容打包到SlotContent组件中。创建应用程序组件后,我们会发出slot-content事件,传递我们要使用的组件。我们的组件结构如下:>
监听此事件并将slotContent设置为我们的App组件发送给我们的任何内容。然后,使用内置组件,可以动态渲染组件。然而,通过事件传递组件感觉很奇怪而且不是主流。幸运的是,还有一种方法可以完全避免使用事件。使用$options由于Vue组件只是JS对象,我们可以向它们添加任何我们想要的属性。我们可以简单地将其作为字段添加到组件中,而不是使用事件来传递插槽内容://App.vueimportSlotContentfrom'./SlotContent';exportdefault{name:'Application',slotContent:SlotContent,props:{/***/},计算:{/***/},};通过首页中的App.slotContent获取对应的组件>从'./App'导入应用程序;从'./FullPageError'导入FullPageError;从'./ActionBar'导入ActionBar;导出默认{名称:'脚手架',组件:{App,FullPageError,ActionBar,}数据(){return{slotContent:App.slotContent,}},};这更像是静态配置,更漂亮、更干净,但这仍然是错误的。理想情况下,我们不会在我们的代码中混合范式,一切都应该以声明的方式完成。但是在这里,我们不是将我们的组件组合在一起,而是将它们作为JS对象传递。如果我们能以正常的Vue方式在插槽中写入我们想要的内容,那就太好了。考虑Vue中的Portal技术。在Vue项目中,我们使用模板来声明dom嵌套关系。但是有时候有些组件需要脱离固定的层级关系,不再受级联上下文的限制,比如Modal和Dialog这类组件希望能够脱离当前模板所在的级联上下文.在Vue中有两种方法可以实现这种效果。一种是使用指令操作真实的dom,使用大家熟知的dom操作方法,将指令所在的元素追加到另一个dom节点上。另一种方式是定义一组组件,将组件中的vnode转移到另一个组件中,然后分别渲染。它们的工作方式与您想象的完全一样。你可以将任何东西从一个地方传送到另一个地方。在我们的例子中,我们将元素从DOM中的一个位置“传送”到另一个位置。无论组件树如何显示,我们都可以控制组件在DOM中的显示位置。例如,假设我们要填充模态框。但是我们的模式必须在根页面呈现,这样我们才能正确地覆盖它。首先,我们需要在模态中指定我们想要的内容:
然后,在我们的模式组件中,我们将有另一个呈现内容的门户:
这是一个改进,因为现在我们实际上是在编写HTML,而不仅仅是传递对象。它更具声明性,也更容易查看应用程序中发生的事情。由于portal在幕后做了一些事情来渲染不同位置的元素,它完全打破了Vue中DOM渲染的工作模型。看起来您正在正常渲染元素,但根本不工作,这会导致很多混乱和沮丧。还有一个大问题,我们稍后再谈。提升状态“提升状态”意味着将状态从子组件移动到父组件或祖父组件,将其向上移动到组件树中。这会对应用程序的体系结构产生很大影响。出于我们的目的,这将是更简单的解决方案。这里的“状态”是我们试图传递到ActionBar组件的插槽中的内容。但该状态包含在页面组件中,我们无法真正将页面特定逻辑移至布局组件中。我们的状态必须保存在我们动态呈现的页面组件中。因此,为了提升状态,我们必须提升整个Page组件。当前,我们的Page组件是Layout组件的子组件:布局组件成为页面组件的子组件。我们的Page组件看起来像这样:
现在,我们的Layout组件看起来像这样,我们可以在其中使用插槽插入页面content:
但这还不能让我们自定义任何东西。我们必须在Layout组件中添加一些命名的插槽,以便我们可以传递应该放在ActionBar中的内容。最简单的方法是使用槽完全替换ActionBar组件:
/template>这样,如果您不指定“actionbar”插槽,则默认使用ActionBar组件。但是我们可以用我们自己的自定义ActionBar配置覆盖这个槽:对我来说,这是一种理想的处理方式,但它确实需要我们重构页面的布局方式。对于复杂的接口,这可能是一项艰巨的任务。当我们第一次定义问题时进行简化:我们可以让子组件填充父组件的插槽吗?但实际上,这个问题与道具无关。更简单地说,它是关于让子组件控制在其自己的子树之外呈现的内容。我们可以这样表述这个问题:组件控制在其子组件之外呈现的内容的最佳方式是什么?通过这个镜头检查我们提出的每个解决方案,为我们提供了一个有趣的新视角。向父组件发送事件数据流经组件树的唯一方式是通过使用props。向上沟通的方式是使用事件。这意味着如果我们想让子组件与父组件通信,我们需要使用事件来实现。静态配置只是向其他组件提供必要的信息,而不是主动要求另一个组件做事。Portal组件无法控制其子树之外的内容。这里的每个方法都是让另一个组件执行我们的命令并控制我们真正感兴趣的元素的不同方式。门户在这方面更好的原因是它们允许我们将所有这些通信逻辑封装到单独的组件中。提升状态提升状态是一种比我们之前看到的3种更简单、更强大的技术,我们这里的主要限制是我们想要控制的是在子组件之外。最简单的修复是:提升状态和操作该状态的逻辑,以便我们可以拥有更大范围的组件并将目标元素包含在该组件中。如果你能做到这一点,这就是解决这个特定问题以及所有相关问题的最简单方法。请记住,这并不一定意味着抬起整个组件。您还可以重构您的应用程序,将逻辑移至组件树中更高层的组件。依赖注入熟悉软件工程设计模式的人可能已经注意到,我们在这里做的是依赖注入,这是我们在软件工程中使用了几十年的技术。它的用途之一是编写易于配置的代码。在我们的例子中,我们在我们使用的每个页面中配置不同的Layout组件。交换页面和布局组件时,我们正在执行所谓的控制反转。在基于组件的框架中,父组件控制子组件的运行,所以我们选择让Page控制Layout组件,而不是让Layout组件控制Page。为此,我们使用插槽为Layout组件提供完成其工作所需的内容。正如我们所见,使用依赖注入可以使我们的代码更加模块化和可配置。总结我们讨论了解决这个问题的4种不同方法,展示了每种方法的优缺点。然后我们更进一步,将问题转化为更一般的问题,即控制组件子树之外的内容。、BoostState和DependencyInjection是两种非常有用的模式。它们是我们工具库中最好的工具,因为它们可以应用于无数的软件开发问题。但最重要的是,希望您还将学到:通过使用一些常见的软件模式,将具有丑陋解决方案的问题变成一个非常优雅的问题。通过将丑陋、复杂的问题变成更简单、更容易解决的问题,许多其他问题都可以通过这种方式解决。代码部署后可能存在的bug,无法实时获知。事后为了解决这些bug,花费了大量的时间在日志调试上。顺便推荐一个好用的bug监控工具Fundebug。原文:https://dev.to/michaelthiesse...每周更新交流文章。可以微信搜索“大千世界”阅读即时更新(比博文早一两篇)。这篇文章在GitHubhttps://github.com/qq449245884/xiaozhi已经收录,整理了很多我的文档。欢迎star和改进。可以参考考点面试。另外,关注公众号,后台会回复福利,看到福利就知道了。