优雅地跨层级获取vue组件实例

在实际的项目开发中,经常遇到组件一层嵌套一层的场景。当组件一两层还好,可以直接用$refs.A.$refs.B形式去获取组件的实例。但是,如果组件嵌套层级超过两层时,使用这种方式就不是那么方便了,尤其是获取兄弟组件节点的实例,更加困难。

vue 2.2版本时,新增了provide/inject属性,通过provide可以轻松地跨层级获取父组件的实例。

// 父组件
provide() {
  return { root: this };
},
// 子组件
inject: ['root'];

这就解决了从子级获取父级的问题。

但是父级怎么获取子级呢?

使用遍历递归的方式可以做到。通过给子组件设置一个名称,不断的递归$children,直到找到正确名称的组件为止。虽然,使用这种方式可以找到自组件实例,但是总是执行递归操作是一件对性能造成浪费的事情。

下面就介绍一下另一种解决办法。

在根组件中设置缓存变量,在每一个组件初始化或更新的时候将对应的实例或者HTMLElement添加到根组件的缓存中,为了实现每一次初始化和更新都调用,这里使用自定义指令。

核心代码如下

export default {
  install(Vue, options = {}) {
    const directiveName = options.name || "ref";
    Vue.directive(directiveName, {
      bind(el, binding, vnode) {
        binding.value(vnode.componentInstance || el, vnode.key);
      },
      update(el, binding, vnode, oldVnode) {
        if (oldVnode.data && oldVnode.data.directives) {
          const oldBinding = oldVnode.data.directives.find(
            directive => directive.name === directiveName
          );
          if (oldBinding && oldBinding.value !== binding.value) {
            oldBinding && oldBinding.value(null, oldVnode.key);
            binding.value(vnode.componentInstance || el, vnode.key);
            return;
          }
        }
        if (
          vnode.componentInstance !== oldVnode.componentInstance ||
          vnode.elm !== oldVnode.elm
        ) {
          binding.value(vnode.componentInstance || el, vnode.key);
        }
      },
      unbind(el, binding, vnode) {
        binding.value(null, vnode.key);
      }
    });
  }
};

这也是vue-ref的实现代码。

在根组件中通过provide向外提供设置实例与获取实例的几个关键方法。

provide() {
  return {
    setChildrenRef: (name, ref) => {
      this[name] = ref;
    },
    getChildrenRef: name => this[name],
    getRef: () => this
  };
},

使用指令的代码

Vue.use(vueRef, {name: 'ref'})

这样,在全局就有了v-ref指令,它有点类似于React的ref指令,可以在里面传递一个回调函数,第一个参数就是当前的实例。

<E ref="E" v-ref="c => setChildrenRef($refs.E.$options.name, c)"></E>

示例代码参见: https://github.com/DulJuly/vue-ref-demo

如果您觉得本文对您有用,欢迎捐赠或留言~
微信支付
支付宝

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注