前端框架Vue3(二)——Vue3核心语法之OptionsAPI与CompositionAPI与setup

发布于:2025-08-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

Vue3核心语法

OptionsAPI与CompositionAPI

  • Vue2API设计是Options(配置)风格的。
  • Vue3API设计是Composition(组合)风格的。

Options APl的弊端

Options类型的API,数据、方法、计算属性等,是分散在:datamethodscomputed中的,若想新
增或者修改一个需求,就需要分别修改:datamethodscomputed,不便于维护和复用。

Composition API的优势

可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。

Vue3.0的响应式

  • 实现原理:
    • 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。
    • 通过Reflect(反射):对被代理对象的属性进行操作。
    • MDN文档中描述的Proxy与Reflect:
      Proxy:Proxy的MDN文档
      Reflect:Reflect的MDN文档
new Proxy(data,{
	//拦截读取属性值
	get(target,prop){
	return Reflect.get(target,prop)
	},
	//拦截设置属性值或添加新属性
	set (target,prop,value){
	return Reflect.set(target,prop,value)
	},
	//拦截删除属性
	deleteProperty (target,prop){
	return Reflect.deleteProperty(target,prop)
	}
	})
	proxy.name 'tom'

拉开序幕的setup

setup概述

  1. setupVue3中一个新的配置项,值是一个函数,它是Composition API“表演的舞台”,组件中所用到的:数
    据、方法、计算属性、监视等等,均配置在setup中。
    特点如下:
  • setup函数返回的对像中的内容,可直接在模板中使用。
  • setup中访问thisundefined
  • setup函数会在beforeCreate之前调用,它是"领先"所有钩子执行的。
  1. setup函数的两种返回值:
    1. 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注!)
    2. 若返回一个渲染函数:则可以自定义渲染内容。(了解)
    3. 注意点:
      1. 尽量不要与Vue2.x配置混用
        • Vue2.x配置(datamethodscomputed)中可以访问setup中的属性、方法
        • 但在setup中不能访问到Vue2.x配置(datamethodscomputed)。
        • 如果有重名,setup优先。
      2. setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性。
  2. setup的两个注意点
  • setup执行的时机
    • 在peforeCreate之前执行一次,this是undefined
  • setup的参数
    • props:值为对象包含:组件外部传递过来且组件内部声明接收了的属性。
    • context:上下文对象
      • attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs
      • slots:收到的插槽内容,相当于this.$slots
      • emit:分发自定义事件的函数,相当于this.$emit

ref创建:基本类型的响应式数据

  • 作用:定义响应式变量。
  • 语法:let xxx=ref(初始值)
  • 返回值:一个RefImpl的实例对象,简称ref对象ref,ref对象的value属性是响应式的,
  • 注意点:
    • JS中操作数据需要:xxx.value,但模板中不需要.value,直接使用即可。
    • 对于let name=ref('张三')来说,name不是响应式的,name.value是响应式的。
<template>
  <div class="person">
    <h2>姓名:{{ name }}</h2>
    <h2>年龄:{{ age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="showSex">查看性别</button>
  </div>
</template>

<script lang="ts">
  export default {
    name:'PersonComponent',
  }
</script>
<script lang="ts" setup name='PersonComponent'>
import { ref } from 'vue';
  const name = ref("张三")
  const age = ref(18)
  const sex = "男"
  // 方法
  function changeName() {
    name.value = "王五"
    console.log(name)
  }
  function changeAge() {
    age.value+=1
  }
  function showSex() {
    alert(sex)
  }
</script>
<style  scoped>
.person{
  background-color: pink;
  box-shadow: 0 0 10px ;
  border-radius: 10px;
  padding: 20px;
}
button{
  margin-right: 10px;
}
</style>

reactive创建:对象类型的响应数据

<template>
  <div class="person">
    <h2>一辆{{ car.brand }}车,价值{{ car.price }}</h2>
    <button @click="changePrice">修改汽车的价格</button>
    <br>
    <h2>游戏列表:
      <ul>
        <li v-for="g in games" :key="g.id">{{ g.name }}</li>
      </ul>
    </h2>
    <button @click="changeFirstGameName">修改游戏</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { defineComponent, reactive } from 'vue';

const car = reactive({
  brand: 'Benz',
  price: 100
});

const games = reactive([
  { id: "1", name: "王者荣耀" },
  { id: "2", name: "英雄联盟" },
  { id: "3", name: "和平精英" }
])

console.log(car)

function changePrice() {
  car.price += 10;
}

function changeFirstGameName() {
  games[0].name = "羊了个羊"
}
defineComponent({
  name: 'PersonInfo'
})
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

ref创建:对象类型的响应数据

<template>
  <div class="person">
    <h2>一辆{{ car.brand }}车,价值{{ car.price }}</h2>
    <button @click="changePrice">修改汽车的价格</button>
    <br>
    <h2>游戏列表:
      <ul>
        <li v-for="g in games" :key="g.id">{{ g.name }}</li>
      </ul>
    </h2>
    <button @click="changeFirstGameName">修改游戏</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { defineComponent, ref } from 'vue';

const car = ref({
  brand: 'Benz',
  price: 100
});

const games = ref([
  { id: "1", name: "王者荣耀" },
  { id: "2", name: "英雄联盟" },
  { id: "3", name: "和平精英" }
])

console.log(car)

 function changePrice() {
   car.value.price += 10;
 }
 function changeFirstGameName() {
   games.value[0].name = "羊了个羊"
 }
defineComponent({
  name: 'PersonInfo'
})
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

ref对比reactive

  • 宏观角度看:
    1. ref用来定义:基本类型数据对象类型数据
    2. reactive用来定义:对象类型数据
  • 区别:
    1. ref创建的变量必须使用.value(可以使用volar插件自动添加.value)。
    2. reactive重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)。
  • 使用原则:
    1. 若需要一个基本类型的响应式数据,必须使用ref
    2. 若需要一个响应式对象,层级不深,refreactive都可以。
    3. 若需要一个响应式对象,且层级较深,推荐使用reactive

<template>
  <div class="person">
    <h2>一辆{{ car.brand }}车,价值{{ car.price }}</h2>
    <button @click="changePrice">修改汽车的价格</button>
    <button @click="changeBrand">修改汽车的品牌</button>
    <button @click="changeCar">修改汽车</button>
    <hr>
    <h2>当前求和为:{{ sum }}</h2>
    <button @click="changeSum">sum+1</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { defineComponent, ref } from 'vue';

// const car = reactive({
//   brand: 'Benz',
//   price: 100
// });
const car = ref({
  brand: 'Benz',
  price: 100
});
const sum = ref(0);

// function changePrice() {
//    car.price += 10;
//  }
// function changeBrand() {
//    car.brand = 'hongqi';
//  }
function changePrice() {
   car.value.price += 10;
 }
function changeBrand() {
   car.value.brand = 'hongqi';
 }
 function changeCar() {
  // Object.assign(car, {
  //   brand: 'Aodi',
  //   price: 10
  // });
  car.value = {
    brand: 'Aodi',
    price: 10
  };
 }
function changeSum() {
  sum.value += 1;
 }
defineComponent({
  name: 'PersonInfo'
})
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

toRefs与toRef

  • 作用:将一个响应式对象中的每一个属性,转换为ref对象。
  • 备注:toRefstoRef功能一致,但toRefs可以批量转换
  • 语法如下:
<template>
  <div class="person">
    <h2>name:{{ name }}</h2>
    <h2>age:{{ age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import { defineComponent, reactive, toRefs } from 'vue';
  const person = reactive({
    name: '张三',
    age: 18,
  })
  defineComponent({
    name: 'PersonComponent',
  })
  const {name,age} = toRefs(person);
  function changeName() {
    name.value += '三';
  }
  function changeAge() {
    age.value += 1;
  }

</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

计算属性(computed)

<template>
  <div class="person">
    姓:<input type="text" v-model="firstname"><br>
    名:<input type="text" v-model="lastname"><br>
    <button @click="changeFullName()">将全名改为li-si</button>
    全名:<span>{{ fullname}}</span>
  </div>
</template>

<script lang="ts" setup>
import { computed, ref } from 'vue';

const firstname = ref('张');
const lastname = ref('三');
// 这样定义的fullname是一个计算属性,且是只读的
// const fullname=computed(() => {
//   return firstname.value.slice(0,1).toUpperCase() + firstname.value.slice(1)+'-'+ lastname.value;
// });
const fullname = computed( {
  get(){
    return firstname.value.slice(0,1).toUpperCase() + firstname.value.slice(1)+'-'+ lastname.value;
  },
  set(val){
    const [str1,str2]=val.split('-');
    firstname.value=str1
    lastname.value=str2
  }
});

function changeFullName() {
  fullname.value='李-四'
}
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

watch

  • 作用:监视数据的变化(和Vue2中的watch作用一致)
  • 特点:Vue3中的watch只能监视以下四种数据
    1. ref定义的数据。
    2. reactive定义的数据。
    3. 函数返回一个值(getter函数)。
    4. 一个包含上述内容的数组。
      我们在Vue3中使用watch的时候,通常会遇到以下几种情况:
*情况一

监视ref定义的【基本类型】数据:直接写数据名即可,监视的是其value值的改变。

<template>
  <div class="person">
    <h1>情况一:监视【ref】定义的【基本类型】数据</h1>
    <h2>当前求和为:{{ sum }}</h2>
    <button @click="changeSum">点击sum+1</button>
  </div>
</template>

<script lang="ts" setup>
import { ref, watch } from 'vue';

const sum=ref(0)

function changeSum() {
  sum.value+=1;
}
// 监视
watch(sum,(newValue,oldValue)=>{
  console.log('sum被修改了',newValue,oldValue)
})
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

*情况二

监视ref定义的【对像类型】数据:直接写数据名,监视的是对像的【地址值】,若想监视对像内部的数据,要
手动开启深度监视。
注意:

  • 若修改的是ref定义的对象中的属性,newValueoldValue都是新值,因为它们是同一个对象。
  • 若修改整个ref定义的对象,newValue是新值,oldValue是旧值,因为不是同一个对象了。
<template>
  <div class="person">
    <h1>情况二:监视【ref】定义的【对象类型】数据</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changePerson">修改人</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { ref, watch } from 'vue';

const person = ref({
  name:'张三',
  age:18
})

function changeName(){
  person.value.name += '五'
}

function changeAge(){
  person.value.age += 1
}

function changePerson(){
  person.value = {
    name:'李四',
    age:90
  }
}
// 监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内容属性的变化,需要手动开启深度监视
// watch(person,(newValue,oldValue)=>{
//   console.log('person变化了',newValue,oldValue)
// })
// 监视`ref`定义的【对像类型】数据:直接写数据名,监视的是对像的【地址值】,若想监视对像内部的数据,要手动开启深度监视
// watch的第一个参数是监视的数据
// watch的第二个参数是监视的回调
// watch的第二个参数是配置对象()
watch(person,(newValue,oldValue)=>{
  console.log('person变化了',newValue,oldValue)
},{deep:true,immediate:true})
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>
*情况三

监视reactive定义的【对象类型】数据,且默认开启了深度监视。

<template>
  <div class="person">
    <h1>情况三:监视【ref】定义的【对象类型】数据</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changePerson">修改人</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { reactive, watch } from 'vue';

const person = reactive({
  name:'张三',
  age:18
})

function changeName(){
  person.name += '五'
}

function changeAge(){
  person.age += 1
}

function changePerson(){
  Object.assign(person,{
    name:'王五',
    age:19
  })
}
//监视,情况三:监视`reactive`定义的【对象类型】数据,且默认开启了深度监视。
watch(person,(newValue,oldValue)=>{
  console.log('person被修改了',newValue,oldValue)
})
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

*情况四

监视refreactive定义的【对象类型】数据中的某个属性,注意点如下:

  1. 若该属性值不是【对象类型】,需要写成函数形式。
  2. 若该属性值是依然是【对象类型】,可直接编,也可写成函数,不过建议写成函数。
    结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,
    需要手动开启深度监视。
<template>
  <div class="person">
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <h2>汽车:{{ person.car.c1 }}{{ person.car.c2 }}</h2>
  <button @click="changeName">修改名字</button>
  <button @click="changeAge">修改年龄</button>
  <button @click="changeFirstCar">修改第一台车</button>
  <button @click="changeSecondCar">修改第二台车</button>
  <button @click="changeCar">修改车</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { reactive, watch } from 'vue';
const person = reactive({
  name: '张三',
  age: 18,
  car:{
    c1:"Benz",
    c2:"BMW"
  }
});
  function changeName() {
    person.name += '三';
  }
  function changeAge() {
    person.age += 1;
  }
  function changeFirstCar() {
    person.car.c1 ='hongqi'
  }
  function changeSecondCar() {
    person.car.c2 ='Aodi'
  }
  function changeCar() {
    person.car.c1 ='hongqi';
    person.car.c2 ='Aodi';
  }
  watch(()=>{return person.name},(newValue, oldValue) => {
    console.log('person.name被修改了', newValue, oldValue);
  })
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>
*情况五

情况五:监视多个数据

<template>
  <div class="person">
    <h1>情况五:监视多个数据</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <h2>汽车:{{ person.car.c1 }}{{ person.car.c2 }}</h2>
  <button @click="changeName">修改名字</button>
  <button @click="changeAge">修改年龄</button>
  <button @click="changeFirstCar">修改第一台车</button>
  <button @click="changeSecondCar">修改第二台车</button>
  <button @click="changeCar">修改车</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { reactive, watch } from 'vue';
const person = reactive({
  name: '张三',
  age: 18,
  car:{
    c1:"Benz",
    c2:"BMW"
  }
});
  function changeName() {
    person.name += '三';
  }
  function changeAge() {
    person.age += 1;
  }
  function changeFirstCar() {
    person.car.c1 ='hongqi'
  }
  function changeSecondCar() {
    person.car.c2 ='Aodi'
  }
  function changeCar() {
    person.car.c1 ='hongqi';
    person.car.c2 ='Aodi';
  }

 // 监视,情况五:监视上述多个数据
  watch([()=>person.name,()=>person.car.c1] ,(newValue, oldValue) => {
    console.log("person.car被修改了", newValue, oldValue);
  },{deep:true})
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

watchEffect

  • 官网:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。
  • watch对比watchEffect
    1. 都能监听响应式数据的变化,不同的是监听数据变化的方式不同
    2. watch:要明确指出监视的数据
    3. watchEffect:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)。
      示例代码:
<template>
  <div class="person">
    <h2>需求:当水温达到60度,或水位达到80cm,给服务器发送请求</h2>
   <h2>当前水温为:{{ temp }}°C</h2>
   <h2>当前水位为:{{ height }}cm</h2>
   <button @click="changeTemp">点我temp+10</button>
   <button @click="changeHeight">点我height+10</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { ref, watchEffect } from 'vue';
const temp = ref(10);
const height = ref(0);
function changeTemp() {
  temp.value+=10
}
function changeHeight() {
  height.value+=10
}
// 监视 watch实现
/*watch([temp,height],(value)=>{
const [newTemp,newHeight]=value
console.log(newTemp,newHeight)
if(newTemp>=60||newHeight>=80){
console.log("预警!")
}
})*/

// 监视 watchEffect实现
watchEffect(()=>{
  if(temp.value>=60||height.value>=80){
    console.log("预警!")
  }
})
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

【标签的ref属性】

作用:用于注册模板引用。

  • 用在普通DOM标签上,获取的是DOM节点。
  • 用在组件标签上,获取的是组件实例对象。
    用在普通DOM标签上:
<template>
  <div class="person">
    <h1>中国</h1>
    <h2 ref="title2">河南</h2>
    <h3>前端</h3>
    <button @click="showLog">点我输出h2</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { defineExpose, ref } from 'vue';
//创建一个title2,用于存储ref标记的内容
const title2 = ref()
const a = ref(1)
const b = ref(2)
const c = ref(3)
function showLog() {
  console.log(title2.value)
}
defineExpose({a,b,c})
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

回顾TS中的接口泛型自定义类型

<template>
  <div class="person">
<h3>{{ person }}</h3>
<h3>{{ personList }}</h3>
  </div>
</template>

<script lang="ts" setup name="Person">
import { type PersonInter, type Persons } from '@/types';

const person:PersonInter={id:'1',name:'张三',age:18}
const personList:Persons=[
  {id:'1',name:'张三',age:18},
  {id:'2',name:'李四',age:20},
  {id:'3',name:'王五',age:30}
]
</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

props的使用

//Person.vue
<template>
  <div class="person">
    <ul>
      <li v-for="item in list" :key="item.id">
        {{ item.name }}--{{ item.age }}
      </li>
    </ul>
  </div>
</template>

<script lang="ts" setup name="Person">
import { withDefaults } from 'vue';
import {type Persons } from '../types/index';

// 只接收list
// defineProps(['list'])

// 接收list+限制类型
// defineProps<{list:Persons}>()

//接收list+限制类型+限制必要性+指定默认值
// withDefaults(defineProps<{list?:Persons}>(),{
//   list:()=>[{id:'1',name:'haha',age:18}]
// })

// 接收a,同时将props保存起来
// const x=defineProps(['a'])
// console.log(x)

</script>

<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>
//App.vue
<template>
  <Person a="haha" :list="personList"/>
</template>

<script lang="ts" setup name="App">
import Person from './components/Person.vue';
import {reactive} from 'vue';
import {type Persons} from "@/types";
const personList=reactive<Persons>([
  {
    id:'1',
    name:'李四',
    age:18,
    x:10
  },
  {
    id:'2',
    name:'王五',
    age:20,
  },
  {
    id:'3',
    name:'张三',
    age:35,
  }
])
</script>

生命周期

人的生命周期:

【时刻】 【要做的事】
出生
经历 哭、笑
死亡 遗嘱

组件的生命周期:

【时刻】 【调用特定的函数】
创建 created
挂载 mounted
更新
销毁
  • 概念:Vue组件实例在创建时要经历一系列的初始化步骤,在此过程中Vue会在合适的时机,调用特定的函
    数,从而让开发者有机会在特定阶段运行自己的代码,这些特定的函数统称为:生命周期钩子
  • 规律:
    生命周期整体分为四个阶段,分别是:创建、挂载、更新、销毁,每个阶段都有两个钩子,一前一后。
  • Vue2的生命周期
    • 创建阶段:beforeCreatecreated
    • 挂载阶段:beforeMountmounted
    • 更新阶段:beforeUpdateupdated
    • 销毁阶段:beforeDestroydestroyed
  • Vue3的生命周期
    • 创建阶段:setup
    • 挂载阶段:onBeforeMountonMounted
    • 更新阶段:onBeforeUpdateonUpdated
    • 销毁阶段:onBeforeUnmountonUnmounted
  • 常用的钩子:onMounted(挂载完毕)、onUpdated(更新完毕)、onBeforeUnmount(卸载之前)
  • 示例代码:
<template>
  <div class="person">
    <h2>当前求和为:{{ sum }}</h2>
    <button @click="add">点我sum+1</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated, ref } from 'vue'
// 数据
const sum=ref(0)

// 方法
function add() {
  sum.value+=1
}

// 创建
console.log("子--创建成功")
// 挂载
onBeforeMount(()=>{
  console.log("子--挂载成功")
})
// 挂载完毕
onMounted(()=>{
  console.log("子--挂载完毕")
})
// 更新前
onBeforeUpdate(()=>{
  console.log("子--更新前")
})
// 更新后
onUpdated(()=>{
  console.log("子--更新后")
})
// 销毁前
onBeforeUnmount(()=>{
  console.log("子--销毁前")
})
// 销毁后
onUnmounted(()=>{
  console.log("子--销毁后")
})
</script>
<style scoped>
.person {
  background-color: pink;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
button {
  margin-right: 10px;
}
</style>

自定义hooks

import { computed, onMounted, ref } from 'vue'
export default function () {
  // 数据
  const sum = ref(0)
  const bigSum=computed(()=>{
    return sum.value*10
  })
  // 方法
  function add() {
    sum.value += 1
  }
  // 钩子
  onMounted(() => {
    add()
  })
// 向外部提供东西
  return {
    sum,
    add,
    bigSum
  }
}
import axios from 'axios'
import { reactive,onMounted } from 'vue'

export default function (){
  // 数据
const dogList = reactive([
  'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
])
// 方法
async function addDog() {
  try {
    const result = await axios.get('https://dog.ceo/api/breeds/image/random')
    if (result.data && result.data.message) {
      dogList.push(result.data.message)
    }
  } catch (error) {
    console.error('获取狗狗图片失败:', error)
  }
}
// 钩子
onMounted(() => {
  addDog()
})
//向外部提供东西
return {dogList, addDog}
}

import { computed, onMounted, ref } from 'vue'
export default function () {
  // 数据
  const sum = ref(0)
  const bigSum=computed(()=>{
    return sum.value*10
  })
  // 方法
  function add() {
    sum.value += 1
  }
  // 钩子
  onMounted(() => {
    add()
  })
// 向外部提供东西
  return {
    sum,
    add,
    bigSum
  }
}