注册
web

错怪react的半年-聊聊keepalive

背景


在半年前的某一天,一个运行一年的项目被人提了建议,希望有一个tabs页面,因为他需要多个页面找东西。


我:滚,自己开浏览器标签页(此处吹牛逼)


项目经理:我审核的时候也需要看别人提交的数据是否正确,我也需要看,很多人提了建议


我:作为一个合格的外包,肯定以项目经理体验为主(狗头保命)


1 React的Keepalive


简单实现React KeepAlive不依赖第三方库(附源码)React KeepAlive 实现,不依赖第三方库,支持 - 掘金
神说要有光《React通关秘籍》


....还有一些没有收藏的keepalive实现


代码不想多赘述,我找了很多资料基本思路讲一下


基本是通过react-router来实现



  • 弄一个map来存储{path:<OutLet|useOutlet>}
  • 然后根据当前路由path来决定哪些组件需要渲染,不渲染的hidden
  • 然后在最外层布局套一层Tabs布局用react的context来传递

出现的问题是:


072932bf075694f67855441b3d7c883.jpg


就是当依赖项是一个公共数据的时候,useEffect会触发,如图片中的searchParams同名的key、存在state内存中的同名key之类的



详情页打开多个,地址栏清一色pkId的情况



使用的是ant design pro v6,也是看到有加配置就可以用Keepalive,大家可以掘金找找,我测试过也是有一样的问题,但是和目前能找到功能差不多了,免去自己封装


看很多react Keepalive的实现目前是还没找到什么方案,也皮厚的找过神光,但是要是知道我是被人忽悠了,绝对不会去打扰大佬



tip:先自己敲,再问,不要让自己陷于蠢逼的尴尬



发现问题后,我去问日常开发Vue的童鞋们,因为我两年没写vue了,我说:你们是怎么实现tabs布局的,Vue的Keepalive是怎么实现只有显示哪个页面,别的组件存储而不运行watch的,然后说了一下我在react开发tabs的时候尴尬。


Vueer:vue不会有这个问题,自带的Keepalive封装的很完美,react没有这个功能吗?


就这样我信了半年,但是这个bug我说,我只能到这种程度了,要是说新项目还可以再useEffect再封装一层自定义的hooks,增加一点心智负担,让别人用封装的来,再useEffect内做判断是否是显示的path之类的。


这边年来反正这个功能做了一个开关,项目经理自己用,别人要是没问也不告诉别人有开发这东西,嘿嘿嘿


之所以又捞起来是别的几个项目也有人问,然后问我能不能迁移给别的项目,那他妈不得王炸,别的项目有的还是umi3没升级的。


2 vue3的Keepalive


先说结论吧:vue的Keepalive也能解决这个问题,纯属胡扯


实在想不到什么好的方案,闲余时间,就用vue写了一个demo,想看看vue是怎么实现的,因为react除了就是hidden,或者超出overflow 然后切换是平移像轮播图一样,实在想不出什么方案能保存原本的数据了。


image.png


<template>
home
<ul>
<li v-for="item in router.getRoutes().filter(r=>r.path!=='/')" @click="liHandler(item)">
{{ item.path }}
</li>

</ul>
</template>
<script lang="ts" setup>
import {useRouter} from "vue-router";

const router = useRouter()

const liHandler = (route) => {
router.push({name: route.name, query: {path: route.path}})
}
</script>


简单来个demo的目录结构和代码,代码是会有问题的代码,不用细看...


两年没写vue还是遇到几个坑,先记录一下


2.1 vue3的routerview不能写在keepAlive内


// home.vue
<template>
<!-- <RouterView v-slot="{Component}">-->
<!-- <KeepAlive>-->
<!-- <component :is="Component"/>-->
<!-- </KeepAlive>-->
<!-- </RouterView>
-->

<KeepAlive>
<RouterView></RouterView>
</KeepAlive>

</template>

image.png


注释掉的是正确的,这里不提一嘴,vue的console.log的体验感很ok,下面列的就是正确的,我还去百度了为啥Keepalive无效,直到截这张图写这文的时候才看到,人家都谢了,哈哈哈哈


2.2 router.push地址不变


主要原因是路由创建的是
createMemoryHistory 这玩意不知道是啥 没用过,我是复制vueRoute官网demo的,一开始没注意,改成createWebHistory就好


import {createRouter, createWebHistory} from "vue-router";

const routes = [
{path: "/", component: () => import("./components/Home.vue")},
{path: "/aaa", component: () => import("./components/Aaa.vue"), name: 'aaa'},
{path: "/bbb", component: () => import("./components/Bbb.vue"), name: 'bbb'},
{path: "/ccc", component: () => import("./components/Ccc.vue"), name: 'ccc'},
{path: "/ddd", component: () => import("./components/Ddd.vue"), name: 'ddd'},
]
const router = createRouter({
history: createWebHistory(),
routes,
})

export default router

2.3 router.push不拼接?传参


router.push(path, query: {path}})

一开始是这么写的,用path+query,这个问题纯粹弱智了,太久没写有点菜,改成name+query就行


2.4 vue Keepalive测试


我先点了去aaa,然后返回首页点ccc,这里可以看到,aaa页面的watch触发了!触发了!触发了!


image.png


然后去看了下源码,简约版如下


const KeepAliveImpl = {
name: `KeepAlive`,

// 私有属性 标记 该组件是一个KeepAlive组件
__isKeepAlive: true,
props: {
// 用于匹配需要缓存的组件
include: [String, RegExp, Array],
// 用于匹配不需要缓存的组件
exclude: [String, RegExp, Array],
// 用于设置缓存上线
max: [String, Number]
},
setup(props, { slots }) {
// 省略部分代码...

// 返回一个函数
return () => {
if (!slots.default) {
return null
}

// 省略部分代码...

// 获取子节点
const children = slots.default()
// 获取第一个子节点
const rawVNode = children[0]
// 返回原始Vnode
return rawVNode
}
}
}

这不就是存下来了children,但是有个有意思的是


前面说过,react是通过缓存住组件,然后用hidden来控制展示哪个隐藏哪个,vue这边的dom渲染出来不是。


image.png


官网也说了动态组件是会卸载的


image.png


3 分析一波


image.png


直接用光哥的代码跑起来,这么一比可以看出来dom上的差异


然后把代码的显示改成判断语句渲染,测试一下


image.png


测试图:


image.png


image.png


然后去首页,再回到/bbb,发现数字又变回0了


先来看下React的渲染,通过卡颂大佬的文章,找到了react在render阶段


500行左右


image.png
这是判断是mountd还是update的一个标志,然而我们已经卸载了,这里肯定是null,那么就会去创建组建,仅仅是创建。


而vue因为自身有keepalive,在render阶段,是有对标志为keepalive的做patch的逻辑


image.png


image.png
所以keepalive的组件不会再走created和mounted,而是直接进行diff进行parch


总结



  • 公共参数无论是vue还是react都会被监听到
  • react想要实现像vue一样的效果只能等react官方适配

image.png
也是有提过啦


作者:不喝酒不会写代码
来源:juejin.cn/post/7436955628263784475

0 个评论

要回复文章请先登录注册