赞
踩
为了优化用户的体验,可能会遇到这样的需求:返回列表的时候,需要保持状态和滚动位置;或是页面内切换组件(比如切换 Tab )的时候,需要保持状态。
如果使用 Vue,就可以用 <keep-alive> 组件来让其包含的组件保留状态,实现组件缓存。但是在 React 中并没有这样的功能,而且在这个 issues 中也可以看到,官方认为 <keep-alive> 容易造成内存的泄露,因此不准备引入这样的 API,但也许未来会提供更好的缓存方式,所以目前还需通过其他方法实现这类需求。
其实使用方法很简单,用 <KeepAlive> 包裹需要进行缓存的组件,并在一个不会卸载的父组件内包裹上 <AliveScope> 即可。
react-activation 分别为类组件和函数组件提供了生命周期 componentDidActivate、componentWillUnactivate 或 hooks useActivate、useUnactivate,来对应恢复缓存和进行缓存两种状态,以及是否保存滚动位置、手动控制缓存等等功能。
通过 作者的最简实现 可以看到组件缓存的效果和使用的示例,以及 react-activation 的最简实现方式,可以分析一下它的实现思路。
最简实现:
import React, { Component, createContext } from 'react' const { Provider, Consumer } = createContext() const withScope = WrappedComponent => props => ( <Consumer>{keep => <WrappedComponent {...props} keep={keep} />}</Consumer> ) export class AliveScope extends Component { nodes = {} state = {} keep = (id, children) => new Promise(resolve => this.setState( { [id]: { id, children } }, () => resolve(this.nodes[id]) ) ) render() { return ( <Provider value={this.keep}> {this.props.children} {Object.values(this.state).map(({ id, children }) => ( <div key={id} ref={node => { this.nodes[id] = node }} > {children} </div> ))} </Provider> ) } } @withScope class KeepAlive extends Component { constructor(props) { super(props) this.init(props) } init = async ({ id, children, keep }) => { const realContent = await keep(id, children) this.placeholder.appendChild(realContent) } render() { return ( <div ref={node => { this.placeholder = node }} /> ) } } export default KeepAlive
实现的过程大致是,由不会被卸载的 AliveScope 组件通过上下文,把一个 keep 方法传递出去。
然后一个高阶组件获取到 keep 方法,并把 children 属性传入 KeepAlive 组件(这也是 react-activation 实现了数据驱动,而 react-keep-alive 数据驱动失效,两个库的主要区别原因 )。
在 KeepAlive 组件中调用 keep 方法,把 children 属性缓存到 AliveScope 的 state 中。
在 state 更新后,把 ref (真实 DOM)返回给 KeepAlive 组件。KeepAlive 组件拿到真实 DOM 后,把它移动到自己组件内的某个占位中。
在 KeepAlive 组件卸载的以后,如果还需要重新加载,还可以从 AliveScope 组件中获取到缓存的虚拟 DOM 信息。
这个其实是伪造组件的思路,把 children 包裹起来并且传递出去,在缓存组件内被渲染,当前组件正常地更新卸载。
当前组件卸载的时候,children 也被卸载了,但是它的虚拟 DOM 已经被缓存在了缓存组件中。
这个组件重新被加载的时候,把缓存直接渲染后移入当前组件,就恢复了组件卸载前状态。
不过这个库也因为破坏了原来的渲染层级,遇到了一些已修复和还未修复的问题,还是希望官方可以支持实现这个功能吧。
React 中的状态自动保存(KeepAlive)—— react-activation 作者
在React中实现和Vue一样舒适的keep-alive
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。