一. 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方法实现依赖收集和派发更新功能。