vue3使用语法糖setup+ts的使用总结

2023-11-07

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
无标题文档<弹窗 visible不能直接绑定props, 可以绑定计算属性>
使用时:
在这里插入图片描述
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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vue3使用语法糖setup+ts的使用总结 的相关文章

随机推荐