react 面试总结

react是什么

a javascript library for building user interface

  1. 打造用户界面
  2. 响应各种事件

注意:面试装B 可以直接看react源码上的change log ,如果看看那些一次更新很多的版本,看自己能理解的log 。记住版本好,精确到小数位。

react 虚拟dom

  1. 先将代码转成js对象,再把js对象转化成真实dom,这个js对象就是虚拟dom
   <div class="box">
        <h1>标题</h1> 
        <p><span>内容</span></p>  
    </div>

对应的虚拟dom

 const vdom =  {
            type : 'div',
            props: {class:'box' },
            chlidren : [
                {
                    type : 'h1',
                    chlidren: '标题'
                },
                {
                    type : 'p',
                    chlidren:  {
                        type : 'span',
                        chlidren: '内容'
                    }
                } 
            ]
        }

扩展:
上面的vdom 即可以叫对象,也可以叫json
JSON scheme也叫JSON模式 。 大家基于json的格式标准定义数据结构并用于传输。
在JS 里面 json 的就是 JavaScript的对象字面量的表达式。
如下

var a = {
  name : '张三',
  age: 11
}

这里定义的a 即是对象,也是json。
由于互联网的普及,JS的这个JSON模式也被广泛应用在其他后端语言的接口定义规范
其他的接口规范有 SOAP,WebService 基于xml的传输格式。

fiber原理解析

在react 16.0之前 diff比较 直接使用 虚拟dom树进行 循环+递归(广度遍历,深度优先方式)。

缺点:

  1. 由于代码是同步执行,所以会一直循环+递归直到结束。主线程会一直占用。
  2. 当大量组件实例存在时候,执行效率会变的越来越慢。
  3. 导致用户的UI交互卡顿

新增fiber 关系链

fiber 的引入是扩展,原来的虚拟dom依然保留

fiber 的优势

  1. 将对比dom的操作拆细,碎片化执行
  2. 碎片化的任务,可以根据需要被暂停。
  3. 利用浏览器空闲时间执行

碎片化使用的是 浏览器API -> requestIdleCallback ,他会在空闲的时候执行,根据优先级可暂停当前任务,交给更高级的任务。

fiber 结构定义

export type Fiber = {
  // Fiber 类型信息
  type: any, 
  // 指向父节点,或者render该节点的组件
  return: Fiber | null,
  // 指向第一个子节点
  child: Fiber | null,
  // 指向下一个兄弟节点
  sibling: Fiber | null,
} 

利用以上的 链表结构,就可以快速找到当前的下一个上一个,从而实现暂停与继续。

执行逻辑

  1. 根据虚拟dom,生成fiber对象。
  2. 根据fiber 再去做渲染真实的dom 也就是diff

核心代码

// 创建任务队列
const taskQueue = createTaskQueue()
// 空闲时间执行的具体方法
const performTask = deadline => {
    // 执行任务, workLoop方法后续补充
    workLoop(deadline)
    //实现持续调用
    if (subTask | !taskQueue.isEmpty()) {
        requestIdleCallback(performTask)
    }
}
// 暴露的render方法
export const render = (element, dom) => {
    // 1.添加任务=》 构建fiber对象
    taskQueue.push({
        dom,
        props: { children: element }
 3)
    // 2.指定浏览器空闲时间执行performTask 方法
    requestIdleCallback(performTask)
}

// 子任务
let subTask = null
// commit操作标志
let pendingCommit = null
const workLoop = deadline => {
    // 1.构建根对象
    if (!subTask) {
        subTask = getFirstTask()
    }
    // 2.通过while循环构建其余对象
    while (subTask && deadline.timeRemaining() > 1) {
        subTask = executeTask(subTask)
    }
    // 3.执行commit操作,实现Dom挂载
    if (pendingCommit) {
        commitAllWork(pendingCommit)
    }
}
const getFirstTask = () => {
    // 获取任务队列中的任务
    const task = taskQueue.pop()
    // 返回Fiber对象
    return {
        props: task.props,
        stateNode: task.dom
                // 代表虚拟Dom挂载的节点
                tag: "host_root",
        effects: [],
        child: null
    }
}

react虚拟dom了解吗?

用来描述真实dom的一个json对象。

你了解react fiber吗?

用于提升dom 做diff算法时效率所引入的新对象与数据结构。

Fiber的优势是什么?

可以中断,碎片化执行,diff的时候原主线程不受影响,UI不卡顿。

Fiber怎么做到⽐之前的渲染要快的?
传统的diff 需要整颗树 完成的遍历才能更新渲染 时间复杂度(o3),用fiber就可以跨层级跳转不用全部遍历,很快找到对应节点进行更新。 时间复杂度(o)

你了解react虚拟dom渲染机制吗?

react 生命周期

16.0之前

  1. 初始化 -> 挂载props -> 初始化state -> render -> 完成
  2. 获取数据完毕 -> 更新state -> diff -> render -> 完成


    image.png

更新state的方式

  1. 组件⾃⼰setState触发更新
  2. ⽗组件触发传给⼦组件的props值变化,引发⼦组件更新

常用的几个钩子

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate
  • shouldComponentUpdate

shouldComponentUpdate 可以控制当前ui是否需要diff ,这也是人为可以干预react渲染效率的唯一手段。

// 父组件重新render,重传props,无论porps值有没有改变,子组件都会接收并改变 
class SomeComponent extends Component {
    shouldComponentUpdate(nextProps) {
        //应该使用这个方法,否则无论props是否有变化都将会导致组件跟着重新宣染
        if (nextProps.someThings === this.props.someThings) {
            return false;
        }
        render() {
            return <div>this.props. someThings)</div>;
        }
    }
}
class SomeComponent extends Component {
    constructor(props) {
        super(props);
        this.state = {
            someThings: props.someThings
        }
    }
    componentWillReceiveProps(nextProps) {
        //父组件重传props时就会调用这个方法
        this.setState({ someThings: nextProps.someThings });
    }
    render() {
        return <div>(this.state.someThings)</div>
    }
}
image.png

常见问题:
React⽣命周期
React⽣命周期函数以及各个函数的应⽤
React 16.0前后⽣命周期函数的变化为什么
React对于⽣命周期函数的变化你怎么理解
React性能优化(虚拟dom渲染上优化)

diff算法

React diff算法策略

分为3种不同的类型进行对比

  1. 针对树结构(tree diff):对UI层的DOM节点跨层级的操作进行忽略。(数量少)
  2. 针对组件结构(component diff):拥有相同类的两个组件生成相似的树形结构,拥有不同类的两个组件会生成不同的属性结构。
  3. 针对元素结构(element-diff):对于同一层级的一组节点,使用具有唯一性的id区分(key属性)

比较过程

  1. 通过state计算出新的fiber节点
  2. 对⽐节点的tag和key确定节点操作(修改,删除,新增,移动)
  3. effectTag标记fiber对象
  4. 收集所有标记的fiber对象,形成effctList
  5. Commit阶段⼀次性处理所有变化的节点

核心调用方法

function reconcileChildFibers(
    returnFiber: Fiber,
    currentFirstChild: Fiber null, newChild: any,
    lanes: Lanes,
): Fiber null
image.png
image.png

常见问题:

  1. diff原理和策略?
  2. fiber diff的策略?
  3. react diff的优势?

状态管理篇

react 自己不带状态管理工具,都是第三方库自己实现的。

flux

flux是最早的状态管理工具

  • UI产⽣动作消息,将动作传递给分发器
  • 分发器⼴播给所有store
  • 订阅的store做出反应,传递新的state给UI


    image.png

redux

  • 单⼀数据源,整个应⽤state存储在⼀个单⼀store树中
  • State状态为只读,不应该直接修改state,⽽是通过action触发state修改
  • 没有dispatch,使⽤纯函数进⾏状态修改,需要开发者书写reducers纯函数进⾏处理,reducer通过当前状态树和action进⾏计算,返回⼀个新的state

redux 使用 包裹需要使用的地方

<Provider store = {store} >
<App />
</Provider >

flux 与 redux 对比


image.png

mobx

  1. 定义状态并使其可观察
  2. 创建视图以响应状态变化
  3. 更改状态(⾃动响应UI变化)
    通过注解的方式编写

MobX 支持单向数据流,也就是动作改变状态,而状态的改变会更新所有受影响的视图。


image.png

当状态改变时,所有衍生都会进行原子级的自动更新。因此永远不可能观察到中间值。
所有衍生默认都是同步更新。这意味着例如动作可以在改变状态之后直接可以安全地检查计算值。
计算值是延迟更新的。任何不在使用状态的计算值将不会更新,直到需要它进行副作用(1/0)操作时。如
果视图不再使用,那么它会自动被垃圾回收。
所有的计算值都应该是纯净的。它们不应该用来改变状态。


image.png

通过装饰器实现快速响应

1. 为什么要选择hooks

  1. 组件间复用状态逻辑很难
  2. 复杂的组件变得难以维护
  3. class 难以学习和理解

hooks 是一个数组,里面存的是函数,不需要学习class 复杂的状态或者响应式的技术。
React hook:not magic, just arrays

react 实际上也是只提供简单的虚拟dom 和

重要的钩子

useState: 用于定义组件的 State,对标到类组件中this.state的功能
useEffect:通过依赖触发的钩子函数,常用于模拟类组件中的componentDidMount, componentDidUpdate, componentWillUnmount方法其它内置钩子:
useContext:获取context对象
useReducer:类似于Redux 思想的实现,但其并不足以替代Redux,可以理解成一个组件内部的redux,并不是持久化存储,会随着组件被销毁而销毁;属于组件内部,各个组件是相互隔离的,单纯用它并无法共享数据;配合useContext的全局性,可以完成一个轻量级的Redux
useCallback:缓存回调函数,避免传入的回调每次都是新的函数实例而导致依赖组件重新宣染,具有性能优化的效果;
useMemo: 用于缓存传入的props,避免依赖的组件每次都重新宣染;
useRef: 获取组件的真实节点;
useLayoutEffect: DOM更新同步钩子。用法与useEffect类似,只是区别于执行时间点的不同。useEffect属于异步执行,并不会等待DOM真正渲染后执行,而useLayoutEffect则会真正渲染后才触发;可以获取更新后的state;
自定义钩子(useXxxxx):基于Hooks可以引用其它Hooks这个特性,我们可以编写自定义钩子。

useState

function RenderFunctionComponent() {
    const [firstName, setFirstName] = useState("Rudi");
    const [lastName, setLastName] = useState("Yardley");
    return (
        <Button onClick=f() => setFirstName("Fred"))> Fred</Button >
    );
}

源码实现


image.png

创建两个空的array,state and setters
第⼀次渲染,循环所有useState,把state和setter⽅法分别压⼊两个数组
更新state,触发render,cursor被重置,根据useState的声明顺序依次拿出state值,更新UI

image.png

第⼀次执⾏,state有三个元素
更新state触发render
此时tag=false,不会执⾏if流程,也就是没有useState
最后导致setNum2不起作⽤

image.png

结论:不要在循环、判断逻辑中使⽤useState,最好是在函数顶部使⽤

useEffect

副作⽤函数?

  • useEffect函数第⼀个参数为callback函数,就是要执⾏的函数
  • 第⼆个参数为⼀个数组,数组中是要监听的变量
  • []空数组之所以会起到componentDidMount的作⽤,是因为每次更新,[]空数组没有任何监听的变量,也就是不存在变化⼀说,所以只会被执⾏⼀次
  • useEffect函数返回的第⼀个函数作为销毁state使⽤
image.png

useLayoutEffect

useLayoutEffect主要在useEffect函数结果不满意时才被⽤到,⼀般的经验是当处理dom改变带的副作⽤才会被⽤到,该Hook执⾏时,浏览器并未对dom进⾏渲染,较useEffect执⾏要早

useEffect 和 useLayoutEffect差异

  • useEffect和useLayoutEffect在Mount和Update阶段执⾏⽅法⼀样,传参不⼀样
  • useEffect异步执⾏,⽽useLayoutEffect同步执⾏
  • Effect⽤HookPassive标记useEffect,⽤HookLayout标记useLayoutEffect

useMemo

传递⼀个函数和依赖项,当依赖项发⽣变化,执⾏函数,该函数需要返回⼀个值


image.png

image.png

useCallback

返回⼀个函数,当监听的数据发⽣变化才会执⾏回调函数

useRef

image.png

useReducer

  • 聚合参数,以达到开发效率的提升以及代码的简洁
  • useReducer在re-render时候不会改变存储位置,state作为props传给⼦component不会产⽣diff的效率问题(useMemo优化)
image.png

image.png

通过useReducer优化


image.png

useXXX ⾃定义

与React组件不同的是,自定义Hook不需要具有特殊的标识。我们可以自由的决定它的参数是什么,以及它应该返回什么(如果需要的话)。换句话说,它就像一个正常的函数。但是它的名字应该始终以use开头,这样可以一眼看出其符合Hook的规则。


image.png

class 和 hooks 对比

Class

  • 代码逻辑清晰(构造函数、componentDidMount等)
  • 不容易内存泄漏

Hooks

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

推荐阅读更多精彩内容