天天看點

【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);
      }
    });
  }
};