天天看点

【Vue 开发实战】基础篇 # 13:如何优雅地获取跨层级组件实例(拒绝递归)

说明

【Vue 开发实战】学习笔记。

ref 引用信息

【Vue 开发实战】基础篇 # 13:如何优雅地获取跨层级组件实例(拒绝递归)

递归查找

  • 代码繁琐.
  • 性能低效
【Vue 开发实战】基础篇 # 13:如何优雅地获取跨层级组件实例(拒绝递归)

callback ref

  • 主动通知(setXxxRef)
  • 主动获取(getXxxRef)

比如E节点更新就通知A组件,A组件进行ref的缓存即可

组件A

<template>
  <div class="border">
    <h1>A 结点</h1>
    <button @click="getHRef">获取子孙节点 E Ref</button>
    <ChildrenB />
    <ChildrenC />
    <ChildrenD />
  </div>
</template>
<script>import ChildrenB from "./ChildrenB";
import ChildrenC from "./ChildrenC";
import ChildrenD from "./ChildrenD";
export default {
  components: {
    ChildrenB,
    ChildrenC,
    ChildrenD
  },
  provide() {
    return {
      setChildrenRef: (name,) => {
        console.log("A 组件 setChildrenRef", name);
        this[name] = ref;
      },
      getChildrenRef: name => {
        console.log("A 组件 getChildrenRef", name);
        return this[name];
      },
      getRef: () => {
        console.log("A 组件 getRef");
        return this;
      }
    };
  },
  data() {
    return {
      color: "blue"
    };
  },
  methods: {
    getHRef() {
      console.log(this.childrenE);
    }
  }
};</script>      

组件D

<template>
  <div class="border1">
    <h2>D 结点</h2>
    <ChildrenG />
    <ChildrenH v-ant-ref="c => setChildrenRef('childrenH', c)" />
    <ChildrenI />
  </div>
</template>
<script>import ChildrenG from "./ChildrenG";
import ChildrenH from "./ChildrenH";
import ChildrenI from "./ChildrenI";
export default {
  components: {
    ChildrenG,
    ChildrenH,
    ChildrenI
  },
  inject: {
    setChildrenRef: {
      default: () => {}
    }
  }
};</script>      

组件E

<template>
  <div class="border2">
    <h3 v-ant-ref="c => setChildrenRef('childrenE', c)">
      E 结点
    </h3>
  </div>
</template>
<script>export default {
  components: {},
  inject: {
    setChildrenRef: {
      default: () => {}
    }
  }
};</script>      

组件F

<template>
  <div class="border2">
    <h3>F 结点</h3>
    <button @click="getARef">获取祖先节点 A Ref</button>
    <button @click="getHRef">获取同级节点 H Ref</button>
  </div>
</template>
<script>export default {
  components: {},
  inject: {
    getParentRef: {
      from: "getRef",
      default: () => {}
    },
    getParentChildrenRef: {
      from: "getChildrenRef",
      default: () => {}
    }
  },
  methods: {
    getARef() {
      console.log(this.getParentRef());
    },
    getHRef() {
      console.log(this.getParentChildrenRef("childrenH"));
    }
  }
};</script>      
【Vue 开发实战】基础篇 # 13:如何优雅地获取跨层级组件实例(拒绝递归)

然后点击三个按钮

【Vue 开发实战】基础篇 # 13:如何优雅地获取跨层级组件实例(拒绝递归)

这里面使用了 ​

​v-ant-ref​

​ 这个指令,用于通知上层节点更新。我们可以找一下依赖 vue-ref 这个包,看一下指令源码

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = {
  install: function install(Vue) {
    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    var directiveName = options.name || 'ref';
    Vue.directive(directiveName, {
      bind: function bind(el, binding,) {
        binding.value(vnode.componentInstance || el, vnode.key);
      },
      update: function update(el, binding, vnode,) {
        if (oldVnode.data && oldVnode.data.directives) {
          var oldBinding = oldVnode.data.directives.find(function (directive) {
            var name = directive.name;
            return name === directiveName;
          });
          if (oldBinding && oldBinding.value !== binding.value) {
            oldBinding && oldBinding.value(null, oldVnode.key);
            binding.value(vnode.componentInstance || el, vnode.key);
            return;
          }
        }
        // Should not have this situation
        if (vnode.componentInstance !== oldVnode.componentInstance || vnode.elm !== oldVnode.elm) {
          binding.value(vnode.componentInstance || el, vnode.key);
        }
      },
      unbind: function unbind(el, binding,) {
        binding.value(null, vnode.key);
      }
    });
  }
};