Vue Props篇
🌤️ Vue3
相比于组件内的状态 ref
和 reactive
,props
用来在组件之间传值(父组件 -> 子组件)
声明
❗❗ defineProps()
宏中的参数不可访问<script setup>
中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中。
普通
🎇 在单文件组件的<script setup>
中
1 | <script setup> |
- 如果只在模板(
<template>
)中使用,可不用声明变量props
,可直接在模板中使用foo
大写
String
和小写string
不是同一个东西;大写是 JavaScript 中的构造函数,小写用在 TypeScript,是字符串类型
🎇 如果没有使用<script setup>
1 | export default { |
🎇 或者在JSX
语法中
1 | import { defineComponent } from "vue"; |
TS 中
🪄 上面使用构造函数声明 props 的叫做运行时声明,接下来用作泛型传递给<>
的方式叫做基于类型的声明
- 两种方式不能同时使用
就像创建接口对象一样
1 | <script setup lang="ts"> |
可以直接提取成interface
1 | interface Props { |
🎇 多行书写时
;
可省略。由于限制,当前条件类型仅可指定单个属性,不能指定整个 props 对象
校验
普通模式中,检验不是必须的,不进行校验的话直接传递字符串数组就行:
1 | defineProps(["name", "age", "isShow"]); |
普通
type
指定props
类型required
为true
指定为必传 props,false
代表默认可选default
指定默认值
使用type
指定类型,这些类型时原生 JavaScript 的构造函数:
🌸 | 🌺 | 🏵️ | 🪷 |
---|---|---|---|
String | Number | Boolean | Array |
Object | Date | Function | Symbol |
``
除此之外,可以通过自定义类或者构造函数来用作 props 的类型,Vue 会通过instanceof
来检查:
1 | class Person { |
- Vue 校验的时候会使用到
instanceof Person
校验选项参考:
1 | defineProps({ |
TS
使用到了 TypeScript 就说明是在校验了:
1 | defineProps<{ |
默认值:需要使用withDefaults
编译宏
1 | export interface Props { |
在非<script setup>
的情况下
1 | import { defineComponent } from "vue"; |
复杂的 props 类型
🤔 刚接触的时候还挺容易混淆
简单的情况:
1 | <script setup lang="ts"> |
- props 将会有 title,author,year
复杂的情况,实则是再嵌套:
1 | <script setup lang="ts"> |
- 现在是 props 只有 book 一个,然后 book 下面才有这三个属性
即普通又 TS
想用一定的 TypeScript 类型检查,但是又不想传递范式(<T>
),会这样使用。通常在在Options API中出现
需要导入工具类型PropType
1 | import type { PropType } from "vue"; |
使用
🎇 在<script>
中使用的时候推荐 camelCase 的命名方式;在模板中使用的时候推荐 kebab-case 的形式
传递 props(父组件向子组件传值):
- 动态绑定值需要在属性前加上
v-bind
或者简写:
- 不加上的都认作是传递字符串
父组件:<Page>
1 | <template> |
子组件:<MyTitle>
1 | <template> |
单向数据流
所有的 props 都遵循单向绑定原则
- 因父组件变化而变化,更新随之传向子组件,所以子组件中的 props 是最新的
- 子组件中不要修改 props。要想获得衍生值
- 可以使用计算属性
- 可以创建新的变量接收
当 props 是一个对象或者数组时,可以改变内部的值,但是不推荐
Boolean 类型转换
方便在模板中使用时更加简洁,避免类似:disabled='true'
的出现,使用简单的disabled
表示即可
在<MyComponent>
中:
1 | definedProps({ |
使用时:
1 | <!-- 等同于传入 :disabled="true" --> |
当 props 有多种类型的时候,这种转换依然适用。不过与String
搭配时有例外。在排列时Boolean
需要排在String
的前面
1 | // disabled 将被转换为 true |
泛型
使用generic
属性,属性值和和在 TypeScript 中的<T>
一样
1 | <script setup lang="ts" generic="T"> |
同时也可以使用extends
进行泛型约束
1 | <script setup lang="ts" generic="T extends string | number, U extends Item"> |
provide 和 inject
不局限于相邻的父子组件传值,可以更深层次传递(React 中的context)
provide()
提供数据
1 | <script setup> |
- 一个组件可以多次调用,传递不同的注入名
- 如果传入的是一个普通变量,还可以使用
readonly()
将它包住
应用层 provide,全局提供数据。(在编写插件的时候会用到)
1 | import { createApp } from "vue"; |
inject()
获取数据
1 | <script setup> |
如果上游组件并没有提供message
这样一个数据,给inject
传递的第二个参数将会是作为当前返回的默认值
默写情况下为避免产生副作用,默认值可以是一个工厂函数,这时候第三个参数需要设置为true
1 | const value = inject("key", () => new ExpensiveClass(), true); |
目前 TypeScript 标注好像作用不大
状态管理
- 多个组件共享状态;
- 可通过
props
来解决,但是组件嵌套深了后容易混乱 - 可通过
provide
和inject
- 不同的视图交互可修改同一状态
- 通过模板获取对应组件实例,然后触发组件内对应的事件
状态管理中最直接的方法,就是将对应的共享状态抽离出来
响应式 API 做状态管理
将共享的状态单独抽离出来
1 | // store.js |
1 | <!-- ComponentA.vue --> |
1 | <!-- ComponentB.vue --> |
每当store
被改动时,这两个组件都会自动更新自己的视图
不局限于reactive
其他的响应式 API 同样适用
1 | import { ref } from "vue"; |
其他
- 使用
Pinia
- SSR ???