# 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)