React的设计思路
UI编程的痛点
- 状态更新,UI不会自动更新,需要手动调用DOM进行更新
- 欠缺基本的代码层面的封装和隔离,代码层面没有组件化
- UI之间的数据依赖关系,需要手动维护,如果依赖链路过长,则会遇到"Callback Hell"
响应式与转换式
- 转换式系统:
- 给定输入求解输出
- 如编译器、数值计算
- 响应式系统:
- 监听事件,消息驱动
- 如监控系统、UI界面
响应式系统
响应式系统流程:
- 事件发生
- 执行既定的回调函数
- 状态变更
- 对于前端UI,需要再执行一步UI更新
React响应式编程解决的问题:
- 状态更新,UI自动更新
- 前端代码组件化,可复用,可封装
- 状态之间的互相依赖关系,只需声明即可
组件化
组件化准则:
- 组件是组件的组合/原子组件
- 组件内拥有状态,外部不可见
- 父组件可以将状态传入内部组件(父组件可以控制内部组件)
- 状态归属于两个节点时,向上寻找最近的共同的祖宗节点(状态上升)
- 修改父组件的状态可以通过向下传递函数(JS中的函数可以视为变量),内部组件调用父组件的函数(单向数据流)
组件设计
- 组件声明了状态和UI的映射
- 组件有Props(外部接收状态)/State(私有状态)两种状态
- 组件可由其他组件拼装而成
组件实现
- 组件内部拥有私有状态State
- 组件接受外部的Props状态,提供复用性
- 根据当前的State/Props,返回一个UI
组件大致实现:
function Component(props) {
const { url } = props; // props状态
this.text = 'click'; // state状态
return (<div>
<SubComponent props={{ color: 'red' }}></SubComponent> // 作为父组件为内部组件传递props状态
<img src={url}></img>
<button>text</button>
</div>)
}
生命周期
组件的生命周期可分成三个状态:
- Mounting(挂载):已插入真实 DOM
- Updating(更新):正在被重新渲染
- Unmounting(卸载):已移出真实 DOM
React(hooks)写法
hooks代码示例:
import React, { useState, useEffect } from 'react';
function Example() {
// 使用react提供的useState函数(传入初始值)创建状态变量
const [count, setCount] = useState(0);
// 副作用:会对组件外部系统造成改变
// 当函数存在副作用时,需要使用react提供的useEffect函数,useEffect只会在组件挂载的时候执行一次
useEffect(() => {
document.title = `You clicked {count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
)
}
hooks使用法则:不要在循环、条件或嵌套函数中调用hook。
React实现
React存在的问题:
- JSX不符合JS标准语法
- 返回的JSX反生改变时,如何更新DOM
- State/Props更新时,要重新触发render函数(组件函数自身)
JSX语法问题
通过语法转译,将标记语言片段转译为符合JS语法的React方法。
标记语言本质上是可以有属性的各结点组成的树状结构,可以转换为JS中的JSON或Object结构。 转换前:
const Test= (props) => {
cosnt { url } = props;
return (<div className="root"><img src={url} />
</div>);
}
转换后:
const Test= (props) => {
cosnt { url } = props;
return /*#__PURE__*/React.createElement("div", {className: "root"},
/*#__PURE__*/React.createElement("img", {src: url})
);
}
DOM更新问题
真实DOM是浏览器内部的对象,不是JS中的对象,修改真实DOM的性能损耗较大。
Virtual DOM (虚拟DOM),是一种用于和真实DOM同步,而在JS内存中维护的一个对象,它具有和DOM类似的树状结构,并和DOM可以建立一一对应的关系。
利用虚拟DOM,React实现了声明式的API:React确保DOM匹配修改的UI状态。从而省去属性操作、事件处理和手动DOM更新这些在构建应用程序时必要的操作。
虚拟DOM运作原理:
- 在虚拟DOM上进行diff运算,得到需要更新的DOM结点
- 对于不同的区别类型采取不同的处理策略
区别类型 策略 不同类型的元素 替换 同类型的DOM元素 更新 同类型的组件元素 递归
React状态管理库
React状态管理库核心思想:将状态剥离到UI组件外部进行管理。
这种方式的弊端:会降低组件的复用性,组件会与状态管理库产生强耦合。
所以状态管理库一般应用于业务代码:
- 对于组件拥有的状态,一般放在组件内部,提高复用性
- 对于需要共享的状态,一般放在状态管理库中,方便共享
- 状态管理库可以视为所有组件整体的状态
常用的状态管理库:
- redux
- xstate
- mobx
- recoil
状态机
状态机会接收外部事件,根据当前状态及外部事件类型,发生状态迁移/转换。
Modern.js/Reduck状态机示例:
import { model } from '@modern-js/runtime/model';
// 创建出来的model可以在程序中的任意位置使用,且操作的是同一个model对象及其状态
const countModel = model('count').define({
// 状态机状态
state: {
x: 0,
y: 0,
sum: 0,
},
// 状态机事件
actions: {
incrementX(state) {
state.x += 1;
},
incrementY(state) {
state.y += 1;
},
}
})