在绝大多数情况下,我们最好不要触达另一个组件实例内部或手动操作 DOM 元素。不过也确实在一些情况下做这些事情是合适的。

访问根实例—$root

在每个子组件中,可以通过 $root 访问根实例。

  1. // Vue 根实例
  2. new Vue({
  3. data: {
  4. foo: 1
  5. },
  6. computed: {
  7. bar () { /* ... */ }
  8. },
  9. methods: {
  10. baz () { /* ... */ }
  11. }
  12. })

所有的子组件都可以将这个实例作为一个全局 store 来访问或使用。

  1. // 获取根组件的数据
  2. this.$root.foo
  3. // 写入根组件的数据
  4. this.$root.foo = 2
  5. // 访问根组件的计算属性
  6. this.$root.bar
  7. // 调用根组件的方法
  8. this.$root.baz()

在demo或在有少量组件的小型应用中使用是非常方便的。但是在大型应用里使用就会很复杂了。所以,我们还是要用Vuex(后面会学)来管理应用的状态。

访问父级组件实例—$parent

在子组件中,可以通过 $parent 访问 父组件实例。这可以替代将数据以prop的方式传入子组件的方式。
如:

  1. <cmp-parent>
  2. <cmp-a></cmp-a>
  3. </cmp-parent>

若 cmp-parent 需要共享一个属性 share,它的所有子元素都需要访问 share 属性,在这种情况下 cmp-a 可以通过 this.$parent.share的方式访问share。
但是,通过这种模式构建出来的组件内部仍然容易出现问题。比如,我们在cmp-a 中嵌套一个一个子组件 cmp-b,如:

  1. <cmp-parent>
  2. <cmp-a>
  3. <cmp-b></cmp-b>
  4. </cmp-a>
  5. </cmp-parent>

那么,在cmp-b组件中去访问share时,需要先查看一下,其父组件中是否存在share,如果不存在,则在向上一级查找,落实到代码上为:

  1. var share = this.$parent.share || this.$parent.$parent.share;

这样做,很快组件就会失控:触达父级组件会使应用更难调试和理解,尤其是当变更父组件数据时,过一段时间后,很难找出变更是从哪里发起的。
碰到上述情况,可以使用依赖注入解决。

依赖注入—proveide-injuect

在上面的例子中,利用 $parent 属性,没有办法很好的扩展到更深层级的嵌套组件上。这也是依赖注入的用武之地,它用到了两个新的实例选项:provide 和 inject。
provide 选项允许我们指定想要提供给后代组件的数据/方法,例如:

  1. Vue.component('cmp-parent', {
  2. provide () {
  3. return {
  4. share: this.share,
  5. }
  6. },
  7. data () {
  8. return {
  9. share: 'share',
  10. }
  11. },
  12. template: `<div>cmp-parent</div>`
  13. })

然后再任何后代组件中,我们都可以使用 inject 选项来接受指定想要添加在实例上的属性。

  1. Vue.component('cmp-a', {
  2. inject: ['share'],
  3. template: `<div>cmp-a</div>`
  4. })

相比 $parent 来说,这个用法可以让我们在任意后代组件中访问share,而不需要暴露整个 cmp-parent 实例。这允许我们更好的持续研发该组件,而不需要担心我们可能会改变/移除一些子组件依赖的东西。同时这些组件之间的接口是始终明确定义的,就和 props 一样。
实际上,你可以把依赖注入看作一部分“大范围有效的 prop”,除了:

  • 祖先组件不需要知道哪些后代组件使用它提供的属性
  • 后代组件不需要知道被注入的属性来自哪里

然而,依赖注入还是有负面影响的。它将你应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的属性是非响应式的。这是出于设计的考虑,因为使用它们来创建一个中心化规模化的数据跟使用 $root做这件事都是不够好的。如果你想要共享的这个属性是你的应用特有的,而不是通用化的,或者如果你想在祖先组件中更新所提供的数据,那么这意味着你可能需要换用一个像 Vuex 这样真正的状态管理方案了。

访问子组件实例或子元素—ref

尽管存在prop和事件,但是有时候我们仍可能需要在JS里直接访问一个子组件,那么此时,我们可以通过 ref 特性为子组件赋予一个ID引用:

  1. <my-cmp ref="cmp"></my-cmp>

这样就可以通过this.$refs.cmp 来访问<my-cmp>实例。
ref 也可以 对指定DOM元素进行访问,如:

  1. <input ref="input" />

那么,我们可以通过 this.$refs.input 来访问到该DOM元素。
当ref 和 v-for 一起使用时,得到的引用将会是一个包含了对应数据源的这些子组件的数组。
注意:$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。应该避免在模板或计算属性中访问 $refs。