iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Vue keep-alive的实现原理分析
  • 161
分享到

Vue keep-alive的实现原理分析

2024-04-02 19:04:59 161人浏览 薄情痞子
摘要

目录keep-alive的实现原理这里以vue3为例大致流程如下keep-alive生命周期keep-alive的使用总结1.App.Vue中使用keep-alive2.App.vu

keep-alive的实现原理

使用vue的时候,想必大家都是用过keep-alive,其作用就是缓存页面以及其状态。使用了这么久vue只知道如何使用但不明白其中原理,昨天翻看实现代码,这里做个笔记

这里以vue3为例

整个组件的源码为:

const KeepAliveImpl = {
  name: `KeepAlive`,
 
  // Marker for special handling inside the renderer. We are not using a ===
  // check directly on KeepAlive in the renderer, because importing it directly
  // would prevent it from being tree-shaken.
  __isKeepAlive: true,
 
  props: {
    include: [String, RegExp, Array],
    exclude: [String, RegExp, Array],
    max: [String, Number]
  },
 
  setup(props: KeepAliveProps, { slots }: SetupContext) {
    const cache: Cache = new Map()
    const keys: Keys = new Set()
    let current: Vnode | null = null
 
    const instance = getCurrentInstance()!
    // console.log('instance',instance)
    // KeepAlive communicates with the instantiated renderer via the "sink"
    // where the renderer passes in platfORM-specific functions, and the
    // KeepAlive instance exposes activate/deactivate implementations.
    // The whole point of this is to avoid importing KeepAlive directly in the
    // renderer to facilitate tree-shaking.
    const sink = instance.sink as KeepAliveSink
    const {
      renderer: {
        move,
        unmount: _unmount,
        options: { createElement }
      },
      parentSuspense
    } = sink
    const storageContainer = createElement('div')
    // console.log('sink',sink)
    sink.activate = (vnode, container, anchor) => {
      move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
      queuePostRenderEffect(() => {
        const component = vnode.component!
        component.isDeactivated = false
        if (component.a !== null) {
          invokeHooks(component.a)
        }
      }, parentSuspense)
    }
 
    sink.deactivate = (vnode: VNode) => {
      move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
      queuePostRenderEffect(() => {
        const component = vnode.component!
        if (component.da !== null) {
          invokeHooks(component.da)
        }
        component.isDeactivated = true
      }, parentSuspense)
    }
 
    function unmount(vnode: VNode) {
      // reset the shapeFlag so it can be properly unmounted
      vnode.shapeFlag = ShapeFlags.STATEFUL_COMPONENT
      _unmount(vnode, instance, parentSuspense)
    }
 
    function pruneCache(filter?: (name: string) => boolean) {
      cache.forEach((vnode, key) => {
        const name = getName(vnode.type as Component)
        if (name && (!filter || !filter(name))) {
          pruneCacheEntry(key)
        }
      })
    }
 
    function pruneCacheEntry(key: CacheKey) {
      const cached = cache.get(key) as VNode
      if (!current || cached.type !== current.type) {
        unmount(cached)
      } else if (current) {
        // current active instance should no longer be kept-alive.
        // we can't unmount it now but it might be later, so reset its flag now.
        current.shapeFlag = ShapeFlags.STATEFUL_COMPONENT
      }
      cache.delete(key)
      keys.delete(key)
    }
 
    watch(
      () => [props.include, props.exclude],
      ([include, exclude]) => {
        include && pruneCache(name => matches(include, name))
        exclude && pruneCache(name => matches(exclude, name))
      },
      { lazy: true }
    )
 
    onBeforeUnmount(() => {
      cache.forEach(unmount)
    })
 
    return () => {
      if (!slots.default) {
        return null
      }
 
      const children = slots.default()
      let vnode = children[0]
      if (children.length > 1) {
        if (__DEV__) {
          warn(`KeepAlive should contain exactly one component child.`)
        }
        current = null
        return children
      } else if (
        !isVNode(vnode) ||
        !(vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT)
      ) {
        current = null
        return vnode
      }
 
      const comp = vnode.type as Component
      const name = getName(comp)
      const { include, exclude, max } = props
 
      if (
        (include && (!name || !matches(include, name))) ||
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }
 
      const key = vnode.key == null ? comp : vnode.key
      const cached = cache.get(key)
 
      // clone vnode if it's reused because we are Going to mutate it
      if (vnode.el) {
        vnode = cloneVNode(vnode)
      }
      cache.set(key, vnode)
      if (cached) {
        // copy over mounted state
        vnode.el = cached.el
        vnode.anchor = cached.anchor
        vnode.component = cached.component
        if (vnode.transition) {
          // recursively update transition hooks on subTree
          setTransitionHooks(vnode, vnode.transition!)
        }
        // avoid vnode being mounted as fresh
        vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE
        // make this key the freshest
        keys.delete(key)
        keys.add(key) 
      } else {
        keys.add(key)
        // prune oldest entry
        if (max && keys.size > parseInt(max as string, 10)) { 
          pruneCacheEntry(Array.from(keys)[0])
        }
      }
      // avoid vnode being unmounted
      vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
      current = vnode
      return vnode
    }
  }
}

很容易看出keep-alive其实就是vue自己封装的一个组件,和普通组件一样。

再讲keep-alive组件前先了解下vue组件的整个渲染

大致流程如下

keep-alive生命周期

组件挂载:

调用setupStatefulComponent函数触发组件setup方法,其中组件的setup方法核心代码其实就几行:

return () => {
    const children = slots.default()
    let vnode = children[0]
    cache.set(key, vnode)
 
    if (cached) {
      vnode.el = cached.el
      vnode.anchor = cached.anchor
      vnode.component = cached.component
      vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE
      keys.delete(key)
      keys.add(key) 
    } else {
      keys.add(key)
    }
    return vnode
}

主要逻辑为三:

1.确认需要渲染的slot、

2.将其状态置入缓存或读取已存在的缓存、

3.返回slot对应的vnode,紧接着调用setupRenderEffect,渲染出dom。

组件更新(slot变化):

当slot变化后,首先会调用keep-alive组件的render即setup的返回函数,逻辑见上面setup方法。紧接着当某个slot卸载时,会调用deactivate函数,当某个slot重新挂载时,则会调用activate函数,核心代码如下:

const storageContainer = createElement('div')
sink.activate = (vnode, container, anchor) => {
      move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
      queuePostRenderEffect(() => {
        const component = vnode.component!
        component.isDeactivated = false
        if (component.a !== null) {
          invokeHooks(component.a)
        }
      }, parentSuspense)
    }
 
    sink.deactivate = (vnode: VNode) => {
      move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
      queuePostRenderEffect(() => {
        const component = vnode.component!
        if (component.da !== null) {
          invokeHooks(component.da)
        }
        component.isDeactivated = true
      }, parentSuspense)
    }

逻辑也很简单,当组件卸载时,将其移入缓存的dom节点中,调用slot的deactivate生命周期,当组件重新挂载时候,将其移入至挂载的dom节点中。

总结来说,keep-alive实现原理就是将对应的状态放入一个cache对象中,对应的dom节点放入缓存dom中,当下次再次需要渲染时,从对象中获取状态,从缓存dom中移出至挂载dom节点中。

keep-alive的使用总结

在平常开发中,有些组件只需要加载一次,后面的数据将不存在变化,亦或者是组件需要缓存状态,滚动条位置等,这个时候,keep-alive的用处就立刻凸显出来了。

1.App.vue中使用keep-alive

include表示需要缓存的页面,exclude表示不需要缓存的页面,你可以只设置其中一个即可,但两个同时设置的时候,切记exclude优先级高于include,例如a组件在exclude中和include中都存在,那么,a组件是不会被缓存的

<template>
    <div id="app">
      <keep-alive :include="whiteList" :exclude="blackList">
        <router-view  v-if="isRouterAlive" ></router-view>
      </keep-alive>
    </div>
</template>
<script>
export default {
    name: 'App',
    data(){
      return{
          isRouterAlive:true,
          whiteList:['styleLibrary','OrderList','SalesData'],
          blackList:['Footer'],
          personShow:false,
      }
    },
}
</script>

2.App.vue中配合router进行使用

<template>
    <div id="app">
  <keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>    <!--缓存组件-->
  </keep-alive>
  <router-view v-if="!$route.meta.keepAlive"></router-view>      <!--非缓存组件-->
    </div>
</template>

将需要缓存的组件的$route.meta中的keepAlive设置为true,反之为false

 {
      path:'/login',
      name:'login',
      component:resolve=>require(['@/pages/login'],resolve),
      meta:{
        keepAlive:true,
        title:'登录',
        savedPosition:true,
      }
    },

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。 

--结束END--

本文标题: Vue keep-alive的实现原理分析

本文链接: https://www.lsjlt.com/news/146174.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Vue keep-alive的实现原理分析
    目录keep-alive的实现原理这里以vue3为例大致流程如下keep-alive生命周期keep-alive的使用总结1.App.vue中使用keep-alive2.App.vu...
    99+
    2024-04-02
  • keep-alive原理的示例分析
    这篇文章将为大家详细讲解有关keep-alive原理的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、前言本文介绍的内容包括:keep-alive用法:动态组件...
    99+
    2024-04-02
  • Vue keep-alive的实现原理是什么
    这篇“Vue keep-alive的实现原理是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Vue ...
    99+
    2023-06-30
  • Vue中keep-alive的示例分析
    这篇文章主要为大家展示了“Vue中keep-alive的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Vue中keep-alive的示例分析”这篇文章吧...
    99+
    2024-04-02
  • Vue中对iframe实现keep alive无刷新的示例分析
    这篇文章主要介绍Vue中对iframe实现keep alive无刷新的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Vue的keep-alive原理要实现对保持iframe页...
    99+
    2024-04-02
  • 五分钟理解keep alive用法及原理
    目录引言Home.vueAbout.vueApp.vue结合 Router,缓存页面router.jsHome.vueApp.vuekeep-alive 原理引言 keep-aliv...
    99+
    2024-04-02
  • 聊聊对Vue中的keep-alive的理解
    什么是 keep-alive 在平常开发中,有部分组件没有必要多次初始化,这时,我们需要将组件进行持久化,使组件的状态维持不变,在下一次展示时,也不会进行重新初始化组件。 也就是说,...
    99+
    2022-11-16
    Vue keep-alive Vue中的keep-alive Vue keep-alive使用
  • vue中keep-alive如何实现列表页缓存
    小编给大家分享一下vue中keep-alive如何实现列表页缓存,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!需求:商品列表页面...
    99+
    2024-04-02
  • Vue中如何对iframe实现keep alive无刷新
    今天小编给大家分享一下Vue中如何对iframe实现keep alive无刷新的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。...
    99+
    2023-07-04
  • 聊聊对vue内置组件keep-alive的理解
    Keep-alive 是什么?下面本篇文章带大家聊聊对vue内置组件keep-alive的理解,希望对大家有所帮助!一、Keep-alive 是什么keep-alive是vue中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染...
    99+
    2023-05-14
    keep-alive 前端 JavaScript Vue.js
  • 分析Vue指令实现原理
    目录一、基本使用二、指令工作原理2.1、初始化2.2、模板编译2.3、生成渲染方法2.4、生成VNode2.5、生成真实DOM三、注意事项四、小结一、基本使用 官网案例: <...
    99+
    2024-04-02
  • vue中keep-alive组件实现多级嵌套路由的缓存
    目录现状(问题):探索方案:实现方式现状(问题): keep-alive 组件对第三级及以上级的路由页面缓存失效 探索方案: 方案1、直接将路由扁平化配置,都放在一级或二级路由中方案...
    99+
    2024-04-02
  • vue-router实现原理的示例分析
    这篇文章将为大家详细讲解有关vue-router实现原理的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。大致流程可以看成这样:浏览器发出请求服务器监听到80端口(...
    99+
    2024-04-02
  • Vue组件的实现原理详细分析
    目录渲染组件组件更新父子组件setup函数emit 实现渲染组件 一个组件内部必须要使用 render 进行渲染,且返回虚拟 DOM 这是一个最简组件实例 const MyCompo...
    99+
    2023-01-03
    Vue组件实现原理 Vue组件
  • Vue中$on和$emit的实现原理分析
    目录Vue中发布订阅模式$emit和$on用法深挖首先剖析一下$on的原理实现有一点要记住先定义的先触发分析$emit先打个断点Vue中发布订阅模式 在Vue中采用了发布订阅模式,典...
    99+
    2024-04-02
  • Vue组件实现原理详细分析
    目录1.渲染组件2.组件的状态与自更新3.组件实例和生命周期4.props与组件状态的被动更新5.setup函数的作用与实现6.组件事件和emit的实现7.插槽的工作原理及实现8.注...
    99+
    2023-01-18
    Vue组件 Vue组件原理
  • Vue3嵌套路由中使用keep-alive缓存多层的实现
    目录前言Demo项目结构路由层级扁平化给所有的 router-view 都嵌套上 keep-alive前言 keep-alive是Vue中的缓存标签, 组件在标签中的内容会被缓存下来...
    99+
    2023-05-16
    Vue3 keep-alive缓存多层 Vue3 缓存多层
  • Gochannel实现原理分析
    目录channelchannel类型创建channelchannel操作发送接收关闭无缓冲的通道有缓冲的通道close()如何优雅的从通道循环取值单向通道通道遍历异步通道通道总结ch...
    99+
    2023-05-14
    Go channel Go channel实现原理
  • 分析C# Dictionary的实现原理
    目录一、理论知识1.1、Hash算法1.2、Hash桶算法1.3、解决冲突算法二、Dictionary实现2.1、Entry结构体2.2、其它关键私有变量2.3、Dictionary...
    99+
    2024-04-02
  • CSS Scoped的实现原理分析
    这篇文章主要介绍CSS Scoped的实现原理分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!CSS Scoped的实现原理在Vue单文件组件中,我们只需要在style标签上加上s...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作