02 Vue3核心语法--setup、ref、reactive、toRefs 、toRef 、computed、watch、watchEffect、ref 属性、props传参、hooks

选项式API OptionsAPI 和 组合式API CompositionAPI

Vue2API设计是Options(配置)风格的,就是选项式API
Vue3API设计是Composition(组合)风格的,就是组合式API

选项式API的弊端

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

Composition API 组合式API 的优势

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

setup

setupVue3中一个新的配置项,值是一个函数,它是 Composition API “表演的舞台,组件中所用到的:数据、方法、计算属性、监视......等等,均配置在setup

setup的特点

  • setup函数返回的对象中的内容,可直接在模板中使用。
  • setup中访问thisundefined
  • setup函数会在beforeCreate之前调用,它是“领先”所有钩子执行的
<template>
  <div class="person">
    <h2>姓名:{{name}}</h2>
    <h2>年龄:{{age}}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">年龄+1</button>
    <button @click="showTel">点我查看联系方式</button>
  </div>
</template>

<script lang="ts">
  export default {
    name:'Person',
    setup(){
      // 数据,原来写在data中(注意:此时的name、age、tel数据都不是响应式数据)
      let name = '张三'
      let age = 18
      let tel = '13888888888'

      // 方法,原来写在methods中
      function changeName(){
        name = 'zhang-san' //注意:此时这么修改name页面是不变化的
        console.log(name)
      }
      function changeAge(){
        age += 1 //注意:此时这么修改age页面是不变化的
        console.log(age)
      }
      function showTel(){
        alert(tel)
      }

      // 返回一个对象,对象中的内容,模板中可以直接使用
      return {name,age,tel,changeName,changeAge,showTel}
    }
  }
</script>

setup 的返回值

若返回一个对象:则对象中的:属性、方法等,在模板中均可以直接使用(重点关注)。

若返回一个函数:则可以自定义渲染内容,代码如下:

setup(){
  return ()=> '你好啊!'
}

setup 与 Options API 的关系

Vue2 的配置(datamethos......)中可以访问到 setup中的属性、方法,但在setup不能访问到Vue2的配置(datamethos......)

如果与Vue2冲突,则setup优先

setup 语法糖

setup函数有一个语法糖,这个语法糖,可以让我们把setup独立出去,代码如下:

<template>
  <div class="person">
    <h2>姓名:{{name}}</h2>
    <h2>年龄:{{age}}</h2>
    <button @click="changName">修改名字</button>
    <button @click="changAge">年龄+1</button>
    <button @click="showTel">点我查看联系方式</button>
  </div>
</template>

<script lang="ts">
  export default {
    name:'Person',
  }
</script>

<!-- 下面的写法是setup语法糖 -->
<script setup lang="ts">
  console.log(this) //undefined
  
  // 数据(注意:此时的name、age、tel都不是响应式数据)
  let name = '张三'
  let age = 18
  let tel = '13888888888'

  // 方法
  function changName(){
    name = '李四'//注意:此时这么修改name页面是不变化的
  }
  function changAge(){
    console.log(age)
    age += 1 //注意:此时这么修改age页面是不变化的
  }
  function showTel(){
    alert(tel)
  }
</script>

ref 和 reactive 创建响应式数据

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

作用:定义响应式变量,接收的数据可以是:基本类型对象类型

ref接收的是对象类型,内部其实也是调用了reactive函数

语法:let xxx = ref(初始值)

返回值:一个RefImpl的实例对象,简称ref对象refref对象的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">年龄+1</button>
    <button @click="showTel">点我查看联系方式</button>
  </div>
</template>

<script setup lang="ts" name="Person">
  import {ref} from 'vue'
  // name和age是一个RefImpl的实例对象,简称ref对象,它们的value属性是响应式的。
  let name = ref('张三')
  let age = ref(18)
  // tel就是一个普通的字符串,不是响应式的
  let tel = '13888888888'

  function changeName(){
    // JS中操作ref对象时候需要.value
    name.value = '李四'
    console.log(name.value)

    // 注意:name不是响应式的,name.value是响应式的,所以如下代码并不会引起页面的更新。
    // name = ref('zhang-san')
  }
  function changeAge(){
    // JS中操作ref对象时候需要.value
    age.value += 1 
    console.log(age.value)
  }
  function showTel(){
    alert(tel)
  }
</script>

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

<template>
  <div class="person">
    <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
    <h2>游戏列表:</h2>
    <ul>
      <li v-for="g in games" :key="g.id">{{ g.name }}</li>
    </ul>
    <h2>测试:{{obj.a.b.c.d}}</h2>
    <button @click="changeCarPrice">修改汽车价格</button>
    <button @click="changeFirstGame">修改第一游戏</button>
    <button @click="test">测试</button>
  </div>
</template>

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

// 数据
let car = ref({ brand: '奔驰', price: 100 })
let games = ref([
  { id: 'ahsgdyfa01', name: '英雄联盟' },
  { id: 'ahsgdyfa02', name: '王者荣耀' },
  { id: 'ahsgdyfa03', name: '原神' }
])
let obj = ref({
  a:{
    b:{
      c:{
        d:666
      }
    }
  }
})

console.log(car)

function changeCarPrice() {
  car.value.price += 10
}
function changeFirstGame() {
  games.value[0].name = '流星蝴蝶剑'
}
function test(){
  obj.value.a.b.c.d = 999
}
</script>

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

作用:定义一个响应式对象(基本类型不要用它,要用ref,否则报错)

语法:let 响应式对象= reactive(源对象)

返回值:一个Proxy的实例对象,简称:响应式对象

reactive定义的响应式数据是“深层次”的

<template>
  <div class="person">
    <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
    <h2>游戏列表:</h2>
    <ul>
      <li v-for="g in games" :key="g.id">{{ g.name }}</li>
    </ul>
    <h2>测试:{{obj.a.b.c.d}}</h2>
    <button @click="changeCarPrice">修改汽车价格</button>
    <button @click="changeFirstGame">修改第一游戏</button>
    <button @click="test">测试</button>
  </div>
</template>

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

// 数据
let car = reactive({ brand: '奔驰', price: 100 })
let games = reactive([
  { id: 'ahsgdyfa01', name: '英雄联盟' },
  { id: 'ahsgdyfa02', name: '王者荣耀' },
  { id: 'ahsgdyfa03', name: '原神' }
])
let obj = reactive({
  a:{
    b:{
      c:{
        d:666
      }
    }
  }
})

function changeCarPrice() {
  car.price += 10
}
function changeFirstGame() {
  games[0].name = '流星蝴蝶剑'
}
function test(){
  obj.a.b.c.d = 999
}
</script>

ref 和 reactive 的对比

1. 定义的对象不同

ref用来定义:基本类型数据对象类型数据

reactive用来定义:对象类型数据

2. 使用方式不同

ref创建的变量必须使用.value才可以获取对应的数值

可以使用volar插件自动添加.value
image

reactive创建的变量直接以对象的方式 对象.name 就可以获取值

reactive重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)

let obj=reactive({id:1,name:'张三'})

# 如果直接赋值替换,会失去响应式
obj={id:2,name:'李四'}

# 可以采用 `Object.assign` 重新赋值,不会失去响应式
Object.assign(obj,{id:2,name:'李四'})

使用原则总结

若需要一个基本类型的响应式数据,必须使用ref

若需要一个响应式对象,层级不深refreactive都可以

若需要一个响应式对象,且层级较深,推荐使用reactive

toRefs 与 toRef : 转为 ref 对象

作用:将一个响应式对象中的每一个属性,转换为ref对象,一般用于解构函数后的数据处理

toRefstoRef功能一致,但toRefs可以批量转换

批量转换为ref对象--toRefs

#  通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
let {name,gender} =toRefs(person)

单个转换--roRef

# 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
let age = toRef(person,'age')

下面是 toRefs 和 toRef 的是使用案例:

<template>
  <div class="person">
    <h2>姓名:{{person.name}}</h2>
    <h2>年龄:{{person.age}}</h2>
    <h2>性别:{{person.gender}}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changeGender">修改性别</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,reactive,toRefs,toRef} from 'vue'

  // 数据
  let person = reactive({name:'张三', age:18, gender:'男'})
	
  // 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
  let {name,gender} =  toRefs(person)
	
  // 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
  let age = toRef(person,'age')

  // 方法
  function changeName(){
    name.value += '~'
  }
  function changeAge(){
    age.value += 1
  }
  function changeGender(){
    gender.value = '女'
  }
</script>

computed计算属性

计算属性——只读取,不修改

<template>
  <div class="person">
    姓:<input type="text" v-model="firstName" /> <br />
    名:<input type="text" v-model="lastName" /> <br />
    全名:<span>{{ fullName }}</span> <br />
  </div>
</template>

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

let firstName = ref('zhang');
let lastName = ref('san');

// 计算属性——只读取,不修改
let fullName = computed(() => {
  return firstName.value + '-' + lastName.value;
});
</script>

计算属性-既可读又可改-get() set()

<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">全名改为:li-si</button>
  </div>
</template>

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

let firstName = ref('zhang');
let lastName = ref('san');

// 计算属性——既读取又修改
let fullName = computed({
  // 读取
  get() {
    return firstName.value + '-' + lastName.value;
  },
  // 修改
  set(val) {
    console.log('有人修改了fullName', val);
    firstName.value = val.split('-')[0];
    lastName.value = val.split('-')[1];
  },
});

function changeFullName() {
  fullName.value = 'li-si';
}
</script>

watch 监视

作用:监视数据的变化,和Vue2中的watch作用一致

Vue3监视的特点

Vue3中的watch只能监视以下四种数据

  1. ref定义的数据
  2. reactive定义的数据
  3. 函数返回一个值(getter函数)
  4. 一个包含上述内容的数组

情况一:监视ref定义的【基本类型】数据

直接写数据名即可,监视的是其value值的改变

<template>
  <div class="person">
    <h2>当前sum值:{{ sum }}</h2>
    <button @click="changeSum">点我sum+1</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { ref, watch } from 'vue';
// 数据
let sum = ref(0);
// 方法
function changeSum() {
  sum.value += 1;
}

// 监视,情况一:监视【ref】定义的【基本类型】数据
const stopWatch = watch(sum, (newValue, oldValue) => {
  console.log('sum变化了', newValue, oldValue);

  // 停止监视
  if (newValue >= 10) {
    stopWatch();
  }
});
</script>

情况二:监视ref定义的【对象类型】数据,默认不开启深度监视

直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视

若修改的是ref定义的对象中的属性,newValueoldValue 都是新值,因为它们是同一个对象
若修改整个ref定义的对象,newValue 是新值, oldValue 是旧值,因为不是同一个对象

<template>
  <div class="person">
    <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';
// 数据
let person = ref({
  name: '张三',
  age: 18,
});
// 方法
function changeName() {
  person.value.name += '~';
}
function changeAge() {
  person.value.age += 1;
}
function changePerson() {
  # 通过 .value可以修改整个值
  person.value = { name: '李四', age: 90 };
}
/* 
    监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视
    watch的第一个参数是:被监视的数据
    watch的第二个参数是:监视的回调
    watch的第三个参数是:配置对象(deep、immediate等等.....) 
  */
watch(
  person,
  (newValue, oldValue) => {
    console.log('person变化了', newValue, oldValue);
  },
  { deep: true }
);
</script>

情况三:监视ractive定义的【对象类型】数据,默认开启了深度监视

<template>
  <div class="person">
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changePerson">修改整个人</button>
    <hr />
    <h2>测试深度监视:{{ obj.a.b.c }}</h2>
    <button @click="test">修改obj.a.b.c</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { reactive, watch } from 'vue';
// 数据
let person = reactive({
  name: '张三',
  age: 18,
});
let obj = reactive({
  a: {
    b: {
      c: 666,
    },
  },
});
// 方法
function changeName() {
  person.name += '~';
}

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

function changePerson() {
  Object.assign(person, { name: '李四', age: 80 });
}

function test() {
  obj.a.b.c = 888;
}

// 监视,情况三:监视【reactive】定义的【对象类型】数据,且默认是开启深度监视的
watch(person, (newValue, oldValue) => {
  console.log('person变化了', newValue, oldValue);
});

watch(obj, (newValue, oldValue) => {
  console.log('Obj变化了', newValue, oldValue);
});
</script>

情况四:监视refreactive定义的【对象类型】数据中的某个属性,推荐写函数式

若该属性值不是【对象类型】,需要写成函数形式

若该属性值是依然是【对象类型】,可直接编,也可写成函数,建议写成函数

若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视

<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="changeC1">修改第一台车</button>
    <button @click="changeC2">修改第二台车</button>
    <button @click="changeCar">修改整个车</button>
  </div>
</template>

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

// 数据
let person = reactive({
  name: '张三',
  age: 18,
  car: {
    c1: '奔驰',
    c2: '宝马',
  },
});

// 方法
function changeName() {
  person.name += '~';
}

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

function changeC1() {
  person.car.c1 = '奥迪';
}

function changeC2() {
  person.car.c2 = '大众';
}

function changeCar() {
  person.car = { c1: '雅迪', c2: '爱玛' };
}

// 监视响应式对象中的某个属性,且该属性是基本类型的,写成函数式
watch(
  () => person.name,
  (newValue, oldValue) => {
    console.log('person.name变化了', newValue, oldValue);
  }
);

// 监视响应式对象中的某个属性,且该属性是对象类型的
watch(
  () => person.car,
  (newValue, oldValue) => {
    console.log('person.car变化了', newValue, oldValue);
  },
  { deep: true }
);
</script>

情况五:监视多个数据

<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="changeC1">修改第一台车</button>
    <button @click="changeC2">修改第二台车</button>
    <button @click="changeCar">修改整个车</button>
  </div>
</template>

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

// 数据
let person = reactive({
  name: '张三',
  age: 18,
  car: {
    c1: '奔驰',
    c2: '宝马',
  },
});
// 方法
function changeName() {
  person.name += '~';
}
function changeAge() {
  person.age += 1;
}
function changeC1() {
  person.car.c1 = '奥迪';
}
function changeC2() {
  person.car.c2 = '大众';
}
function changeCar() {
  person.car = { c1: '雅迪', c2: '爱玛' };
}

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

watchEffect

立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数

watchwatchEffect 对比

  1. 都能监听响应式数据的变化,不同的是监听数据变化的方式不同

  2. watch:要明确指出监视的数据

  3. watchEffect:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)

  4. watchEffect:是立即执行函数,相当于 watch 设置了 immediate 配置属性

<template>
  <div class="person">
    <h2 id="demo">水温:{{ temp }}</h2>
    <h2>水位:{{ height }}</h2>
    <button @click="changePrice">水温+1</button>
    <button @click="changeSum">水位+10</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { ref, watch, watchEffect } from 'vue';
// 数据
let temp = ref(0);
let height = ref(0);

// 方法
function changePrice() {
  temp.value += 10;
}
function changeSum() {
  height.value += 1;
}

// 用watch实现,需要明确的指出要监视:temp、height
watch([temp, height], (value) => {
  // 从value中获取最新的temp值、height值
  const [newTemp, newHeight] = value;
  // 室温达到50℃,或水位达到20cm,立刻联系服务器
  if (newTemp >= 50 || newHeight >= 20) {
    console.log('联系服务器');
  }
});



// 用watchEffect实现,不用指定特定的字段
const stopWtach = watchEffect(() => {

  // 室温达到50℃,或水位达到20cm,立刻联系服务器
  if (temp.value >= 50 || height.value >= 20) {    
    console.log('联系服务器');
  }
  
  // 水温达到100,或水位达到50,取消监视
  if (temp.value === 100 || height.value === 50) {
    console.log('取消监视');
    stopWtach();
  }
});
</script>

标签的 ref 属性

作用:用于注册模板引用

  • 用在普通DOM标签上,获取的是DOM节点
  • 用在组件标签上,获取的是组件实例对象

用在普通DOM标签上

<template>
  <div class="person">
    <h2 ref="title1">前端HTML</h2>
    <button @click="showLog">点我打印内容</button>
  </div>
</template>

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

let title1 = ref();

function showLog() {
  // 通过ref获取元素
  console.log(title1.value);
}
</script>

用在组件标签上-可以修改子组件的值

父组件:

<template>
  <Person ref="ren"/>
  <button @click="test">测试</button>
</template>

<script lang="ts" setup name="App">
  # 组件引用后,直接使用
  import Person from './components/Person.vue'
  import {ref} from 'vue'
  let ren = ref()

  function test(){
    // 必须是 子组件通过 defineExpose 向外 暴露的值 
    ren.value.name='新的值' 
    console.log(ren.value.name)
    console.log(ren.value.age)
  }
</script>

子组件:子组件Person.vue中要使用defineExpose暴露内容

<script lang="ts" setup name="Person">
  import {ref,defineExpose} from 'vue'
  // 数据
  let name = ref('张三')
  let age = ref(18)

  // 使用defineExpose将组件中的数据交给外部,如果没有将数据暴露出去,那么父组件是获取不到的
  defineExpose({name,age})
</script>

props -- 父组件向子组件传参【defineProps】

要使用 props 接收来自父组件的参数,需要【子组件】使用 defineProps

1. 直接接收数据,不限制类型

父组件
<template>
  <person :msg1="msg1" :msg2="msg2" />
</template>

<script setup lang="ts">
import person from '@/components/person.vue';
// 定义2个变量,并将这2个变量直接传递给子组件
let msg1 = 'Hello';
let msg2 = 'world';
</script>

子组件
<template>
  # 在模板中直接使用接收到的变量
  <h1>子组件-{{ msg1 }}=={{ msg2 }}</h1>
</template>

<script lang="ts" setup>
// 直接接收来自 【父组件的参数】
defineProps(['msg1', 'msg2']);
</script>

但是我们是不可以在 script 中打印使用的,我们可以通过定义一个变量来接收,并使用
const props = defineProps(['msg1', 'msg2']);
console.log(props.msg1);

2. 接收数据,限制类型+设置默认值

设置 ts 类型限制
// 定义一个接口,用来限制 person对象的
export interface personObjInter {
  id: string;
  name: string;
  age: number;
}

// 自定义类型,限制persons数组的类型
// 定义一个数组,数组里面的对象类型是 personObjInter
export type personsInter = Array<personObjInter>;
// 简写
// type personsInter =personObjInter[];

父组件-传递一个类型限制的对象和数组
<template>
  <Person :obj="obj" :list="list" />
</template>

<script setup lang="ts">
import Person from '@/components/person.vue';
import { reactive } from 'vue';
// 引入类型限制
import { type personObjInter, type personsInter } from '@/types';

// 定义一个类型为【personsInter】的obj对象
let obj = reactive<personObjInter>({
  id: 'sdjghjkhhe01',
  name: '张三',
  age: 20,
});

// 定义一个数组对象为【personsInter】的list数组
let list = reactive<personsInter>([
  {
    id: 'sdjghjkhhe01',
    name: '张三',
    age: 20,
  },
  {
    id: 'sdjghjkhhe02',
    name: '李四',
    age: 23,
  },
]);
</script>

</script>

子组件--接收
<template>
  <h1>子组件 【defineProps】 接收参数</h1>
  <hr />
  <h3>对象参数obj:{{ obj }}</h3>
  <hr />
  <h3>数组对象:{{ list }}</h3>
</template>

<script lang="ts" setup>
// 限制类型 只能接收 type 类型限制的对象
import { type personObjInter, type personsInter } from '@/types';
// 第二种写法:接收+限制类型
defineProps<{
  obj: personObjInter;
  list: personsInter;
}>();



// 第三种写法:接收+限制类型+指定默认值+限制必要性
// ? 表示list的必要性
// 使用 withDefaults 设置默认值
let props = withDefaults(defineProps<{ obj: personObjInter; list?: personsInter }>(), {
  // list 设置默认值
  list: () => [{ id: 'asdasg01', name: '小猪佩奇', age: 18 }],
});
</script>

生命周期

概述

概念:Vue组件实例在创建时要经历一系列的初始化步骤,在此过程中Vue会在合适的时机,调用特定的函数,从而让开发者有机会在特定阶段运行自己的代码,这些特定的函数统称为:生命周期钩子

生命周期整体分为四个阶段,分别是:创建、挂载、更新、销毁,每个阶段都有两个钩子,一前一后

生命周期的阶段

创建阶段:setup

挂载阶段:onBeforeMountonMounted

更新阶段:onBeforeUpdateonUpdated

卸载阶段:onBeforeUnmountonUnmounted

常用的钩子

onMounted(挂载完毕)、onUpdated(更新完毕)、onBeforeUnmount(卸载之前)

hook

本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin,其主要作用还是让一个功能的数据和方法贴合在 一起

自定义hook的优势:复用代码, 让setup中的逻辑更清楚易懂

  1. 封装 求和 sum等功能的hook
import { ref, onMounted } from 'vue';
// 默认向外暴露一个函数
export default function () {
  // 初始化 sum
  let sum = ref(0);

  // sum求值加1
  function addSum() {
    sum.value += 1;
  }

  const increment = () => {
    sum.value += 1;
  };
  const decrement = () => {
    sum.value -= 1;
  };

  onMounted(() => {
    // 初始数值加1
    increment();
  });

  return { sum, increment, decrement };
}

  1. 封装获取小狗图片功能的一个hook
import { reactive, onMounted } from 'vue';
import axios from 'axios';

// 默认向外暴露
export default function () {
  // 初始化 定义一个值为string的数组
  let dogImgsList = reactive<string[]>([]);

  // 新增一个dog图片
  async function addDogImgs() {
    const { data } = await axios.get('https://dog.ceo/api/breed/pembroke/images/random');
    dogImgsList.push(data.message);
  }

  // 初始化获取图片列表
  onMounted(() => {
    addDogImgs();
  });

  // 向外暴露对象
  return { dogImgsList, addDogImgs };
}

  1. 组件直接调用并使用
<template>
  <h1>当前求和是:{{ sum }}</h1>
  <button @click="increment">点我加1</button>
  <button @click="decrement">点我减1</button>
  <hr />

  <img :src="imgs" v-for="imgs in dogImgsList" />
  <button @click="addDogImgs">点击新增一个图片</button>
</template>

<script lang="ts" setup>
import useSum from '@/hooks/useSum';
import useDog from '@/hooks/useDog';

const { sum, increment, decrement } = useSum();
const { dogImgsList, addDogImgs } = useDog();
</script>

posted @ 2024-04-12 10:02  songxia777  阅读(45)  评论(0编辑  收藏  举报