认知React框架

eact框架的出现,意味着前端进入了一个新的时代。

作为后端,开始做前端的相关项目以来已经有段时间,刚开始使用React觉得很爽,挺好用的嘛,不过做着做着发现,理解react整个生态,理解整个Js的生态,理解样式,理解结构前端才是最难的部分。不过这里我只想谈谈自己对React的一些了解和认知吧,关于生态的东西我后面再聊。自己的感觉学习整个React的开发生态,还是很陡峭的,React框架本身还是很好理解的,不过如果加上各种东西柔和在一起,那真是挺烦躁的!!不过嘛,我们工程师就是为了搞清这些东西而存在的!!

之前在在‘认知Web前端’中,我觉得现代的Web前端的发展是分分合合,到了React我觉是首次实现了分和合的高境界。所谓的分是组件化的思维,所谓合是前端技术栈的开发方式之合。组件化的思维其实很早就出现,但是没有很好的实现,知道React的出现,把其推向的高潮。而与此同时迎着组件化的浪潮,我们的开发方式也是把原来的html、css、js全都柔和在了一次开发,组件作为单体是合,作为一个更大的整体的一部分又是分。而这使得复用性、开发效率大大提升。

这里是我使用学习React框架,写前端有一段时间了,参考各种文章以来的一些总结吧。整个是比较粗粒度了,后面我想对每一个有意思的部分都单独写一篇文章来详细深入了解。不断去理解深入实践,不断的更新这里的认知。

理念

Web前端的主要开发技术HTML、CSS、JS三大金刚。其中只有JS是一个图灵完备的编程语言,这也使得只有JS能够更加灵活,能够解决所有的计算问题。为什么这里要提一下这个呢?

因为我觉得这就是React的理念,把JS作为主体,H5、CSS作为辅助,使用JS来控制构建HTML、CSS。并且React也做到了这一点,而且随着整个生态的扩展,这种趋势越发明显。

在React的官网上它把自己定位为:

正是因为这一理念,让React变得无比强大。我们不是在h5中嵌入JS来控制它,而是我们用JS来完全控制整个绘制、交互的一切,而HTML和CSS变成了JS的一个附属品。原来JS、CSS不过是HTML的一个附属品。整个关系链路发生了反转式的变化。

基础理论概念

有一些基本的概念我们首先需要了解一下,这些概念在我看来是为我自身构建了一个内在的模型去理解React背后的设计原理,这里许多的概念都是可以水平迁移的,了解这些对了解相似框架,以及背后的思考极为重要。当这些概念内化到我们内心的时候,就形成了一张图,来龙去脉,即使是对我们书写代码、架构React应用也有着极好的作用,也对于我们理解前端的一些原理有着很好的作用。

Transformation

UI到底是什么?我们在屏幕上所看到的到底是什么?

React团队给到的答案是,我们所看到的UI,是背后数据的一个映射;不同的数据反映不同的UI展现。

这个答案简单但是极为重要。

Abstraction

抽象的意义是:我们不可能通过一个组件就去构建一个应用,而是通过把React应用分解成一个个复用的组件来去组合。组件化的过程其实就是抽象的过程,比如理解业务,构建通用的业务组件,这是一种抽象的能力。比如我们平时用的Ant Design的组件,本身这个设计规范就是一种抽象能力的体现,抽象出真正可以长期复用的组件出来。

这种抽象能力使我们这些程序员非常重要的一个能力,以前很多时候更多的在后端中会提到抽象,因为后端是看不见摸不着的,所以我们更多的去强调,但是现在作为前端,我们也需要这种能力。组件化的本质是一种抽象的能力,这种能力可大可小,小的我们在一个项目中抽象一些业务组件,大的就像做一个Ant Design一样的基础组件库,更大的可能是更加抽象的组件,比如React Motion这类的。这种抽象的能力会伴随着我们的职业生涯,这也是需要我不断精进的地方。

Composition (And Inheritance)

在后端领域就一直有组合还是继承的争论,什么时候用组合?什么时候用继承?不过在React里面的Composition的概念和后端是有一些不同的,继承的概念比较相似。我也是刚开始通过和后端的概念的类比,比较去理解的。在React官方的最佳实践中其实是不推荐使用继承的,这也是我踩得坑,之前因为一些后端的思路,在项目中用了大量的继承,后来发现这样是不可行的。这也是提醒了自己,对于不同的领域知识,即使有的概念相似,但是也不能生搬硬套去应用相同的思路,可以参考帮助理解,但是如果完全使用后端的思路去写前端,真的会有大麻烦的。

State And Update

每一个组件实例都会在它的内部保存一份状态,状态其实是组件内部的数据,这些数据通常用来控制、存储一些元素的属性值。比如一个输入框,输入框用户输入的值我们可以保存在状态中。

状态的更改会伴随着一次更新,通常我们使用setState来更改状态数据,数据的变化最终会导致组件的重新渲染(或局部重新渲染)。

Memoization

每一个我们写的React组件其实是一个function,当我们用高级语法写的时候会忽略这么一个概念,最后react是在执行一个个的function。这就遇到一个问题就是如果fuction的入参相同,那么其实function的结果都是相同的,每次不断的执行function其实是有消耗的,所以在React的内部会有一个机制,对于反复相同的function,入参相同那么就只执行一次,后面直接拿结果就好了。

这就好像我们把1*1的结果直接保存,而不是每次都去计算一个1*1然后得到结果。这种优化方式还是会经常碰到的。

Reconciliation

不知道怎么样能够很好的翻译这个词,所谓的Reconciliation就是React所使用的算法用来区分一棵树和另外一棵树之间的区别,找到哪些部分需要更新。Reconciliation是这种算法的名称,就我所知,React15及之前和React16分别有两个不同的实现,这个后面我会讲到。

Scheduling

调度安排的作用其实就是去安排一系列的工作如何执行、何时执行。

生命周期

React通过一系列的计算,最后的目的是渲染成Web页面也就是一棵DOM树,然后有因为一些比如我们的操作关闭了页面等等,最后整个DOM树销毁了。这样一个从计算到渲染再到销毁的过程,就好像是一个生命从出生到死亡的生命历程。这个就是一个生命周期,有了生命周期,我们就可以更好去控制React在不同的阶段的操作。

那么在整个过程中,React是一步一步的做的,就好像是一个管道,中间会有很多步,那么React在中间的许多步都埋了一个一个的钩子,这一个个的钩子就是React暴露给我们能够在生命周期内自定义的部分。下面列举了React提供的生命周期的函数:

componentWillMount

componentDidMount

componentWillReceiveProps

getSnapshotBeforeUpdate

shouldComponentUpdate

componentWillUpdate

componentDidUpdate

render

componentWillUnmount

componentDidCatch

以上在React15及之前的版本都是可用的,但是React16之后有些生命周期被移除了。

可以先参考这里的文档了解详情:https://reactjs.org/docs/react-component.html

理解JSX

JSX是React团队创建的一种语法糖吧,方便我们开发人员可以在JS里面书写HTML形式的Tag。它只是开发阶段的过渡形式,在编译之后会转换成一个个的Function。你如我们可以这样:

consthelloWorld=

Hello World

;我们可以对比一下不用JSX。// 上面的编译后就自动转换为下面的了,方便很多!consthellWorld=React.createElement('p',null,'Hello World');

有了JSX之后变得非常的直观,上面的例子,如果没有JSX那么真的开发起来很痛苦啊!!

理解Virtual DOM树和Diffing算法

在React最最常提到的两个概念就是VDOM树,以及Diffing算法。

Virtual DOM直译过来就是虚拟DOM树,这个概念更多的是一个编程的模式的存在。所谓的虚拟DOM树就是抽象真正的DOM,比如一个div的dom,那么在会抽象一个div的虚拟dom,这个dom里面包含了真正dom元素的引用、类型等等参数,然后在内存中构建一个和真实的dom树一样的结构的树。正因为做了这么一层抽象,React就可以先不用直接操作dom了,而是在内存中先构建一个必要的虚拟dom树,然后做一系列的运算,等做完后,在把最后的计算的结果,再绘制成真正的DOM树。这样可以大大减少DOM操作的次数,提高性能。

我们知道同一时间只有一个完整的dom树,当我们做数据变动的时候,我们就是在更改dom树的结构,最粗暴的方法的全量替换,那么这肯定是不可取的,这就提到了React团队使用的Diffing算法,称为Reconciliation。整个算法做的事情就是计算出当前状态的dom树和下一个状态的dom的区别,从而重新渲染的时候只渲染更改的部分。比如下图示意图,我们需要变更的部分其实就是右边的分支多了一个元素。

这个算法的本质是寻找两颗树的异同部分,然后把一棵树变成另外一颗树。实现算法的过程其实是一个大的递归,去遍历所有的元素,为了是算法性能,React做了两个假设:

两个不同类型的元素会产生两颗不同的树

通过开发人员手动的设定唯一的key值来标示子元素的不变性

通过这两个假设,通过一次递归就可以计算出下一棵树其复杂度控制在O(n).

算法执行步骤

首先是比较组件根元素的类型,如果不同直接删除原来元素直接替换。比如把div改为了img,把a改为了div等等。这个过程是把老的元素删除,然后新建新的元素插入。

如果组件根元素类型相同,那么比较新老元素的属性,并把新的属性更新上去。

接着遍历组件的根元素的子元素,看是否有唯一的key,如果有唯一的key,react就知道哪些元素是新增,哪些是已有的,那么就没比较重新渲染子元素了,如有有新增就直接插入就可以了。

React应用的性能优化点:

算法不难理解,了解了这些机制后,我们就知道平时写react代码的时候,可以有针对性,如何写出搞性能的React代码。

React最后更具算法得出的变更,最后转换成一些列直接调用了原生的dom操作。

很明显这里产生了几个注意点

尽量保证组件根元素不要有变化

组件根元素内的子元素保证有唯一的Key

通过上述的算法,我们就知道了到底是哪一部分变化了,这就是这个Reconciliation,也就是diffing算法做的事情。

那么接下来的一步就是render了,把变化的部分变成一系列真实的DOM操作。 也正是这种两步走的方式,使得React可以做到跨平台,对于IOS、android平台,重写render部分。

理解React Fiber架构

React Fiber的出现是为了解决上述的算法的问题而出现的,基本上是对于React核心算法的一次重写。这个算法要比上述的diffing算法要复杂很多。在React 16版本之后已经实现了对核心算法的重写,所以React 15和React 16其实是一个分水岭了,虽然在使用的过程中我们感知不多,但是巨大的改变已经发生。这里讲讲自己的一些理解吧。

React Fiber的出现为React打开了一个新的未来。和上一章描述的算法有本质的区别。

让我们用白话的方式重新在走一下上一章的Reconciliation算法。首先比如我们创建一个React App,如下:

Hello World<Title></Page></App>

那么首先第一次创建的时候,React会把它转换为Virtual DOM,然后渲染到可能是网页也可能是native的app上。接下来的改动就是一次次的更新了,当这个app改动的时候,React会重新创建一棵Virtual DOM树,然后比较两棵树这件的差异,至此无关任何平台。如果我们是在网页上使用需要渲染为DOM即使是一个小小的改动,那么React会遍历整个虚拟树,找出不同处,比如更新一个属性,改变一个样式又或者删除一个dom节点等等。这样一个过程就是React 15及以前的Reconciliation算法,简单有效。这个过程在任何时候都会反复的进行,不管我们的浏览器是否变慢了,或者渲染的是否在屏幕上等等,任何事情都无法阻止这个渲染的流程。

问题到底处在哪里?如果没有问题React团队也就不需要去做这么大的改动了。

通过上述的描述,React的整个重diffing到更新的整个过程是自动进行的并且无法被停止的,而这就是问题所在。当我们应用很小的时候,其实没什么问题,即使是有很大的更新也不会多到哪里去,也不会发现有什么性能问题,但是如果我们应用开始变得很大很复杂,那么每次React这么一个渲染的流程会导致性能下降,React需要更多的时间来计算,从而导致失帧的现象,拉长了某一帧的时间,导致每秒的帧率不均衡。一般要保证体验的良好,保持每秒60帧是比较好的,也就是大约每一帧的16ms,并且要保持这种频率,而不是一会儿40帧一会儿60帧,如果是这样对用户来说是不能接受的,尤其是有动画等效果的时候更是如此,失帧带来的体验下降是巨大的。也就是整个浏览器页面的计算量,我们需要保证每一帧在16ms内完成。我们会发现:

1.其实在UI界面中,我们并不需要每次有更新就执行整个渲染流程。

2.不同的更新渲染,其实是有优先级的,比如动画、用户的交互等我们可能希望执行更快,比起更新一个数据。

如何达到这种效果呢?

通过上述的场景描述,React Fiber就是为了解决这些事情,它把不同的更新渲染操作分解为一个个小的任务块,他们有优先级、性能要求等等,然后React就可以调度安排这些任务,不再一次性不停的执行,而是把不同的任务根据不同的优先级、性能等把这些任务均匀的分布在每一帧里面,从而使客户体验更加顺滑。

之前在其他文章中看到一个比喻来形容React Fiber。我们是Renderer程序员,然后有个Reconciler产品经理,本来呢,产品经理提需求比如做一个hotfix、新功能,Renderer程序员没法拒绝只能做,即使现在手头上已经有很多事情,这就导致完成需求需要更长时间,业务方就不满意了。那么有了React Fiber之后呢,我们就可以把hotfix、新需求做优先级安排的去做,这样各方都会比较满意。

这里就不具体展开了,后面在通过另一篇文章来详细阐述。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 原教程内容详见精益 React 学习指南,这只是我在学习过程中的一些阅读笔记,个人觉得该教程讲解深入浅出,比目前大...
    leonaxiong阅读 2,813评论 1 18
  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,810评论 0 24
  • 本笔记基于React官方文档,当前React版本号为15.4.0。 1. 安装 1.1 尝试 开始之前可以先去co...
    Awey阅读 7,663评论 14 128
  • 中国学习西方国家的素质教育,可能学来了表面的一套。 我们把各种东西列入评判标准,比如艺术、体育,但是本质还是工业化...
    天堂的色彩阅读 527评论 0 2
  • 2017.7.28 星期五 雨转多云 亲子日记(95) 今天早早的收拾一些吃的喝的,随时用的,比如雨伞,纸巾……反...
    于泽妈妈阅读 171评论 0 1