使用场景
举个例子啊,简单的单链条组件dom树
平时不跨层的第一层的A组件获取第二层的A组件很容易,直接一个ref的官方api搞定,但是如果有这样以下的跨级场景:
- Index要获取第二层的A组件实例
- 第二层的A组件想获取Index实例
- 第二层的B组件想获取第二层的A组件实例
是不是就比较麻烦,使用传统的方法解决也是麻烦的,白嫖一下别人写的传统方法哈哈
this.$parent // 访问父实例
this.$children // 当前实例的直接子组件。(不保证顺序,不是响应式)
this.$parent.$parent.$refs.xxx // 跨级访问父组件
this.$children.$children.$refs.xxx // 跨级访问子组件
这种递归的方式,执行流程繁琐,不能缓存,性能低效,不能及时更新实例。
现在有个vue-ref的api能够完美的解决以上需求。
vue-ref的使用
以下以上图dom组件结构举例
安装与引入
// npm安装
npm install vue-ref --save
// main.js中
import ref from "vue-ref"
Vue.use(ref, { name: "ant-ref" }) // name是自定义api名称
在根组件中提供api
注意:这个根组件不是固定说一定要最高层的那个组件,任意一个组件都可作为起始的根组件
<template>
<div>
<button @click="getSecondFloorADom">index获取secondFloorA实例</button>
<firstFloorA></firstFloorA>
<firstFloorB></firstFloorB>
</div>
</template>
<script>
import firstFloorA from "@/components/firstFloorA.vue";
import firstFloorB from "@/components/firstFloorB.vue";
export default {
//使用provide在根组件提供api
provide() {
return {
//这个api设定主动通知 将组件实例绑定在根组件上
setChildrenRef: (name, ref) => {
this[name] = ref;
},
//这个api是主动获取绑定的组件
getChildrenRef: (name) => {
return this[name];
},
// 这个是获取根组件
getRef: () => {
return this;
},
};
},
methods: {
getSecondFloorADom() {
console.log('secondFloorA', this.secondFloorA)
},
},
components: {
firstFloorA,
firstFloorB,
},
};
</script>
链条下的子组件
主要通过inject来获取根组件提供的api,子组件需要哪些api就写哪些
inject: {
setChildrenRef: { // 注册子组件
default: () => {},
},
getParentRef: { // 获取根组件实例
from: "getRef",
default: () => {},
},
getParentChildrenRef: { // 获取根组件链下任何注册的子组件
from: "getChildrenRef",
default: () => {},
},
},
firstFloorA
<template>
<div>
<secondFloorA
v-ant-ref="(dom) => setChildrenRef('secondFloorA', dom)" // 看到没注册的形式和根组件提供的api是一样的
></secondFloorA>
<button @click="getSecondFloorADom">firstFloorA获取secondFloorA的实例</button>
</div>
</template>
<script>
import secondFloorA from "../components/secondFloorA";
export default {
inject: {
setChildrenRef: {
default: () => {},
},
getParentChildrenRef: {
from: "getChildrenRef",
default: () => {},
},
},
methods: {
getSecondFloorADom() {
console.log('secondFloorA' , this.getParentChildrenRef('secondFloorA'));
},
},
components: {
secondFloorA,
},
};
</script>
secondFloorA
<template>
<div></div>
</template>
<script>
export default {
inject:{
setChildrenRef:{
default: () => {}
}
},
}
</script>
firstFloorB
<template>
<div>
<secondFloorB
v-ant-ref="(dom) => setChildrenRef('secondFloorB', dom)"
></secondFloorB>
<button @click="getParentDom">firstFloorB获取index的实例</button>
</div>
</template>
<script>
import secondFloorB from "../components/secondFloorB";
export default {
inject: {
setChildrenRef: {
default: () => {},
},
getParentRef: {
from: "getRef",
default: () => {},
},
},
methods: {
getParentDom() {
console.log('index',this.getParentRef());
},
},
components: {
secondFloorB,
},
};
</script>
secondFloorB
<template>
<div>
<button @click="getParentDom">secondFloorB获取index实例</button>
<button @click="getSecondFloorADom">secondFloorB获取secondFloorA实例</button>
</div>
</template>
<script>
export default {
inject: {
getParentRef: {
from: "getRef",
default: () => {},
},
getParentChildrenRef: {
from: "getChildrenRef",
default: () => {},
},
},
methods: {
getParentDom() {
console.log("index", this.getParentRef());
},
getSecondFloorADom() {
console.log('secondFloorA', this.getParentChildrenRef('secondFloorB'))
}
},
};
</script>
对于普通dom节点
普通的dom节点也是能够这样获取的,未来可以试试,比如index获取secondFloorB的某个普通dom节点
vue-ref的优点
除了使用上的优点外,被注册的组件实例如果有更新,会让根组件缓存中对应的实例也进行实时的更新。