【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组件:

html 复制代码
<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组件:通过配置的形式 来使用生命周期钩子

html 复制代码
<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---')
  },
  // 挂载前钩子 模板编译完成,尚未挂载到 DOM
  beforeMount() {
    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函数的引用,他们都是函数数据类型,他们都可以传递一个回调函数,这个回调函数都是在钩子挂载之前执行,就是相当于你在外面写生命周期一样

javascript 复制代码
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函数

html 复制代码
<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.pageX
        point.y = e.pageY
        console.log(e.pageX, e.pageY)
      })
    }) 

    return {
      sum,
      point
    }
  },
}
</script>

生命周期顺序:

beforeCreate → created → beforeMount → mounted

mounted钩子,页面渲染完成了,Vue 组件的 DOM 元素也出现在浏览器上了,此时你可以安全地操作它们。
由于当前组件在被销毁后,但是该组件内部的挂载的各种事件却没有被销毁,还是能够发生,所以我们就要在组件被销毁前进行事件的取消效果!

beforeUnmount组件被销毁前:

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

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

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

javascript 复制代码
    function savaPoint(e) {
        point.x = e.pageX
        point.y = e.pageY
        console.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组件的功能抽离出来!做到,并且进行暴露!但是注意一定要求给返回值!

javascript 复制代码
import { reactive, onMounted, onBeforeUnmount } from 'vue'
export default function () {
  // 实现鼠标"打点"相关的数据
  let point = reactive({
    x: 0,
    y: 0
  })

  // 实现鼠标"打点"相关的方法
  function savaPoint(e) {
    point.x = e.pageX
    point.y = e.pageY
    console.log(e.pageX, e.pageY)
  }

  // 当组件挂载(即页面加载)完毕后,给 window 绑定了一个 点击事件监听器
  onMounted(() => {
    window.addEventListener('click', savaPoint)
  })

  // 在组件即将销毁之前,移除事件监听器;
  onBeforeUnmount(() => {
    window.removeEventListener('click', savaPoint)
  })

  return point
}

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

javascript 复制代码
<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++的&引用,这样就不会是属于新建的一个形参,不会对对象本体产生影响!

javascript 复制代码
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })

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

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

javascript 复制代码
<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.name
    console.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()数据呢?

javascript 复制代码
    return {
      person,
      name:ref(person.name),
      age: ref(person.age),
      salary:ref(person.job.j1.salary)
    
    }

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

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

javascript 复制代码
    const x = toRefs(person)
    console.log(x)

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

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

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

javascript 复制代码
    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)

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

相关推荐
竹林81820 小时前
用 wagmi v2 + viem 监听链上事件,我踩了三天坑终于搞懂了实时日志与历史补全
javascript
Momo__20 小时前
VueUse createReusableTemplate —— 单文件组件内的模板复用神器
前端·vue.js
只一20 小时前
😭从回调地狱到 async/await:一文打通 Ajax 与 JS 异步编程
javascript
程序员小富20 小时前
我开源了一个开发者专属的智能 JSON 工具,得到了媳妇高度认可
前端·vue.js·后端
小小小小宇20 小时前
程序员如何给 LLM 装工具以及看懂推理过程
前端
写代码的皮筏艇20 小时前
React中的forwardRef
前端·react.js·面试
槑有老呆21 小时前
花三个月工资请了个 AI 程序员,结果它连青岛啤酒股价都查不了
前端
风骏时光牛马21 小时前
Verilog开发常见问题汇总解析
前端
子兮曰21 小时前
AI Coding Method Map:一张图看懂 AI 编程的完整链路
前端·人工智能·后端