1. ref和reactive
1-1 ref
● 用ref声明基本类型变量:会隐式地进行类型推导
import {ref} from 'vue'
const name = ref('zhangsan');//Ref<string>
const age = ref(12);//Ref<number>
age.value = '12'//会报错,类型不一致
const n = ref<number>()//Ref<number | undefined>
● 用ref声明复杂类型变量:两种方式指定类型
// 方式一
const year= ref<string|number>('2020')
// 方式二
import type { Ref } from 'vue'
const year:Ref<string|number> = ref('2020')
year.value = 2023
1-2 reactive
● 会隐式地进行类型推导
const state = reactive({//推导得到的类型:{ name: string }
name:'zhangsan'
})
● 显式指明类型
interface Person {
name: string;
age?: number;
}
// 使用方式一:
const state = reactive<Person>({
name: 'zhangsan',
});
// 使用方式二:
const state: Person = reactive({
name: 'zhangsan',
});
官网说:不推荐使用 reactive() 的泛型参数,因为处理了深层次 ref 解包的返回值与泛型参数的类型不同。
2. props (defineProps)
2-1 props类型声明
● 普通声明
const props = defineProps({
name: {
type: String,
required: true,
},
age: Number,
});
这被称之为“运行时声明”,因为传递给 defineProps() 的参数会作为运行时的 props 选项使用。
● <script setup>
声明(通过泛型定义)
// 方式一:一个类型字面量
const props = defineProps<{
name: string;
age?: number;
}>();
// 方式二:一个单独的接口
interface Person {
name: string;
age?: number;
}
const props = defineProps<Person>();
这被称之为“基于类型的声明”。编译器会尽可能地尝试根据类型参数推导出等价的运行时选项。
注意:虽然类型和接口可以从其他文件导入,但是不能用在defineProps 的泛型中!!运行时会报错!
● 有默认值的ts专有声明:结合withDefaults
interface Person {
name: string;
age?: number;
}
const props = withDefaults(defineProps<Person>(),{
name:'zhangsan'
});
● 复杂类型声明
对象、数组、函数等,都算复杂变量,列如数组中有多种数据类型,需要声明具体的数据类型
注意:单一数据类型的数组,就直接声明为string[] , number []
● <script setup>
声明
interface Book {
title: string;
author: string;
year: number;
}
const props = defineProps<{
bookList: Book[];//或者Array<Book>
}>();
● 普通声明:使用ProtoType
进行类型断言
import type { PropType } from 'vue';
interface Book {
title: string;
author: string;
year: number;
}
const props = defineProps({
bookList: [] as PropType<Book[]>,//或者Array as PropType<Book[]>
});
3. emits (defineEmits)
● 普通声明
// 定义自定义事件类型
const emit= defineEmits(['sendData'])
// 发送自定义事件
emit(...)
● <script setup>
声明
const emit= defineEmits<{
(e: 'sendData', num: number): void
}>()
4. 暴露组件自身的属性、方法 (defineExpose)
使用<script setup>
的组件是默认关闭的,无法被其他组件获得内部的各种属性,比如通过 ref和 $parent如果要被外部访问,使用defineExpose暴露自己的属性和方法.
● 组件暴露自己的属性
<template>
<div>子组件helloword.vue</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(123456)
const changeCount = () =>{
count.vlaue = 654321
}
defineExpose({
count,
changeCount
})
</script>
<style scoped lang="less">
</style>
● 在别处调用
<template>
<div @click="helloClick">父组件</div>
<div @click="$event => helloRef.changeCount()">改变count</div>
<helloword ref="helloRef"></helloword>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import helloword from './components/HelloWorld.vue'
const helloRef = ref<InstanceType<typeof helloword> | null>(null);
const helloClick = () => {
console.log(helloRef.value?.count) // 123456
}
</script>
<style lang="less" scoped>
</style>
注意: 该值会在template中会自动解包,不用加value
Tips: defineProps、defineEmits、defineExpose、withDefaults不用导入,因为是编译器宏
想知道什么是编译器宏? 传送门:宏定义和编译器宏的定义和区别
获取自定义组件的类型:InstanceType<typeof 组件>
5. computed
接受一个 getter
函数,返回一个只读的响应式 ref
对象。该 ref 通过 .value
暴露 getter 函数的返回值。它也可以接受一个带有 get
和 set
函数的对象来创建一个可写的 ref 对象。
● 会隐式地进行类型推导
const count = ref(0);
const plusOne = computed(() => count.value + 1); //ComputedRef<number>
plusOne.value.toString();
plusOne.value.split('')//报错:类型“number”上不存在属性“split”
plusOne.value++; //报错:因为它是只读属性
● 也可以指定类型
const count = ref(0);
const plusOne = computed<number>(() => count.value + 1);
const name = ref('zhangsan')
const age = ref(18)
const info = computed<string>(() =>name.value+''+age.value)
● 创建一个可写的计算属性
const count = ref(0);
const plusOneWrite = computed({
get: () => count.value,
set: (val) => (count.value += val),
});
console.log(plusOneWrite.value); //0
plusOneWrite.value = 3;
console.log(plusOneWrite.value); //3
应用:
1. 通常应用在element-plus弹窗双向绑定visible
使用时:
2. 绑定格式化后的数据
6. watch与watchEffect
watch
只有当监听的值发生变化,才会执行。
第一个参数:要监听的值,可以时一个ref, 一个响应式对象,一个函数返回的值
第二个参数:监听的值发生变化后执行的回调函数,回调函数的三个参数(新值、旧值,以及一个用于注册副作用清理的回调函数)
第三个参数:可选的对象,最常用的deep
(深度监听,监听一个对象时用),immediate
(立即执行)
const name = ref('zhangsan');
const age = ref(18);
setTimeout(() => {
name.value = 'lisi';
age.value = 20;
}, 2000);
watch(
() => [name.value, age.value], //监听多个值
(newVal, oldVal) => {
console.log('output:', newVal);
},
{
immediate: true,
},
);
watchEffect
自动收集要监听的值,不用手动指定,并且是立即执行的,页面刚加载就会执行一次。
const age = ref(18);
setTimeout(() => {
age.value = 20;
}, 2000);
watchEffect(() => {
console.log('output:', age.value);//先输出了18, 再输出了20
});
7. 使用组件、动态组件
自定义组件导入后,不用注册
<template>
<MyComponent ref="myComponentRef"/>
</template>
<script setup>
import MyComponent from './MyComponent.vue'
</script>
动态组件:component
是vue的内置组件,不需要注册可以直接使用。
通过is
去选择要渲染的组件,可以是一个组件名称字符串(选项式API),也可以是组件的定义(组合式API)。
<template>
<div class="auto-wrap">
<div class="tabs-wrap">
<div
v-for="item in state.tabs"
:key="item"
:class="{ 'active-tabs': state.currentTab == item }"
class="tabs"
@click="tabsClick(item)">
{{ item }}
</div>
</div>
<component :is="showTab" :msg="showMsg" class="content"></component>
</div>
</template>
<script setup lang="ts">
import Child1 from './Child1.vue';
import Child2 from './Child1.vue';
const state = reactive({
tabs: ['tab1', 'tab2'],
currentTab: 'tab1',
});
const tabsClick = (tab: string) => {
state.currentTab = tab;
};
const showTab = computed(() => {
return state.currentTab == 'tab1' ? Child1 : Child2; //按定义渲染组件
});
const showMsg = computed(() => {
return state.currentTab == 'tab1' ? 'tab1' : 'tab2';
});
</script>
<style lang="scss" scoped>
.auto-wrap {
background: #fff;
display: flex;
.tabs-wrap {
width: 100px;
height: 50px;
line-height: 50px;
.tabs {
border: 1px solid #eee;
padding: 3px;
text-align: center;
}
.active-tabs {
color: red;
}
}
.content {
padding: 12px;
}
}
</style>
具体用法:在vue3+setup+ts中使用内置动态组件component
8. 非父组件provide和inject
provide和inject可以用于非父组件之间的通信,使用provide定义要提供的数据,inject去获取数据。
provide 和 inject 通常会在不同的组件中运行。要正确地为注入的值标记类型,Vue 提供了一个InjectionKey
接口,它是一个继承自 Symbol 的泛型类型,可以用来在提供者和消费者之间同步注入值的类型。
具体可以查看:vue3+ts中使用provide和inject类型会丢失为unknown
import { provide, inject } from 'vue'
import type { InjectionKey } from 'vue'
const key = Symbol() as InjectionKey<string>
provide(key, 'foo') // 若提供的是非字符串值会导致错误
const foo = inject(key) // foo 的类型:string | undefined