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

image.png

开发者编写的JSX,其本质就是React.createElement的语法糖,React.createElement 格式化数据以后调用了ReactElement,ReactElement组合数据以后返回虚拟DOM,虚拟DOM作为参数传入ReactDOM.render()渲染出真实的DOM,插入到根节点。

为什么 React 16 要更改组件的生命周期?

React 15生命周期函数

image.png

React 16生命周期函数

https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
image.png

Mounting 阶段:组件的初始化渲染(挂载)

image.png
static getDerivedStateFromProps(props, state)

  • 目的主要是为了替换componentWillReceiveProps(nextProps)
  • getDerivedStateFromProps 是一个静态方法,不能在内部访问this
  • 接受两个参数 props 和state: 来自父组件的props和组件自身的state
  • getDerivedStateFromProps 的返回值是一个对象类型的返回值,如果没有返回值,返回null
  • getDerivedStateFromProps 派发state是定向更新state而不是覆盖式更新

    Updating 阶段:组件的更新

    image.png
    getSnapshotBeforeUpdate(prevProps, prevState)

  • 目的主要是为了替换componentWillUpdate(nextProps, nextState)

  • getSnapshotBeforeUpdate 的返回值作为 componentDidUpdate 的第三个入参 componentDidUpdate(prevProps, prevState, valueFromSnapshot)
  • 他的执行时机是render执行之后,真实DOM更新之前,可以同时获取到更新前的真实 DOM 和更新前后的 state&props 信息

    Unmounting 阶段:组件的卸载

    image.png

    数据是如何在 React 组件之间流动的

    基于 props 的单向数据流

  • 父-子组件通信

  • 子-父组件通信
  • 兄弟组件通信

    利用“发布-订阅”模式驱动数据流

    image.png ```javascript class EventEmitter{ constructor(){
    1. this.eventMap = {}
    } // 注册事件函数 on(type, handler){
    1. if(!handler instanceof Function){
    2. throw Error('handler必须是函数')
    } if(!this.eventMap[type]){
    1. this.eventMap[type] = []
    } this.eventMap[type].push(handler) } // 触发事件函数 emit(type, params){
    1. if(this.eventMap[type]){
    2. this.eventMap[type].forEach((handler)=>{
    3. handler(params);
    4. })
    } } // 解除注册事件函数 off(type, handler){
    1. if(this.eventMap[type]){
    2. // 此处的无符号右移动,是为了防止 indexOf -1(找不到)的情况下错误删除 -1 >>> 0 4294967295 正数不受影响
    3. 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’)

  1. <a name="I7thq"></a>
  2. #### Context API
  3. ![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)
  4. <a name="hfGJb"></a>
  5. #### 第三方数据流管理框Redux
  6. ![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)
  7. > 用户通过View派发Action, Action被Reducer读取,根据Reducer的不同去修改数据,生成新的state,这个state会更新到store里,进而去驱动视图做出相应的更新
  8. ![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)
  9. 1. 使用 createStore 来完成 store 对象的创建
  10. 2. reducer 的作用是将新的 state 返回给 store
  11. 3. action 的作用是通知 reducer “让改变发生”
  12. 4. 派发 action,靠的是 dispatch
  13. <a name="M449J"></a>
  14. ## React-Hooks 设计动机与工作模式
  15. <a name="wKayx"></a>
  16. ### 类组件 VS 函数组件(无状态组件)
  17. [https://overreacted.io/zh-hans/how-are-function-components-different-from-classes/](https://overreacted.io/zh-hans/how-are-function-components-different-from-classes/)
  18. - **类组件**
  19. - 需要继承React.Component
  20. - 有各种生命周期函数
  21. - 需要绑定this,并基于this做各种事情
  22. - 组件内即可维护自己的state
  23. - 太重、学习成本高、有时候会小题大做
  24. - **函数组件**
  25. - 无继承、无state、无声明周期函数、无this
  26. - 轻巧、有时候无法完成特定的功能
  27. - **函数组件会捕获 render 内部的状态,这是两类组件最大的不同。 **[https://codesandbox.io/s/pjqnl16lm7](https://codesandbox.io/s/pjqnl16lm7)
  28. - 函数组件更加契合 React 框架的设计理念。
  29. <a name="EnGFA"></a>
  30. ### React Hooks
  31. <a name="D1ALS"></a>
  32. #### useState: 为函数组件引入状态
  33. `const [state, setState] = useState(initVal)`
  34. <a name="vNvDj"></a>
  35. #### useEffect: 允许函数组件执行副作用操作
  36. `useEffect(callBack, [])`
  37. - 每一次渲染都执行(传入回调函数,不传依赖数组)
  38. ```javascript
  39. useEffect(callBack)
  • 仅在挂载阶段执行一次(传入回调函数,且这个函数的返回值不是一个函数,同时传入一个空数组) ```javascript useEffect(()=>{ // 这里是业务逻辑 }, [])
  1. - 仅在挂载阶段和卸载阶段执行副作用(传入回调函数,**且这个函数的返回值****是一个函数**,同时传入一个空数组。假如回调函数本身记为 A 返回的函数记为 B,那么将在挂载阶段执行 A,卸载阶段执行 B)
  2. ```javascript
  3. useEffect(()=>{
  4. // 业务逻辑A
  5. ...
  6. // 返回一个函数记为 B
  7. return ()=>{}
  8. }, [])
  • 每一次渲染都触发,且在卸载阶段也会触发(传入回调函数,且这个函数的返回值是一个函数,同时不传第二个参数)

    1. useEffect(()=>{
    2. // 业务逻辑A
    3. ...
    4. // 返回一个函数记为 B
    5. return ()=>{}
    6. })
  • 根据一定的依赖条件来触发的副作用(传入回调函数(若返回值是一个函数,仍然仅影响卸载阶段对副作用的处理,此处不再赘述),同时传入一个非空的数组, 只要数组中的变量有变化,则会去触发useEffect里定义的逻辑)

    1. useEffect(()=>{
    2. // 这是回调函数的业务逻辑
    3. ...
    4. // 若 xxx 是一个函数,则 xxx 会在组件卸载时被触发
    5. return xxx;
    6. }, [num1, num2,num3])

    why React Hooks?(为什么需要 React-Hooks)

    通过对类组件和函数组件的对比,会发现函数组件更加符合React的设计哲学,及UI = render(state) 但是函数组价有很多局限,如没有state、没有生命周期等功能,导致对于一些功能完备、复杂的组件无法用函数组件完成,所以推出了React Hooks, 通过提供一些类组件的功能来完善、武装函数组件,让装备后的函数组件能完成更多类组件所能的完成的功能

  • 告别难以理解的 Class

    • this:实践层面的约束来解决设计层面的问题
    • 生命周期: 学习成本高、不合理的逻辑规划
  • 解决业务逻辑难以拆分的问题;
    • 类组件会把相关联的逻辑分散在不同的声明周期里,不便于管理
    • Hooks能够帮助我们实现相关逻辑的聚合,避免复杂的组件和冗余的代码
  • 使状态逻辑复用变得简单可行;
    • 类组件的状态逻辑复用,高阶组件、render Props: 破坏组件代码结构
    • Hooks: React 为解决状态逻辑复用这个问题所提供的一个原生途径
  • 函数组件从设计思想上来看,更加契合 React 的理念

    React Hooks目前仍然存在的问题

  • Hooks 暂时还不能完全地为函数组件补齐类组件的能力

    • getSnapshotBeforeUpdate、componentDidCatch暂时未实现
  • “轻量”几乎是函数组件的基因,这可能会使它不能够很好地消化“复杂”
    • 对开发者提出了更高的要求
  • Hooks 在使用层面有着严格的规则约束

    深入 React-Hooks 工作机制:“原则”的背后,是“原理”

    两条 React-Hooks 的使用原则

  • 只在 React 函数中调用 Hook;

  • 不要在循环、条件或嵌套函数中调用 Hook。
    • 首次渲染阶段

image.png

hook 相关的所有信息收敛在一个 hook 对象里,而 hook 对象之间以单向链表的形式相互串联。

  • 更新过程

image.png
按顺序去遍历之前构建好的链表,取出对应的数据信息进行渲染。

模块一:系统深入学习“基础知识” - 图10> 在首次渲染阶段,通过mountState构建顺序链表并渲染,更新阶段,通过遍历顺序链表,读取数据渲染,虽然我们通过useState为变量命名,但是Hooks内部并没有存储这些标识符,只是按照顺序存取数据,所以如果再循环、判断中使用hook,会破坏hook按照顺序取值导致取值错位。