目录
(4): 监视【ref】或【reactive】定义的【对象类型】数据中的某个属性
一:简介
在vue3中,编码风格进行了一些变化。
编码语言:JavaScript、TypeScript(推荐使用TypeScript)
编码风格:组合式API、选项式API(推荐使用组合式API)
简写形式:setup语法糖
性能提升:打包大小减少41%;初次渲染快55%,更新渲染快133%;内存减少54%。
源码升级:使用proxy代替defineProperty;重写虚拟dom的实现和Tree-Shaking。
二:使用vite创建vue项目
npm creat vue@latest
env.d.ts文件报错(飘红): 因为没有安装node_modules。这个文件的存在,是为了让ts去识别.txt等类型的文件
⚠️:vs code中Vue - Official 插件合并了TypeScript Vue Plugin (Volar)和 Vue Language Features (Volar) 这两个插件
Vite 项目中,index.html 是项目的入口文件,在项目最外层。
加载index.html后,Vite 解析 <script type="module" src="xxx"> 指向的JavaScript。
Vue3中是通过 createApp 函数创建一个应用实例。
三:Vue3核心语法
【OptionsAPI 与 CompositionAPI】
Vue2的API设计是Options(配置)风格的。
Vue3的API设计是Composition(组合)风格的。
Options API 的弊端:
Options类型的 API, 数据、方法、计算属性等, 是分散在: data、methods、computed中的, 若想新增或者修改一个需求, 就需要分别修改: data、methods、computed, 不便于维护和复用。
Composition API 的优势:
可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。
四:setup
setup是Vue3中一个新的配置项,同Vue2中的data,methods等为兄弟并列元素。
特点:setup函数返回的对象中的内容,可直接在模板中使用。
setup中访问this是undefined。
setup函数会在beforeCreate之前调用,它是“领先”所有钩子执行的。
注意⚠️:
- Vue2 的配置(data、methods......)中可以访问到setup中的属性、方法。
- 但在setup中不能访问到Vue2的配置(data、methods......)。
- 如果与Vue2冲突,则setup优先。
1:若返回一个对象:则对象中的:属性、方法等,在模板中均可以直接使用(重点关注)
<template>
<div>人员信息</div>
<ul>
<li>姓名:{{ name }}</li>
<li>年龄:{{ age }}</li>
<li>地址:{{ adress }}</li>
<br>
<li>测试:{{ d }}</li>
<li>测试:{{ e }}</li>
</ul>
<br>
<div>
<button @click = changeAge()>修改年龄</button>
<button @click = showAdress()>显示地址</button>
</div>
</template>
<script lang ='ts'>
export default{
name:'thePerson',
//vue2的写法 - 尽量不要与vue3混用
data(){
return{
d: 123,
e: this.name //可以使用vue3的数据
}
},
setup(){
console.log(this); //undefined
//注意:vue3可以使用vue2的数据。vue2不可以使用vue3的数据
// console.log(this.d); //报错-Cannot read properties of undefined (reading 'd')
// 数据,原来写在data中(注意:此时的name、age、tel数据都不是响应式数据)
let name = '章三';
let age = 18;
let adress = '北京市XXX';
// 方法,原来写在methods中
function changeAge(){
//注意:此时这么修改name页面是不变化的
age++;
console.log(age)
}
function showAdress(){
alert(adress);
}
// 返回一个对象,对象中的内容,模板中可以直接使用
return {name,age,adress,changeAge,showAdress}
}
}
</script>
2:setup若返回一个函数:则可以自定义渲染内容。
setup(){
return ()=> '你好啊!'
}
3:setup语法糖
这个语法糖,可以让我们把setup独立出去。借助vite中的插件vite-plugin-vue-setup-extend,去指定组件名字。
1. 第一步:安装插件命令 ---- npm i vite-plugin-vue-setup-extend -D
2. 第二步:配置文件 ---- vite.config.ts//vite.config.ts import { defineConfig } from 'vite' import VueSetupExtend from 'vite-plugin-vue-setup-extend' export default defineConfig({ plugins: [ VueSetupExtend() ] })
<template>
<div>人员信息</div>
<ul>
<li>姓名:{{ name }}</li>
<li>年龄:{{ age }}</li>
<li>地址:{{ adress }}</li>
</ul>
<br>
<div>
<button @click = chageAge()>修改年龄</button>
<button @click = showAdress()>显示地址</button>
</div>
</template>
<!-- 使用插件后 定义组件名称这块就可以简化了 -->
<!-- <script lang ='ts'>
export default{
name:'thePerson'
}
</script> -->
<script lang ='ts' setup name='thePerson'>
let name = '章三';
let age = 18;
let adress = '北京市XXX';
function chageAge(){
age++;
console.log(age)
}
function showAdress(){
alert(adress);
}
</script>
五:ref和reactive
ref:
作用: 定义响应式变量。
语法: let xxx = ref(XXX)。
返回值: 一个RefImpl的实例对象, 简称ref对象或ref, ref对象的value属性是响应式的。
注意点:
JS中操作数据需要: xxx.value, 但模板中不需要.value, 直接使用即可。
对于let name = ref('张三')来说, name不是响应式的, name.value是响应式的。
其实ref接收的数据可以是: 基本类型、对象类型。
若ref接收的是对象类型, 内部其实也是调用了reactive函数。
reactive:
作用: 定义一个响应式对象(基本类型不要用它, 要用ref, 否则报错)
语法: let 响应式对象= reactive(源对象)。
返回值: 一个Proxy的实例对象, 简称: 响应式对象。
注意点: reactive定义的响应式数据是“深层次”的。
区别:
1. ref创建的变量必须使用.value(可以使用TypeScript Vue Plugin (Volar)插件自动添加.value)
2. reactive重新分配一个新对象, 会失去响应式(可以使用Object.assign去整体替换)
使用原则:
1. 若需要一个基本类型的响应式数据, 必须使用ref。
2. 若需要一个响应式对象, 层级不深, ref、reactive都可以。
3. 若需要一个响应式对象, 且层级较深, 推荐使用reactive。
<template>
<div class="obj">
<span>汽车数量{{ num }}</span>
<button @click="changeCarNum()">汽车数量+1</button>
<hr>
<h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
<button @click="changeCarPrice()">修改汽车价格</button>
<button @click="changeCar()">修改汽车信息</button>
<hr>
<h2>游戏列表:</h2>
<ul>
<li v-for="g in games" :key="g.id">{{ g.name }}</li>
</ul>
<button @click="changeFirstGame()">修改第一游戏</button>
<hr>
<h2>测试1: {{obj.a.b.c.d}}</h2>
<button @click="deepTestReactive()">深层次数据测试-reactive</button>
<h2>测试2: {{objRef.a.b.c.d}}</h2>
<button @click="deepTestRef()">深层次数据测试-ref</button>
</div>
</template>
<script lang ='ts' setup>
import {ref, reactive} from 'vue'
// 数据
let num = ref(0);
let car = reactive({ brand: '奔驰', price: 100 })
let games = ref([
{ id: 'game01', name: '英雄联盟' },
{ id: 'game02', name: '王者荣耀' },
{ id: 'game03', name: '开心消消乐' }
])
let obj = reactive({
a:{
b:{
c:{
d:666
}
}
}
})
let objRef = ref({
a:{
b:{
c:{
d:666
}
}
}
})
function changeCarNum(){
//ref的数据必须使用value--报错信息:Must use `.value` to read or write the value wrapped by `ref()`.
num.value += 1;
}
function changeCarPrice(){
car.price += 10;
}
function changeCar(){
// car = { brand: '小米', price: 25.9 } //❌,不会变化。修改了之前的响应式绑定
// car = reactive({ brand: '小米', price: 25.9 }) //❌,不会变化。 reactive重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)
//会变化,效率低
// car.brand = '小米';
// car.price = 25.9;
//正解
Object.assign(car,{ brand: '小米', price: 25.9 })
}
function changeFirstGame(){
games.value[0].name = '刺激战场'
// 如果使用reactive做响应式,写法为:games[0].name = '刺激战场';;
}
function deepTestReactive(){
obj.a.b.c.d = 999;
}
function deepTestRef(){
objRef.value.a.b.c.d = 888;
}
</script>
注意⚠️://reactive中的ref 已经进行了 拆包处理,不需要再次.value
let objRR = reactive({ a:1, b:2, c:ref(3) }) console.log(objRR.a) //1 console.log(objRR.b) //2 console.log(objRR.c) //3
六:toRefs和toRef
作用: 将一个响应式对象中的每一个属性, 转换为ref对象。
备注: toRefs与toRef功能一致, 但toRefs可以批量转换。
<template>
<div class="obj">
<h2>汽车信息:一台{{ color }}的{{ car.brand }}汽车,价值{{ car.price }}万</h2>
<button @click="changeCar()">修改汽车信息</button>
<hr>
</div>
</template>
<script lang ='ts' setup>
import {reactive, toRefs, toRef} from 'vue'
// 数据
let car = reactive({ brand: '奔驰', price: 100, color:'橄榄绿' });
//toRefs, toRef是为了让对象进行解构赋值之后仍然具有响应式的能力
//解构赋值 -- 多个 -- toRefs
let {brand,price} = toRefs(car);
//解构赋值 -- 单个 -- toRef
let color = toRef(car,'color');
function changeCar(){
//修改方式一
// car.brand = '法拉利'
// car.price += 10;
//修改方式二
brand.value = '法拉利'
price.value += 10;
//单一解构修改
color.value = '霞光紫'
}
七:computed
作用:根据已有数据计算出新数据(和Vue2中的computed作用一致)。
有缓存
<template>
<div class="person">
姓:<input type="text" v-model="firstName"> <br>
名:<input type="text" v-model="lastName"> <br>
全名:<span>{{fullName}}</span> <br>
<button @click="changeFullName">全名改为: 石昊</button>
</div>
</template>
<script lang ='ts' setup>
import {ref, computed } from 'vue'
let firstName = ref('王');
let lastName = ref('林');
//计算属性 —— 只读不可写
// let fullName = computed(() => {
// return firstName.value + lastName.value;
// })
//计算属性 —— 可读可写
let fullName = computed({
get(){
return firstName.value + '-' + lastName.value;
},
set(val){
firstName.value = val.split('-')[0];
lastName.value = val.split('-')[1];
}
})
function changeFullName(){
fullName.value = '石-昊';
}
</script>
八:watch
let watchRefObj = watch( // 被监视的数据, person, (newValue, oldValue)=>{ console.log(监视回调); },{ // 配置对象 deep:true })
watch的第一个参数是:被监视的数据
watch的第二个参数是:监视的回调
watch的第三个参数是:配置对象(deep、immediate等等.....)deep:深度监听
immediate: 立即执行,首次加载就进行监听
作用: 监视数据的变化( 和Vue2中的watch作用一致 )
特点: Vue3中的watch只能监视以下四种数据:
- ref定义的数据。
- reactive定义的数据。
- 函数返回一个值( getter函数 )。
- 一个包含上述内容的数组。
(1): 监视【ref】定义的【基本类型】数据
<template> <h2>情况一: 监视【ref】定义的【基本类型】数据</h2> <div>当前求和为:{{sum}}</div> <button @click="changeSum()">点我sum+1</button> <hr> </template> <script lang ='ts' setup> import {ref, reactive, watch } from 'vue' // 情况一:监视【ref】定义的【基本类型】数据 //直接写数据名即可,监视的是其value值的改变。不需要 .value let sum = ref(0); function changeSum(){ sum.value ++; } let stopwatch = watch(sum, (newValue, oldValue) => { console.log('新--' + newValue + ' ******* 旧--' + oldValue); //超过5就停止监听 if(newValue >= 5){ stopwatch(); } }) </script>
(2): 监视【ref】定义的【对象类型】数据
<template> <h2>情况二: 监视【ref】定义的【对象类型】数据</h2> <div>姓名:{{ person.name }}</div> <div>年龄:{{ person.age }}</div> <button @click="changeName()">修改名字</button> <button @click="changeAge()">修改年龄</button> <button @click="changePerson()">修改整个人</button> <hr> </template> <script lang ='ts' setup> import {ref, reactive, watch } from 'vue' // 情况二: 监视【ref】定义的【对象类型】数据 // 若修改的是ref定义的对象中的属性, newValue 和 oldValue 都是新值, 因为它们是同一个对象。 // 若修改整个ref定义的对象, newValue 是新值, oldValue 是旧值, 因为不是同一个对象了。 let person = ref({name:'石昊', age:18}); function changeName(){ person.value.name += '~' } function changeAge(){ person.value.age += 1 } function changePerson(){ person.value = {name:'王林',age:500} } //监视整个person对象,因为监视的是对象的地址值,导致不是同一个对象,会发生监听 //deep 会进行深度监听。 监听person下的name和age属性 let watchRefObj = watch(person, (newValue, oldValue)=>{ console.log('新--' + newValue + ' ******* 旧--' + oldValue); },{deep:true}) //ref定义的类型,只能监听到ref本身 /*如果监听person.value.name会报错 ----- 信息如下: Invalid watch source: 石昊 A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types. */ // let watchRefObjName = watch(person.value.name, (newValue, oldValue)=>{ // console.log('新--' + newValue + ' ******* 旧--' + oldValue); // }) </script>
(3): 监视【reactive】定义的【对象类型】数据
<template> <h2>情况三: 监视【reactive】定义的【对象类型】数据</h2> <div>深度测试:{{obj.a.b.c}}</div> <button @click="test()">修改obj.a.b.c</button> <hr> </template> <script lang ='ts' setup> import {ref, reactive, watch } from 'vue' // 情况三: 监视【reactive】定义的【对象类型】数据 //默认开启了深度监视 let obj = reactive({ a:{ b:{ c:666 } } }) function test(){ obj.a.b.c = 999; } let testDeepWatch = watch(obj,(newValue, oldValue) => { console.log('新--' + newValue.a.b.c + ' ******* 旧--' + oldValue.a.b.c); }) </script>
(4): 监视【ref】或【reactive】定义的【对象类型】数据中的某个属性
(5): 监视上述的多个数据
<template> <h2>情况四: 监视【ref】或【reactive】定义的【对象类型】数据中的某个属性</h2> <div>姓名:{{ info.name }}</div> <div>年龄:{{ info.age }}</div> <div>汽车:{{ info.car.c1 }}、{{ info.car.c2 }}</div> <button @click="changeInfoName">修改名字</button> <button @click="changeInfoAge">修改年龄</button> <button @click="changeC1">修改第一台车</button> <button @click="changeC2">修改第二台车</button> <button @click="changeCar">修改整个车</button> <hr> <h2>情况五:监视上述的多个数据</h2> <div>直接监听情况四中的姓名, 年龄, 汽车</div> </template> <script lang ='ts' setup> import {ref, reactive, watch } from 'vue' // 情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性 let info = reactive({ name:'火灵儿', age:18, car:{ c1:'奔驰', c2:'宝马' } }) function changeInfoName(){ info.name += '~' } function changeInfoAge(){ info.age += 1 } function changeC1(){ info.car.c1 = '奥迪' } function changeC2(){ info.car.c2 = '特斯拉' } function changeCar(){ info.car = {c1:'小米',c2:'法拉利'} } //监听对象中的某个属性,若该属性是基本类型的,要写成函数式 let infoAgeWatch = watch(()=>info.age, (newValue, oldValue)=>{ console.log('新--' + newValue + ' ******* 旧--' + oldValue); }) //监视对象中的某个属性,若该属性是对象类型的,可以直接写,也可写函数,更推荐写函数 /*注意: 1:直接写 - info.car 修改对象的单一属性可触发监听,修改整体属性,不触发 2:函数式 - ()=>info.car 修改对象的整体属性可触发监听,修改单一属性,不触发,使用深度监听deep可以解决这个问题 */ let infoCarWatch = watch(()=>info.car, (newValue, oldValue)=>{ console.log('新--' + JSON.stringify(newValue) + ' ******* 旧--' + JSON.stringify(oldValue)); },{deep:true}) // 情况五:监视上述的多个数据 //监听数据类型为数组,打印值如此:火灵儿~,19,[object Object] let moreDataWatch = watch([()=> info.name,()=> info.age, ()=>info.car], (newValue, oldValue)=>{ console.log('多个数据变化:新--' + newValue + ' ******* 旧--' + oldValue); },{deep:true}) </script>
九:watchEffect
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。
watch对比watchEffect
1. 都能监听响应式数据的变化,不同的是监听数据变化的方式不同
2. watch: 要明确指出监视的数据
3. watchEffect: 不用明确指出监视的数据(函数中用到哪些属性,自动监听)。
<template> <h1>需求: 水温达到80℃, 或水位达到30cm, 请求服务</h1> <h2>水温: {{temp}}</h2> <h2>水位: {{height}}</h2> <button @click="changePrice">水温+10</button> <button @click="changeSum">水位+6</button> </template> <script lang ='ts' setup> import {ref, watchEffect } from 'vue' let temp = ref(20); let height = ref(0); function changePrice(){ temp.value += 10; } function changeSum(){ height.value += 6; } //watchEffect不需要指明监听哪些属性,自动监视 let stopWtach = watchEffect(()=>{ if(temp.value >= 80 || height.value >= 30){ console.log('请求服务') } // 水温达到100,或水位达到50,取消监视 if(temp.value === 100 || height.value === 50){ console.log('清理了') stopWtach() } }) </script>
十:ref
作用:用于注册模板引用。
1: 用在普通DOM标签上,获取的是DOM节点。
2: 用在组件标签上,获取的是组件实例对象。
defineExpose用来暴露组件内的数据(默认被保护)
//APP组件 <template> <theRefTag ref="title1"/> <button @click="showLog">父 - 点我打印</button> </template> <script setup lang="ts"> import theRefTag from './components/theRefTag.vue'; import {ref } from 'vue' //1:用在普通html标签上是没有问题,获取的是DOM节点。 //2:用在组件标签上,是绑定的组件内的内容,获取的是组件实例对象。但是不可以直接看到,需要借助defineExpose来暴露内容 let title1 = ref(); function showLog(){ console.log(title1.value) } </script>
//子组件 - theRefTag.vue <template> <div ref = "title1">清明</div> <div ref = "title2">五一</div> <div ref = "title3">端午</div> <button @click="showLog">子 - 点我打印</button> </template> <script lang ='ts' setup> import {ref, defineExpose} from 'vue' //防止id名称在组件内混淆,引入的ref。只在本vue中生效 let title1 = ref(); let title2 = ref(); let title3 = ref(); function showLog(){ console.log(title1.value) } // 借助defineExpose来暴露内容,将组件中的数据交给外部 defineExpose({title1,title2}) </script>
十一:props
用法:
* defineProps:接收 defineProps(数组类型):defineProps(['list','a'])
* <>:范式,用来限制类型:defineProps<{list:PersonList}>();
* withDefaults:指定默认值 withDefaults(类型,值:对象格式):
withDefaults(defineProps<{list:PersonList}>(), {
list:()=>[{id:4,name:'云曦',sex:'女',age:18}]
})
//APP.vue <template> <theTsInterface :list="personList" a="123"/> </template> <script setup lang="ts"> import { reactive } from 'vue'; import {type PersonList} from '@/types/person'; import theTsInterface from './components/theTsInterface.vue'; let personList = reactive<PersonList>([ {id:1,name:'石昊',sex:'男',age:18}, {id:2,name:'火灵儿',sex:'女',age:18}, {id:3,name:'清漪',age:18} ]) </script> //theTsInterface.vue <template> <!-- <div>a:{{a}}</div> --> <ul> <li v-for="item in list" :key="item.id"> {{item.name}}--{{item.age}} </li> </ul> </template> <script lang ='ts' setup> import { PersonList } from '@/types/person'; //defineProps, withDefaults在vue3中属于宏方法,可以不引入直接使用 // import {defineProps, withDefaults} from 'vue' // 第一种写法:仅接收。数组,可接受多个 // defineProps(['list','a']) // 第二种写法:接收+限制类型 // 父组件只能传递自组件指定的类型 // defineProps<{list:PersonList}>(); // 第三种写法:接收+限制类型+指定默认值+限制必要性 /*** * 注意 * defineProps:接收 defineProps(数组类型) * <>:范式,用来限制类型 * withDefaults:指定默认值 withDefaults(类型,值:对象格式) */ withDefaults(defineProps<{list:PersonList}>(), { list:()=>[{id:4,name:'云曦',sex:'女',age:18}] }) </script>
十二:生命周期
概念:Vue组件实例在创建时要经历一系列的初始化步骤, 在此过程中Vue会在合适的时机, 调用特定的函数, 从而让开发者有机会在特定阶段运行自己的代码, 这些特定的函数统称为: 生命周期钩子,简单来说,就是vue自动调用对应的钩子函数。
创建阶段: setup(无需再次引入,直接创建)
挂载阶段: onBeforeMount、onMounted
更新阶段: onBeforeUpdate、onUpdated
卸载阶段: onBeforeUnmount、onUnmounted
//父组件 APP.vue <template> <theRefTag ref="title1" v-if="showTag"></theRefTag> <button @click="showLog">父 - 点我打印</button> </template> <script setup lang="ts"> import { ref,reactive } from 'vue'; import theRefTag from './components/theRefTag.vue'; let title1 = ref(); let showTag = ref(true); function showLog(){ console.log(title1.value); showTag.value = !showTag.value; } </script>
//子组件theRefTag.vue <template> <div ref = "title1">清明</div> <div ref = "title2">五一</div> <div v-if="showDuan">端午</div> <button @click="showLog">子 - 打印 并 显隐端午标签</button> </template> <script lang ='ts' setup> import {ref, defineExpose} from 'vue' import { onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated } from 'vue'; console.log('setup中即为生命周期 - 创建') onBeforeMount(()=>{ console.log('挂载之前'); }) onMounted(()=>{ console.log('挂载完毕'); }) onBeforeUpdate(()=>{ console.log('更新之前'); }) onUpdated(()=>{ console.log('更新完毕'); }) onBeforeUnmount(()=>{ console.log('卸载之前'); }) onUnmounted(()=>{ console.log('卸载完毕'); }) //防止id名称在组件内混淆,引入的ref。只在本vue中生效 let title1 = ref(); let title2 = ref(); let showDuan = ref(true); function showLog(){ console.log(title1.value); showDuan.value = !showDuan.value; } // 借助defineExpose来暴露内容,将组件中的数据交给外部 defineExpose({title1,title2}) </script>
十三:自定义hooks
定义:本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin。
优势:复用代码, 让setup中的逻辑更清楚易懂。每个功能点一个单独的ts文件。
可复用功能抽离为外部ts文件
文件名以use开头,形如:
useSum
引用时将响应式变量或者方法显式解构暴露出来如:
let {sum, add, bigAdd} = useSum();
在setup函数解构出自定义hooks的变量和方法注意⚠️:在hooks中依然可以使用生命周期钩子函数
//theHooks.vue <template> <h2>当前求和为:{{sum}}</h2> <button @click="add">点我+1</button> <button @click="bigAdd">点我*10</button> <hr> <img v-for="(dog,index) in dogList" :key="index" :src="dog"> <br> <button @click="getDog">再来一只狗</button> </template> <script lang ='ts' setup> import {ref, defineExpose, reactive} from 'vue' import axios,{AxiosError} from 'axios' import useSum from '../hooks/useSum' import useDog from '../hooks/useDog' let {sum, add, bigAdd} = useSum(); let {dogList,getDog} = useDog(); </script>
//useSum.ts import {ref,onMounted} from 'vue' export default function(){ const sum = ref(1); function add(){ sum.value ++; } function bigAdd(){ sum.value *= 10; } //向外部暴露数据 return {sum, add, bigAdd} }
//useDog.ts import { reactive } from "vue"; import axios, {AxiosError} from "axios"; //使用default,可以不用写方法名。不使用default,必须写方法名 export default function dog(){ const dogList = reactive(['https://images.dog.ceo/breeds/pembroke/n02113023_1896.jpg']) //使用axios进行异步接口调用,使用try-catch进行接口失败处理 //一个第三方接口,可以随机返回图片 async function getDog(){ try{ const {data} = await axios.get('https://dog.ceo/api/breed/pembroke/images/random') console.log(data); // 维护数据 dogList.push(data.message); }catch(error){ // 处理错误 const err = <AxiosError>error console.log(err.message) } } //必须将声明的变量或方法向外部暴露 return {dogList, getDog} }
ts相关
interface:自定义接口
<>: 范型
type:自定义类型
//theTsInterface.vue
<script lang ='ts' setup>
import {type PersonInter, type PersonList} from '../types/person';
let person:PersonInter = {id:1,name:'石昊',sex:'男',age:18}
console.log(person);
// 写法一:
//<PersonInter,XXX,XXX> 即为范型,可以有多个范型
// let personList:Array<PersonInter> = [
// {id:1,name:'石昊',sex:'男',age:18},
// {id:2,name:'火灵儿',sex:'女',age:18},
// {id:3,name:'清漪',age:18}
// ]
// 写法二:
let personList:PersonList = [
{id:1,name:'石昊',sex:'男',age:18},
{id:2,name:'火灵儿',sex:'女',age:18},
{id:3,name:'清漪',age:18}
]
console.log(personList);
</script>
//person.ts
//定义一个Person接口,用来限制person对象的具体属性
export interface PersonInter{
id: number
name: string,
sex?: string,//可选属性
age: number,
}
//自定义类型
export type PersonList = Array<PersonInter>
// 或者
// export type PersonList = PersonInter[]