组件间的数据传递
父子组件 props 和 emit
- 在父组件中通过元素属性标签即可传 prop
- 子组件中可以通过 defineProps 明确接受参数(组合式 api)
- 接受的 props 在模板中可用,在代码中可用需要变量接受上面的 defineProps
- ts 中可以用 withDefault 来专门设置默认值
js
// 父组件中向子组件这样传值
<waterFall :title="name" :arr="[1, 2, 3, 4, 5, 6]"></waterFall>
// 子组件中这样接受props
const props = defineProps({
title: {
type: String,
default: "默认",
},
});
// 然后就可以使用
<div>{{ title }}</div>
<div>{{ arr }}</div>
// 这是ts加泛型后的写法
const props = defineProps<{ title: string; arr: number[] }>();
- 子组件向父组件传值
- 需要首先指定传递的内容 defineEmit
- 然后设置事件触发 emit 来上传 defineEmit 中的内容
js
// emit被设计为需要触发的事件
<button @click="send"></button>
// 自定义一个让父组件监听的触发事件on-click
const emit = defineEmits(["on-click"]);
const send = () => {
// 定义需要自定义触发事件中传递的具体数据
emit("on-click", "param1", "param2", "paramN");
};
// 父组件直接监听子组件自定义的触发事件
<waterFall @on-click="getName">
</waterFall>
// 然后就可以通过参数接收上传数据
const getName = (name: string) => {
console.log(name, "从子组件来的");
};
- 子组件向父组件暴露方法或者属性
- defineExpose 传出,然后父组件中用 ref 从 dom 里拿
单向数据流规范
prop 基本是挂在元素上的 data 属性,用处就像 vue 文档里的 blog
- prop 对于子组件来说是只读的
- 数据只从父组件往下传,子组件自己不要乱改
- 如果要改,要么明确通知父组件让它改,要么子组件给个占位数据,承接 prop 为本地数据再改
- 显然也是可以动态绑定的
- 如果想直接传整个对象,不用手动一个一个绑定属性,就要求组件属性和对象属性一样,这样 vue 就能自动解析
prop 的配置项
type ,可以传数组接受多个类型
required,可以确认是否必须
default,设置默认值
- 指定 type 为 object 或函数时,default 要按照工厂函数形式书写 default(){return {key:value}}
validator:自定义校验函数
非 prop 的属性,比如 class,style 和 id 等有不同的处理条件
- 组件有单个根节点时,上述属性自动添加到根节点中,inheritAttrs 就是管这个的,阻止访问可以设置 inheritAttrs 为 false
- 一般在想让非根节点使用这些 class 才有用,在子组件中可以通过$attrs.xxx 来获取这些属性
- 当然也可以 v-bind=$attrs 一次绑定
- 默认将属性传递到 template 上最外层的元素,(经常是个 div),所以你在使用组件的地方写样式的时候也要想好
- 手动访问可以用$attrs 在被传入的组件中观察 prop
同时响应式又让更新的父组件数据实时传给子组件,奇妙双向数据流?
emit 方法
- 子组件向父组件发事件来传间接传数据 emit
- 事件可以传参数,参数能被父组件接收到
- 有 emit 属性和 prop 属性风格一样,直接指定要上传的事件,然后再具体指定事件的内容
- $emit()就是通知父组件的方法,头一个参数是命名,后面 n 个参数都是数据,此时父组件某种程度上也需要一个占位数据/方法
- 如果内联传无所谓,在 method 方法里传就要记得要指定 this,不然传不对数据
js
handleClick() {
this.$emit("deletePost", this.id);
},
- 父组件可以直接@监听子组件定义的事件
- 父组件属性变化时子组件自动刷新
- 组件接受的 prop 本身也是动态属性时,组件接受的 prop 跟着变,页面跟着渲染,放心放心
前代后代组件传递 provide 和 inject
多层-级级 prop 传递
不安全,不可靠,不能保证每一层的 template 都是根组件,也不能保证 inheritAttr 畅通无阻
provide 向所有后代组件提供 prop,inject 接受 prop
用 provide 提供 data 属性时要变 data() return 这种,不能直接传数组或者对象
provide 中的属性不是响应式的,不会同步更新 provide
要按函数形式使用,才能正确绑定 publicThis
provide 如果希望使用 data 中的变动数据(比如 data 中某个数组的长度),就需要使用 computed 函数
兄弟组件
- vuex 和 pinia
组件 v-model
在组件上实现 v-model/封装表单输入控件为组件
一般情况下可以通过 v-model 让数据实时呈现在页面其他地方
但比如说引入的子组件是一个输入表单的时候,组件之间就不能默认 v-model 可行
接收固定名为 modelValue 的属性,并触发固定名为 update 的事件
js:value="modelValue" @input="$emit('update:modelValue', $event.target.value)" prop:["modelValue"] emits:["update:modelValue"]
这种方式只能支持一个 v-model
遇到选择情境时,可能需要多个 v-model,此时需要给 v-model 加入参数
也就是
js<!-- 父组件指定v-model --> <xxx v-model:searchTerm="searchTerm" v-model:category="category" /> <!-- 子组件v-model的设置 --> <!-- 配置项 --> props: ["xxx1", "xxx2"] emits: ["update:xxx1", "update:xxx2"] <!-- 模板中的指令设置 --> <xxx> :value="xxx1" @input="$emit('update:xxx1', $event.target.value)" </xxx1> <xxx> :value="xxxx2" @change="$emit('update:xxx2', $event.target.value)" </xxx>
组件生命周期和根组件一致
- 基本上符合常识的就是子组件周期正好发生在父组件周期中
- 父 beforeCreated
- 父 Created
- 父 beforeMounted
- 子 beforeCreated
- 子 Created
- 子 beforeMounted
- 子 Mounted
- 父 Mounted
- 父 beforeUpdated
- 子 beforeUpdated
- 子 updated
- 父 updated
- 父 beforeDestroy
- 子 beforeDestroy
- 子 destroyed
- 父 destroyed