1. 首页

浅谈react 那些事~

正文从这里开始

react是什么?其官网给出了明确定义: AJavaScriptlibraryforbuilding user interfaces,一个用于构建用户界面的JavaScript库。

Thinking in React

通常情况下前端的界面可以用一个简单的公式来抽象:

view = fn(state)

举个例子,现在有这么一个需求:根据一组信息渲染一个列表

let infos =[

    {name:'zhangsan'},
    {name:'lisi'},
    {name:'wangwu'},
    ...
]


function render(){

// 清空列表
  $ul.removeAll();

// 重新渲染列表
    infos.forEach(i => $ul.append(generateLi(i)));
}

很完美,只要数据一变执行 render方法就好了,坏处是每次都要重新渲染整个页面,而浏览器的渲染和js执行是同一个线程,数据量很大时可能会造成页面的卡顿。现在我们再回过头来看公式, fn是什么?事实上 fn是实现数据到视图映射的方法,上面就是一种比较原始的实现。对于react,笔者认为其 fn可以表示为:

function fn(){

    virtualDom();
    component();
}

1.1 虚拟dom

上文中提到视图是数据的表现,当数据改变时可以清空所有的dom节点然后重新渲染视图,缺点是可能造成了很大的浪费,因为大部分的dom节点可能并没有变,比如渲染一个列表,可能只是插入了一条数据:
Hooks are an upcoming feature that lets you use state and other React features without writing a class. They're currently in React v16.7.0-alpha.React v16.0 comes with some major changes and an update to the react core algorithm. With React v16.0 Facebook has completely revised the ...

那么有没有方法是在渲染之前先进行比较,然后只改变更新的dom节点?虚拟dom正好可以做这件事,虚拟dom是真实dom节点的数据结构映射,只要给出了一定的规范,就可以利用虚拟dom表示真实的dom。举个例子,更新前的列表可以这么表示:


// real dom
  • zhang san
  • wang wu
// virtual dom

{
  tag:'ul',
  children:[
        {
            tag:'li',
            text:'zhang san'
        },
        {
            tag:'li',
            text:'wang wu'

        }
    ]

}

同样的更新后的列表依然可以使用虚拟dom表示,虚拟dom之间的diff是十分快的,只要把虚拟dom的差异映射到真实dom节点上即可完成视图的更新。

1.2 组件化

谈到组件化首先想到的是代码复用,但组件化不止如此。虚拟dom只是优化了数据到视图的映射方式,但是当数据改变时,应该选择什么样的范围进行diff其并没有给出。假设我们的页面结构如下:

就在今天,React 团队正式发布了React 16,这次发布带来了哪些内容呢? 全新的内部架构React 16 采用了称为“Fiber”的全新的内部架构。

想象一下,假如仅仅因为list插入了一条数据就对整个视图diff,即使js引擎很快但这样的效率无疑也是很低的,我们更希望的是将数据拆分,每次数据改变只对一个可控的范围进行diff,而其余的部分不受影响。组件化刚好解决了这样的问题,我们可以将页面拆分为如下:

通过划分组件,可以将视图隔离为相互独立的部分,List组件的状态改变时只要对List进行diff即可,而其余的组件不受影响。所以组件化让我们有了定义diff粒度的能力,提高了数据变化时视图的更新效率。

React渲染流程

注:本文没有考虑fiber架构

2.1 组件渲染流程

一个React应用可以看做是一个相对较大的组件,所以标题React渲染流程更多是指一个组件的渲染流程。组件的渲染流程可简单的分为两步:
+ 初始渲染
+ 更新渲染

2.1.1 初始渲染

初始渲染的流程相对简单,根据状态构建虚拟dom,根据虚拟dom渲染真实dom:

state => virtual dom => view

虚拟dom的构建是在render方法中进行的,首先需要明确jsx语法只是 React.createElement的语法糖

// JSX

this is parent
// Babel转译之后

React.createElement(
    "div",
    null,
    React.createElement(
        "p",
        null,
        "this is parent"
    ),
    React.createElement(ChildComponent,{ msg: msg })

);

可以猜测, React.createElement就是构造虚拟dom的方法,事实上上例会返回一个类似如下的虚拟dom,然后根据虚拟dom深度优先构建真实dom树
This week, Sophie Alpert and I presented the “Hooks” proposal at React Conf, followed by a deep dive from Ryan Florence: I strongly

2.1.2 更新渲染

更新渲染时首先会对更新前后的虚拟dom进行diff,然后将差异patch到真实dom即可完成组件的更新。虚拟dom进行diff时会根据类型的不同采取不同的策略,笔者根据虚拟dom nodeName类型的不同将其划分为两种:

~ html vnode 原生dom标签对应的虚拟dom
~ component vnode 组件标签对应的虚拟dom

参考上面例子, html vnode对应:
简介不久前,react在新的16.7.0-alpha.0版本中推出了新的hooks函数,其作用就是让你可以不用类组件就可以使用react的state和其他功能。
component vnode对应:
![Hooks 是一个React 函数组件内一类特殊的函数(通常以”use” 开头, … 在React 里,function component 就是一个pure render component]https://susouth.com(/assets/reprint/images/react05.webp)

html vnode的更新相对简单,可以简单的理解为对比虚拟dom的nodeName,相同表明真实dom可以复用,只更新属性和子节点即可,不同则创建新的dom并删除掉旧的dom节点。

component vnode的diff相对复杂,也可以近似理解为先对比虚拟dom的nodeName是否相同,如果相同则走组件更新对应的生命周期,反之旧虚拟dom对应的组件会被 unmount,同时实例化新虚拟dom对应的组件。这里需要注意 component vnode的nodeName指向的是组件的构造函数,我们在写jsx时要确保组件的标签名指向的确实是同一个组件,否则可能会出现组件的状态丢失,除非你有意为之。

2.2 组件更新时机

什么行为会导致触发组件更新?
+ 组件执行了setState
+ 组件的父组件执行了rerender

我们知道,只要执行了 setState组件就会执行更新过程,但是父组件rerender一定会引起子组件rerender吗?结论是肯定的,react中只要某一组件的状态发生改变,就会以该组件为根重新渲染整个组件树,即使子组件的 props没有发生改变。这似乎很傻,因此react暴露了 shouldComponentUpdate方法给我们手动控制组件是否渲染:


shouldComponentUpdate ( nextProps , nextState ) { if ( this . props . color !== nextProps . color ) { return true ; } if ( this . state . count !== nextState . count ) { return true ; } return false ; }

shouldComponentUpdate返回 false时就会跳过组件更新流程。

PureComponent和Immutable

在使用 redux时我们知道 reducer必须是 purefunction,纯函数有两个显著的特点:

  • 相同的输入必然会得到相同的输出
  • 函数是无副作用的,不依赖于外部状态也不会改变外部状态

抽象点来看组件其实也是一个函数,它接收两个参数 propsstate,返回一个 view。借鉴纯函数的概念, PureComponent显然是指当 props和 state不变时渲染相同视图的组件,其原理类似是一个高阶组件,内部实现了 shouldComponentUpdate方法,当 props和 state不变时会跳过组件更新过程,省去了虚拟dom生成和diff的过程。这样看来似乎使用 PureComponent可以极大的提供react的性能,但是这里还有一个重要的前提: PureComponent执行的是浅比较,稍不注意可能会出现问题,看下面的代码:

class List extends PureComponent { 
    render () { 
        const list = this . props . list ; 
        return (
            
    { list.map ( i => { return
  • { i . now } }) } ) } } class App extends Component { state = { list : [{ now : new Date (). getTime () }] } add = () => { const list = this.state.list ; list.push ({ now : new Date (). getTime () }); this.setState ({ list }) } render () { const list = this.state.list ; return (

上例中List是一个 PureComponent,其props中list是一个复杂数据结构(数组),而 PureComponent只会对props进行浅比较,本例中list的指向不会发生改变,因此无论怎么点击button组件都不会更新。

  • 那么应该什么时候使用 PureComponent呢?
  • 显然当组件的 props和 state为简单类型时必然可以使用

当组件的 propsstate为复杂类型时保证数据的Immutable

理论上我们可以改变state的唯一途径就是执行 setState方法,只要保证 setState是immutble的即可,结合上例:

class App extends Component { 
    ... 
    add = () => { 
        const list = this.state.list ;  
        const newList = [... list , { now : new Date (). getTime () }]; 
        this.setState ({ list : newList }); 
    } 
    ... 
}

事实上 setState还可以传入一个函数,参数是当前的state,返回值为新的state:

setState ( updater [, callback ])

类似于reduxreducer,上例可改写为:

const genListUpdater = payload => state => { 
    const list = state.list; 
    return { list : [... list , payload ] }; 
} 

class App extends Component { 
    ... 
    add = () => { 
        this . setState ( genListUpdater ({ now : new Date (). getTime () })); 
    } 
    ... 
}

但是还有一个问题,当数据的嵌套层级较深时保持数据的immutable很费劲,根据具体情况可以考虑使用ImmutableJs(https://facebook.github.io/immutable-js/)处理。

这里推荐一下,可以使用immutability-helper —— 苏南

热门推荐

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文来源于网络,其版权属原作者所有,如有侵权,请与小编联系,谢谢!

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:浅谈react 那些事~

链接:https://www.javascriptc.com/178.html

原文链接:https://mp.weixin.qq.com/s/yOIwzdiG7Qz4fEJKKjoTog

« 前端你应该知道的Web性能信息采集指南
React16.7 hooks之setTimeout引发的bug小记»
Flutter 中文教程资源

相关推荐

QR code