Vue3 props得基本使用
像Vue2一样的那些定义自然还是OK的,比如Number、String等等因为有Ts在,Vue3可以有更方便、快捷的复杂类型定义简单用例比如说现在需要定义一个string[]的prop,那么就像下面这样写})同样地,如果想定义一个类似{name: string, age: number}这样的,可以如下这样写})当然,你也可以把类型分离出去,像这样})函数自然也是可以的,可以像这样})这里使用了v
基本使用
// 使用 <script setup>
defineProps({
title: String,
likes: Number
})
// 非 <script setup>
export default {
props: {
title: String,
likes: Number
}
}
直接使用defineProps
defineProps() 宏函数支持从它的参数中推到类型:
<script setup lang="ts">
const props = defineProps({
foo: { type: String, required: true },
bar: Number
})
props.foo // string
props.bar // number / undefined
</script>
通过泛型参数来定义 props 的类型:
<script setup lang="ts">
const props = defineProps<{
foo: string
bar?: number
}>()
</script>
/* or
<sctipt setup lang="ts">
interface Props {
foo: string
bar?: number
}
const props = defineProps<Props>()
</script>
*/
针对类型的 props声明
// 2.defineProps<泛型参数>()的方式 没办法做默认赋值
type propsType = {
options: {
label: string
value: string | number
}[]
}
const props = defineProps<propsType>()
这被称为 基于类型的声明,编译器会尽可能地尝试根据类型参数推导出等价的运行时选项。这种方式的不足之处在于,失去了定义 props 默认值的能力。为了解决这个问题,我们可以使用 withDefaults 编译器:
withDefaults
// 3.withDefaults 编译器宏 泛型加默认值
type propsType = {
options: {
label: string
value: string | number
}[]
}
const props = withDefaults(defineProps<propsType>(), {
options: () => [
{
label: '男',
value: 1
}
]
})
PropType
// PropsType需要提前引入
import { type PropType } from 'vue';
// 忽略上述一大段的类型声明
defineProps({
data: {
type: [String, Number],
default: '--',
validaor(value: string) {
if(value === '暂无数据') {
return false
}
}
},
position: {
type: Object as PropType<Position>,
required: false,
deault: {
left: 0,
top: 0,
zIndex: 0,
}
},
options: {
type: Object as PropType<DataNumberOptionsType>,
require: true,
default: {
animate: false,
thousandsCharacter: false,
direction: "horizontal",
label: "待入馆",
suffix: "人",
}
}
});
这样既能利用TS的类型声明,又能完整的使用Props的配置功能。
但仍然不完美,类型声明代码太多了,能不能抽取到独立文件,从外部文件引入呢?
Vue官方文档给出了解释:“因为 Vue 组件是单独编译的,编译器目前不会抓取导入的文件以分析源类型。计划在未来的版本中解决这个限制。”
PropType+ExtractPropTypes
我们先把类型声明放到DataNumber.d.ts文件,然后通过ExtractPropTypes进行导出
// ExtractPropTypes需要提前引入
import { ExtractPropTypes } from 'vue';
enum DirectionEnum {
horizontal = 'HORIZONTAL',
vertical = 'VERTICAL',
}
type Position = {
top: string | number;
left: string | number;
zIndex: number;
width?: string | number;
height?: string | number;
};
type DataNumberOptionsType = {
animate?: boolean;
thousandsCharacter?: boolean;
direction: DirectionEnum;
label: string;
suffix: string;
};
// export暴露出去
export type Position = ExtractPropTypes<typeof Position>;
export type DataNumberOptionsType = ExtractPropTypes<
typeof DataNumberOptionsType
>;
<script setup lang="ts">
import { type PropType } from 'vue';
// 使用的时候,从类型文件引入
import { Position, DataNumberOptionsType } from './DataNumber.d'
defineProps({
data: {
type: [String, Number],
default: '--',
validaor(value: string) {
if (value === '暂无数据') {
return false
}
}
},
position: {
type: Object as PropType<Position>,
required: false,
deault: {
left: 0,
top: 0,
zIndex: 0,
}
},
options: {
type: Object as PropType<DataNumberOptionsType>,
require: true,
default: {
animate: false,
thousandsCharacter: false,
direction: "horizontal",
label: "待入馆",
suffix: "人",
}
}
});
</script>
特点:单向数据流
所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。
const props = defineProps(['foo'])
// ❌ 警告!prop 是只读的!
props.foo = 'bar'
导致你想要更改一个 prop 的需求通常来源于以下两种场景:
- prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。在这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可:js
const props = defineProps(['initialCounter'])
// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.initialCounter)
- 需要对传入的 prop 值做进一步的转换。在这种情况中,最好是基于该 prop 值定义一个计算属性:js
const props = defineProps(['size'])
// 该 prop 变更时计算属性也会自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())
Prop 校验
defineProps({
// 基础类型检查
// (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
propA: Number,
// 多种可能的类型
propB: [String, Number],
// 必传,且为 String 类型
propC: {
type: String,
required: true
},
// Number 类型的默认值
propD: {
type: Number,
default: 100
},
// 对象类型的默认值
propE: {
type: Object,
// 对象或数组的默认值
// 必须从一个工厂函数返回。
// 该函数接收组件所接收到的原始 prop 作为参数。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定义类型校验函数
// 在 3.4+ 中完整的 props 作为第二个参数传入
propF: {
validator(value, props) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 函数类型的默认值
propG: {
type: Function,
// 不像对象或数组的默认,这不是一个
// 工厂函数。这会是一个用来作为默认值的函数
default() {
return 'Default function'
}
}
})
Vue3+Ts Props类型自定义
要点
- 像Vue2一样的那些定义自然还是OK的,比如Number、String等等
- 因为有Ts在,Vue3可以有更方便、快捷的复杂类型定义
简单用例
比如说现在需要定义一个string[]的prop,那么就像下面这样写
defineProps({
acb: Array as () => string[]
})
同样地,如果想定义一个类似{name: string, age: number}这样的,可以如下这样写
defineProps({
abc: Object as () => ({name: string, age: number})
})
当然,你也可以把类型分离出去,像这样
type TAbc = {
name: string
age: number
}
defineProps({
abc: Object as () => TAbc
})
函数自然也是可以的,可以像这样
import {PropType} from "vue";
type TFunction = (args?: string[]) => string
defineProps({
abc: Function as PropType<TFunction>
})
这里使用了vue提供的PropType,前面提到的那几个定义使用PropType来实现当然也是可以的
非 <script setup>
如果没有使用 <script setup>,那么为了开启 props 的类型推导,必须使用 defineComponent() 。传入 setup() 的 props 对象类型是从 props 选项中推导而来。
import { defineComponent } from 'vue'
export default defineComponent({
props: {
message: String
},
setup(props){
props.message // 类型:string
}
})
若没有使用 <script setup>,defineComponent() 也可以根据 emits 选项推导暴露在 setup 上下文中的 emit 函数的类型:
import { defineComponent } from 'vue'
export default definComponent({
emits: ['change'],
setup(props, { emit }) {
emit('change') //类型检查 / 自动补全
}
})
为 ref() 标注类型
默认推导类型
ref 会根据初始化时的值自动推导其类型:
import { ref } from 'vue'
// 推导出的类型:Ref<number>
const year = ref(2022)
// TS Error: Type 'string' is not assignable to type 'number'.
year.value = '2022'
通过接口指定类型
有时我们可能想为 ref 内的值指定一个更复杂的类型,可以通过使用 Ref 这个接口:
import { ref } from 'vue'
import type { Ref } from 'vue'
const year: Ref<string | number> = ref(2022)
year.value = 2022 //成功
更多推荐
所有评论(0)