React实现vue中keep-alive缓存效果
日期:2021-04-10
来源:程序思维浏览:5006次
这几天在做后台管理系统,之前用vue开发过一套后台管理系统的模板,现在用React开发,但是vue的keep-alive非常好用,想着用React实现类似keep-alive的效果。
其实要想实现vue的keep-alive的效果可以手动实现,实现的思路:
可以配合 React 组件的 componentWillUnmount 生命周期通过 redux 之类的状态管理层对数据进行保存,通过 componentDidMount 周期进行数据恢复。这种方式可以比较快地实现我们所需功能,但在数据量大或者情况多变时,手动保存状态就会变成一件麻烦事了。
在网上发现可以使用react-activation来实现,源码库下载地址:https://github.com/CJY0208/react-activation。
如果在React安装使用可以运行一下命令:
yarn add react-activation或npm install react-activation --save,如果npm安装慢可以使用cnpm安装。
接下来看一下如何使用,代码如下:
import React, { useState } from 'react'
import { render } from 'react-dom'
import KeepAlive, { AliveScope } from './KeepAlive'
...
function App() {
const [show, setShow] = useState(true)
return (
<div>
<button onClick={() => setShow(show => !show)}>Toggle</button>
<p>无 KeepAlive</p>
{show && <Counter />}
<p>有 KeepAlive</p>
{show && (
<KeepAlive id="Test">
<Counter />
</KeepAlive>
)}
</div>
)
}
....
render(
<AliveScope>
<App />
</AliveScope>,
document.getElementById('root')
)
注意 :缓存的虚拟DOM元素会储存在AliveScope 组件中,所以它不能被卸载
使用AliveScope 配合KeepAlive即可达到缓存效果,类似react-keep-alive
首先我们看看AliveScope 组件做了什么事情?
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>
)
}
}
它的源码只有几十行,很简单,这里的this.props.children是虚拟DOM,经过Babel编译和React处理,最终会转化成真实DOM节点渲染。
逐步解析:
{this.props.children}
是这个组件的所有子元素,必须要渲染。
使用React的Context API进行传递KEEP方法给所有的子孙组件,每次这个方法被调用,都会造成AliveScope 组件重新渲染,进而刷新子组件,并且返回一个真实的DOM节点,这个真实的DOM节点就可以被直接DOM操作。这张思维导图,可以很清楚的表示,我们的缓存实现方式,如果看不懂,慢慢往下看。
KeepAlive组件的源码
import React, { Component, createContext } from 'react'
const { Provider, Consumer } = createContext()
const withScope = WrappedCompoennt => props => (
<Consumer>{keep => <WrappedCompoennt {...props} keep={keep} />}</Consumer>
)
@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
withScope是一个高阶组件,将KeepAlive组件传入,返回一个新的组件,这里使用了装饰器,@withScope.其实最终export default 的是withScope(KeepAlive)
这里就是跟react-keep-alive的真正区别,withScope使用了context api捕获了传入的虚拟DOM节点,桥接了父组件以及KeepAlive组件的关联,一旦children属性改变,那么withScope被刷新,进而传入新的children属性给KeepAlive组件,导致数据驱动可以进行组件刷新
这又印证了那句话
在计算机的世界里,如果出现解决不了的问题,那就加一个中间层,如果还不行就加两个 。
这里按照代码运行逻辑,完整的解析了它的简单缓存机制实现,思路整体比较清晰,加上代码自己断点调试难度应该比较低,个人觉得这个库的设计和思想,都是不错的。
其实要想实现vue的keep-alive的效果可以手动实现,实现的思路:
可以配合 React 组件的 componentWillUnmount 生命周期通过 redux 之类的状态管理层对数据进行保存,通过 componentDidMount 周期进行数据恢复。这种方式可以比较快地实现我们所需功能,但在数据量大或者情况多变时,手动保存状态就会变成一件麻烦事了。
在网上发现可以使用react-activation来实现,源码库下载地址:https://github.com/CJY0208/react-activation。
如果在React安装使用可以运行一下命令:
yarn add react-activation或npm install react-activation --save,如果npm安装慢可以使用cnpm安装。
接下来看一下如何使用,代码如下:
import React, { useState } from 'react'
import { render } from 'react-dom'
import KeepAlive, { AliveScope } from './KeepAlive'
...
function App() {
const [show, setShow] = useState(true)
return (
<div>
<button onClick={() => setShow(show => !show)}>Toggle</button>
<p>无 KeepAlive</p>
{show && <Counter />}
<p>有 KeepAlive</p>
{show && (
<KeepAlive id="Test">
<Counter />
</KeepAlive>
)}
</div>
)
}
....
render(
<AliveScope>
<App />
</AliveScope>,
document.getElementById('root')
)
注意 :缓存的虚拟DOM元素会储存在AliveScope 组件中,所以它不能被卸载
使用AliveScope 配合KeepAlive即可达到缓存效果,类似react-keep-alive
首先我们看看AliveScope 组件做了什么事情?
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>
)
}
}
它的源码只有几十行,很简单,这里的this.props.children是虚拟DOM,经过Babel编译和React处理,最终会转化成真实DOM节点渲染。
逐步解析:
{this.props.children}
是这个组件的所有子元素,必须要渲染。
使用React的Context API进行传递KEEP方法给所有的子孙组件,每次这个方法被调用,都会造成AliveScope 组件重新渲染,进而刷新子组件,并且返回一个真实的DOM节点,这个真实的DOM节点就可以被直接DOM操作。这张思维导图,可以很清楚的表示,我们的缓存实现方式,如果看不懂,慢慢往下看。
KeepAlive组件的源码
import React, { Component, createContext } from 'react'
const { Provider, Consumer } = createContext()
const withScope = WrappedCompoennt => props => (
<Consumer>{keep => <WrappedCompoennt {...props} keep={keep} />}</Consumer>
)
@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
withScope是一个高阶组件,将KeepAlive组件传入,返回一个新的组件,这里使用了装饰器,@withScope.其实最终export default 的是withScope(KeepAlive)
这里就是跟react-keep-alive的真正区别,withScope使用了context api捕获了传入的虚拟DOM节点,桥接了父组件以及KeepAlive组件的关联,一旦children属性改变,那么withScope被刷新,进而传入新的children属性给KeepAlive组件,导致数据驱动可以进行组件刷新
这又印证了那句话
在计算机的世界里,如果出现解决不了的问题,那就加一个中间层,如果还不行就加两个 。
这里按照代码运行逻辑,完整的解析了它的简单缓存机制实现,思路整体比较清晰,加上代码自己断点调试难度应该比较低,个人觉得这个库的设计和思想,都是不错的。
精品好课