浅谈React底层实现原理

目录
  • 1. props,state与render函数关系,数据和页面如何实现互相联动?
  • 2. React中的虚拟DOM
    • 常规思路
    • 改良思路(仍使用DOM)
    • React的思路
  • 深入理解虚拟DOM
  • 3. 虚拟DOM的diff算法
  • 4. React中ref的使用
  • 5. React中的生命周期函数
  • 6. 生命周期函数的使用场景

1. props,state与render函数关系,数据和页面如何实现互相联动?

当组件的state或者props发生改变的时候,自己的render函数就会重新执行。
注意:当父组件的render被执行的时候,子组件的render也会被重新执行一次(因为在父组件的render里面)。
即当绑定的事件改变了state或者props,render函数就会重新执行解析页面,这时解析的时候就会使用新的数据了,所以页面就会变化。

2. React中的虚拟DOM

因为只要state、props改变就会重新渲染render,可以想象要不断的重新渲染页面对性能要求非常高,实际上render的性能时非常高的,这归功于虚拟DOM。
首先提前明确DOM的相关操作需要调用web application对性能损耗是比较高的。

常规思路

  • state数据
  • JSX模板
  • 数据+模板相结合,生成真实的DOM来展示。
  • state改变
  • 数据+模板相结合,生成真实的DOM,替换原始的DOM
    缺点:第一次生成了一个完整的DOM片段,第二次又生成了一个完整的DOM片段,第二次的DOM替换第一次的DOM,这样生成、替换非常的消耗性能。

改良思路(仍使用DOM)

  • state数据
  • JSX模板
  • 数据+模板相结合,生成真实的DOM来展示
  • state改变
  • 数据+模板相结合,生成真实的DOM,并不直接替换原始的DOM
  • 新的DOM(文档碎片)原始的DOM作对比,找差异(性能损耗大)
  • 找出发生了什么变化,比如找出了只有input框有差异
  • 只用新的DOM中的input元素替换掉老的DOM中的input元素
    缺点:性能提升并不明显,因为性能消耗在了对比上。

React的思路

  • state数据
  • JSX模板
  • 数据+模板相结合,生成虚拟DOM(虚拟DOM就是一个JS数组对象,完整的描述真实的DOM)( [ ‘ idv ‘ , { id : ‘ abc ‘ } , [ ‘ span ‘ , { } , ‘ hello ‘ ] ] ),用JS生成JS对象性能损耗极小,生成DOM性能损耗大要调用web application。
  • 用虚拟DOM的结构生成真实的DOM,来显示( < div id=’abc’>< span>hello</ span></ div> )。
  • state发生变化(< div id=’abc’>< span>bye</ span></ div>)
  • 数组+模板生成了新的虚拟DOM( [ ‘ idv ‘ , { id : ‘ abc ‘ } , [ ‘ span ‘ , { } , ‘ bye ‘ ] ] )(极大的提升了性能)
  • 比较原始虚拟DOM和新的虚拟DOM的区别,找到区别是span中的内容(极大的提升了性能)
  • 直接操作DOM改变span中的内容
    总结:减少了对真实DOM的创建和对比,而创建和对比的是JS对象,从而实现了极大的性能飞跃。

深入理解虚拟DOM

Vue与React中的虚拟DOM的原理和步骤是完全一致的。
React中真实DOM的生成步骤:JSX -> createElement方法 -> JS对象(虚拟DOM) -> 真实的DOM。
因此可见,JSX中的div等标签仅仅是JSX的语法,并不是DOM,仅用于生成JS对象。
其实在React中创建虚拟DOM(JS对象)使用的是(没有JSX语法也可用下面的方法生成)

// 传三个参数:标签 属性 内容
// <div>item</div>
// 所以其实没有JSX语法也可以用下面的方式生成
React.createElement('div',{},'item')

虚拟DOM的优点:

  • 性能提升DOM对比变成了JS的对比
  • 它使得跨平台应用得以实现,React Native(安卓和ios中没有DOM的概念,使用虚拟DOM(JS对象)在所有应用中都可以被使用,然后变成原生客户端的组件)

3. 虚拟DOM的diff算法

  • Diff算法用于比较原始虚拟DOM和新的虚拟DOM的区别,即两个JS对象该如何比对。
  • diff算法全称为difference算法
  • setState实际上是异步的,这是为了提升react底层的性能,是为了防止时间间隔很短的情况下多次改变state,React会在这种情况下将几次改变state合并成一次从而提高性能。
  • diff算法是同级比较,假设第一层两个虚拟DOM节点不一致,就不会往下比了,就会将原始页面虚拟DOM全部删除掉,然后用新的虚拟DOM进行全部的替换,虽然有可能有一些性能的浪费,但是由于同层对比的算法性能很高,因此又弥补了性能的损耗。
  • 做list循环的时候有一个key值,这样比对的时候就可以相对应的比对,找出要改变的,以及不需要渲染的,这样使用key做关联,极大地提升了虚拟DOM比对的性能,这要保证在新的虚拟DOM后key值不变,这就说明了为什么做list循环的时候key的值不要是index,因为这样没有办法保证原虚拟DOM和新虚拟DOM的key值一致性,而造成性能的损耗,一般这个key对应后台数据的唯一id字段,而不是循环的index。

4. React中ref的使用

  • 在react中使用ref来操作DOM
  • 在react中也可以使用e.target来获取DOM
  • ref这个参数是一个函数
<input
    id = "insertArea"
    className="input"
    value={this.state.inputValue}
    onChange={this.handleInputChange}
    ref={(input)=>{this.input = input}}
/>

handleInputChange(e){
    // const value = e.target.value; // 原始的方法
    const value = this.input.value;
    this.setState(() => ({
        inputValue: value
    }))
}

一般情况下不推荐使用ref这种方法,因为setState是一个异步函数,因此去操作DOM的时候可能无法正确的输出页面的最新DOM情况,有时候比较复杂的操作如动画之类的,如果一定要使用,就需要在setState的第二个函数,这个是回调函数,在setState完成的时候触发。

handleBtnClick(e){
    this.setState((prevState)=>({
        list: [...prevState.list, prevState.inputValue], // 展开运算符
        inputValue: '',
    }), ()=>{
        console.log(this.ul.querySelectorAll('div').length);
    });
}

5. React中的生命周期函数

  • 生命周期函数是指在某一个时刻组件会自动调用执行的函数。
  • render函数就是一个生命周期函数的例子,当state或props的时候改变的时刻就会自动执行。
  • contructor 可以理解成一个生命周期函数,在组件被创建的时候就会被执行,但是它是es6语法,不是react特殊的语法。

组件挂载的过程:

  • componentWillMount 在组件即将被挂载到页面的时刻自动执行,在渲染之前被执行
  • render 进行挂载,是必须存在的
  • componentDidMount 在组件被挂载到页面之后被执行。
  • 注意:在state和props 改变的时候只有render会执行,componentWillMount和componentDidMount不会执行,他们只会在第一次挂载到页面的时候被执行。
  • 组件更新:
  • componentWillReceiveProps 两个条件都要满足:1. 当一个组件从父组件接收参数 2. 如果这个组件第一次存在于父组件中不会执行,如果这个组件之前已经存在于父组件中,才会执行。
  • shouldComponentUpdate 组件即将被更新之前会执行,如焦点input框的时候,会返回一个true和false来判断要不要更新。
  • componentWillUpdate 组件更新之前会自动执行,在shouldComponent返回true之后才会执行。
  • componentDidUpdate 组件更新完成之后被执行。
  • 组件去除的过程:
  • componentWillUnmount:但这个组件即将被从页面中剔除的时候执行。

6. 生命周期函数的使用场景

  • 防止父组件render的时候,子组件也要render,从而提升性能。
  • shouldComponentUpdate(nextProps,nextState){if(nextProps.content !== this.props.content){return true} return false}
  • 页面初始化的时候,在componentDidMount中发送AJAX请求(推荐),或者在constructor中,千万不要放在render里面,会造成死循环,也最好不要在componentWillMount中发送ajax,放这里面是没有问题的,但是如果在react native 中会有问题。
  • react 没有内置ajax,使用axios。

到此这篇关于浅谈React底层实现原理的文章就介绍到这了,更多相关React底层内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React前端框架实现原理的理解

    目录 vdom dsl 的编译 渲染 vdom 组件 状态管理 react 架构的演变 fiber 架构 总结 vdom react 和 vue 都是基于 vdom 的前端框架,我们先聊下 vdom: 为什么 react 和 vue 都要基于 vdom 呢?直接操作真实 dom 不行么? 考虑下这样的场景: 渲染就是用 dom api 对真实 dom 做增删改,如果已经渲染了一个 dom,后来要更新,那就要遍历它所有的属性,重新设置,比如 id.clasName.onclick 等. 而 dom

  • React合成事件原理解析

    目录 事件介绍 什么是事件? 举个栗子 代码实现 React合成事件基础知识 什么是合成事件? 在React中事件的写法和原生事件写法的区别? 为什么会有合成事件? 合成事件机制简述 React合成事件实现原理 事件注册 事件触发-事件监听器做了什么 React中模拟冒泡和捕获 总结 事件介绍 什么是事件? 事件是在编程时系统内发生的动作或者发生的事情,而开发者可以某种方式对事件做出回应,而这里有几个先决条件 事件对象 给事件对象注册事件,当事件被触发后需要做什么 事件触发 举个栗子 在机场等待

  • React 原理详解

    目录 1.setState() 说明 1.1 更新数据 1.2 推荐语法 1.3 第二个参数 2.JSX 语法的转化过程 3.组件更新机制 4.组件性能优化 4.1 减轻 state 4.2 避免不必要的重新渲染 总结 1.setState() 说明 1.1 更新数据 setState() 是异步更新数据 可以多次调用 setState() ,只会触发一次重新渲染 import React from 'react' import ReactDOM from 'react-dom' class O

  • 深入理解JavaScript的React框架的原理

    如果你在两个月前问我对React的看法,我很可能这样说: 我的模板在哪里?javascript中的HTML在做些什么疯狂的事情?JSX开起来非常奇怪!快向它开火,消灭它吧! 那是因为我没有理解它. 我发誓,React 无疑是在正确的轨道上, 请听我道来. Good old MVC 在一个交互式应用程序一切罪恶的根源是管理状态. "传统"的方式是MVC架构,或者一些变体. MVC提出你的模型是检验真理的唯一来源 - 所有的状态住在那里. 视图是源自模型,并且必须保持同步. 当模式的转变,

  • React深入了解原理

    目录 VDOM(虚拟dom) Fiber架构 初始化渲染 更新时 render阶段 commit阶段 VDOM(虚拟dom) react和vue都是基于vdom的前端框架. web界面由DOM树来构建,当其中一部分发生变化时,其实就是对应的某个节点发生了变化. 若一次操作中有十次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这十次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,再进行后续的操作. 用js对象模拟DOM节点的好处是: 页面的更

  • 浅谈React底层实现原理

    目录 1. props,state与render函数关系,数据和页面如何实现互相联动? 2. React中的虚拟DOM 常规思路 改良思路(仍使用DOM) React的思路 深入理解虚拟DOM 3. 虚拟DOM的diff算法 4. React中ref的使用 5. React中的生命周期函数 6. 生命周期函数的使用场景 1. props,state与render函数关系,数据和页面如何实现互相联动? 当组件的state或者props发生改变的时候,自己的render函数就会重新执行.注意:当父组

  • 浅谈React双向数据绑定原理

    目录 什么是双向数据绑定 实现双向数据绑定 数据影响视图 视图影响数据 如果已经学过Vue,并且深入了解过Vue的双向数据绑定的话,就会明白 Vue 2.0 双向数据绑定的核心其实是通过Object.defineProperty来实现数据劫持和监听. 在 Vue 3.0 中则通过 Proxy来实现对整体对象的监听,对 Vue2.0 的优化. 什么是双向数据绑定 数据模型和视图之间的双向绑定. 当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化:可以这样说用户在视图

  • 浅谈React useDebounce 防抖原理

    目录 1.js防抖 2.React custom Hook useDebounce 1.js防抖 // func 需要去抖动的函数 delay 延时执行时间 const debounce = (func, delay) => { let timeout; //...param es6语法 获取形参数组 argaments return (...param) => { // 每次 进入清除上一个已经执行/未执行的定时器 if (timeout) { clearTimeout(timeout);

  • 浅谈React Event实现原理

    React 元素的事件处理和 DOM元素的很相似.但是有一点语法上的不同: React事件绑定属性的命名采用驼峰式写法,而不是小写. 如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM元素的写法) 并且 React 自己内部实现了一个合成事件,使用 React 的时候通常你不需要使用 addEventListener 为一个已创建的 DOM 元素添加监听器.你仅仅需要在这个元素初始渲染的时候提供一个监听器. 我们看一下这是怎么实现的 React 事件机制分为 事

  • 浅谈react受控组件与非受控组件(小结)

    我们都知道,有许多的web组件可以被用户的交互发生改变,比如:<input>,<select>,或者是我现在正在使用的富文本编辑器.这些组件在日常的开发中很不显眼,我们可以很轻易的通过输入一些内容或者设置元素的value属性来改变组件的值.但是,因为React是单向数据流绑定的,这些组件可能会变得失控: 1.一个维护它自己state里的value值的<Input>组件无法从外部被修改: 2.一个通过props来设置value值的<Input>组件只能通过外部

  • 浅谈React中组件逻辑复用的那些事儿

    基本每个开发者都需要考虑逻辑复用的问题,否则你的项目中将充斥着大量的重复代码.那么 React 是怎么复用组件逻辑的呢?本文将一一介绍 React 复用组件逻辑的几种方法,希望你读完之后能够有所收获.如果你对这些内容已经非常清楚,那么略过本文即可. 我已尽量对文中的代码和内容进行了校验,但是因为自身知识水平限制,难免有错误,欢迎在评论区指正. 1. Mixins Mixins 事实上是 React.createClass 的产物了.当然,如果你曾经在低版本的 react 中使用过 Mixins,

  • 浅谈springboot自动装配原理

    一.SpringBootApplication @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFi

  • 浅谈JMeter engine启动原理

    目录 一.简介 二.配置简介 三.开始原理讲解 四.JMeter 引擎启动链路图 一.简介 本文主要介绍jmeter在控制台在点击执行之后底层所做的一些主要事情及内容,由于便于断点调试采用GUI方式进行操作 二.配置简介 为了调试方便,采用单线程,方式访问百度(若多线程可能断点会看晕,后面会讲到) 三.开始原理讲解 首先GUI下点击执行,在ActionRouter该类下执行performAction()方法,该类为执行后续流程的入口 ActionRouter是一个监听器,用于监听GUI事件流变化

  • 浅谈React 属性和状态的一些总结

    一.属性 1.第一种使用方法:键值对 <ClaaNameA name = "Tom" /> <ClaaNameA name = {Tom} /> <ClaaNameA name = {"Tom"} /> <ClaaNameA name = {[1,2,3]} />//数组 <ClaaNameA name = {FunctionNAme} /> //定义一个函数 2.第二种方法:三个点的展开对象形式 var

  • 浅谈react.js中实现tab吸顶效果的问题

    在react项目开发中有一个需求是,页面滚动到tab所在位置时,tab要固定在顶部. 实现的思路其实很简单,就是判断当滚动距离scrollTop大于tab距离页面顶部距离offsetTop时,将tab的position变为fixed. 在react中,我在state中设置一个navTop属性,切换这个属性的值为true或者false,然后tab标签使用classnames()这个方法来利用navTop的值添加类名fixed. 一开始我是这样写的: import cs from 'classnam

随机推荐