接着前面的内容:https://www.cnblogs.com/yanggb/p/12631279.html。
循环引用
递归组件
组件是可以在它们自己的模板中调用自身的,不过它们只能通过【name】选项来做这件事。
name: 'unique-name-of-my-component'
当你使用【Vue.component】全局注册一个组件的时候,这个全局的id就会自动设置为该组件的【name】选项。
Vue.component('unique-name-of-my-component', { // ... })
但是稍有不慎,递归组件就可能会导致无限循环,进而导致页面渲染引擎奔溃的情况:
name: 'stack-overflow',
template: '<div><stack-overflow></stack-overflow></div>'
类似上述的组件将会导致【max stack size exceeded】错误,所以请确保递归调用是有条件的(例如使用一个最终会得到false的【v-if】指令)。
组件之间的循环引用
假设你需要构建一个文件目录树,像资源管理器那样的,你可能就会有一个<tree-folder>组件,模板是这样的:
<p> <span>{{ folder.name }}</span> <tree-folder-contents :children="folder.children"/> </p>
还会有一个<tree-folder-contents>组件,模板是这样的:
<ul> <li v-for="child in children"> <tree-folder v-if="child.children" :folder="child"/> <span v-else>{{ child.name }}</span> </li> </ul>
当你仔细观察的时候,会发现这些组件在渲染树中互为对方的后代和祖先——一个悖论!当通过【Vue.component】全局注册组件的时候,这个悖论会被自动解开。当时如果你使用的是一个模块系统依赖/导入组件,例如通过webpack或browserify,你就会遇到一个错误:
Failed to mount component: template or render function not defined.
为了解释这里发生了什么,我们先把这两个组件称为A和B。模块系统发现它需要A,但是首先A依赖B,但是B又依赖A,但是A又依赖B,如此反复,就变成了一个死循环,解析器不知道如何不经过其中一个组件而完全解析出另一个组件。因此,为了解决这个问题,我们需要给模块系统一个点,告诉模块系统:A反正是需要B的,但是我们不需要先解析B。
在上面的例子中,我们可以把<tree-folder>组件设为那个点。我们知道,那个产生悖论的子组件是<tree-folder-contents>组件,所以我们会等到生命周期钩子函数【beforeCreate】时去注册它:
beforeCreate: function () { this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default }
或者,在本地注册组件的时候,你可以使用webpack的异步import(推荐):
components: { TreeFolderContents: () => import('./tree-folder-contents.vue') }
这样问题就解决了!
模板定义的替代品
内联模板
当【inline-template】这个特殊的属性出现在一个子组件上的时候,这个组件将会使用其里面的内容作为模板,而不是将其作为被分发的内容。这使得模板的撰写工作更加灵活:
<my-component inline-template> <div> <p>These are compiled as the component's own template.</p> <p>Not parent's transclusion content.</p> </div> </my-component>
内联模板需要定义在vue所属的dom元素内。
但是要注意的是,【inline-template】属性会让模板的作用域变得更加难以理解。所以,作为最佳实践,请在组件内优先选择【template】选项或在【.vue】文件里的一个<template>元素来定义模板。
x-template
另一个定义模板的方式是在一个<script>元素中,并为其带上【text/x-template】的类型,然后通过一个id将模板引用过去,例如:
<script type="text/x-template" id="hello-world-template"> <p>Hello hello hello</p> </script>
Vue.component('hello-world', { template: '#hello-world-template' })
要特别注意的是,x-template需要定义在vue所属的dom元素外。
上面的这两种方式可以用于模板特别大的demo或者极小型的应用,但是在其它的情况下请尽量避免使用,因为这回将模板和该组件的其他定义分离开,不利于后期的维护。
控制更新
vue的响应式系统可以知道何时进行dom更新(如果你用对了的话),不过其终究会存在局限性。因此,在一些边界情况你会想要强制更新,尽管表面上看响应式的数据没有发生改变,也有一些情况你会想要阻止不必要的更新。
强制更新
如果你发现你需要在vue中做一次强制更新,官方文档强调,99.9的情况,是你在某个地方做错了事。
你可能还没有留意到数组或对象的变更检测注意事项,或者你可能依赖了一个未被vue响应式系统追踪的状态。然而,如果你已经做到了上述的事项仍然发现在极少数的情况下需要手动强制更新的话,那么你可以通过【$forceUpdate】全局属性来做这件事。
vm.$forceUpdate()
这样会迫使vue实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有的子组件。
通过【v-once】指令创建低开销的静态组件
渲染普通的html元素在vue中是非常快速的,但有的时候你可能有一个组件,这个组件包含了大量的静态内容。在这种情况下,你可以在根元素上添加【v-once】指令以确保这些内容只计算一次然后缓存起来,就像这样:
Vue.component('terms-of-service', { template: ` <div v-once> <h1>Terms of Service</h1> ... a lot of static content ... </div> ` })
可能前面已经说过了,但是在这里还是要再说一次:要格外注意不要过度使用这个模式。当你需要渲染大量的静态内容的时候,极少数的情况下它会给你带来便利,除非你非常留意渲染变慢了,不然它完全是没有必要的——再加上它在后期会带来很多的困惑。例如,设想另一个开发者并不熟悉【v-once】指令或漏看了它在模板中,他可能会花费很多个小时去找出模板为什么无法被正确更新的原因。
"我还是很喜欢你,像无缘花了荼靡,小楼寂寂。"