Vue2 - keep-alive 作用和原理

news2024/12/23 18:57:43

目录

  • 1,介绍和作用
  • 2,原理
  • 3,使用场景
    • 3.1,效果展示
    • 3.2,实现思路

1,介绍和作用

<!-- 非活跃的组件将会被缓存! -->
<keep-alive>
  <component :is="activeComponent" />
</keep-alive>

1,是一个内部组件,用于缓存组件实例。在组件切换时,不用重新创建而是使用缓存中的组件实例。

  • 可以避免创建组件的开销。
  • 可以保留组件状态

2,有3个属性:

  • includeexclude 属性,可以控制哪些组件进入缓存。
  • max 属性可以设置最大缓存数,当缓存的实例超过该数时,vue会移除最久没有使用的组件缓存。
<!-- 以英文逗号分隔的字符串 -->
<keep-alive include="Comp1,Comp2">
  <component :is="view" />
</keep-alive>
<!-- 数组 -->
<keep-alive :include="['Comp1', 'Comp2']">
  <component :is="view" />
</keep-alive>

3,受 keep-alive 影响,内部所有嵌套的组件都有2个生命周期函数。activateddeactivated。第1次 activated 是在 mounted 之后。

4,当嵌套组件是 <router-view> 时,缓存的是路由对应的组件。

因为 <router-view> 是函数式组件,只有一个目的,生成虚拟DOM。它没有状态,不生成实例。有穿透的效果。

<keep-alive>
  <router-view></router-view>
</keep-alive>

2,原理

源码

keep-alive 在内部维护了一个缓存对象和 keys 数组:

// keep-alive 组件的生命周期函数
created () {
  this.cache = Object.create(null)
  this.keys = []
}
  • keys 数组记录当前缓存组件的 key,如果组件没有指定,则会自动为组件生成唯一的 key

  • cache 对象以 key 为键,vnode 为值,来缓存组件对应的虚拟 DOM。

keep-alive 组件的 render 函数中,大致逻辑:

判断当前渲染的 vnode 是否有对应的缓存,有则从缓存中读取对应组件实例,没有则将其缓存。当缓存数量 > max 时,会移除掉 keys 数组的第1个元素。

// 更新逻辑其实在 update 函数中,这里结合在一起容易理解。
render(){
  const slot = this.$slots.default; // 获取默认插槽
  const vnode = getFirstComponentChild(slot); // 得到插槽中的第一个组件的 vnode
  const name = getComponentName(vnode.componentOptions); // 获取组件名字
  const { cache, keys } = this; // 获取当前的缓存对象和key数组
  const key = ...; // 获取组件的key值,若没有,会按照规则自动生成
  if (cache[key]) {
    // 有缓存
    // 关键:重用组件实例
    vnode.componentInstance = cache[key].componentInstance
    remove(keys, key); // 删除key
    // 将 key加入到数组末尾,这样是为了保证最近使用的组件在数组中靠后,反之靠前
    keys.push(key); 
  } else {
    // 无缓存,则进行缓存
    cache[key] = vnode
    keys.push(key)
    if (this.max && keys.length > parseInt(this.max)) {
      // 超过最大缓存数量,移除第一个key对应的缓存
      pruneCacheEntry(cache, keys[0], keys, this._vnode)
    }
  }
  return vnode || (slot && slot[0])
}

注意到,render() 返回的就是插槽中的第一个组件的 vnode。所以页面上显示的也就是这个子组件。

3,使用场景

在后台管理系统中比较常见,比如有2种情况就会用到:

  • 侧边栏的菜单,一般对应的是路由。切换页面(路由)时想保留之前的页面信息。
  • 列表页进详情页时,想保留列表页信息,因为列表可能是通过输入框等多个过滤条件得到的,如果从详情页返回列表页再次操作会比较繁琐。

这2种情况,都可以做成 tab 选项卡。

3.1,效果展示

在这里插入图片描述

3.2,实现思路

首先维护一个【tab选项卡】的状态,比如放到 Vuex 中。不放到组件内部,是因为不好使用,因为可能不止侧边栏会添加 tab,其他地方可能也会添加页面到 tab 选项卡。

export default new Vuex.Store({
  modules: {
    tabs: {
      namespaced: true,
      state: {
        pageNames: [] // 选项卡的页面
      },
      mutations: {
        addPage(state, newPageName) {
          if (!state.pageNames.includes(newPageName)) {
            state.pageNames.push(newPageName)
          }
        },
        removePage(state, pageName) {
          const index = state.pageNames.indexOf(pageName)
          if (index >= 0) {
            state.pageNames.splice(index, 1)
          }
        }
      }
    }
  }
})

其他的看代码就一目了然了:(省略了CSS)

<template>
  <div>
    <!-- 固定在页面左侧 -->
    <div>
      <h1>侧边栏</h1>
      <ul>
        <router-link
          v-for="item in $router.options.routes"
          :key="item.path"
          tag="li"
          :to="{ name: item.name }"
          active-class="blue"
        >
          <span>{{ item.name }}</span>
          <button @click="handleAddPage(item.name)">+</button>
        </router-link>
      </ul>
    </div>
    
    <!-- 页面内容区域 -->
    <div>
      <!-- 固定在页面顶部,左侧和侧边栏对齐 -->
      <div v-if="$store.state.tabs.pageNames.length">
        <span>tab选项卡:</span>
        <ul>
          <router-link
            v-for="pageName in $store.state.tabs.pageNames"
            :key="pageName"
            tag="li"
            active-class="green"
            :to="{ name: pageName }"
          >
            <span>{{ pageName }}</span>
            <button @click="handleRemovePage(pageName)">x</button>
          </router-link>
        </ul>
      </div>
      <!-- 页面内容展示区域 -->
      <keep-alive :include="$store.state.tabs.pageNames">
        <router-view></router-view>
      </keep-alive>
    </div>
  </div>
</template>

<script>
export default {
  methods: {
    handleAddPage(pageName) {
      this.$store.commit('tabs/addPage', pageName)
    },
    handleRemovePage(pageName) {
      this.$store.commit('tabs/removePage', pageName)
    }
  }
}
</script>

以上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1409153.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

静态web服务器实战

准备html页面&#xff0c;包含两个页面(index.html, index2.html)和一个404(404html)页面&#xff0c;目录示意&#xff1a; 1.返回固定页面 with open("website/index.html","r") as file: import socket# # 返回固定的页面 website/index.html if __na…

深度学习笔记(九)——tf模型导出保存、模型加载、常用模型导出tflite、权重量化、模型部署

文中程序以Tensorflow-2.6.0为例 部分概念包含笔者个人理解&#xff0c;如有遗漏或错误&#xff0c;欢迎评论或私信指正。 本篇博客主要是工具性介绍&#xff0c;可能由于软件版本问题导致的部分内容无法使用。 首先介绍tflite: TensorFlow Lite 是一组工具&#xff0c;可帮助开…

调优 mybatis saveBatch 25倍性能

调优 mybatis saveBatch 25倍性能 最近在压测一批接口&#xff0c;发现接口处理速度慢的有点超出预期&#xff0c;感觉很奇怪&#xff0c;后面定位发现是数据库批量保存这块很慢。 这个项目用的是 mybatis-plus&#xff0c;批量保存直接用的是 mybatis-plus 提供的 saveBatch…

Backtrader 文档学习-Order OCO orders

Backtrader 文档学习-Order OCO orders 主要是可以使用订单组的管理策略&#xff0c;使用订单组策略&#xff0c;则一组订单中&#xff0c;有一个符合条件的订单成交&#xff0c;订单组中其他的订单就自动被取消。 1.概述 V1.9.36.116 版本交互式代理支持StopTrail、StopTra…

Django笔记(六):DRF框架

首 前后端分离是互联网应用开发的标准使用方式&#xff0c;让前后端通过接口实现解耦&#xff0c;能够更好的进行开发和维护。 RESTful接口常见规范 在接口设计中&#xff0c;大家遵循一定的规范可以减少很多不必要的麻烦&#xff0c;例如url应有一定辨识度&#xff0c;可以…

Database__进阶

文章目录 &#x1f60a; 作者&#xff1a;Lion J &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_69252724?spm1000.2115.3001.5343 &#x1f389; 主题&#xff1a; 数据库mysql&#xff08;高级部分&#xff09; ⏱️ 创作时间&#xff1a;2024年01月24…

MySQL-进阶-索引-结构

一、索引概述 1、介绍 2、有误索引搜索效率演示 3、优缺点 二、索引结构 1、B-Tree&#xff08;多路平衡查找树&#xff09; 2、BTree 3、Hash

初识人工智能,一文读懂机器学习之逻辑回归知识文集(4)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

GitLab入门指南:上传与下载操作一网打尽

GitLab简介&#xff1a; GitLab是一个基于Git的开源仓库管理系统&#xff0c;提供了一个Web界面的Git存储库管理器&#xff0c;并集成了多种开发工具的功能&#xff0c;如代码审查、问题跟踪、持续集成和持续部署等。GitLab可以在本地服务器上部署&#xff0c;也可以使用其提供…

Flink入门教程

使用flink时需要提前准备好scala环境 一、创建maven项目 二、添加pom依赖 <properties><scala.version>2.11.12</scala.version></properties><dependency><groupId>org.scala-lang</groupId><artifactId>scala-library<…

【服务器Midjourney】Midjourney网站0基础搭建

目录 🌺【前言】 🌺【准备】 🌺【宝塔搭建MJ】 🌼1. 给服务器添加端口 🌼2. 使用Xshell连接服务器 🌼3. 安装docker 🌼4. 安装Midjourney程序 🌼5. 绑定域名+申请SSL证书 🌼6. 更新网站

oracle vm安装ubuntu使用桥接网络不能访问外网

1. 问题描述 公司网络环境中&#xff0c;可以ping通内网中的所有电脑&#xff0c;ping不通百度域名以及百度的ip地址在热点共享时或者家里未出现此问题 2. 尝试的解决办法 设置网络共享&#xff0c;未起作用。后来测试通以后发现共享不共享都可以通 3. 最终解决办法 H3C禁…

树莓派基础应用:智能家居监控系统

引言&#xff1a; 随着智能家居的普及&#xff0c;家居安全与监控逐渐成为人们关注的焦点。树莓派作为一种功能强大的迷你计算机&#xff0c;为我们提供了实现智能家居监控系统的可能。在本篇博客中&#xff0c;我们将通过构建一个简单的智能家居监控系统&#xff0c;来探索树莓…

NRF24L01模块传输MPU6050数据,接收端数据一直为0问题记录

问题描述&#xff1a; 一、发射端 1、正确配置NRF模块&#xff0c;以及测试过能够正常通信&#xff0c;在发射端的发射线程中进行了如下操作 2、这里是获取了陀螺仪的x轴数据&#xff0c;将其而分为两个8位的数据存入发送缓冲区中。因为一个陀螺仪x轴数据是16位的&#xff0c…

Android 水印效果

Android 水印效果 本文主要介绍下android 中水印的实现效果. 实现的方式有多种,就不一一赘述了, 本文就是通过自定义drawable来实现水印. 不多说,直接上代码吧: import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; i…

QQ云端机器人登录系统php源码

这款源码主要是针对群机器人爱好者的&#xff0c;这是一个通过对接挂机宝里面机器人框架的一个网页站点&#xff0c;用户通过网页登录 QQ 账号至挂机宝里面框架&#xff08;可扫码登录、账密登录、跳转 QQ 快捷登录&#xff09;&#xff0c;无需通过机器人即可实现登录&#xf…

C/S客户端安装卸载更新

今天这个我一直没想写&#xff0c;因为现在好像c/s客户端的安装比较少 &#xff0c;应该很多公司都没有了&#xff0c;但是erp&#xff0c;一些自己公司内部使用的可能比较多&#xff0c;但是现在都比较倾向于BS结构的了&#xff0c;浅浅的了解下C/S的安装卸载更新吧~ 1、安装 …

【算法Hot100系列】合并区间

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

matlab GUI实现PID控制器参数配置

1、内容简介 略 39-可以交流、咨询、答疑 2、内容说明 略 3、 基于GUI的PID研究 本例子中设计一个PID控制器来研究不同参数对输出结果的影响&#xff0c;PID控制器由比例单元 P、积分单元 I 和微分单元 D 组成。PID 控制器是一个在工业控制应用中常见的反馈回路部件&…

消息中间件之八股面试回答篇:一、问题概览+MQ的应用场景+RabbitMQ如何保证消息不丢失(生产者确认机制、持久化、消费者确认机制)+回答模板

问题概览 目前主流的消息队列技术&#xff08;MQ技术&#xff09;分为RabbitMQ和Kafka&#xff0c;其中深蓝色为只要是MQ&#xff0c;一般都会问到的问题。浅蓝色是针对RabbitMQ的特性的问题。蓝紫色为针对Kafka的特性的问题。 MQ的应用场景 MQ主要提供的功能为&#xff1a;异…