近些年,随着工业互联网的发展,越来越多的应用选择了在浏览器端实现。浏览器开放的功能也越来越多。
可是,在浏览器功能越来越强大的日子,前端也变得繁重起来。状态仓库需要存放的东西也越来越多。如一个简单的前端监控系统,就涉及到错误展示,数据报表,错误筛选查询等等功能。这其中有许多数据都是存在交集的 一旦我们的数据获取存在交集,则就意味着有以下问题存在:
当然,以上是交集存在的问题。这也会导致单条数据不纯,无法做到很高的抽象和通用性。久而久之,此类管理方式存放的数据模型会越来越混杂,越来越多,造成管理上的麻烦。
于是,我们非常希望可以将数据的管理模型使其更加抽象,使其可以在任意业务场景都可以灵活组装和使用。这一点也和函数式编程中的“纯函数”概念类似:
纯函数 + 纯函数 = 纯函数
我们将视角转向后端来看。假设错误监控的后端接口,要给我们返回一条 错误捕获信息
,后端的数据查询逻辑又该如何编写呢?
下图是2张表的联查实现。其中一张issue
表,一张error
表。在后端的数据库设计中,issue
和error
关联,常常以引用对方的id来实现。这样我们就可以将2张表解耦设计,在需要联合查询时再进行组装。
image
可以看到,得益于许多数据库的多表联查,后端可以轻松地从多张表中拿到想要的数据,最后组装起来,通过接口进行返回。
基于以上考虑,我们可以采用状态范式化方案。在使用范式化方案之前,我们先来了解一下它到底是什么。
根据redux
官方文档的介绍(https://redux.js.org/usage/structuring-reducers/normalizing-state-shape#designing-a-normalized-state):
Each type of data gets its own "table" in the state. (每种类型的数据在状态树中应该有属于自己的表)
Each "data table" should store the individual items in an object, with the IDs of the items as keys and the items themselves as the values.(每一条数据都应当把数据存在一个对象里面。项目的ID作为key,本身作为value)
Any references to individual items should be done by storing the item's ID.(对于单个数据模型的引用应当通过存储ID来实现)
Arrays of IDs should be used to indicate ordering.(应该用包含ID的数组来声明所有数据的排列顺序)
简单来讲,就是将我们的数据从立体化变为扁平化,将可以抽象的数据模型进一步独立管理,数据之间连接模型用ID进行引用连接查找,可以加快查找数据的速度。如:
image.png
这种存取查找方式,类似数据库的多表联查一样。所以在很多时候,我们期待前端的范式化模型和数据库的模型一一对应。我们根据范式化的概念,可以将我们目前的状态根据模型进行抽象。根据模型将数据抽离,随后根据查询关系,做关联引用
抽象完毕后,我们在业务中查找数据的方案也需要进行联合查询。这样以来,我们查询的复杂度就由O(N)降为了O(1)。查询性能大幅度提升
当然,这样的数据组装方式虽然让读取速度加快,但也让源数据的分离实现变得复杂起来。这里我们可以使用 Redux 官方推荐的 normalizr.js,他可以根据预先设置好的数据模型,把我们的数据快速根据模型进行剥离,我们的数据转换可以变的更加简单。
我们可以经过简单的数据模型定义,就可以将数据按照模型进行分离。像上面的演示转换结果一样
import { normalize, schema } from 'normalizr';
// Define a users schema
const user = new schema.Entity('users');
// Define your comments schema
const comment = new schema.Entity('comments', {
commenter: user
});
// Define your article
const article = new schema.Entity('articles', {
author: user,
comments: [comment]
});
const normalizedData = normalize(originalData, article);
当然,redux
天生的状态管理方案是存在巨大的性能问题的 —— 需要将状态提升到公共组件去管理。这种实现方式常常会导致不必要的组件重新生成组件树。举个例子,我们有一个错误监控系统,当我们获取最新的错误信息列表时:虽然我们的错误信息条目有所增加,错误类型却始终没有变化。但只使用错误类型的组件依然触发了重新渲染。我们当然不希望这种状况存在,毕竟如果碰到比较复杂的计算时,不必要的重复渲染往往对性能影响都比较大。
当然,我们可以借助 react-redux
的 useSelector
钩子来筛选需要的 state
。useSelector
自身拥有了多级缓存,可以确保只有在用到的数据更新时,才会触发组件,不会造成不必要的组件更新。
从源码中可以看到,每次提交 action
后,都会去执行 equalityFn
函数,将本次 selector
的执行结果与上次的结果进行对比。如果一致,则直接 return
。不会触发后面重复渲染的逻辑
image.png
但这种方案依然存在缺陷。在每次 action
提交后,虽然组件不会重新生成,useSelctor
的selector
的选择函数依然会重新生成(虽然有 reselect
,但缓存也是个成本)。且倡导一个useSelctor
每次只返回单个非引用类型字段值,不然触发浅比较会导致组件再次重新渲染。
Recoil
是 Facebook
推出的基于 React
的状态管理框架(目前还是试验阶段)。它的最大优势就是可以基于正交有向图,精准的只触发渲染状态更新的组件,而这一切都是基于订阅来实现。基于订阅,也就避免了 useSelector 的选择器,每次状态更新都需要重新生成的问题。
下图可以看到,比起之前redux
一颗全局大的状态树的玩法,recoil
更推荐将状态拆为一个个碎片状态,只与用到的组件进行共享。
image.png
在recoil
中,有2个最核心的概念:atom
和 selector
。atom
是状态的最小单位。当atom
被更新时,订阅的组件也会被触发更新。如果多个组件都订阅了同一个atom
,则它们共享这份状态。你可以简单地认为,atom 是recoil中最小的数据源
const fontSizeState = atom({
key: 'fontSizeState',
default: 14,
});
而 selector
的意义则是搭配 atom
使用。selector
可以为 atom
加入自定义的 getter
和setter
。而 atom
发生更改时,订阅它的 selector
也会发生变化,从而被订阅 selector
的组件重新 render
const fontSizeLabelState = selector({
key: 'fontSizeLabelState',
get: ({get}) => {
const fontSize = get(fontSizeState);
const unit = 'px';
return `${fontSize}${unit}`;
},
});
当然,recoil
也支持状态的读写粒度不一致问题。例如我的状态中包含了 a
和 b
两个属性,我在读的时候,只读其中的 a
属性,则只用到 b
属性的组件不会更改。
这一点对性能的提升巨大,也一定程度上间接避免了recoil
的状态拆分过细问题
当然,Recoil
最赞的地方是 状态读取支持异步函数。且同步异步可以混用,同步函数也可以接受异步读取的值。 当然,这一个点要配合 Suspense
优势才最大。
例如下面代码。我在 selector
中定义的状态 get
为异步函数,而在我组件中使用时却是同步的。这对于使用者来说是无感使用的。
当然,配合 Suspense
的效果更好,我们就不需要另外的状态,来判断这个异步计算是否已经拿到数据。
const currentUserIDState = atom({
key: 'CurrentUserID',
default: 1,
});
const currentUserNameQuery = selector({
key: 'CurrentUserName',
get: async ({get}) => {
const response = await myDBQuery({
userID: get(currentUserIDState),
});
return response.name;
},
});
function CurrentUserInfo() {
const userName = useRecoilValue(currentUserNameQuery);
return <div>{userName}</div>;
}
function MyApp() {
return (
<RecoilRoot>
<React.Suspense fallback={<div>加载中。。。</div>}>
<CurrentUserInfo />
</React.Suspense>
</RecoilRoot>
);
}
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/vEXNQPGLOj1q_B-218mtNA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。