一个功能在多个页面中被使用到,可以抽成了组件,以实现复用。
vue使用单项数据流保持数据的统一,业务中难免遇到父子组件通信问题,这篇做一个使用记录。
执行顺序
执行顺序:父组件先创建,然后子组件创建;子组件先挂载,然后父组件挂载,即“父beforeCreate-> 父create -> 子beforeCreate-> 子created -> 子mounted -> 父mounted”。
单向数据流
所有的prop
都使得其父子之间形成了一个单向下行绑定:父级prop
的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致应用的数据流向难以理解。额外的,每次父级组件发生更新时,子组件中所有的prop
都将会刷新为最新的值。
父子组件通信实现—父组件修改子组件数据
应用场景:父组件改变子组件的值。
应用方法:子组件通过defineExpose暴露自己的属性,父组件通过ref获取子组件的属性并进行修改。
子组件 child.vue
| <template> <div>我是demo组件</div> </template> <script setup> import { ref, reactive } from 'vue' const data = reactive({ name: 'ginny', })
defineExpose({ data, }) </script>
|
| <template> <Child ref="refChild"></Child> </template>
<script setup> import { ref ,reactive,onMounted} from 'vue' import Child from './view/child.vue'
let refChild = ref(null)
onMounted(() => { const { data } = refChild.value }) </script>
|
注意:子组件只能是vue形式写的组件,之前复用了一个tsx组件,defineExpose方法失效。
父子组件通信—自定义事件
应用场景:子组件的状态改变时,父组件对应状态也发生改变。
应用方法:子组件通过$emit
派发一个自定义事件,父组件接收到后,由父组件修改自身状态。
业务描述:公司的项目需要实现针对组织架构选择部门—即树状展示部门,同时多选。部门展示被抽成子组件,调用时,需要获取子组件已经勾选的内容。
实现方案:父级向子级dept-tree通过props传入状态sum(这里为父级sumbit事件中的触发的状态改变)。子组件观察状态的变化,通过 $emit 事件告诉父组件。父组件使用自定义的getDeptNodes
方法获取payload(即子组件已选内容)。
父组件调用代码如下:
| <dept-tree @getDeptNodes="getDeptNodes" :sum="deptChosen" > </dept-tree>
|
| import DeptTree from "@/components/Tree/dept.vue"; components: { DeptTree }, setup: { const state = reactive({ deptChosen: [], }); const getDeptNodes = (params) => { console.log('deptNodes=',params); } return { getDeptNodes } }
|
子组件模板渲染部门数据。
通过观察状态sum的变化,通过emit
触发自定义的getDeptNodes
方法,传入已选内容。
| <template> <el-tree ref="treeRef" :data="deptTree" show-checkbox default-expand-all check-strictly=true node-key="id" :props="defaultProps" /> </template>
|
Vue对定义了type
的prop
执行运行时验证。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <script lang="ts"> import { defineComponent, reactive, toRefs, onMounted, watch } from 'vue'; import { getDept} from "@/api/users"; import { ElMessage, ElTree } from "element-plus"; import { arrToTree } from "@/utils";
export default defineComponent({ name: "deptTree", props: { sum: { type: Object, default: null } }, setup(props, { emit }) { const state = reactive({ deptTree:[] });
onMounted(() => { getDataDept(); });
const getDataDept = () => { getDept({}).then(res => { const { data } = res.data; let deptTreeArr = []; deptTreeArr.push(arrToTree(data)); state.deptTree = deptTreeArr }).catch((error) => { ElMessage.error('获取部门数据错误!'); }) }; const treeRef = ref<InstanceType<typeof ElTree>>(); const defaultProps = { children: 'children', label: 'label', }
const sum = props.sum; watch(sum,() => { console.log('props.sum=',sum) emit('getDeptNodes',{"deptNodes":getCheckedNodes()}) });
const getCheckedNodes = () => { return treeRef.value!.getCheckedNodes(false, false); }
return { treeRef, defaultProps, ...toRefs(state), } } }); </script>
|