在绝大多数情况下,我们最好不要触达另一个组件实例内部或手动操作 DOM 元素。不过也确实在一些情况下做这些事情是合适的。
访问根实例—$root
在每个子组件中,可以通过 $root 访问根实例。
// Vue 根实例
new Vue({
data: {
foo: 1
},
computed: {
bar () { /* ... */ }
},
methods: {
baz () { /* ... */ }
}
})
所有的子组件都可以将这个实例作为一个全局 store 来访问或使用。
// 获取根组件的数据
this.$root.foo
// 写入根组件的数据
this.$root.foo = 2
// 访问根组件的计算属性
this.$root.bar
// 调用根组件的方法
this.$root.baz()
在demo或在有少量组件的小型应用中使用是非常方便的。但是在大型应用里使用就会很复杂了。所以,我们还是要用Vuex(后面会学)来管理应用的状态。
访问父级组件实例—$parent
在子组件中,可以通过 $parent 访问 父组件实例。这可以替代将数据以prop的方式传入子组件的方式。
如:
<cmp-parent>
<cmp-a></cmp-a>
</cmp-parent>
若 cmp-parent 需要共享一个属性 share,它的所有子元素都需要访问 share 属性,在这种情况下 cmp-a 可以通过 this.$parent.share的方式访问share。
但是,通过这种模式构建出来的组件内部仍然容易出现问题。比如,我们在cmp-a 中嵌套一个一个子组件 cmp-b,如:
<cmp-parent>
<cmp-a>
<cmp-b></cmp-b>
</cmp-a>
</cmp-parent>
那么,在cmp-b组件中去访问share时,需要先查看一下,其父组件中是否存在share,如果不存在,则在向上一级查找,落实到代码上为:
var share = this.$parent.share || this.$parent.$parent.share;
这样做,很快组件就会失控:触达父级组件会使应用更难调试和理解,尤其是当变更父组件数据时,过一段时间后,很难找出变更是从哪里发起的。
碰到上述情况,可以使用依赖注入解决。
依赖注入—proveide-injuect
在上面的例子中,利用 $parent 属性,没有办法很好的扩展到更深层级的嵌套组件上。这也是依赖注入的用武之地,它用到了两个新的实例选项:provide 和 inject。
provide 选项允许我们指定想要提供给后代组件的数据/方法,例如:
Vue.component('cmp-parent', {
provide () {
return {
share: this.share,
}
},
data () {
return {
share: 'share',
}
},
template: `<div>cmp-parent</div>`
})
然后再任何后代组件中,我们都可以使用 inject 选项来接受指定想要添加在实例上的属性。
Vue.component('cmp-a', {
inject: ['share'],
template: `<div>cmp-a</div>`
})
相比 $parent 来说,这个用法可以让我们在任意后代组件中访问share,而不需要暴露整个 cmp-parent 实例。这允许我们更好的持续研发该组件,而不需要担心我们可能会改变/移除一些子组件依赖的东西。同时这些组件之间的接口是始终明确定义的,就和 props 一样。
实际上,你可以把依赖注入看作一部分“大范围有效的 prop”,除了:
- 祖先组件不需要知道哪些后代组件使用它提供的属性
- 后代组件不需要知道被注入的属性来自哪里
然而,依赖注入还是有负面影响的。它将你应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的属性是非响应式的。这是出于设计的考虑,因为使用它们来创建一个中心化规模化的数据跟使用 $root做这件事都是不够好的。如果你想要共享的这个属性是你的应用特有的,而不是通用化的,或者如果你想在祖先组件中更新所提供的数据,那么这意味着你可能需要换用一个像 Vuex 这样真正的状态管理方案了。
访问子组件实例或子元素—ref
尽管存在prop和事件,但是有时候我们仍可能需要在JS里直接访问一个子组件,那么此时,我们可以通过 ref 特性为子组件赋予一个ID引用:
<my-cmp ref="cmp"></my-cmp>
这样就可以通过this.$refs.cmp 来访问<my-cmp>
实例。
ref 也可以 对指定DOM元素进行访问,如:
<input ref="input" />
那么,我们可以通过 this.$refs.input 来访问到该DOM元素。
当ref 和 v-for 一起使用时,得到的引用将会是一个包含了对应数据源的这些子组件的数组。
注意:$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。应该避免在模板或计算属性中访问 $refs。