Vue中的钩子函数

前言

今天我们来聊聊Vue中的钩子函数, 例如生命周期的钩子函数,常见的 Vue.js 生命周期钩子包括 beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy 和 destroyed。

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

生命周期钩子

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

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

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

onBeforeMount

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

onMounted

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

js 复制代码
<script setup>
import { onMounted, onBeforeMount } from 'vue';

console.log('hello')

onMounted(() => {
  console.log('onMounted')
})

onBeforeMount(() => {
    console.log('onBeforeMount')
})
</script>

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

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

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

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

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

js 复制代码
<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。

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

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

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

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

onUnMounted

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

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

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

onBeforeUnmount

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

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

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

onUpdated()

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

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

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

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

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

onBeforeUpdated

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

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

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

用法跟onUpdated一样。

路由的钩子函数

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

全局守卫

全局前置守卫 router.beforeEach

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

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

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

next表示跳转

js 复制代码
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。

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

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

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

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

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

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

js 复制代码
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()进行一个跳转,并弹出一个警告。

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

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

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

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

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

全局解析守卫 router.beforeResolve

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

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

全局后置钩子 router.afterEach

它只有两个参数, to和from

js 复制代码
router.afterEach((to, from) => {
  console.log(to, from)
})

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

同样to和from表示去哪和从哪来。

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

独享守卫

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

独享守卫是什么意思?

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

beforeEnter

js 复制代码
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:

从about跳去/:

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

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

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

组件内的守卫

onBeforeRouteLeave

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

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

js 复制代码
<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组件中,当我们跳转到/时,该钩子触发:

onBeforeUpadated

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

keep-alive

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

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

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

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

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

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

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

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

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

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

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

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

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

onActivated

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

onDeactivated

当离开缓存的组件时触发

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

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

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

js 复制代码
<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组件生效了

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

最后

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

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

相关推荐
wangruofeng40 分钟前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
怕浪猫42 分钟前
这个十几个agent 协作项目值得关注
人工智能·面试·github
wangruofeng43 分钟前
给前端开发者的 100 个设计参考网站
前端·设计
星栈4 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技4 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户47949283569154 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马7 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng7 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构
清晨Coding8 小时前
为什么用 React Developer Tools 查看组件树这么乱?
前端·react.js
鶴哥只手遮天8 小时前
《Electron 45.2.3 内核解剖》第 4 章 补丁体系:380 个补丁改了什么
前端·electron