ref
ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在自定义组件上,引用就指向该组件实例:
<!-- `vm.$refs.p` will be the DOM node -->
<p ref="p">hello</p>
<!-- `vm.$refs.child` will be the child component instance -->
<child-component ref="child"></child-component>
- 如何跨层级获取子组件实例 ?
递归查找(平常套路)
A节点要访问E子组件的实例,通过this.parent层层递归上一级查找父级实例,但是这样递归的方式代码非常繁琐,包括性能也非常低效。
当你每次去访问实例的时候,都需要走一遍递归的形式。因为实例是不能被缓存的,当你的子组件的实例更新后,父组件并不能及时的知道子组件实例更新了,所以说你每次去使用的时候,都需要这样递归层级去获取。
拒绝递归 (优雅方案)
方案:
- 主动通知:当子组件实例更新后,主动通知父组件其实例被更新(调用父组件的方法callback)
- 主动获取:子组件主动通知父组件后,父组件进行缓存子组件的实例,当子组件与子组件需要互相访问实例的时候,直接调用父组件已缓存的对应的子组件实例
一、A节点记录子节点实例:
// A节点
<template>
<div class="border">
<h1>A 结点</h1>
<button @click="getEH3Ref">获取E h3 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, ref) => {
this[name] = ref;
},
getChildrenRef: name => {
return this[name];
},
getRef: () => {
return this;
}
};
},
data() {
return {
color: "blue"
};
},
methods: {
getEH3Ref() {
console.log(this.childrenE);
}
}
};
</script>
A节点通过provide通知子节点回调setChildrenRef进行记录子节点各自的实例。
其他子节点可通过getChildrenRef获取其他节点的实例。
子节点通过getRef获取A节点的实例。
二、子节点传递自己的实例
// E节点
<template>
<div class="border2">
<h3 v-get-ref="c => setChildrenRef('childrenE', c)">
E 结点
</h3>
</div>
</template>
<script>
export default {
components: {},
inject: {
setChildrenRef: {
default: () => {}
}
}
};
</script>
子节点通过指令v-get-ref第一次绑定到元素时(bind生命周期),执行父级的回调callback: "setChildrenRef",传递自己的实例给父节点A记录下来。
(非常实用) 自定义指令 v-get-ref
// ./utils/getRef
"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, vnode) {
binding.value(vnode.componentInstance || el, vnode.key);
},
update: function update(el, binding, vnode, oldVnode) {
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, vnode) {
binding.value(null, vnode.key);
}
});
}
};
自定义指令全局注册
// main.js
import Vue from "vue";
import getRef from './utils/getRef'
Vue.use(getRef, { name: 'get-ref'});
三、获取父节点实例、获取兄弟节点的实例
// 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>
getARef F节点获取父节点A的实例
getHRef F节点获取H节点的实例