重谈react优势——react技术栈回顾

发布时间:2020-06-18 13:06:54 作者:周陆军
来源:网络 阅读:983

react刚刚推出的时候,讲react优势搜索结果是几十页。

现在,react已经慢慢退火,该用用react技术栈的已经使用上,填过多少坑,加过多少班,血泪控诉也不下千文。

今天,再谈一遍react优势,WTF?

React的收益有哪些?React的优势是什么?react和vue、angularJS等其它框架对比优势?


而作为总结回顾。react在工程实践中,带来哪些思想上的质变?


virtual dom虚拟DOM概念

它并不直接对DOM进行操作,引入了一个叫做virtual dom的概念,安插在javascript逻辑和实际的DOM之间,好处是减少DOM操作,减少DOM操作的目的是提高浏览器的渲染性能。

虚拟dom就中小型项目而言,的确从表象上看不出太多的优势,因为它解决的是底层的dom渲染,IO开销问题。但是想想facebook的体量,不难猜出react的诞生是为了解决更复杂更大型的项目开发和管理的。

实际上React和Vue其实也在操作DOM,只是比较高效地在操作DOM而已,虚拟DOM其实最终也会映射到真实DOM,虽然虚拟DOM只会将变化的部分更新到真实DOM,但实际上直接操作DOM也可以通过某些方式去优化,那么:

    1、操作data,不直接操作DOM有什么好处?

         更少的代码做更多的事。

    2、操作data会给DOM操作带来什么不好的地方吗?

          不会,但是不是所有功能“使用操作data”都可以代替的。

    3、会不会比直接操作DOM存在什么难度?

         不会有难度,但是思维需要有一些转变。


JSX虽然做了抽象视图,但她是声明式API,能够保证你看一眼就知道组件树的结构,譬如:


这结构还算清楚吧,基本一眼就知道这个一个面板由输入框、列表、摘要组成,而且布局也清楚了,自上而下。而且,通过查看一个源文件就可以知道你的组件将会如何渲染。这是最大的好处,尽管这和 Angular 模板没什么不同。具体参看:ReactJS For Stupid People


之前写UI的时候往往为了性能,要设计很多DOM的操作逻辑,用了react之后,这些都不给你做了,由他的state跟props来传递给VDOM,很省事,更专注于UI层面。


学会了react以及这个JSX语法,你不光可以通过react写web;也可以通过react-native写ios或者android的应用;甚至可以通过react-blessed写terminal可视化应用;当然也可以通过react-native-desktop写桌面应用。因为JSX这种声明式语法实际是在构建一个抽象的视图层,这种抽象可以通过不同适配器适配到各种显示终端,这总够屌吧?


unidirectional data flow-单向数据流

React倡导使用flux模式来进行组件间数据传输,这种做法叫unidirectional data flow(单向数据流),单向数据流的好处是与之前angularJS提出的two-way data binding相比较而言,因为单向,所以各种变化都是可预计、可控制的。不像two-way data binding那样,变化一但复杂起来,大家都互相触发变化,到最后一个地方变了,你根本猜不出来她还会导致其他什么地方跟着一起变。这个需要大量实践才能有所感受,如果你初学,那听听就算了,不必死磕。


react项目结构更加清晰:

virtual dom、redux、action,分部分别存放,就象java写后台查数据本来用jdbc一条sql就搞定,但分成action service dao分门别类地存放,这样维护性好,大公司的代码需要规范,这样出了问题好找原因。


组件化

一切都是component:代码更加模块化,重用代码更容易,可维护性高。

这里就涉及到react的 架构,比如:

smart, dumb component  

把组件分成两大类 Smart Components (容器) & Dumb Components(颗粒化组件)

这样做的好处:

参看文章:Smart and Dumb Components 

高阶组件(HOC-higher order component) 

高阶组件(HOC)是react中对组件逻辑进行重用的高级技术。但高阶组件本身并不是React API。它只是一种模式,这种模式是由react自身的组合性质必然产生的。

具体而言,高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件

const EnhancedComponent = higherOrderComponent(WrappedComponent);

对比组件将props属性转变成UI,高阶组件则是将一个组件转换成另一个新组件。

好处:使用高阶组件(HOC)解决交叉问题

参看文章:高阶组件

总结下,看看一个人的组件化水准,

并理解react 内部实现原理

具体问题如下:


同构、纯粹的javascrip

因为搜索引擎的爬虫程序依赖的是服务端响应而不是JavaScript的执行,预渲染你的应用有助于搜索引擎优化。


react一些常见问题:

setState()函数在任何情况下都会导致组件重渲染吗?如果setState()中参数还是原来没有发生任何变化的state呢?

对setState用得深了,就容易犯错,所以我们开门见山先把理解setState的关键点列出来。


传入 setState 函数的第二个参数的作用是什么?

该函数会在setState函数调用完成并且组件开始重渲染的时候被调用,我们可以用该函数来监听渲染是否完成(一般没有什么卵用)

 调用 setState 之后发生了什么?

 在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个UI界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。


用shouldComponentUpdate做优化的意义大吗?shouldComponentUpdate将带来可测量和可感知的提升?

如果不能,那就别用:你可能应该避免用它。据React团队的说,shouldComponentUpdate是一个保证性能的紧急出口,意思就是你不到万不得已就别用它。具体参考:什么时候使用shouldComponentUpdate方法?

一般情况下setState() 确立后总是触发一次重绘,除非在 shouldComponentUpdate() 中实现了条件渲染逻辑。如果使用可变的对象,但是又不能在 shouldComponentUpdate() 中实现这种逻辑,仅在新 state 和之前的 state 存在差异的时候调用 setState() 可以避免不必要的重新渲染。


react异步数据如ajax请求应该放在哪个生命周期?

对于同步的状态改变,是可以放在componentWillMount,对于异步的,最好好放在componentDidMount。但如果此时有若干细节需要处理,比如你的组件需要渲染子组件,而且子组件取决于父组件的某个属性,那么在子组件的componentDidMount中进行处理会有问题:因为此时父组件中对应的属性可能还没有完整获取,因此就让其在子组件的componentDidUpdate中处理。

具体参考:《react异步数据如ajax请求应该放在哪个生命周期?》


React 中的 keys 是什么,为什么它们很重要?

在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。

keys 是帮助 React 跟踪哪些项目已更改、添加或从列表中删除的属性。

每个keys 在兄弟元素之间是独一无二的。我们已经谈过几次关于一致化处理(reconciliation)的过程,而且这个一致化处理过程(reconciliation)中的一部分正在执行一个新的元素树与最前一个的差异。keys 使处理列表时更加高效,因为 React 可以使用子元素上的 keys 快速知道元素是新的还是在比较树时才被移动的。

而且 keys 不仅使这个过程更有效率,而且没有keys,React 不知道哪个本地状态对应于移动中的哪个项目。所以当你 map 的时候,不要忽略了 keys 。


受控组件( controlled component )与不受控制的组件( uncontrolled component )有什么区别?

React 的很大一部分是这样的想法,即组件负责控制和管理自己的状态(任何改变代用setSate处理)

那么不受控组件呢?组件数据不全部是setState来处理,还有DOM交互,比如refs这玩意来操控真实DOM

虽然不受控制的组件通常更容易实现,因为您只需使用引用从DOM获取值,但是通常建议您通过不受控制的组件来支持受控组件。


主要原因是受控组件支持即时字段验证,允许您有条件地禁用/启用按钮,强制输入格式,并且更多的是 『the React way』。


描述事件在React中的处理方式


为了解决跨浏览器兼容性问题,您的 React 中的事件处理程序将传递SyntheticEvent 的实例,它是 React 的浏览器本机事件的跨浏览器包装器。


这些 SyntheticEvent 与您习惯的原生事件具有相同的接口,除了它们在所有浏览器中都兼容。有趣的是,React 实际上并没有将事件附加到子节点本身。React 将使用单个事件监听器监听顶层的所有事件。这对于性能是有好处的,这也意味着在更新DOM时,React 不需要担心跟踪事件监听器。




在什么情况下你会优先选择使用 Class Component 而不是 Functional Component?

在组件需要包含内部状态或者使用到生命周期函数的时候使用 Class Component ,否则使用函数式组件。


简单介绍下react的diff

计算一棵树形结构转换成另一棵树形结构的最少操作,是一个复杂且值得研究的问题。传统 diff 算法通过循环递归对节点进行依次对比,效率低下,算法复杂度达到 O(n^3),其中 n 是树中节点的总数。O(n^3) 到底有多可怕,这意味着如果要展示1000个节点,就要依次执行上十亿次的比较。这种指数型的性能消耗对于前端渲染场景来说代价太高了!现今的 CPU 每秒钟能执行大约30亿条指令,即便是最高效的实现,也不可能在一秒内计算出差异情况。。React 通过制定大胆的策略,将 O(n^3) 复杂度的问题转换成 O(n) 复杂度的问题。

 react的diff 策略:

基于以上三个前提策略,React 分别对 tree diffcomponent diff 以及 element diff 进行算法优化,事实也证明这三个前提策略是合理且准确的,它保证了整体界面构建的性能。 


既然 DOM 节点跨层级的移动操作少到可以忽略不计,针对这一现象,React 通过 updateDepth 对 Virtual DOM 树进行层级控制,只会对相同颜色方框内的 DOM 节点进行比较,即同一个父节点下的所有子节点。当发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。这样只需要对树进行一次遍历,便能完成整个 DOM 树的比较。

重谈react优势——react技术栈回顾

updateChildren: function(nextNestedChildrenElements, transaction, context) {
  updateDepth++;
  var errorThrown = true;
  try {
    this._updateChildren(nextNestedChildrenElements, transaction, context);
    errorThrown = false;
  } finally {
    updateDepth--;
    if (!updateDepth) {
      if (errorThrown) {
        clearQueue();
      } else {
        processQueue();
      }
    }
  }
}

分析至此,大部分人可能都存在这样的疑问:如果出现了 DOM 节点跨层级的移动操作,React diff 会有怎样的表现呢?是的,对此我也好奇不已,不如试验一番。


如下图,A 节点(包括其子节点)整个被移动到 D 节点下,由于 React 只会简单的考虑同层级节点的位置变换,而对于不同层级的节点,只有创建和删除操作。当根节点发现子节点中 A 消失了,就会直接销毁 A;当 D 发现多了一个子节点 A,则会创建新的 A(包括子节点)作为其子节点。此时,React diff 的执行情况:create A -> create B -> create C -> delete A。

重谈react优势——react技术栈回顾

由此可发现,当出现节点跨层级移动时,并不会出现想象中的移动操作,而是以 A 为根节点的树被整个重新创建,这是一种影响 React 性能的操作,因此 React 官方建议不要进行 DOM 节点跨层级的操作。


提示:在开发组件时,保持稳定的 DOM 结构会有助于性能的提升。例如,可以通过 CSS 隐藏或显示节点,而不是真的移除或添加 DOM 节点。

component diff:

如下图,当 component D 改变为 component G 时,即使这两个 component 结构相似,一旦 React 判断 D 和 G 是不同类型的组件,就不会比较二者的结构,而是直接删除 component D,重新创建 component G 以及其子节点。虽然当两个 component 是不同类型但结构相似时,React diff 会影响性能,但正如 React 官方博客所言:不同类型的 component 是很少存在相似 DOM tree 的机会,因此这种极端因素很难在实现开发过程中造成重大影响的。

重谈react优势——react技术栈回顾

element diff:

当节点处于同一层级时,React diff 提供了三种节点操作,分别为:INSERT_MARKUP(插入)、MOVE_EXISTING(移动)和 REMOVE_NODE(删除)

如下图,老集合中包含节点:A、B、C、D,更新后的新集合中包含节点:B、A、D、C,此时新老集合进行 diff 差异化对比,发现 B != A,则创建并插入 B 至新集合,删除老集合 A;以此类推,创建并插入 A、D 和 C,删除 B、C 和 D。

重谈react优势——react技术栈回顾

React 提出优化策略:允许开发者对同一层级的同组子节点,添加唯一 key 进行区分,虽然只是小小的改动,性能上却发生了翻天覆地的变化!

重谈react优势——react技术栈回顾

总结


diff算法作为react的核心,非三言两语能够说起道明,建议参看:React 源码剖析系列 - 不可思议的 react diff 


怎么看待不可变数据?

这个暂待完善


*** (server side render)会有什么性能问题,哪些会引起内存泄露,引入 redux 后怎么处理请求的逻辑


参考:从零开始搭建React同构应用(三):配置×××


参考文章:

什么时候使用shouldComponentUpdate方法?

setState为什么不会同步更新组件状态

setState:这个API设计到底怎么样

高阶组件

转载请注明文章来源:重谈react优势--react技术栈回顾 - ECMAScript,js,javascript - 周陆军的个人网站https://www.zhoulujun.cn/html/webfront/ECMAScript/jsBase/2018_0424_8101.html,如有不妥之处,望告知!

推荐阅读:
  1. vue2.x入坑总结—回顾对比angularJS/React
  2. React是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

react 前端 技术栈

上一篇:Mac终端解压:让解压更逼格

下一篇:Linux 中的MYSQL数据库常用管理语言

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》