阿里云前端工程师靖鑫带来了React高阶组件在业务场景中的应用。首先从高阶组件的思想开始谈起,帮助我们灵活管理代码,介绍了简单的HOC示例、传递参数的HOC示例、反向继承HOC,进而讲述了HOC库recompose,然后通过图解重点说明了DOM DIFF算法,最后对新架构Fiber进行了简要介绍。
数十款阿里云产品限时折扣中,赶快点击这里,领券开始云上实践吧!
以下是精彩视频内容整理:
什么是高阶组件?
所谓高级组件本质上就是一个函数,接收一个组件后返回另外一个新的组件,这么做的意图就是为了增强(enhance),比如装饰、增添行为、增添逻辑等,都可以放在高阶组件中,高阶组件是一种可以增强组件的能力。
最简单的HOC示例
如图所示,使用装饰器方式来使用,声明一个函数,定义一个高阶组件,给目标组件包裹一层样式。接收一个组件,返回一个函数式组件props,将props全部解构传递给wrappedcomponent,高阶组件一定不可以影响原来的业务。在此基础上,我们在外面增加一些能力,比如在被包裹的组件外围,由一个card的dom元素包裹在一起。
传递参数的HOC示例
首先设置参数,生成特定的HOC,再传递给目标组件使用。定义一个stylefactory方法,返回高阶组件,而不是组件。在高阶组件的前一步做了一层处理,利用闭包思想,它的入参是classname,返回的高阶组件一直内嵌classname。
反向继承HOC
前两种高阶组件的特点是不会修改原来的组件,只是在上面包裹一些东西。而有时会更改渲染逻辑,比如当wrappedcomponent资源没有加载回来时,页面显示loading,加载完成后才会显示,这就需要更改显示逻辑。反向继承HOC返回的React组件继承了被传入的组件,既然继承了被传入组件,是不是所有方法都可以使用呢?比如在render方法中渲染出之前的内容,可以直接使用super.render(),我们可以在render方法中重新写一个render,这样原有内容就都展示不出来了,想展示原有内容只需要调用super.render()即可。所以它能够访问到的区域、权限更多,相比属性代理方式,它更像打入组织内部,对其进行修改。
脑洞大开的HOC库——recompose
Recompose有很多很多实用的库,下面为大家介绍几个常见场景应用。
第一个情景是展示哪一个。有些时候,你需要根据后端返回值来做决策,例如展示Loading组件,之前你可能会将这部分判断写在render方法里,现在你可以将它们抽离出来了,将判断逻辑提前拿出来,我们的组件尽可能都是函数组件,不需要额外维护状态。顺带也可以将生命周期等方法挪出来,用高阶组件lifecycle代替。
如图所示,高阶组件是一层层被包裹起来的,最里面为wrappedcomponent组件,外部包裹一层branch高阶组件,branch专门用来做条件判断的,根据某一个值去判断真假,如果是真就会直接加载loading组件传递给你的业务组件,如果是假就会把另外一个高阶组件传递给你的业务组件,我们不需要在render方法里做这个判断。然后将代码发起请求的方式拿到lifecycle中去做,lifecycle本身可以写一些生命周期方法,state修改完后传递到branch高阶组件中透传,最终到达业务组件。
第二个情景是维护组件自身的State。我们经常会有Tab切换,选择第一个、选择第二个等,将高亮的加上classname,点击展开(收起)等与业务数据无关的状态需要维护,回想以前的代码,我们可能会使用setState,但有时侯state和setstate不是很方便,组件可能非常长,也可能与真实业务代码融合在一起,容易使开发者混淆逻辑。有了recompose,我们可以使用withState和withHandlers来避免手动setState的出现,将展示逻辑和UI进行分离。
第三个情景是组件嵌套。在实际项目中,我们会有多个地方使用到Modal组件,如果都重复一遍上述代码,boilerplate代码将非常多,为此,我们可以将它转变为一个高阶组件,与常规方式不同,我们本次使用recompose提供的HOC来组织我们的代码。
DOM DIFF算法简析
DOM DIFF是react应用中的精华所在,DOM DIFF在使用时有一些约定如下:
1. DOM节点跨层级的移动操作特别少,可以忽略不计(例如A原本和B平级,随后A变成B的子节点)
2. 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构(A和B组件结构不一致)
3. 同一层级的一组子节点,它们可以通过uuid进行区分。
DIFF算法在执行时有三个维度,分别是Tree DIFF、Component DIFF和Element DIFF,执行时按顺序依次执行,它们的差异仅仅因为DIFF粒度不同、执行先后顺序不同。
Tree DIFF是对树的每一层进行遍历,如果某组件不存在了,则会直接销毁。如图所示,左边是旧属,右边是新属,第一层是R组件,一模一样,不会发生变化;第二层进入Component DIFF,同一类型组件继续比较下去,发现A组件没有,所以直接删掉A、B、C组件;继续第三层,重新创建A、B、C组件。
如图所示,第一层遍历完,进行第二层遍历时,D和G组件是不同类型的组件,不同类型组件直接进行替换,将D删掉,再将G重建。
Element DIFF紧接着以上统一类型组件继续比较下去,常见类型就是列表。同一个列表由旧变新有三种行为,插入、移动和删除,它的比较策略是对于每一个列表指定key,先将所有列表遍历一遍,确定要新增和删除的,再确定需要移动的。如图所示,第一步将D删掉,第二步增加E,再次执行时A和B只需要移动位置即可。
React Fiber
React Fiber是react执行渲染时的一种新的调度策略,JavaScript是单线程的,一旦组件开始更新,主线程就一直被React控制,这个时候如果再次执行交互操作,就会卡顿。
React Fiber重构这种方式,渲染过程采用切片的方式,每执行一会儿,就歇一会儿。如果有优先级更高的任务到来以后呢,就会先去执行,降低页面发生卡顿的可能性,使得React对动画等实时性要求较高的场景体验更好。
文章部分配图来自此文章:https://zhuanlan.zhihu.com/p/20346379
本文由云栖志愿小组毛鹤整理,编辑百见