Vue中的钩子函数

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Vue中的钩子函数

前言

今天我们来聊聊Vue中的钩子函数, 例如生命周期的钩子函数,常见的 Vue.js 生命周期钩子包括 beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroydestroyed

再是路由的钩子函数以及keep-alive

生命周期钩子

所谓生命周期就是让函数在页面加载的过程中去自动执行,而这些生命周期钩子函数都是去接受一个回调函数。

那么我们就可以将函数放在这些生命周期钩子函数中,去控制它们什么时候执行。

首先,我们在template中写入一段代码时,那么Vue就会先去编译这段template,编译完之后经过一系列操作然后进行一个挂载mount,然后生命周期就会去执行。

onBeforeMount

该钩子函数在组件挂载之前被调用,注意: 这个钩子在服务器端渲染期间不会被调用。

onMounted

该钩子函数在组件挂载完成后被调用,同样,这个钩子在服务器端渲染期间不会被调用。

<script setup>
import { onMounted, onBeforeMount } from 'vue';
console.log('hello')
onMounted(() => {
  console.log('onMounted')
})
onBeforeMount(() => {
    console.log('onBeforeMount')
})
</script>

这段代码的打印顺序是怎么样的呢?

image.png

首先,先执行的是全局的console.log,这个执行是在编译之后执行的,全局函数最优先执行,所以打印hello

当模板template完之后,进行挂载,那么再挂载之前,执行onBeforeMount,所以打印onBeforeMount

再挂载时,执行onMounted,所以,执行onMounted

如果我们想要获得拿到一个DOM结构,那么在哪个生命周期可以拿到呢?

<template>
  <div ref="el">
    <p>hello</p>
  </div>
</template>
<script setup>
import { onMounted, onBeforeMount, ref } from 'vue';
const el = ref(null);
console.log('hello')
console.log(el.value);
onMounted(() => {
  console.log('onMounted')
  console.log(el.value);
})
onBeforeMount(() => {
  console.log('onBeforeMount')
  console.log(el.value);
})
</script>
<style lang="scss" scoped></style>

这里我们通过ref去获得一个标签的DOM结构,并且在全局和两个生命周期中都去获取DOM结构el

image.png

我们发现,只能在挂载完成onMounted中去获取一个元素的DOM结构。

只有挂载了之后,我们才能去获取元素的DOM结构,而在全局中和在组件挂载之前我们是并不能去获取DOM结构的。

再拓展一��:如果我们向后端发送一个网络请求,那么这段请求代码放在哪里执行比较合适呢?

其实以上三个地方放哪里都可以,但是我们一般会放在onMounted当中,因为请求到的数据可能会对DOM结构进行操作。

onUnMounted

该钩子函数在页面卸载时的时候执行,也就是离开页面的时候。

可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接。

这个钩子在服务器端渲染期间不会被调用。

onBeforeUnmount

该钩子函数在页面卸载之前执行。

当这个钩子被调用时,组件实例依然还保有全部的功能。

这个钩子在服务器端渲染期间不会被调用。

onUpdated()

该钩子函数在组件因为响应式状态变更而更新其 DOM 树之后调用。

这个钩子会在组件的任意 DOM 更新后被调用,这些更新可能是由不同的状态变更导致的,因为多个状态变更可以在同一个渲染周期中批量执行 (考虑到性能因素)。如果你需要在某个特定的状态更改后访问更新后的 DOM,请使用 nextTick() 作为替代。

<template>
  <div>
    <p @click="change">{{ count }}</p>
  </div>
</template>
<script setup>
import { ref, onUpdated } from 'vue';
const count = ref(0);
onUpdated(() => {
  console.log('onUpdated');
});
const change = () => {
  count.value++;
}
</script>
<style lang="scss" scoped></style>

通过refcount变成响应式数据,点击p标签时,count值+1。

onUpdated在响应式状态变更时执行,所以,我们每次将count值增加,都会打印一遍onUpdated

image.png

onBeforeUpdated

该钩子函数在组件即将因为响应式状态变更而更新其 DOM 树之前调用。

这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。在这个钩子中更改状态也是安全的。

这个钩子在服务器端渲染期间不会被调用。

用法跟onUpdated一样。

路由的钩子函数

路由的钩子函数一般写入路由配置文件router/index.js中,我们也可以写入main.js中进行配置。

全局守卫

全局前置守卫 router.beforeEach

该钩子函数接受三个参数to, from, next

to表示我们想要跳转到的页面

from表示我们从哪个页面跳转过来

next表示跳转

import { createRouter, createWebHistory } from 'vue-router'
const routes = [
  {
    path: '/',
    name: 'home',
    component: () => import('../views/Home.vue'),
    meta: {
      title: '商城首页'
    }
  },
  {
    path: '/about',
    name: 'about',
    component: () => import('../views/About.vue'),
    meta: {
      title: '关于我们'
    }
  }
]
const router = createRouter({
  history: createWebHistory(),
  routes
})
// 全局的前置钩子
router.beforeEach((to, from, next) => {
  console.log(to, from);
  next()
})

这里我们先去创建一份路由, 有两个页面, 一个是/, 一个是/about

<template>
  <div>
    <router-link to="/">首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-view />
  </div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>

image.png

这就是我们的页面,当我们点击关于跳到/about页面时:

image.png

第一行打印to,也就是我们想要去到的地方/about

第二行打印from,也就是我们来的地方/

当用户想要去其他页面时,我们可以使用这个钩子函数去判断用户有没有登录,如果没有登录就送他去登录页面。

并且我们还可以使用此钩子去给页面设置一个标题。

router.beforeEach((to, from, next) => {
  // console.log(to, from);
  document.title = to.meta.title
  if (to.path !== '/') {
    const isLogin = localStorage.getItem('isLogin')
    if (isLogin) {
      next()
    } else {
      // router.push('/')
      alert('请先登录')
      return
    }
  }
  next()
})

这里我们通过一个简单的办法来模拟一下:

如果用户登录了,我们就在本地存储localStorage当中存入用户信息。

先使用to.path去判断用户是不是想去非登录页面,如果是的话,就去检查localStorage当中是否有用户信息。如果没有的话,就不使用next()进行一个跳转,并弹出一个警告。

image.png

如图,注意看url地址栏,我们这里点击关于页面,那么它本该会跳去/about

但是通过路由守卫判断出我们并没有登录,所以弹出警告,跳转失败。

我们看左上角,发现多了一个标题关于我们,这是因为我们在路由配置中加入了meta,去设置一个路由的详细信息。

然后通过document.title = to.meta.title,这样就实现了一个标题。

但是这样去判断一个用户是否登录是不严谨的,用户可以手动的去localStorage中设置一个isLogintrue的字段。

全局解析守卫 router.beforeResolve

解析钩子和全局前置区别不大,解析钩子是在路由被解析,代码被编译之前触发。

我们同样可以使用该钩子去处理上面的事情。

全局后置钩子  router.afterEach

它只有两个参数, tofrom

router.afterEach((to, from) => {
  console.log(to, from)
})

image.png

该方法表示路由跳转后去干什么事情,它没有next参数

同样tofrom表示去哪和从哪来。

上面我们所介绍的三个全局钩子函数, 它们是全局的,我们只要用了这三个钩子中的任意一个,那么它将会影响项目中所有路由的跳转。

独享守卫

接下来我们来看看独享守卫。

独享守卫是什么意思?

在有些场景,我们希望跳去某些页面时,钩子触发,而跳去别的页面时,钩子不触发。

beforeEnter

import { createRouter, createWebHistory } from 'vue-router'
const routes = [
  {
    path: '/',
    name: 'home',
    component: () => import('../views/Home.vue'),
    meta: {
      title: '商城首页'
    }
  },
  {
    path: '/about',
    name: 'about',
    component: () => import('../views/About.vue'),
    meta: {
      title: '关于我们'
    },
    beforeEnter: (to, from, next) => {  // 单独的路由守卫
      console.log(to, from);
      next()
    }
  }
]
const router = createRouter({
  history: createWebHistory(),
  routes
})

比如,我们只想跳到关于页面去触发钩子函数,那么我们只需要在/about的路由配置中加入beforeEnter

同样,to, from, next的作用都是与全局守卫钩子相同。

/跳去/about

image.png

about跳去/:

image.png

可以看出,是没有触发钩子函数的。

我们还可以想象一个场景:

假设一个项目有一百个页面,只有一个页面才需要登录后才能查看,那么我们只需要给那个页面单独配置一个独享守卫钩子, 去进行一个判断是否登录的逻辑。

组件内的守卫

onBeforeRouteLeave

它可以用来在我们离开时做的一个操作。

比如我们常见的: 当我们跳转一个页面时,浏览器弹出一个: 你确定要离开该页面吗?

<template>
    <div>
        About
    </div>
</template>
<script setup>
import { onBeforeRouteLeave } from 'vue-router';
onBeforeRouteLeave((to, from, next) => {
    console.log(to, from);
    const flag = window.confirm('你确定要离开这个页面吗?')
    if (flag) {
       next()
    }
});
</script>
<style lang="scss" scoped>
</style>

我们将该钩子写在about组件中,当我们跳转到/时,该钩子触发:

image.png

onBeforeUpadated

当我们两个页面共同用到了一个组件时,当它们进行相互跳转时,那么该钩子函数就会触发。

keep-alive

在我们去开发一个Vue项目中的时候,若是两个页面共用到了一个组件,当跳转页面时,这个共用的组件也是会进行再次渲染的。因为跳转路由的时候页面内所有组件都是会重新销毁并加载的。

但是很多组件是没有必要再次渲染的,这样可以减少性能开销。

比如每个页面都有广告,且广告的内容是一样的,如果每次去重新加载该广告组件也是没有必要的。我们为了减少一个性能开销,就可以使用`keep-alive'

keep-alive 的作用就是包裹一个需要去缓存的组件。

首先我们有两个页面,一个首页页面/,一个关于页面/about,首页中我们加入onMounted去检测是否重新加载了这个组件。

我们在App.vue中使用keep-alive去缓存首页页面:

<template>
  <div>
    <nav>
      <router-link to="/">首页 </router-link>
      <router-link to="/about">关于</router-link>
    </nav>
    <router-view v-slot="{ Component }">
      <KeepAlive :include="['Home']">
        <component :is="Component" />
      </KeepAlive>
    </router-view>
  </div>
</template>
<script setup>
</script>
<style lang="scss" scoped></style>

首先,我们进入到首页页面:

image.png

可以看到是执行了onMounted,因为组件是初次加载,加载完之后才会缓存。

然后我们再跳转到关于页面。

然后我们从/about页面跳到首页/时,再看打印:

image.png

发现我们成功缓存住了首页组件,onMounted没有触发。

Vue中与keep-alive搭配使用的生命周期钩子

onActivated

当被缓存的组件生效时触发

onDeactivated

当离开缓存的组件时触发

这两个钩子一定根缓存有关, 所以一定要跟keep-alive一起使用

同样,我们利用上面的代码来举例:

我们在首页Home组件中加入这两个钩子函数

<template>
    <div>
        <p>home page</p>
    </div>
</template>
<script setup>
import { onActivated, onDeactivated, onMounted } from 'vue';
onMounted(() => {
    console.log('首页页面的onMounted');
})
onActivated(() => {  // 当被缓存的组件生效时触发
    console.log('首页页面的onActivated');
})
onDeactivated(() => {  // 当被缓存的组件离开时触发
    console.log('首页页面的onDeactivated');
})
</script>#
<style lang="scss" scoped>
</style>

当我们进入首页页面, onActivated触发,因为被缓存的Home组件生效了

image.png

当我们进入关于页面, onDeactivated触发,因为我们离开了缓存的组件Home

image.png

最后

若是在面试的时候,面试官有提到生命周期,那么提到两个跟缓存有关的钩子是十分加分的,因为往往我们在学习前端的过程中容易去忽视掉这两个钩子函数。

写文章不易,如果帮助到了小伙伴们,可以给本文点赞收藏评论三连呀。有不懂的地方欢迎到评论区留言,我会及时回复。

相关文章
|
12天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
28 1
vue学习第四章
|
12天前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
25 1
vue学习第九章(v-model)
|
12天前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
27 1
vue学习第十章(组件开发)
|
18天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
18天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
18天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
18天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
17天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉
26 3
|
19天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
17天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。自学前端2年半,熟悉JavaScript与Vue,正向全栈方向发展。博客内容涵盖Vue基础、列表展示及计数器案例等,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
36 2