阅读《React设计原理》确实给到了我不一样的体验,记得上次从书本上对知识汲取的真实感知还是在高中的时候,也是相当久远了,最近公司给了二百多经费让购买书籍,杂七杂八买了一些,从今天开始打卡阅读,望监督!
首先看到了一个非常贴切的对前端框架实现原理的公式,用简单的公式,初中的数学知识:自变量、因变量、无副作用的因变量、有副作用的因变量完美解释了react的hooks
UI=f(state)
- state代表当前视图状态
- f代表框架内部运行机制
- UI代表宿主环境的视图
React中的自变量与因变量
- useState:定义组件内部的自变量
- useReducer:定义组件内部复杂的自变量
- useMemo:采用缓存的方式,定义组件内部无副作用的因变量
- useCallback:采用缓存的方式,定义组件内无副作用的因变量,缓存值为函数形式
- useEffect:定义组件内部有副作用的因变量
接下来就是手写代码环节,来看看react的细粒度更新
简单实现useState
function useState(value){
const getter = ()=>value
const setter = (nextValue)=>value = newValue
return [getter,setter]
}
简单实现useEffect
对于我们手写的useEffect有如下期待:
- useEffect执行后,回调函数立刻执行
- 依赖的自变量变化后,回调函数立刻执行
- 不需要显式依赖
useState和useEffect属于发布订阅关系
const effectStack = []
//手写status
function subscribe(effect, subs) {
//建立订阅关系
subs.add(effect)
//建立依赖关系
effect.deps.add(subs)
}
function cleanup(effect) {
//从该effect订阅的所有state对应的subs中移除该effect
for (const subs of effect.deps) {
subs.delete(effect)
}
//将该effect依赖的所有state对应的subs移除
effect.deps.clear()
}
function useState(value) {
//保存订阅该state变化的effect
const subs = new Set()
const getter = () => {
//获取当前上下文的effect
const effect = effectStack[effectStack.length - 1]
if (effect) {
//建立订阅发布关系
subscribe(effect, subs)
}
return value
}
const setter = (nextValue) => {
value = nextValue
//通知所有订阅该state变化的effect执行
for (const effect of [...subs]) {
effect.execute()
}
}
return [getter, setter]
}
//手写useEffect
function useEffect(callback) {
const execute = () => {
//重置依赖
cleanup(effect);
//将当前effect推入栈顶
effectStack.push(effect);
try {
//执行回调
callback()
} finally {
//effect出栈
effectStack.pop()
}
}
const effect = {
execute,
deps: new Set()
}
//立即执行一次,建立订阅发布关系
execute()
}
//手写useMemo
function useMemo(callback) {
const [s, set] = useState()
//首次执行callback,初始化value
useEffect(() => set(callback()))
return s
}
优点:
- 无需显示指名依赖
- 由于可以自动跟踪依赖,因此不受 不能在条件语句中声明的hooks限制