文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Vue keep-alive的实现原理是什么

2023-06-30 03:25

关注

这篇“Vue keep-alive的实现原理是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Vue keep-alive的实现原理是什么”文章吧。

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组件的整个渲染

大致流程如下

Vue keep-alive的实现原理是什么

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}

主要逻辑为三:

确认需要渲染的slot、

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

返回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,      }    },

以上就是关于“Vue keep-alive的实现原理是什么”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯