【快速入门Vue系列】第十五篇:跨父子组件直接传值的方法

1,230 阅读2分钟

组件_处理边界情况

接下来我们要学习的都是和处理边界情况有关的功能,即一些需要对 Vue 的规则做一些小调整的特殊情况。需要注意的是,这些功能都是有劣势或危险场景的。

访问元素 & 组件

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

访问根实例

在每个子组件中,可以通过 $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 访问 父组件实例。这可以替代将数据以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;

这样做,很快组件就会失控:触达父级组件会使应用更难调试和理解,尤其是当变更父组件数据时,过一段时间后,很难找出变更是从哪里发起的。

碰到上述情况,可以使用依赖注入解决。

依赖注入

在上面的例子中,利用 $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 这样真正的状态管理方案了。

访问子组件实例或子元素

尽管存在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 只会在组件渲染完成之后生效,并且它们不是响应式的。应该避免在模板或计算属性中访问 refs。

最后

如果对您有帮助,希望能给个👍评论/收藏/三连!

博主为人老实,无偿解答问题哦❤