从需求到开源,如何做到刮目相看?

781fb5c054a7537493f87fd629a1b478.gif

作者 | 👽

来源 | 前端Sharing

一、一切根源都从无厘头需求开始

最近在开发业务项目的时候,产品小姐姐突然来到我身边,然后就对着电脑一顿操作,具体场景大致是这样的。

场景一:

e02d1a92a11dffeb7f8d8efb03ac9af8.png

如上图所示,当在数万级别的数据中,选择一条,点击查看,跳转到当前数据的详情页,当点击按钮返回返回来,或者是浏览器前进后退等其他操作,返回到列表页的时候。要记录当前列表的位置。也就是要还原点击查看查看前的页面。但是当点击tab菜单按钮的时候,要清除页面信息。

场景二:

58c4e9cba746963ef1e5ff8c1018ec12.png

如上图所示,当我们编辑内容的时候,一些数据可能从其他页面获得,所以要求,无论切换路由,切换页面,当前页面的编辑信息均不能被置空,只有点击确定 ,重置,表单才内容置空。

场景三:场景一 + 场景二 是更复杂的缓存页面信息场景。

二、梳理需求

接这个需求的时候,咋眼一看,what ,好像是 vue 中的 keepalive + vue router功能,但是,我们几个项目技术栈是react ,react , reactreact 中没有对应的 keepalive内置 api,后来上GitHub上搜索相关项目,感觉有很多不符合业务需求的情况。还有一些潜在的风险。瞬间慌了~~~。内心有一种万只神兽奔腾的感觉。

在漂亮产品小姐姐面前,怎么能说不,那不显得研发能力差,强行装了一波说很简单,只能硬着头皮接下来了。产品小姐姐临走前还说还鬼魅的笑了笑,说可以把几个项目的部分页面都加上这种效果。

1 解决方案

1 数据状态缓存到公共管理可行性

这个需求首先让我想到的是用redux或者是mobx来把页面的状态缓存起来,然后切换页面的时候,把这些数据缓存进去,再次切换回来的时候,将数据取出来,这样就一个问题,即便能缓存state层,但是如果一些表单组件是非受控组件,是无法缓存下来的,还有一些dom状态是缓存不了的,比如手动添加的一些样式等。还有就是实际情况比较复杂,有富文本组件,你是无法直接获取绑定的state的。

第二个原因就是有好几个项目,而且页面比较多,如果都建立数据管理,那么工作量会非常的大。所以数据状态缓存的可行性不高,即便可以实现,也需要大量的复制粘贴,这不是我们的追求。

2 react-keepalive-router诞生

所以我们只能选择自己开发一个项目,然后把它开源,并应用在公司项目中来。既然选择缓存页面,那么为什么不在react-router中的 Route组件和Switch组件中做文章呢,我们需要对Route 和 Switch 组件做一些功能性的拓展,正好笔者之前自己研究过react-router源码,并写了一篇(这一次彻底弄懂react-router路由原理)[https://juejin.cn/post/6886290490640039943#comment] ,感兴趣的同学可以三连一波,因为项目是在router路由层面,所以给它起了一个名字react-keepalive-router。接下来就要对整个项目做一个系统的设计。

三、设计阶段

1 了解react-fiber

为什么我们的项目要提到react-fiber呢,这里我先说一下,react-fiber, React Fiber 是从 v16 版本开始对 Stack Reconciler 进行的重写,是 v16 版本的核心算法实现。react在初始化构建过程中,会产生一个由child指向子fiber,sibling指向兄弟fiber,return指向父fiber三个指针构建的fiber树结构,里面保存着dom信息,update信息,props信息等,我们核心思想就是,在切换页面的时候,组件销毁,但是作为渲染调度的react fiber保存keepalive状态。只要fiber存活,就能获取到dom元素,数据层state等信息。

4d724161144849d0dc22ef0f334f6f84.png

2 基于 react-router-dom 和 react 16.8

首先我们需要对react-router库中的 Route组件和Switch组件作出改造,可以通过路由层面实现缓存路由功能。因为在设计之初,我就想着将用不同的状态管理keepalive状态,这样的好处是,后续可以给缓存路由组件,增加一些额外的声明周期,比如说vue中 activated 和 deactivated一样。因为设计思想是状态管理,项目依赖中不想引入redux等第三方库,所以这里选了react-hooks中 useReducer恰到好处。这就是react基础库 16.8+的原因之一。另外一个原因就是hooks中有useMemo这样防止渲染穿透的api,有助于调节路由组件的更新次数。

工作流程分析

受到react-router-cache-route开源项目的启发,我在设计整个流程的时候,采取了交换dom树的方式。

初始化 :整体设计思路第一次切入缓存页面的时候,会自动生成一个容器组件,缓存Route会把组件,交给容器组件来挂载,然后容器组件生成fiber,render之后生成对应的dom树,将dom树交给Route组件(也就是我们的正常的页面)。

切换页面:切换页面的时候,路由组件是肯定卸载的,这时候需要将我们的dom还给容器组件,然后容器组件进入冻结状态。

再次切换到缓存页面:再次进入路由页面的时候,首先从容器中,发现有该页面的缓存,那么将容器解封状态,然后将dom树,还给当前路由页面。完成keepalive状态。

缓存销毁::项目支持销毁缓存功能,调用销毁方法,会卸载当前缓存容器,进一步销毁fiber 和 dom ,完成整个销毁功能。

设计的优势在哪里?

设计优势:

1 因为内部运用了 useReducer 状态管理,管理缓存状态,可以更灵活,操纵缓存路由组件,采用react hooks全新api,渲染节流,手动解除缓存,增加了缓存的状态周期,监听函数等。

2 这套缓存页面的思想,不仅仅可以用在路由页面级别,后期可以迁移的component组件级别上来。也是后续维护和开发的方向。

四、使用简介 + 快速上手

我们开始设计项目的用法,api,已经应用场景。通过上述工作原理,讲述了 keepliveRouteSwitch 和 keepliveRoute 在整个缓存过程中的作用,

下载

因为我们是把项目上传到了npm方便其他项目用,所以可以直接从 npm 上下载。

npm install react-keepalive-router --save
# or
yarn add react-keepalive-router

1 基本用法

KeepaliveRouterSwitch

KeepaliveRouterSwitch可以理解为常规的Switch,也可以理解为 keepaliveScope,我们确保整个缓存作用域,只有一个 KeepaliveRouterSwitch 就可以了。

常规用法

import { BrowserRouter as Router, Route, Redirect ,useHistory  } from 'react-router-dom'
import { KeepaliveRouterSwitch ,KeepaliveRoute ,addKeeperListener } from 'react-keepalive-router'const index = () => {useEffect(()=>{/* 增加缓存监听器 */addKeeperListener((history,cacheKey)=>{if(history)console.log('当前激活状态缓存组件:'+ cacheKey )})},[])return <div ><div ><Router  ><Meuns/><KeepaliveRouterSwitch><Route path={'/index'} component={Index} ></Route><Route path={'/list'} component={List} ></Route>{ /* 我们将详情页加入缓存 */ }<KeepaliveRoute path={'/detail'} component={ Detail } ></KeepaliveRoute><Redirect from='/*' to='/index' /></KeepaliveRouterSwitch></Router></div></div>
}

这里应该注意⚠️的是对于复杂的路由结构。或者KeepaliveRouterSwitch 包裹的子组件不是Route ,我们要给 KeepaliveRouterSwitch 增加特有的属性 withoutRoute 就可以了。如下例子🌰🌰🌰:

例子一

<KeepaliveRouterSwitch withoutRoute ><div><Route path="/a" component={ComponentA}  /><Route path="/b" component={ComponentB}  /><KeepaliveRoute path={'/detail'} component={ Detail } ></KeepaliveRoute></div>
</KeepaliveRouterSwitch>

例子二

或者我们可以使用 renderRoutes 等api配合 KeepliveRouterSwitch 使用 。

import {renderRoutes} from "react-router-config"
<KeepliveRouterSwitch withoutRoute  >{ renderRoutes(routes) }</KeepliveRouterSwitch>

KeepaliveRoute

KeepaliveRoute 基本使用和 Route没有任何区别。

在当前版本中⚠️⚠️⚠️如果 KeepaliveRoute 如果没有被 KeepaliveRouterSwitch包裹就会失去缓存作用。

效果

82321c76aec348a7f030d3782ad8b933.gif

2 其他功能

1 缓存组件激活监听器

如果我们希望对当前激活的组件,有一些额外的操作,我们可以添加监听器,用来监听缓存组件的激活状态。

addKeeperListener((history,cacheKey)=>{if(history)console.log('当前激活状态缓存组件:'+ cacheKey )
})

第一个参数未history对象,第二个参数为当前缓存路由的唯一标识cacheKey

2 清除缓存

缓存的组件,或是被route包裹的组件,会在props增加额外的方法cacheDispatch用来清除缓存。

如果props没有cacheDispatch方法,可以通过

import React from 'react'
import { useCacheDispatch } from 'react-keepalive-router'function index(){const cacheDispatch = useCacheDispatch()return <div>我是首页<button onClick={()=> cacheDispatch({ type:'reset' }) } >清除缓存</button></div>
}export default index

1 清除所有缓存

cacheDispatch({ type:'reset' })

2 清除单个缓存

cacheDispatch({ type:'reset',payload:'cacheId' })

3 清除多个缓存

cacheDispatch({ type:'reset',payload:['cacheId1','cacheId2'] })

五、验证阶段

由于这里使用公司项目不是很合适,我用了一个自己的项目做demo:

接下来就是验证阶段首先我们看一下产品小姐姐第一个需求:

03c6378b9d06149a58ca2d78fdaf5dc6.gif

第二个需求:

bc7488d8019749d31737029f372e46db.gif

完美实现产品需求。

六、打包阶段 + 发布npm阶段

rollup打包

接下来就是 rollup 打包阶段,rollup打包阶段。项目结构是这样的。

47497a54365ed46089eb6e032b06df15.png

rollup.config.js是整个rollup的配置文件,然后我们通过 rollup 打包后的文件存在 lib文件夹下。

ee5e21ed32a654007f366eb8df7cfe89.png

rollup.config.js 内容如下

import resolve from 'rollup-plugin-node-resolve'
import babel from 'rollup-plugin-babel'
import { uglify } from 'rollup-plugin-uglify'export default [{input: 'src/index.js',output: {name: 'keepaliveRouter',file: 'lib/index.js',format: 'cjs',sourcemap: true},external: ['react','react-router-dom','invariant'],plugins: [resolve(),babel({exclude: 'node_modules/**'})]},/* 压缩` */{input: 'src/index.js',output: {name: 'keepaliveRouter',file: 'lib/index.min.js',format: 'umd'},external: ['react','react-router-dom','invariant'],plugins: [resolve(),babel({exclude: 'node_modules/**'}),uglify()]}]

发布npm

对于发布npm

第一步:需要在npm注册账号。https://www.npmjs.com/signup

第二步:登陆 npm login

第三步:创建 package.json

{  "name": "react-keepalive-router", /* 名称 */  "version": "1.1.0",  /* 版本号 */  "description": "基于`react 16.8+` ,`react-router 4+` 开发的`react`缓存组件,可以用于缓存页面组件,类似`vue`的`keepalive`包裹`vue-router`的效果功能。", /* 描述 */  "main": "index.js", /* 入口文件 */  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1",    "build": "rollup --config"  },  "keywords": [  /* npm 关键词 */    "keep alive",    "react",    "react router",    "react keep alive route",    "react hooks"  ],  "homepage": "https://github.com/GoodLuckAlien/react-keepalive-router", /* 指向 github  */  "peerDependencies": { /* npm 项目依赖 */    "react": ">=16.8",    "react-router-dom": ">=4",    "invariant": ">=2"  },  "author": "alien",  "license": "ISC",  "devDependencies": {  /* 开发环境下依赖 */    "@babel/core": "^7.12.3",    "@babel/preset-react": "^7.12.5",    "@babel/preset-env": "^7.12.1",    "@babel/plugin-proposal-class-properties": "^7.12.1",    "rollup": "^2.33.3",    "rollup-plugin-node-resolve": "^5.2.0",    "rollup-plugin-babel": "^4.4.0",    "rollup-plugin-uglify": "^6.0.4"  },  "dependencies": { /* 生产环境依赖 */    "invariant": "^2.2.4"  }}

第四步:万事俱备之后,用 npm publish 发布。

第五步:升级版本,升级版本很简单,需要我们在package.json 升级版本号,然后重新 npm publish 就可以了。

废弃版本号

如果我们想废弃某个版本 , 执行命令 npm deprecate <pkg>[@<version>] <message>

废弃包

如果我们想废弃包 npm unpublish <pkg> --force

.npmignore

.npmignore里面声明的文件和文件价名称,不会被上传到 npm , 我的项目除了 README.md ,package.json 和 lib 下打包的文件之外,大部分文件是开发时候或者编译阶段用到的,不需要上传到npm,所以需要在 .npmignore 这么写

docs
node_modules
src
md
.babelrc
.gitignore
.npmignore
.prettierrc
rollup.config.js
yarn.lock

七、总结

项目地址

react-keepalive-router  地址   https://github.com/GoodLuckAlien/react-keepalive-router

从需求到开源的流程跑通之后,会有很大的成就感,刚开始独立开发的项目肯定很有很多bug,不怕有bug,要有一颗勇于修复bug并把项目维护下去的决心。

d791b6fdb96d5de718dae57f59fc24f9.gif

往期推荐

使用开源工具 k8tz 优雅设置 Kubernetes Pod 时区

如何优雅保护 Kubernetes 中的 Secrets

Redis 内存满了怎么办?这样置才正确!

云原生的本手、妙手和俗手

e3bf22950911b74fe351a911a3ddc14d.gif

点分享

a17665e2dfe732126c846392b9f360b7.gif

点收藏

ae23a5e67434f7d3ce7cf1d82014bcb5.gif

点点赞

8f30fa2635ab380b79615397571cadec.gif

点在看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/511559.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何高效完成ECS多环境部署?

简介&#xff1a;通过本文&#xff0c;你可以了解到&#xff0c;如何通过云效流水线有效拉通开发与运维&#xff0c;打破二者之间的壁垒墙&#xff0c;让开发与运维高效联动。在软件开发和部署过程中&#xff0c;我们的软件往往需要在不同的运行环境中运行&#xff0c;例如&…

技术探秘: 360数科夺得ICDAR OCR竞赛世界第一

ICDAR&#xff08;国际文档分析与识别会议&#xff09;是OCR识别领域最权威的会议之一。近期&#xff0c;360数科在ICDAR2019-SROIE(Results - ICDAR 2019 Robust Reading Challenge on Scanned Receipts OCR and Information Extraction - Robust Reading Competition) 榜单上…

云原生时代,软件交付有何不同 | 研发效能提升36计

简介&#xff1a;从今天起&#xff0c;我们将开启一个新的专栏&#xff1a;《研发效能提升36计_持续交付篇》。专栏将通过10-20篇文章&#xff0c;系统分享云原生时代&#xff0c;企业如何落地持续交付。 编者按&#xff1a;从今天起&#xff0c;我们将开启一个新的专栏&#…

php 获取字符串完整拼音,PHP 获取中文字符串的首字符拼音字母

class"php"><?php header(Content-Type: text/html; charsetutf-8);$str"阅谁问君诵&#xff0c;水落清香浮";echo getFirstCharCode($str);function getFirstCharCode($str){$str iconv("UTF-8","gb2312", $str);$targetChar*…

IT人的年夜饭,也太香了吧

简介&#xff1a; 平时的IT人&#xff0c;奋战在修复bug前线&#xff0c;起早与贪黑齐飞&#xff0c;调休共假期待定。到了新春佳节&#xff0c;对于IT人来说&#xff0c;没有什么是比一顿年夜饭更让人熨贴肺腑的了。为了让废寝忘食编程序、闻机起早保运维的IT人过一个安稳的好…

小红书消息中间件的运维实践与治理之路

简介&#xff1a;近年来&#xff0c;消息领域的全面云原生化逐渐走向深入&#xff0c;比如 RocketMQ 5.0 版本的存算分离设计和 raft 模式&#xff0c;再比如 Kafka3.0 引入了分层设计的方式&#xff08;tiered storage&#xff09;和 raft 模式&#xff0c;以及近年来新崛起的…

爆测一周,22年必看最细致代码托管工具测评

简介&#xff1a;网上代码托管选型的文章不少&#xff0c;不过大多内容有点久远&#xff0c;很多最新的平台没有包括进来&#xff0c;个人花了大概一个星期的时间&#xff0c;把目前市面上比较火的代码托管平台&#xff08;开源托管平台&#xff1a;Github、Gitee&#xff1b;企…

read 文件一个字节实际会发生多大的磁盘IO?

作者 | 张彦飞allen来源 | 开发内功修炼在日常开发中一些看似司空见惯的问题上&#xff0c;我觉得可能大多数人其实并没有真正理解&#xff0c;或者理解的不够透彻。不信我们来看以下一段简单的读取文件的代码&#xff1a;上图中的代码仅仅只是对某个文件读取了一个字节&#x…

【指标需求思考】如何做好指标类需求建设

简介&#xff1a;大家一直所说的【需求】究竟有哪些&#xff1f;用户需求、业务需求、系统需求...... 但是今天我要给大家介绍一种我自认为一种别出心裁的需求&#xff01;【指标类需求】在庞大的需求体系里&#xff0c;一个完整的系统设计流程是非常必要的&#xff0c;好则效率…

oracle 12c 低版本,oracle高版本迁移数据到低版本(12c至11g)方法

1.12c版本信息&#xff1a;2.11g版本信息&#xff1a;3.查看12c的字符集编码&#xff1a;select userenv(language) from dual;要迁移的两个数据库字符集编码要保持一致。如果不一致请手工修改&#xff0c;修改方法另行百度。4.查看11g数据库字符集编码&#xff1a;5.查看12c数…

构建信创产业生态,移动云立足全栈自主创新连放大招

信创&#xff0c;即信息技术应用创新&#xff0c;它是数据安全、网络安全的基础&#xff0c;也是“新基建”的重要内容。在国际信息安全形势严峻、国家安全需要和数智时代新要求三重因素作用下&#xff0c;信创生态应运而生。进入2022年&#xff0c;云计算将成为信创主要落地方…

游戏行业搜索实践

简介&#xff1a;本文通过游戏行业客户案例带大家了解游戏内容&#xff0c;游戏论坛等场景搜索特性&#xff0c;以及如何通过开放搜索游戏增强版解决方案轻松快速接入,实现高质量搜索效果,提升业务指标和用户体验。 客户背景 国内知名的文化社区和视频平台&#xff0c;其游戏…

序列特征在推荐算法中的应用

简介&#xff1a;行为序列特征在推荐&#xff0c;广告等领域中有着广泛应用&#xff0c;最近几年涌现了很多有关行为序列的研究论文&#xff0c;讲解如何将行为序列应用到实际场景中。但是论文中的实际思想距离落地还有一段距离&#xff0c;因此本文先介绍一些论文中的序列特征…

BlackBerry 软件全球现已部署超过2.15亿辆汽车

BlackBerry近日宣布&#xff0c;据知名独立调研公司Strategy Analytics统计&#xff0c;目前全球已有超过2.15亿辆汽车搭载BlackBerry QNX软件&#xff0c;较2021年增加了2,000万辆。 作为获得安全认证的嵌入式汽车软件市场领导者&#xff0c;BlackBerry深受众多业内汽车制造商…

从托管到原生,MPP架构数据仓库的云原生实践

简介&#xff1a;本文介绍了云原生数据仓库产品AnalyticDB PostgreSQL从Cloud-Hosted到Cloud-Native的演进探索&#xff0c;探讨为了实现真正的资源池化和灵活售卖的底层设计和思考&#xff0c;涵盖内容包括产品的架构设计&#xff0c;关键技术&#xff0c;性能结果&#xff0c…

做到这4点,才是真正的持续交付| 研发效能提升36计

简介&#xff1a;全线专栏《研发效能提升36计_持续交付篇》上线啦&#xff01;本专栏将通过10-20篇文章&#xff0c;系统分享云原生时代&#xff0c;企业如何落地持续交付。本文是该专栏的第2篇。 什么是真正的持续交付&#xff1f; 编者按&#xff1a;全线专栏《研发效能提升…

oracle打patch,Oracle初学者入门指南-How to get Oracle Patch?

Oracle初学者入门指南-How to get Oracle Patch?怎样获得/找到Oracle的Patch&#xff0c;这是一个朋友在Itpub上问到的问题。他还举了一个生动的例子:比如我要使用oracle的全文检索功能&#xff0c;但是这个全文检索在9201里不支持中文&#xff0c;中文的无法检索&#xff0c;…

e签宝:借助钉钉宜搭变革传统项目管理模式,交付效率显著提升

简介&#xff1a;通过钉钉宜搭&#xff0c;e签宝在半个月内搭建了项目交付管理平台&#xff0c;提升了项目管理的效率和质量&#xff0c;推进了团队核心业务的信息化建设。e签宝在有效梳理了各环节的工作进度、质量、成本、职权后&#xff0c;通过宜搭平台保障了内外通畅&#…

24 张图一次性说清楚 TCP

来源 | 杰哥的IT之旅做IT相关的工作&#xff0c;肯定都离不开网络&#xff0c;网络中最重要的协议是TCP。无论是实际工作还是笔试面试&#xff0c;你看哪里能少得了TCP?我看过RFC中与TCP相关的文档&#xff0c;也看过linux中TCP相关的源码&#xff0c;也看过不少框架中的TCP相…

php 实例化模型出错,实例化model出错了

我是按照老师所说的做的。在function user 中写入了这些<?php namespace Home\Controller;use Think\Controller;class IndexController extends Controller {public function index(){//省略了}public function user(){//其实新版的url应该这么写&#xff1a;http://www.t…