JSX 代码是如何“摇身一变”成为 DOM 的?
- JSX 的本质是什么,它和 JS 之间到底是什么关系?
- JSX 是 JavaScript 的一种语法扩展,它和模板语言很接近,但是它充分具备 JavaScript 的能力
- JSX 的本质是React.createElement这个 JavaScript 调用的语法糖
- 为什么要用 JSX?不用会有什么后果?
- JSX允许前端用类似于HTML的语法创建虚拟DOM,在降低学习成本的同时,也提升了研发效率与研发体验
- React.CreateElement创建DOM过于复杂,不利于提升开发效率
- JSX 背后的功能模块是什么,这个功能模块都做了哪些事情?
- createElement —> ReactElement —> 虚拟DOM —> ReactDom.render() —> 真实DOM
开发者编写的JSX,其本质就是React.createElement的语法糖,React.createElement 格式化数据以后调用了ReactElement,ReactElement组合数据以后返回虚拟DOM,虚拟DOM作为参数传入ReactDOM.render()渲染出真实的DOM,插入到根节点。
为什么 React 16 要更改组件的生命周期?
React 15生命周期函数
React 16生命周期函数
https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
Mounting 阶段:组件的初始化渲染(挂载)
static getDerivedStateFromProps(props, state)
- 目的主要是为了替换
componentWillReceiveProps(nextProps)
- getDerivedStateFromProps 是一个静态方法,不能在内部访问this
- 接受两个参数 props 和state: 来自父组件的props和组件自身的state
- getDerivedStateFromProps 的返回值是一个对象类型的返回值,如果没有返回值,返回null
getDerivedStateFromProps 派发state是定向更新state而不是覆盖式更新
Updating 阶段:组件的更新
getSnapshotBeforeUpdate(prevProps, prevState)
目的主要是为了替换
componentWillUpdate(nextProps, nextState)
- getSnapshotBeforeUpdate 的返回值作为 componentDidUpdate 的第三个入参
componentDidUpdate(prevProps, prevState, valueFromSnapshot)
他的执行时机是render执行之后,真实DOM更新之前,可以同时获取到更新前的真实 DOM 和更新前后的 state&props 信息
Unmounting 阶段:组件的卸载
数据是如何在 React 组件之间流动的
基于 props 的单向数据流
父-子组件通信
- 子-父组件通信
- 兄弟组件通信
利用“发布-订阅”模式驱动数据流
```javascript class EventEmitter{ constructor(){
} // 注册事件函数 on(type, handler){this.eventMap = {}
} if(!this.eventMap[type]){if(!handler instanceof Function){
throw Error('handler必须是函数')
} this.eventMap[type].push(handler) } // 触发事件函数 emit(type, params){this.eventMap[type] = []
} } // 解除注册事件函数 off(type, handler){if(this.eventMap[type]){
this.eventMap[type].forEach((handler)=>{
handler(params);
})
} } }if(this.eventMap[type]){
// 此处的无符号右移动,是为了防止 indexOf -1(找不到)的情况下错误删除 -1 >>> 0 4294967295 正数不受影响
this.eventMap[type].splice(this.eventMap[type].indexOf(handler) >>> 0, 1)
// 初始化实例
const myEventEmit = new EventEmitter();
const testEventHandler = function(params){
console.log(我是EventHandler处理函数,我接受的参数是${params}
);
}
myEventEmit.on(‘testEvent’, testEventHandler)
// myEventEmit.off(‘testEvent’, testEventHandler) // 解除订阅的事件函数
myEventEmit.emit(‘testEvent’, ‘newState’)
<a name="I7thq"></a>
#### Context API
![image.png](https://cdn.nlark.com/yuque/0/2021/png/152077/1611070876248-61839ec2-3496-4297-a840-834585b6e1da.png#align=left&display=inline&height=428&margin=%5Bobject%20Object%5D&name=image.png&originHeight=856&originWidth=1019&size=103278&status=done&style=none&width=509.5)
<a name="hfGJb"></a>
#### 第三方数据流管理框Redux
![image.png](https://cdn.nlark.com/yuque/0/2021/png/152077/1611071099100-c9cd1d07-3001-485d-bbda-f0b4a176ebb0.png#align=left&display=inline&height=325&margin=%5Bobject%20Object%5D&name=image.png&originHeight=650&originWidth=1424&size=63394&status=done&style=none&width=712)
> 用户通过View派发Action, Action被Reducer读取,根据Reducer的不同去修改数据,生成新的state,这个state会更新到store里,进而去驱动视图做出相应的更新
![image.png](https://cdn.nlark.com/yuque/0/2021/png/152077/1611071407746-37d580b4-af51-4bd1-a6ac-dd794fdd7479.png#align=left&display=inline&height=402&margin=%5Bobject%20Object%5D&name=image.png&originHeight=804&originWidth=1567&size=99234&status=done&style=none&width=783.5)
1. 使用 createStore 来完成 store 对象的创建
2. reducer 的作用是将新的 state 返回给 store
3. action 的作用是通知 reducer “让改变发生”
4. 派发 action,靠的是 dispatch
<a name="M449J"></a>
## React-Hooks 设计动机与工作模式
<a name="wKayx"></a>
### 类组件 VS 函数组件(无状态组件)
[https://overreacted.io/zh-hans/how-are-function-components-different-from-classes/](https://overreacted.io/zh-hans/how-are-function-components-different-from-classes/)
- **类组件**
- 需要继承React.Component
- 有各种生命周期函数
- 需要绑定this,并基于this做各种事情
- 组件内即可维护自己的state
- 太重、学习成本高、有时候会小题大做
- **函数组件**
- 无继承、无state、无声明周期函数、无this
- 轻巧、有时候无法完成特定的功能
- **函数组件会捕获 render 内部的状态,这是两类组件最大的不同。 **[https://codesandbox.io/s/pjqnl16lm7](https://codesandbox.io/s/pjqnl16lm7)
- 函数组件更加契合 React 框架的设计理念。
<a name="EnGFA"></a>
### React Hooks
<a name="D1ALS"></a>
#### useState: 为函数组件引入状态
`const [state, setState] = useState(initVal)`
<a name="vNvDj"></a>
#### useEffect: 允许函数组件执行副作用操作
`useEffect(callBack, [])`
- 每一次渲染都执行(传入回调函数,不传依赖数组)
```javascript
useEffect(callBack)
- 仅在挂载阶段执行一次(传入回调函数,且这个函数的返回值不是一个函数,同时传入一个空数组) ```javascript useEffect(()=>{ // 这里是业务逻辑 }, [])
- 仅在挂载阶段和卸载阶段执行副作用(传入回调函数,**且这个函数的返回值****是一个函数**,同时传入一个空数组。假如回调函数本身记为 A, 返回的函数记为 B,那么将在挂载阶段执行 A,卸载阶段执行 B)
```javascript
useEffect(()=>{
// 业务逻辑A
...
// 返回一个函数记为 B
return ()=>{}
}, [])
每一次渲染都触发,且在卸载阶段也会触发(传入回调函数,且这个函数的返回值是一个函数,同时不传第二个参数)
useEffect(()=>{
// 业务逻辑A
...
// 返回一个函数记为 B
return ()=>{}
})
根据一定的依赖条件来触发的副作用(传入回调函数(若返回值是一个函数,仍然仅影响卸载阶段对副作用的处理,此处不再赘述),同时传入一个非空的数组, 只要数组中的变量有变化,则会去触发useEffect里定义的逻辑)
useEffect(()=>{
// 这是回调函数的业务逻辑
...
// 若 xxx 是一个函数,则 xxx 会在组件卸载时被触发
return xxx;
}, [num1, num2,num3])
why React Hooks?(为什么需要 React-Hooks)
通过对类组件和函数组件的对比,会发现函数组件更加符合React的设计哲学,及UI = render(state) 但是函数组价有很多局限,如没有state、没有生命周期等功能,导致对于一些功能完备、复杂的组件无法用函数组件完成,所以推出了React Hooks, 通过提供一些类组件的功能来完善、武装函数组件,让装备后的函数组件能完成更多类组件所能的完成的功能
告别难以理解的 Class
- this:实践层面的约束来解决设计层面的问题
- 生命周期: 学习成本高、不合理的逻辑规划
- 解决业务逻辑难以拆分的问题;
- 类组件会把相关联的逻辑分散在不同的声明周期里,不便于管理
- Hooks能够帮助我们实现相关逻辑的聚合,避免复杂的组件和冗余的代码
- 使状态逻辑复用变得简单可行;
- 类组件的状态逻辑复用,高阶组件、render Props: 破坏组件代码结构
- Hooks: React 为解决状态逻辑复用这个问题所提供的一个原生途径
-
React Hooks目前仍然存在的问题
Hooks 暂时还不能完全地为函数组件补齐类组件的能力
- getSnapshotBeforeUpdate、componentDidCatch暂时未实现
- “轻量”几乎是函数组件的基因,这可能会使它不能够很好地消化“复杂”
- 对开发者提出了更高的要求
-
深入 React-Hooks 工作机制:“原则”的背后,是“原理”
两条 React-Hooks 的使用原则
只在 React 函数中调用 Hook;
- 不要在循环、条件或嵌套函数中调用 Hook。
- 首次渲染阶段
hook 相关的所有信息收敛在一个 hook 对象里,而 hook 对象之间以单向链表的形式相互串联。
- 更新过程
按顺序去遍历之前构建好的链表,取出对应的数据信息进行渲染。
> 在首次渲染阶段,通过mountState构建顺序链表并渲染,更新阶段,通过遍历顺序链表,读取数据渲染,虽然我们通过useState为变量命名,但是Hooks内部并没有存储这些标识符,只是按照顺序存取数据,所以如果再循环、判断中使用hook,会破坏hook按照顺序取值导致取值错位。