# Vue3 父 call 子 method
###### tags: `vue`
在 vue3 父 component 要取得子 component 的資料有兩種方式,一種是利用 **ref**,另一種則是 **call back** 的方式。下面是範歷程式
- [ref](#ref)
- [call back](#callback)
## ref
使用上需注意 ref 使用須跟宣告名稱相同
ref data 可能為 null,所以使用前都須判斷是否為空
parent component
```vue=
<template>
parent components
<div>
use ref get child data
<p v-if="RefChild">child value on parent: {{ RefChild.childValue }}</p>
<button v-on:click="callChild">parent call child method</button>
</div>
<Child ref="RefChild"></Child>
</template>
<script>
// @ is an alias to /src
import Child from "@/components/CallChild/Child.vue";
import { ref } from "vue";
export default {
name: "Parent",
components: {
Child,
},
setup() {
const RefChild = ref();
const callChild = () => {
RefChild.value?.childAdd();
};
return { RefChild, callChild};
},
};
</script>
```
child component
```vue=
<template>
<div class="child">
<p>Child components</p>
<p>child vaue: {{ childValue }}</p>
<button v-on:click="childAdd">add child value</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
name: "Child",
setup() {
const childValue = ref(0);
const childAdd = () => {
childValue.value += 1;
};
return { childValue, childAdd };
},
};
</script>
```
使用 Ref 若是多個,也就是用 v-for 生成多個子 component,則需要自己 bind Ref。
若跟單個用一樣的方式實作,則只會把最後一個元件加入 Ref 中。
只是只要子 component 有變動,就會觸發 v-bind:ref 的事件,所以在條件式上需要多一點的判斷。也可能存在效能問題?
下面是簡單的程式碼實作
```javascript=
<template>
<button v-on:click="callChild">parent call child method</button>
<Child v-for="i in 2" v-bind:key="i" v-bind:ref="el => setItemRef(el, i)"></Child>
</template>
<script>
const callChild = () => {
for(var key in RefChilds.value){
RefChilds.value[key].childAdd();
}
};
const setItemRef = (el, key) => {
if (!RefChilds.value.find((cd) => cd === el)) {
RefChilds.value[key] = el;
}
};
</script>
```
### callBack
用 call back 的方式需要在結束時把東西刪除,否則method 裡面可能會有太多不需要方法
且 call back 方式 child 就一定要開接口讓 parent 傳
parent component
```vue=
<template>
<div>
use call back get child data
<p>child value on parent: <template v-for="v in cbChildVal"> {{v}}, </template></p>
<button v-on:click="callChildCbs">
parent use call back call child method
</button>
</div>
<Child v-for="i in 2" v-bind:key="i" v-bind:keyVal="i-1" v-bind:onParentFun="onChildFun" v-on:setCbChildVal="setCbChildVal"></Child>
</template>
<script>
// @ is an alias to /src
import Child from "@/components/CallChild/ChildCallBack.vue";
import { ref } from "vue";
export default {
name: "Parent",
components: {
Child,
},
setup() {
const cbChildVal = ref([]);
const setCbChildVal = (index, val)=>{
cbChildVal.value[index] = val;
}
let childCbs = [];
const onChildFun = (fn) => {
childCbs.push(fn);
const cleanUp = () => {
childCbs = childCbs.filter((item) => item !== fn);
console.log(`cleanUp call back method ${fn.ref}, childCbs num ${childCbs.length}`);
};
return cleanUp;
};
const callChildCbs = () => {
childCbs.forEach((fn) => fn());
};
return { onChildFun, callChildCbs, cbChildVal,setCbChildVal };
},
};
</script>
```
child component
```vue=
<template>
<div class="child2">
<p>Child call back components</p>
<p>child2 vaue: {{ childValue }}</p>
<button v-on:click="childAdd">add child value</button>
</div>
</template>
<script>
import { ref, onUnmounted, watch } from "vue";
export default {
name: "ChildCallBack",
props: {
onParentFun: {
type: Function,
required: true,
},
keyVal:{
type: Number,
default: 0,
}
},
setup(props, context) {
const childValue = ref(0);
const childAdd = () => {
childValue.value += 1;
};
const cleanUp = props.onParentFun(childAdd);
onUnmounted(() => cleanUp());
watch(childValue, (newVal) =>{
context.emit("setCbChildVal", props.keyVal, newVal);
})
return { childValue, childAdd };
},
};
</script>
```
### 其他
[程式碼範例]([https://](https://github.com/lilyen/vue3-demo/tree/main/src/components/CallChild))
參考網址
[v-for 中的 Ref](https://vue3js.cn/docs/zh/guide/migration/array-refs.html)
[vue3.0父组件调用子组件里的方法](https://blog.csdn.net/CSND7997/article/details/116295731)