安装脚手架
- npm install -g create-react-app
- npm config set registry registry.npm.taobao.org
- 创建项⽬:npx create-react-app my-app
- 启动项⽬:npm start
- 暴露配置项:npm run eject
cra文件结构
隐藏的index
1.基础结构
2.两个入口
vscode插件
- 可愉快的使用rcc rfc imp快捷键...
jsx
1.基本使⽤
2.{}
3.函数
4.对象
5.条件语句
6.数组
7.属性的使⽤
8.模块化(index.module.css后缀名是固定的)
- 本质
jsx仅仅只是React.createElement(component,props,...children)函数的语法糖,所有的jsx最终都会转换成React.createElement()的函数调用。
- 看下源码:
- 引申虚拟DOM的创建过程
通过React.createElement最终创建出来一个ReactElement对象
最终利用ReactElement对象组成一个js的对象树,这棵树就叫虚拟DOM。
ReactDOM.render -> 让虚拟DOM转换为真实DOM(这个过程叫做协调Reconciliation)。
- 为啥使用虚拟DOM
1.document.createElement本身创建出来的就是一个非常复杂的对象。
2.DOM操作会引起浏览器的重绘与回流。
3.真实dom很难跟踪状态的改变,不方便调试。
组件
类式组件
- class组件通常拥有状态和⽣命周期,组件名称大写字符开头,继承于Component,必须实现render⽅法。
- state最基本写法
- props最基本写法
函数式组件
- 从React16.8开始引⼊了hooks,函数组件也能够拥有状态(这里先知道有这么一个东西 后面细说)
事件处理(this问题的三大方案)
生命周期
旧(16版本前)
新(16版本后)
- getDerivedStateFromProps
- getSnapshotBeforeUpdate
通信
父子通信
- 父传值给子(就是通过props)
- 子传值给父(通过调用父事件回传参数)
兄弟通信
- 上面的写法Home 与 About就完成了一次兄弟间的通信(也就是给到上级,通过上级获取)
- 消息发布订阅方式-任意组件通信(插件)
- npm i pubsub-js -D / npm i events -D
跨组件通信-context(非hooks写法)
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信,相当于注入在封装库中基本用到。
函数式组件如何声明接受context
setState
- 正确使⽤setState
setState(partialState, callback)
partialState : object|function //⽤于产⽣与当前state合并的⼦集。
callback : function //state更新之后被调⽤。
- 不要直接修改 State
this.state.name = 'Hello'; //此代码不会重新渲染组件
this.setState({name: 'Hello'});//正确使⽤
- State的更新可能是异步的
- setState在生命周期中是异步的
- setState在合成事件是异步的
- 让setState输出同步的结果(callback/componentDidUpdate)
- State的更新可能是同步的
- setState在setTimeout中是同步的
- setState在原生事件中是同步的
- State的更新数据合并
- 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.不同类型的节点,产生不同的树结构然后直接删除替换;
3.同一类型节点:className修改只会更新className,style中某个属性修改只会更新某个属性;
4.同类型组件:更新该组件的Props,调用组件render函数,继续Diff递归比较;
5.开发中,可以通过key来指定哪些节点在不同渲染下保持稳定;
6.对于列表修改数据往最后添加一条数据,最后的数据会生成一个mutation,最终将其插入DOM树即可。此时添加key意义不是很大。
7.对于列表修改数据往前面或中间添加一条数据,React会对每一个子元素产生一个mutation,此时key的意义就重大了,存在key时会进行key的匹配,会产生位移的操作,可以减少不必要的更新。
组件优化
两个问题
- 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
- 只要当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低
- 出现问题的原因就是上面react更新机制第四点提到的,当执行了setState(),就会执行render,render时发现有组件执行组件的render,导致不必要的更新。
效率高的做法
只有当组件的state或props数据发生改变时才重新render()
原因
Component中的shouldComponentUpdate()总是返回true
方案一
重写shouldComponentUpdate()方法
比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false,如果state数据多的话这个方案是不可行的
方案二
使用PureComponent
PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
解决不了函数式组件(使用memo解决)
注意:
- 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
- 不要直接修改state数据, 而是要产生新数据
函数式组件的优化-memo
setState不可变数据的力量
简单的说就是传入新的值的时候必须使用新的值而不是修改原来state中的值。
当使用SCU,PureComponent优化时必须传入新的数据,如果不适用优化可以直接修改原数据,一般来说都使用的是优化的开发写法。