当前位置: 首页 > 后端技术 > Node.js

在React中实现像Vue一样舒适的keep-alive

时间:2023-04-03 21:24:59 Node.js

一开始,vue中有一个天然的keep-alive功能。React也有一个库,react-keep-alive,不过这个库是直接进行DOM操作的,会导致数据驱动失败,fault至于为什么会报错,可以看我之前研究react-keep-alive库的源码。TS代码大约有1500行。最近真的很忙,没有太多时间写文章,但是试着写一些实用的东西给大家,也准备做一个很棒的东西给大家学习如何在React中实现keep-alive?(react-keep-alive源码解释)[](http://mp.weixin.qq.com/s?__b...:https://github.com/search?q=react-activation使用安装yarnaddreact-activation正式启动什么是statepreservation?假设如下场景:在移动端,用户访问一个列表页,向上滚动浏览列表页,随着滚动高度逐渐增加,数据也会使用底部分页加载表单逐渐增加,列表页浏览到某个位置,用户看到感兴趣的商品,点击查看其详情,进入详情页,从详情页返回列表页时,需要停留在离开列表页时的浏览位置类似的数据或场景包括填写但未提交的表单、管理系统中可切换可关闭的功能标签等,这类数据随着用户交互逐渐变化或增长,这里理解为状态。在交互过程中,如果由于某种原因需要暂时离开交互场景,需要在React中保存状态。我们通常使用路由来管理不同的页面,而在切换页面时,路由会卸载不匹配的页面组件,所以上面列表页的例子中,当用户从详情页返回到列表页时,会返回到顶部列表页,因为列表页组件被路由卸载后重新构建,状态丢失。如何在Vue中保存React中的状态,我们可以很方便的通过标签来保存状态。此标记将缓存非活动组件实例而不是销毁它们。React中没有这样的功能。官方曾经有人提到过功能问题,但是官方认为这个功能很容易造成内存泄漏,也就是说暂时不考虑支持,所以我们需要想出一个通用的解决方案:手动保存状态手动保存状态是比较常见的方案,可以配合React组件的componentWillUnmount生命周期通过redux等状态管理层保存数据,通过componentDidMount循环恢复数据。当需要保存的状态很少的时候,这种方式可以比较快速的实现我们需要的功能,但是当数据量很大或者情况发生变化的时候,手动保存状态就会变成一件麻烦的事情。作为程序员,当然要越懒越好。为了不用担心每次如何保存和恢复数据,我们需要研究如何自动保存状态。最初react-keep-alive的版本是1500行TypeScript代码来实现React中的组件keep-alive。本文分析的是源码,但是这个库是有缺陷的。虽然可以缓存上次的状态渲染结果,但是后续的数据变化不能再数据驱动,借助React.createPortal实现,我和下面这个库的作者都觉得这是多余的。其实只需要提取children属性,然后封装HOC高层组件即可。总的来说,react-keep-alive这个库比较重,实现原理不难,但是体积大,故障多,源码跳来跳去。这真的很容易理解。react-activation优雅的实现效果实现:Paodingjieniu,源码分析react中keep-alive实现的最简单版本演示地址使用:开箱即用importReact,{useState}from'react'import{render}from'react-dom'importKeepAlive,{AliveScope}from'./KeepAlive'functionApp(){const[show,setShow]=useState(true)return(

setShow(show=>!show)}>切换

没有KeepAlive

{show&&}

有KeepAlive

{show&&(iv})>)}....render(,document.getElementById('root'))注意:缓存的虚拟DOM元素将被存储在AliveScope组件中,所以不能卸载,使用AliveScope可以通过KeepAlive实现缓存效果,类似于react-keep-alive。首先,让我们看看AliveScope组件的作用。exportclassalivescopeextendscomponent{nodes={}state={}keyp=(id,children)=>NewPromise(resolution=>this.SetState({[id]:{id}},(),()(this.nodes[id]))))render(){return({this.props.children}{Object.values(this.state).map(({id,children})=>({this.Nodes[id]=node}}>{children}
)})}}其源码只有一个几十行,很简单,这里的this.props.children是一个虚拟DOM,经过Babel编译和React处理,最终会转化为真实的DOM节点渲染逐步解析:{this.props.children}是这个组件的所有子元素,必须使用React的ContextAPI渲染,将KEEP方法传递给所有子组件,每次调用这个方法,都会导致AliveScope组件重新渲染,刷新子组件,并返回一个真正的DOM节点。这个真正的DOM节点可以直接被DOM操作这个思维导图可以很清楚的表达我们的缓存实现方式。看不懂的就慢慢往下看KeepAlive组件的源码({keep=>})@withScopeclassKeepAliveextendsComponent{constructor(props){super(props)this.init(props)}init=async({id,children,keep})=>{constrealContent=awaitkeep(id,children)this.placeholder.appendChild(realContent)render}(){return({this.placeholder=node}}/>)}}exportdefaultKeepAlivewithScope是一个高阶组件,这里使用了一个新的KeepAlive组件。装饰器,@withScope。其实最终导出默认是withScope(KeepAlive)。这是与react-keep-alive的真正区别。WithScope使用contextapi来捕获传入的虚拟DOM节点,桥接父组件和KeepAlive组件的关联,一个一旦children属性改变,withScope被刷新,然后新的children属性被传递给KeepAlive组件,导致数据驱动能够刷新组件。添加一个中间层,如果还是不行,再添加两个——Peter,一个不知名的coder,按照代码的逻辑,完整的分析了它简单的缓存机制实现。整体思路比较清晰,代码本身调试难度大。应该是比较低的。个人觉得这个库的设计和思路还是不错的,值得推广。作者也比较愿意回答问题。如果你有任何问题,你可以在github上问他们。