一. preact
介绍
preact
是前端框架,开发体验与React
相似,使用JSX
语法,提供与React
相同的API
。本文主要解析preact
与react
的渲染流程差和preact
的signals
机制。
preact
使用示例
javascript
import { render } from 'preact'
import { useState } from 'preact/hooks'
function App() {
const [counter, setCounter] = useState(0)
return (
<div>
<h1 onClick={() => setCounter(counter + 1)}>{counter}</h1>
</div>
)
}
render(<App />, document.querySelector('#app'))
二. preact
渲染流程
preact
渲染流程中使用了虚拟DOM
树,虚拟DOM
树的节点会维护对应真实DOM
节点。
在首次渲染中,会采用深度优先遍历算法递归创建虚拟DOM
节点,且同时创建其对应的真实DOM
节点,然后会将真实DOM
节点插入到父DOM
节点中,当整个虚拟DOM
树构建完成后,会将创建好真实DOM
树插入到页面指定节点上完成首次渲染流程。
当状态更新时,会触发更新渲染,与React
不同的是,preact
不会重新创建新的虚拟DOM
树,而是从触发状态更新的虚拟DOM
节点开始进行diff
,创建其对应的新的虚拟DOM
节点,然后更新到当前虚拟DOM
树中,也就是preact
的更新颗粒度相比React
会更细,可以做到局部更新。需要注意的是更新时机和React
一样都是异步的。
三. preact signals
在preact
中如果要使用signals
,如果要额外引入@preact/signals
依赖。
preact signals
使用示例如下,从示例中可以发现获取/修改状态的方式与React
有比较大的差异,更贴合Vue
的使用方式。
javascript
import { render } from 'preact'
import { useSignal } from '@preact/signals'
function App() {
const counter = useSignal(0)
return (
<div>
<h1 onClick={() => counter.value++}>{counter.value}</h1>
</div>
)
}
render(<App />, document.querySelector('#app'))
3.1 实现useSignal
useSignal
是preact
提供的hook
方法,底层调用signal
方法创建Signal
对象。
javascript
import { signal } from '@preact/signals'
import { useMemo } from 'preact/hooks'
export function useSignal(value) {
return useMemo(() => signal(value), [])
}
3.2 实现signals
核心逻辑是通过Object.defineProperty
方法添加Signal
对象value
属性的get
和set
拦截器(和Vue2
的响应式更新实现理念很类似)。具体执行逻辑如下:
- 定义
Signal
对象,添加value
属性的get
和set
拦截器 - 创建
Signal
对象实例,记录初始值 - 当访问
Signal
对象实例的value
属性时,会收集当前虚拟DOM
节点的触发更新渲染方法 - 当修改
Signal
对象实例的value
属性时,触发当前虚拟DOM
节点的更新渲染方法
3.2.1 定义Signal
对象
javascript
function Signal(value) {
// 记录value属性值
this._value = value
// 记录虚拟DOM节点的更新渲染方法
this._targets = null
}
Object.defineProperty(Signal.prototype, 'value', {
get(signal) {
// 该方法核心逻辑是收集虚拟DOM节点的更新渲染方法
addDependency(signal)
return signal._value
},
set(signal, value) {
if (value !== signal._value) {
signal._value = value
// 触发更新渲染
signal._targets._notify()
}
},
})
3.2.2 定义signal
方法
javascript
function signal(value) {
return new Signal(value)
}
四. 总结
和React
一样,preact
渲染流程也使用了虚拟DOM
树,不同点在于更新渲染时preact
不会重新构建一颗完整的虚拟DOM
树,而是使用了局部更新机制。
preact signals
的核心实现逻辑与Vue2
很类似,主要是使用到Object.defineProperty
方法实现依赖收集和派发更新功能。