React基础概念一

643 阅读6分钟

安装脚手架

  1. npm install -g create-react-app
  1. npm config set registry registry.npm.taobao.org
  1. 创建项⽬:npx create-react-app my-app
  1. 启动项⽬:npm start
  1. 暴露配置项:npm run eject

cra文件结构

隐藏的index

1.基础结构

2.两个入口

vscode插件

  • 可愉快的使用rcc rfc imp快捷键...

jsx

1.基本使⽤

2.{}

3.函数

4.对象

5.条件语句

6.数组

7.属性的使⽤

image.png

8.模块化(index.module.css后缀名是固定的)

  1. 本质

jsx仅仅只是React.createElement(component,props,...children)函数的语法糖,所有的jsx最终都会转换成React.createElement()的函数调用。

image.png

  • 看下源码:

image.png

image.png

  • 引申虚拟DOM的创建过程

通过React.createElement最终创建出来一个ReactElement对象

image.png

最终利用ReactElement对象组成一个js的对象树,这棵树就叫虚拟DOM。

image.png

ReactDOM.render -> 让虚拟DOM转换为真实DOM(这个过程叫做协调Reconciliation)。

  • 为啥使用虚拟DOM

1.document.createElement本身创建出来的就是一个非常复杂的对象。

2.DOM操作会引起浏览器的重绘与回流。

3.真实dom很难跟踪状态的改变,不方便调试。

组件

类式组件

  • class组件通常拥有状态和⽣命周期,组件名称大写字符开头,继承于Component,必须实现render⽅法。

  • state最基本写法

  • props最基本写法

函数式组件

  • 从React16.8开始引⼊了hooks,函数组件也能够拥有状态(这里先知道有这么一个东西 后面细说)

事件处理(this问题的三大方案)

image.png

生命周期

旧(16版本前)

新(16版本后)

  • getDerivedStateFromProps

  • getSnapshotBeforeUpdate

通信

父子通信

  • 父传值给子(就是通过props)

  • 子传值给父(通过调用父事件回传参数)

兄弟通信

  1. 上面的写法Home 与 About就完成了一次兄弟间的通信(也就是给到上级,通过上级获取)
  1. 消息发布订阅方式-任意组件通信(插件)
  • npm i pubsub-js -D / npm i events -D

跨组件通信-context(非hooks写法)

一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信,相当于注入在封装库中基本用到。

函数式组件如何声明接受context

setState

  1. 正确使⽤setState
setState(partialState, callback)
partialState : object|function //⽤于产⽣与当前state合并的⼦集。
callback : function //state更新之后被调⽤。

image.png

  1. 不要直接修改 State
this.state.name = 'Hello'; //此代码不会重新渲染组件
this.setState({name: 'Hello'});//正确使⽤
  1. State的更新可能是异步的
  • setState在生命周期中是异步的

  • setState在合成事件是异步的

  • 让setState输出同步的结果(callback/componentDidUpdate)

image.png

  1. State的更新可能是同步的
  • setState在setTimeout中是同步的

  • setState在原生事件中是同步的

  1. State的更新数据合并

image.png

image.png

  1. State的更新本身被合并

6.为什么设计setState为异步

  • 显著提升性能

1.如果每次调用setState都进行一次更新,那么意味着render函数或被频繁调用,界面重新渲染,效率较低。

2.最好的方法应该是获取多个更新的操作,之后进行批量更新。

  • 如果同步更新了state,但是还没有执行render函数,那么stste和props不能保持同步,会在开发中产生很多问题,如父组件的state改变了而render还未完成导致子组件props的值未能同步。

7.总结

(1). setState(stateChange, [callback])------对象式的setState
     1.stateChange为状态改变对象(该对象可以体现出状态的更改)
     2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
     
(2). setState(updater, [callback])------函数式的setState
     1.updater为返回stateChange对象的函数。
     2.updater可以接收到state和props。
     3.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
     
总结:
    1.对象式的setState是函数式的setState的简写方式(语法糖)
    2.使用原则:
	(1).如果新状态不依赖于原状态 ===> 使用对象方式
	(2).如果新状态依赖于原状态 ===> 使用函数方式
	(3).如果需要在setState()执行后获取最新的状态数据, 要在第二个callback函数中读取

react更新机制

  • props/state改变
  • render函数重新执行
  • 生产新的虚拟DOM树
  • 新旧虚拟DOM树进行Diff
  • 计算出差异进行更新
  • 更新到真实的DOM

1.同层节点之间相互比较;

2.不同类型的节点,产生不同的树结构然后直接删除替换;

image.png

3.同一类型节点:className修改只会更新className,style中某个属性修改只会更新某个属性;

4.同类型组件:更新该组件的Props,调用组件render函数,继续Diff递归比较;

5.开发中,可以通过key来指定哪些节点在不同渲染下保持稳定;

6.对于列表修改数据往最后添加一条数据,最后的数据会生成一个mutation,最终将其插入DOM树即可。此时添加key意义不是很大。

7.对于列表修改数据往前面或中间添加一条数据,React会对每一个子元素产生一个mutation,此时key的意义就重大了,存在key时会进行key的匹配,会产生位移的操作,可以减少不必要的更新。

组件优化

两个问题

  1. 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
  1. 只要当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低
  • 出现问题的原因就是上面react更新机制第四点提到的,当执行了setState(),就会执行render,render时发现有组件执行组件的render,导致不必要的更新。

效率高的做法

只有当组件的state或props数据发生改变时才重新render()

原因

Component中的shouldComponentUpdate()总是返回true

方案一

重写shouldComponentUpdate()方法

比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false,如果state数据多的话这个方案是不可行的

image.png

方案二

使用PureComponent

PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true

解决不了函数式组件(使用memo解决)

注意:

  • 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
  • 不要直接修改state数据, 而是要产生新数据

image.png

image.png

函数式组件的优化-memo

image.png

image.png

setState不可变数据的力量

简单的说就是传入新的值的时候必须使用新的值而不是修改原来state中的值。

image.png

image.png

当使用SCU,PureComponent优化时必须传入新的数据,如果不适用优化可以直接修改原数据,一般来说都使用的是优化的开发写法。