面试小抄 - react

生命周期

react 生命周期

其中标红的( componentWillMount、 componentWillReceiveProps、 componentWillUpdate )是 react 16.3 版本要移除的生命周期,移除原因:react 打算在17版本中,添加 async rendering , react 将一个更新过程分为 render 前后两个阶段,render 前是可以被打断(比如有紧急任务),当生命周期被打断后,再次执行,并不会从断点继续执行,是重新执行的,所以这些生命周期就可能会运行多次。

同时为了弥补失去这三个生命,react 新增了两个生命周期: static getDerivedStateFromProps、 getSnapshotBeforeUpdate

static getDerivedStateFromProps

getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。

此方法适用于罕见的用例,即 state 的值在任何时候都取决于 props。

static getDerivedStateFromProps(nextProps, prevState) {
  //根据nextProps和prevState计算出预期的状态改变,返回结果会被送给setState
  //这是一个static,简单说应该是一个纯函数
}

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()。

getSnapshotBeforeUpdate 返回的值会做为第三个参数传递给 componentDidUpdate。

getSnapshotBeforeUpdate(prevProps, prevState) {
  ...
  return snapshot;
}

componentDidUpdate(prevProps, prevState, snapshot) {
  
}

性能优化

shouldComponentUpdate

这是一个组件的子树。每个节点中,SCU 代表 shouldComponentUpdate 返回的值,而 vDOMEq 代表返回的 React 元素是否相同。最后,圆圈的颜色代表了该组件是否需要更新。

shouldComponentUpdate

react 父组件触发更新时,它的所有子组件都会触发更新(即使 props 和 state )并没有改变,这样时候我们在子组件中添加 shouldComponentUpdate 生命周期,判断状态是否变更,如果没有变更返回 false , 这个子组件就不会重新 render。

React.PureComponent

React.PureComponent 是 React 自动帮我们在 shouldComponentUpdate 做了一层浅比较。

if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate = !shallowEqual(prevProps, nextProps)
  || !shallowEqual(inst.state, nextState);
}

React.memo

React.memo 是一个高阶组件,类似于 React.PureComponent,不同于 React.memo 是 function 组件,React.PureComponent 是 class 组件。

const MyComponent = React.memo(function MyComponent(props) {
  /* 使用 props 渲染 */
});

长列表优化

  • 虚拟列表:用数组保存所有列表元素的位置,只渲染可视区内的列表元素,当可视区滚动时,根据滚动的 offset 大小以及所有列表元素的位置,计算在可视区应该渲染哪些元素。常用库( react-window 、 react-virtualized )
  • 事件代理:简单的说就是将同类行的子元素事件,绑定在一个父元素上,从而达到减少事件的注册。
  • 懒加载:常用于长列表图片加载,组件先不渲染,当监听到组件可以时,在去渲染组件。常用库( react-lazyload )

key

key 帮助 React 识别哪些元素改变了,比如被添加或删除。当子元素拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素。正确的使用 key 可以使 react 的更新开销变小。

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

代码复用

Mixin

概念:将公用方法包装成 Mixin 方法,然后注入各个组件,从而实现代码复用。( 已经不推荐 )

缺点:

  • 命名覆盖:比如你在 A Mixin 中定义的 get 方法,在 B Mixin 中也定义了 get 方法。
  • 复杂度高后,容易混乱:比如 Mixin 中可以调用 setState 的,当多个 Mixin 中都调用了 setSate , state 的更新来源会变得混淆不清。
  • ES6 class 语法不支持 Mixin。

HOC(高阶组件)

概念:可以理解为组件工厂,传入原始组件,添加功能,返回新的组件。

缺点:

  • 命名覆盖,难以溯源:当存在多个 HOC 时,仅仅通过新组件,并不能知道 props 是来源于哪个 HOC,同时如果两个工厂,传入了相同名称的props,就会产生覆盖。
  • 静态构建:工厂返回的新组件,不会立即执行,即 HOC 工厂函数里定义的生命周期函数只有新组建渲染时才会执行。

Render Props

概念:Render Props 就是一个 render 函数作为 props 传递给了父组件,使得父组件知道如何渲染子组件。

优点:

  • 解决了命名冲突,难以溯源的问题,可以通过 render props 的参数直接看到 props 来源于哪个组件。
  • 动态构建,可以更好的利用组件内的生命周期。

缺点:

  • 无法利用 shouldComponentUpdate 来实现渲染性能的优化。

React hooks

概念:hooks 遵循函数式编程的理念,主旨是在函数组件中引入类组件中的状态和生命周期,并且这些状态和生命周期函数也可以被抽离,实现复用的同时,减少函数组件的复杂性和易用性。

hooks api:

  • 基础:useState、 useEffect、 useContext
  • 额外:useReducer、 useCallback、 useMemo、 useRef、 useImperativeHandle、 useLayoutEffect、 useDebugValue

一个简单的 custom hooks:

    
import { useEffect } from 'react';

function useTitle(title){
  useEffect(()=>{
    document.title = title;
  }, [title]);
}

虚拟 Dom diff 算法

虚拟 Dom:react 将 Dom 抽象成一个对象树,通过对比新旧两个树的区别(diff 算法),然后将更新部分渲染出来。

diff 算法基于两个策略:

  • 两个相同组件产生类似的 DOM 结构,不同的组件产生不同的 DOM 结构。
  • 对于同一层次的一组子节点,它们可以通过唯一的 id 进行区分。

逐层进行节点比较

在 react 中,树的比对非常简单,react 只会对两棵树进行逐层比较,即比较处于同一层级的节点。

逐层比较

节点比较

节点比较分成两种情况:(1)节点类型不同,(2)节点类型相同,属性不同。

情况(1):

节点不同
// DOM Diff 操作
C.destroy();
D = new D();
A.append(D);

情况(2):

//变更钱
<div style={{fontSize: '16px'}} ></div>
//变更后
<div style={{color: 'red'}} ><div/>
//执行操作
=> removeStyle fontSize
=> addStyle color 'red'

列表节点的比较(key)

当列表中插入 F 节点,如下图:

列表节点的比较

这个时候就会有两种情况:(1)节点没有表示唯一 key ,(2)节点表示了唯一 key

情况(1)结果如下图:

列表节点的比较,没有唯一 key
//执行过程
C unmount
D unmount
E unmount
F mount
C mount
D mount
E mount

情况(2)结果如下图:

列表节点的比较,有唯一 key
//执行过程
F mount

React 16.x 部分新特性

Error Boundaries

如果在组件的渲染或生命周期方法中引发错误,整个组件结构就会从根节点中卸载,而不影响其他组件的渲染,可以利用 error boundaries 进行错误的优化处理。

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

createPortal

react 支持将组建挂在到其他 dom 节点,事件还是按组件原有位置冒泡。

render() {
  return ReactDOM.createPortal(
    this.props.children,
    domNode,
  );
}

Fiber

Fiber 是对 react 核心算法 reconciliation 的更新实现,将原本的同步更新分成两个阶段。阶段1(Reconciliation Phase)的任务是片段化执行的,每个片段执行完成之后,就会把控制权重新交给 react 调度模块,如果有更高优先级的任务就去处理,而低优先级更新任务所做的工作则会完全作废,然后等待机会重头再来,如果没有就执行下一个片段。阶段2(Commit Phase)是同步执行的,reactDom 会根据阶段1计算出来的 effect-list 来更新 DOM 。

阶段1涉及的生命周期(可能会执行多次):componentWillMount、 componentWillReceiveProps、 shouldComponentUpdate、 componentWillUpdate

阶段2涉及的生命周期:componentDidMount、 componentDidUpdate、 componentWillUnmount

createContext

全新的 Context API 可以很容易实现祖先节点和子节点通信,并且没有副作用。

  • React.createContext 是一个函数,它接收初始值并返回带有 Provider 和 Consumer 组件的对象
  • Provider 发布方
  • Consumer 订阅方
const ThemeContext = React.createContext('light');

class ThemeProvider extends React.Component {
  state = {theme: 'light'};

  render() {
    return (
      <ThemeContext.Provider value={this.state.theme}>
        {this.props.children}
      </ThemeContext.Provider>
    );
  }
}

class ThemedButton extends React.Component {
  render() {
    return (
      <ThemeContext.Consumer>
        {theme => <Button theme={theme} />}
      </ThemeContext.Consumer>
    );
  }
}

生命周期函数的更新(见上文)

React.memo(见上文)

lazy / Suspense

React.lazy() 提供了动态 import 组件的能力,实现代码分割。

Suspense 作用是在等待组件时 suspend(暂停)渲染,并显示加载标识。

import React, {lazy, Suspense} from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <OtherComponent />
    </Suspense>
  );
}

React hooks(见上文)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容

  • 作为一个合格的开发者,不要只满足于编写了可以运行的代码。而要了解代码背后的工作原理;不要只满足于自己的程序...
    六个周阅读 8,428评论 1 33
  • 说在前面 关于 react 的总结过去半年就一直碎碎念着要搞起来,各(wo)种(tai)原(lan)因(le)。心...
    陈嘻嘻啊阅读 6,850评论 7 41
  • 原教程内容详见精益 React 学习指南,这只是我在学习过程中的一些阅读笔记,个人觉得该教程讲解深入浅出,比目前大...
    leonaxiong阅读 2,813评论 1 18
  • 40、React 什么是React?React 是一个用于构建用户界面的框架(采用的是MVC模式):集中处理VIE...
    萌妹撒阅读 1,005评论 0 1
  • 事件系统 合成事件的绑定方式 Test 合成事件的实现机制:事件委派和自动绑定。 React合成事件系统的委托机制...
    cheneyg916阅读 384评论 0 1