当前位置: 首页 > news >正文

【Vue3】生命周期 hook函数 toRef

目录

一、vue2生命周期钩子

二、Vue3组合式API

三、自定义hook函数

生命周期顺序:

小demo

四、toRef

总结不易~本章节对我有很大的收获,希望对你也是!!!


本章节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.comhttps://gitee.com/liu-yihao-hhh/i-love---vue/tree/master/11_src3_%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F

一、vue2生命周期钩子

我们先来回顾一下各个生命周期钩子:

App组件:

<template><button @click="isShowDemo = !isShowDemo">切换隐藏/显示</button><Demo v-if="isShowDemo" />
</template><script>
import Demo from './components/Demo'
import {ref} from 'vue'
export default {name: 'App',components: {Demo},setup() {let isShowDemo = ref(true)return {isShowDemo}}
}
</script>

Demo组件:通过配置的形式 来使用生命周期钩子

<template><h2>当前求和为:{{ sum }}</h2><button @click="sum++">点我 + 1</button>
</template><script>
import {ref} from 'vue'
export default {name: 'DemoBox',setup() {let sum = ref(0)console.log('---setup---')return {sum,}},// 通过配置的形式 使用生命周期钩子// 创建前钩子 实例刚被创建,data 和 methods 都还未初始化beforeCreate() {console.log('---beforeCreate---')},// 创建后钩子 实例创建完成,data、methods 已经可以使用created() {console.log('---created---')},// 挂载前钩子 模板编译完成,尚未挂载到 DOMbeforeMount() {console.log('---beforeMount---')},// 挂载后钩子 DOM 挂载完成,页面已渲染mounted() {console.log('---mounted---')},// 	更新前钩子 数据更新时调用,DOM 还未重新渲染beforeUpdate() {console.log('---beforeUpdate---')},// 	更新后钩子 数据更改并 DOM 更新完成后调用updated() {console.log('---updated---')},// 卸载前钩子 组件即将被卸载前调用(Vue 3 中新增)beforeUnmount() {console.log('---beforeUnmount---')},// 卸载后钩子 组件卸载完成后调用(Vue 3 中新增)unmounted() {console.log('---unmounted---')},
}
</script>

可以 看到setup是在所有生命周期钩子中最先执行的,也就是在程序启动的时候,setup跟beforeCreate生命周期钩子是平级的,并且比beforeCreate生命周期钩子先执行

因为Demo组件是靠v-if来显示就是进行组件的删除和创建,可以看到组件的删除是考beforeUnmount 和 unmounted来进行操控

Vue3从Vue2演变过来,生命周期也得到了改变, Vue3为我们提供了组合式API的形式,可以写到setup里面,但是这里要注意,beforeCreate 和 created并没有为我们提供组合式API的形式

也就是说setup就相当于beforeCreate 和 created两个生命周期钩子了

本章节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.comhttps://gitee.com/liu-yihao-hhh/i-love---vue/tree/master/11_src3_%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F

二、Vue3组合式API

就是把每一个生命周期钩子都加上一个on来进行vue函数的引用,他们都是函数数据类型,他们都可以传递一个回调函数,这个回调函数都是在钩子挂载之前执行,就是相当于你在外面写生命周期一样

import {ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted} from 'vue'
export default {name: 'DemoBox',setup() {let sum = ref(0)console.log('---setup---')// 通过组合式API形式去使用生命周期钩子onBeforeMount(() => {console.log('---onBeforeMount---')})onMounted(() => {console.log('---onMounted---')})onBeforeUpdate(() => {console.log('---onBeforeUpdate---')})onUpdated(() => {console.log('---onUpdated---')})onBeforeUnmount(() => {console.log('---onBeforeUnmount---')})onUnmounted(() => {console.log('---onUnmounted---')})return {sum,}},

如果你同时写了setup外面 和 里面两种生命周期钩子,那么on-生命周期钩子的优先级会更高一点

本章节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.comhttps://gitee.com/liu-yihao-hhh/i-love---vue/tree/master/12_src3_%E8%87%AA%E5%AE%9A%E4%B9%89hook

三、自定义hook函数

<template><h2>当前求和为:{{ sum }}</h2><button @click="sum++">点我 + 1</button><hr><h2>当前点击时鼠标的坐标为:x:{{ point.x }},y:{{ point.y }}</h2>
</template><script>
import {ref, reactive, onMounted} from 'vue'
export default {name: 'DemoBox',setup() {let sum = ref(0)let point = reactive({x:0,y:0})onMounted(() => {window.addEventListener('click', (e) => {point.x = e.pageXpoint.y = e.pageYconsole.log(e.pageX, e.pageY)})}) return {sum,point}},
}
</script>

生命周期顺序:

beforeCreate → created → beforeMount → mounted
mounted钩子,页面渲染完成了,Vue 组件的 DOM 元素也出现在浏览器上了,此时你可以安全地操作它们。

由于当前组件在被销毁后,但是该组件内部的挂载的各种事件却没有被销毁,还是能够发生,所以我们就要在组件被销毁前进行事件的取消效果!

beforeUnmount组件被销毁前:

  • mounted:演员 上台 了,可以开始表演;

  • beforeUnmount:演员 快下台 了,要把台词稿、话筒、灯光等东西收拾好。

由于这是两个新创建的函数,所以不可取分别在创建和销毁上,所以要单独分离出来同一个函数来进行创建和销毁该事件!

    function savaPoint(e) {point.x = e.pageXpoint.y = e.pageYconsole.log(e.pageX, e.pageY)}// 当组件挂载(即页面加载)完毕后,给 window 绑定了一个 点击事件监听器onMounted(() => {window.addEventListener('click', savaPoint)}) // 在组件即将销毁之前,移除事件监听器;onBeforeUnmount(() => {window.removeEventListener('click', savaPoint)})

但是有没有想过,我们是否能把这个强大的功能给分离出来呢!既然我们能够用,就业想让别人也能够复用这个功能,那么就要引入hook函数了!本质是一个函数,把setup函数中使用的组合式API进行了封装。

我们在src下创建一个hooks文件夹!然后创建该功能的名字一般叫use功能名!

usePoint.js:将demo组件的功能抽离出来!做到,并且进行暴露!但是注意一定要求给返回值!

import { reactive, onMounted, onBeforeUnmount } from 'vue'
export default function () {// 实现鼠标“打点”相关的数据let point = reactive({x: 0,y: 0})// 实现鼠标“打点”相关的方法function savaPoint(e) {point.x = e.pageXpoint.y = e.pageYconsole.log(e.pageX, e.pageY)}// 当组件挂载(即页面加载)完毕后,给 window 绑定了一个 点击事件监听器onMounted(() => {window.addEventListener('click', savaPoint)})// 在组件即将销毁之前,移除事件监听器;onBeforeUnmount(() => {window.removeEventListener('click', savaPoint)})return point
}

Demo组件:来进行函数引入和接受就行!

<template><h2>当前求和为:{{ sum }}</h2><button @click="sum++">点我 + 1</button><hr><h2>当前点击时鼠标的坐标为:x:{{ point.x }},y:{{ point.y }}</h2>
</template><script>
import {ref,} from 'vue'
import usePoint from '../hooks/usePoint'
export default {name: 'DemoBox',setup() {let sum = ref(0)let point = usePoint()return {sum,point}},
}
</script>

小demo

  • 进行函数方法暴露的时候,如果暴露的是已经取好名字的,就需要用别人取好名字的方法{ref,reactive}
  • 但是直接暴露我们自定义的hook函数,就是没有取名字的,我们就可以当场取名字,不需要加{}
  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。

  • 类似于vue2.x中的mixin。

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

本章节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.comhttps://gitee.com/liu-yihao-hhh/i-love---vue/tree/master/13_src3_toRef%E4%B8%8EtoRefs

四、toRef

toRef(想获取的对象, ‘对象中的某个属性值’)

toRef就是定义一个新的变量,来充当对象里面某个属性的本体,就是c++的&引用,这样就不会是属于新建的一个形参,不会对对象本体产生影响!

    let person = reactive({name: '张三',age: 18,job: {j1: {salary: 20}}})// 这个name1 他是字符型 一个新建的常量型const name1 = person.nameconsole.log(name1)// 第一个参数:想取哪个对象// 第二个参数:对象里面的哪个属性名// 此时的name2 就是& 引用类型!const name2 = toRef(person, 'name')console.log('###', name2)

这样就可以返回我们只需要用到的对象,来在模板进行简化写法

<template><h2>姓名:{{ name }}</h2><h2>年龄:{{ age }}</h2><h2>薪资:{{ salary }}k</h2><button @click="name+='~'">修改姓名</button><button @click="age++">增长年龄</button><button @click="salary++">长薪</button>
</template><script>
import {reactive, toRef} from 'vue'
export default {name: 'DemoBox',setup() {let person = reactive({name: '张三',age: 18,job: {j1: {salary: 20}}})// 这个name1 他是字符型 一个新建的常量型const name1 = person.nameconsole.log(name1)// 第一个参数:想取哪个对象// 第二个参数:对象里面的哪个属性名// 此时的name2 就是& 引用类型!const name2 = toRef(person, 'name')console.log('###', name2)return {name:toRef(person, 'name'),age: toRef(person, 'age'),salary:toRef(person.job.j1, 'salary')}}
}
</script>

那么问题就来了,既然toRef目的就是要做到响应式,那么为什么不直接返回ref()数据呢?

    return {person,name:ref(person.name),age: ref(person.age),salary:ref(person.job.j1.salary)}

可以看到,虽然我们同样能够做到响应式的数据变化但是,原本对象里面的值却没有被改变!说明这只是创建了一个新的对象来进行返回进行响应式的!所以这里要对原本的对象进行改变还是需要用到toRef()

总结:toRef()就是引用对象,ref()就是复制对象

那么问题又来了,通过toRef()我们只是一个对象属性的引用来进行,如果有一百个,我们不可能单独写一百个toRef来进行引用对象属性吧!所以这里就引入toRefs()

    const x = toRefs(person)console.log(x)

可以看到x就是通过Proxy进行操控的数据,也就是person对象本身,x就也是一个对象!

那么我们在进行返回的时候,对象里面是不能再次嵌套一个对象的!也就是通过es6语法...toRefs(person)来讲对象里面的内容进行展开,来平铺到该return语句的对象中!

但是toRefs却只能取到对象里面的每个第一层,所以访问后面的深层次对象还是需要手动进行遍历,但是访问第一层就已经很方便了!

    return {...toRefs(person)}<h2>姓名:{{ name }}</h2><h2>年龄:{{ age }}</h2><h2>薪资:{{ job.j1.salary }}k</h2><button @click="name+='~'">修改姓名</button><button @click="age++">增长年龄</button><button @click="job.j1.salary++">长薪</button>

小结:

  • 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。

  • 语法:const name = toRef(person,'name')

  • 应用: 要将响应式对象中的某个属性单独提供给外部使用时。

  • 扩展:toRefstoRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)

总结不易~本章节对我有很大的收获,希望对你也是!!!

相关文章:

  • Foupk3systemX5OSNTXPro引擎
  • 什么是MCP技术,跟http技术有什么区别
  • SpringBoot集成Tika实现文档解析
  • 互联网医疗问诊APP原型设计:12个实战案例解析
  • 计算机组成与体系结构:硬盘驱动器(Hard Disk Drives)
  • 有趣的游戏化单词学习 APP
  • 【FAQ】spring boot 3 集成 nacos
  • MyBatis-Plus一站式增强组件MyBatis-Plus-kit(更新2.0版本):零Controller也能生成API?
  • 数据库MySQL学习——day13(索引与查询优化)
  • 深度解读 Qwen3 大语言模型的关键技术
  • 2025年上半年软考系统架构设计师--案例分析试题与答案
  • 使用Auto-Coder对js文件进行审计并修复漏洞1.3 1.4 1.5版本
  • 组合API-provide和inject函数
  • 颠覆传统,智领未来——UMI企业智脑:重新定义企业智能化转型的全新可能
  • SIGGRAPH 2025 | 快手可灵团队提出3D感知的电影级文本到视频生成框架CineMaster
  • 视频监控联网系统GB28181协议中历史视音频的回放流程详解以及查询失败常见原因
  • 测试 Gemini Pro 2.5
  • 电机控制杂谈(26)——电机驱动系统的编码器的测速噪声
  • 迪米特法则 (Law of Demeter, LoD)
  • ISP图像处理算法之Demosaic
  • 企业网站广告图片轮播代码/app怎么推广
  • 静态网站提交表单怎么做/东莞营销推广公司
  • 免费学校网站模板html/软文写作经验是什么
  • 网站构建/四川seo多少钱
  • 做外贸 建网站要注意什么/百度一键优化
  • 网站换服务器对排名有影响吗/免费的网站域名查询