keep-live原理,react-router如何实现keep-alive
3. keep-live原理,react-router如何实现keep-alive
先说结论:被
keep-alive
标签包裹的组件在第一次初始化时(渲染从render开始)会被缓存起来(以vnode的形式),再次访问时(actived生命周期)从缓存中读取并从patch阶段开始渲染。
Vue的渲染是从图中render阶段开始的,但keep-alive的渲染是在patch阶段,这是构建组件树(虚拟DOM树),并将VNode转换成真正DOM节点的过程。
3.1简单描述从render到patch的过程
- vue在渲染的时候先调用原型上的
_render
函数将组建对象转化为vnode
实例;而_render
是通过调用createElemnt
和createEmptyNode
两个函数进行转化 createElement
的转化过程会根据不同的情形选择new VNode
或createComponent
函数来做VNode实例化- 完成VNode实例化后,Vue调用原型上的
_update
函数把VNode渲染成为真实DOM。这个过程是过调用__patch__
函数完成的。
3.2 keep-alive组件的渲染
3.2.1 <keep-alive></keep-alive>
不会生成DOM节点,其实现原理如下
Vue在初始化生命周期的时候,为组件实例建立父子关系会根据abstract属性决定是否忽略某个组件。在keep-alive中,设置了abstract: true,那Vue就会跳过该组件实例。
// src/core/instance/lifecycle.js
export function initLifecycle (vm: Component) {
const options = vm.$options
// 找到第一个非abstract的父组件实例
let parent = options.parent
/** keep-alive 设置abstract:true,跳过创建组件实例 **/
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
vm.$parent = parent
// ...
}
3.2.2 keep-alive 包裹的组件如何使用缓存?
简单来讲:
第一次加载组件是,将组件实例保存在缓存中,
第二次访问组件时,判断组件实例vnode.componentInstance
和i.keepAlive
是否为true,如果是直接将缓存的组件实例插入到父节点:insert(parentElm, vnode.elm, refElm) // 将缓存的DOM(vnode.elm)插入父元素中
看源码:
在patch阶段,会执行createComponent函数:
// src/core/vdom/patch.js
function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
let i = vnode.data
if (isDef(i)) {
const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
if (isDef(i = i.hook) && isDef(i = i.init)) {
i(vnode, false /* hydrating */)
}
if (isDef(vnode.componentInstance)) {
initComponent(vnode, insertedVnodeQueue)
insert(parentElm, vnode.elm, refElm) // 将缓存的DOM(vnode.elm)插入父元素中
if (isTrue(isReactivated)) {
reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
}
return true
}
}
}
- 首次加载被包裹组件时,由keep-alive.js中的render函数可知,vnode.componentInstance的值是undefined,keepAlive的值是true,因为keep-alive组件作为父组件,它的render函数会先于被包裹组件执行;那么就只执行到i(vnode, false /hydrating/),后面的逻辑不再执行;
- 再次访问被包裹组件时,vnode.componentInstance的值就是已经缓存的组件实例,那么会执行insert(parentElm, vnode.elm, refElm)逻辑,这样就直接把上一次的DOM插入到了父元素中。
3.3 钩子函数
看源码
可以看出,当vnode.componentInstance和keepAlive同时为true值时,不再进入$mount过程,那mounted之前的所有钩子函数(beforeCreate、created、mounted)都不再执行。
被缓存的组件实例会为其设置keepAlive = true,而在初始化组件钩子函数中:
// src/core/vdom/create-component.js
const componentVNodeHooks = {
init (vnode: VNodeWithData, hydrating: boolean): ?boolean {
if (
vnode.componentInstance &&
!vnode.componentInstance._isDestroyed &&
vnode.data.keepAlive
) {
// kept-alive components, treat as a patch
const mountedNode: any = vnode // work around flow
componentVNodeHooks.prepatch(mountedNode, mountedNode)
} else {
const child = vnode.componentInstance = createComponentInstanceForVnode(
vnode,
activeInstance
)
child.$mount(hydrating ? vnode.elm : undefined, hydrating)
}
}
// ...
}
可以看出,当vnode.componentInstance和keepAlive同时为true值时,不再进入$mount过程,那mounted之前的所有钩子函数(beforeCreate、created、mounted)都不再执行。
3.4 actived, deactived
- activated:在 keep-alive 组件激活时调用
- deactivated:在 keep-alive 组件停用时调用
我们在实际开发项目中会有一些需求,***比如跳转到详情页面时,需要保持列表页的滚动条的位置,返回的时候依然在这个位置,**这样可以提高用户体验,这个时候就可以使用缓存组件 keep-alive 来解决。
设置了 keep-alive 缓存的组件,会多出两个生命周期钩子:
首次进入组件时:beforeRouteEnter > beforeCreate > created > mounted > activated > ... ... > beforeRouteLeave > deactivated
再次进入组件时:beforeRouteEnter > activated > ... ... > beforeRouteLeave > deactivated
可以看到,缓存的组件中 activated
钩子函数每次都会触发,所以可以通过这个钩子判断,当前组件时需要使用缓存的数据还是重新调用接口加载数据。如果未使用keep-alive 组件,则在页面回退时会重新渲染页面,首次进入组件的一系列生命周期也会一一被触发。
离开组件时,使用了 keep-alive 不会调用 beforeDestroy 和 destroyed 钩子,因为组件没被销毁,被缓存起来了。所以 deactivated
这个钩子可以看作是 beforeDestroy 和 destroyed 的代替,缓存组件销毁的时候要做的一些操作可以放在这个里面。
3.5 keep-alive总结
被keep-alive
标签包裹的组件在第一次初始化时(渲染从render开始)会被缓存起来(以vnode的形式),再次访问时(actived生命周期)从缓存中读取并从patch阶段开始渲染。
Vue的渲染是从图中render阶段开始的,但keep-alive的渲染是在patch阶段,这是构建组件树(虚拟DOM树),并将VNode转换成真正DOM节点的过程。
3.6 react实现keep-alive
- react-keep-alive
- react-activation
- Portals:将组件渲染到父节点以外的DOM上,可以是内存中的。先通过 document.createElement 在内存中创建一个元素,然后再通过 React.createPoral 把 React 子节点渲染到这个元素上,这样就实现了“空渲染”。再次显示时将这个portal的元素渲染到页面Dom中。
- react-keeper
- react-router-cache-route
- react-live-router
- display:none
参考
彻底揭秘keep-alive原理
Vue 全站缓存之 keep-alive
Vue源码解析,keep-alive是如何实现缓存的?
别问我[vue],VNode、elm、context、el是个啥?
vue的Vnode 和 patch机制
Vue3.0 核心源码解读 | KeepAlive 组件:如何让组件在内存中缓存和调度
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!