winform 父窗口hide后 如何再show_如何在React中实现和Vue一样的keepalive

写在开头
  • 在vue中有天然的keep-alive这个功能实现,react也有一个库,react-keep-alive,但是这个库是直接进行dom操作,会导致数据驱动失效,断层
  • 至于为什么会失效断层,大家可以看我之前对react-keep-alive这个库源码的研究讲解,大概一共1500行TS代码
  • 最近确实比较忙,没什么时间写文章,但是尽量给大家写一些实用的东西,也在准备做一个非常棒的东西给大家去学习
  • 如何在React中实现keep-alive?(react-keep-alive源码讲解)
  • 本源码库地址: 
  • https://github.com/search?q=react-activation
  • 使用安装

  • yarn add react-activation# ornpm install react-activation
正式开始
  • 什么是状态保存?

  • 假设有下述场景:

  • 移动端中,用户访问了一个列表页,上拉浏览列表页的过程中,随着滚动高度逐渐增加,数据也将采用触底分页加载的形式逐步增加,列表页浏览到某个位置,用户看到了感兴趣的项目,点击查看其详情,进入详情页,从详情页退回列表页时,需要停留在离开列表页时的浏览位置上

  • 类似的数据或场景还有已填写但未提交的表单、管理系统中可切换和可关闭的功能标签等,这类数据随着用户交互逐渐变化或增长,这里理解为状态,在交互过程中,因为某些原因需要临时离开交互场景,则需要对状态进行保存

  • 在 React 中,我们通常会使用路由去管理不同的页面,而在切换页面时,路由将会卸载掉未匹配的页面组件,所以上述列表页例子中,当用户从详情页退回列表页时,会回到列表页顶部,因为列表页组件被路由卸载后重建了,状态被丢失

如何实现 React 中的状态保存
  • 在 Vue 中,我们可以非常便捷地通过标签实现状态的保存,该标签会缓存不活动的组件实例,而不是销毁它们

  • 而在 React 中并没有这个功能,曾经有人在官方提过功能 issues ,但官方认为这个功能容易造成内存泄露,表示暂时不考虑支持,所以我们需要自己想办法了

  • 常见的解决方式:手动保存状态

  • 手动保存状态,是比较常见的解决方式,可以配合 React 组件的 componentWillUnmount 生命周期通过 redux 之类的状态管理层对数据进行保存,通过 componentDidMount 周期进行数据恢复

  • 在需要保存的状态较少时,这种方式可以比较快地实现我们所需功能,但在数据量大或者情况多变时,手动保存状态就会变成一件麻烦事了

  • 作为程序员,当然是尽可能懒啦,为了不需要每次都关心如何对数据进行保存恢复,我们需要研究如何自动保存状态

  • 最初的版本react-keep-alive

b726009c60877d85ff6825ae92a46097.png
  • 1500行TypeScript代码在React中实现组件keep-alive 我的这篇文章对源码进行了解析,但是这个库存在断层现象,虽然可以缓存最后一次状态渲染结果,但是后面数据变化无法再进行数据驱动。而且是借助React.createPortal 借助实现,我跟下面这个库的作者都觉得这是多余的,其实只需要抽取children属性,再封装一次HOC高阶组件即可。

  • 总体来说,react-keep-alive这个库比较重,实现原理也不难,就是笨重,断层,源码跳来跳去,真的理清楚了就好

react-activation优雅的实现
  • 效果实现:
9e95f55657ac4e2c2369450dff5f9880.png
7f415e281aa2e8a985f93c5746cd376a.png
庖丁解牛,源码解析
  • 最简单版本的react中keep-alive实现演示地址

  • 使用方式:开箱即用

import React, { useState } from 'react'
import { render } from 'react-dom'
import KeepAlive, { AliveScope } from './KeepAlive'

...

function App() {
  const [show, setShow] = useState(true)
  return (
    
 setShow(show => !show)}>Toggle

无 KeepAlive


      {show && }

有 KeepAlive


      {show && ("Test">
      )}

  )
}

....


render(
  ,
  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 (
      
        {this.props.children}
        {Object.values(this.state).map(({ id, children }) => (
            ref={node => {
              this.nodes[id] = node
            }}
          >
            {children}

        ))}
    )
  }
}
  • 它的源码只有几十行,很简单,这里的this.props.children是虚拟DOM,经过Babel编译和React处理,最终会转化成真实DOM节点渲染

  • 逐步解析:

{this.props.children}
  • 是这个组件的所有子元素,必须要渲染

  • 使用React的Context API进行传递KEEP方法给所有的子孙组件,每次这个方法被调用,都会造成AliveScope 组件重新渲染,进而刷新子组件,并且返回一个真实的DOM节点,这个真实的DOM节点就可以被直接DOM操作。

10aaff50c32dc757934eac0f54a02fdf.png
  • 这张思维导图,可以很清楚的表示,我们的缓存实现方式,如果看不懂,慢慢往下看
KeepAlive组件的源码
import React, { Component, createContext } from 'react'

const { Provider, Consumer } = createContext()

const withScope = WrappedCompoennt => props => (
  {keep => }
)



@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 (
      
          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组件,导致数据驱动可以进行组件刷新

  • 这又印证了那句话

  • 在计算机的世界里,如果出现解决不了的问题,那就加一个中间层,如果还不行就加两个 --来自不知名码农Peter

990c8ef2394534b1b0a0d6b66f7fafed.png
  • 这里按照代码运行逻辑,完整的解析了它的简单缓存机制实现,思路整体比较清晰,加上代码自己断点调试难度应该比较低,个人觉得这个库的设计和思想,都是不错的,值得推广,作者也是比较乐意解答问题。大家有问题可以在github上提问。

3799bc231ca493710c5879273039c52c.png

 相关推荐

sync_hooks、CLS 与 Node 中异步资源生命周期监听

Node 中的全链路式日志标记及处理

使用 Node 开发服务器项目时如何高效地打日志?

用TypeScript学设计模式(享元模式)

用TypeScript学设计模式(模板方法模式)

TypeScript 设计模式之适配器模式

用TypeScript学设计模式(观察者模式)

用TypeScript学设计模式(单例模式)

从应用场景再谈防抖和节流

经典面试题 - 前端性能优化 CRP(关键渲染路径)详解

玩转前端二进制(超详细)

如何用 Babel 为代码自动引入依赖

从源码看Vue生命周期

使用Canvas实现一个在线发牌游戏  [纯前端、附源码]

Kbone原理解析,小程序技术该如何选型?

Node.js结合ProtoBuffer,从零实现一个redis!

详解 ES10 中 Object.fromEntries() 的缘起

JS执行上下文的两个阶段做了些啥?

让你彻底掌握 TS 枚举

遇到这些 TS 问题你会头晕么?

在 TS 中如何减少重复代码

用上这几招,轻松实现 TS 类型提取

TypeScript 期中考试现在开始

前端学习数据结构与算法系列(八):快速排序与三路快排

前端学习数据结构与算法系列(七):堆排序与归并排序

前端学习数据结构与算法系列(六):选择排序与插入排序

前端学习数据结构与算法系列(五):冒泡排序的理解与实现

前端学习数据结构与算法系列(四):哈希、堆和二叉查找树

前端学习数据结构与算法系列(三):栈与队列的基础知识

前端学习数据结构与算法系列(二):链表与数组的基础知识

前端学习数据结构与算法系列(一):初识数据结构与算法

腾讯文档原来是这样用 webpack5 的 Module federation 特性的!

绝大部分的开源项目都投入TypeScript了!你呢?

【收藏系列】JS灵魂之问(下) - 附个人成长经验分享

【收藏系列】JS灵魂之问, 是否有offer看你接到多少个(中)

【收藏系列】JS灵魂之问, 请问你能接得住几个?(上)

【JS进阶深挖】完全弄懂数据类型转换(上)

cbf06a05d6d055c895545c4981962de4.png

5fa09e597b07187f6000a72823d30d16.png点在看的人特别帅/美 1635335279ac9995ab4d5317a053fe68.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值