limbo-reactivity
v1.0.2
Published
一个数据驱动的响应核心
Readme
reactivity
一个数据驱动的响应核心
本仓库并不提供独立的dom操作库,只提供连接符合dom接口(
react标准)规范的dom库的嵌入api。
如果你想使用现成的框架,可以移步limbo(一个集成了调度和事件系统的高性能现代前端框架)。并且使用我们提供的mixInReact来注册limbo。
如果你想使用符合规范的其他框架,请使用reactivity/register进行注册。
当然,最方便的还是使用我们提供的reabo来作为独立的mvvm框架使用。(实际上,它就是通过reactivity/register注册了limbo)
install
yarn add @limbo/reactivityHOW TO USE
注意: reactivity本身可以以一个完整的mvvm框架形态使用,并且提供了高性能的独立diff包及一些静态优化,但我们还是希望用户能使用标准的react生态,所以独立dom需要引入额外的插件.
mixInReact
要想在react里使用reactivity是非常简单的,只需要使用两行代码
import { mixInReact } from '@limbo/reactivity'
mixInReact(React)从此,你可以快乐的在React里使用reactivity了.
rootState
默认的,reactivity会为每一个React.Component注入一个名为rootState的对象,你可以直接操作这个对象上的任意值,并会直接得到响应
const A = (props) => {
const handleClick = () => {
props.rootState.a += 1
}
return (
<button onClick={handleClick}></button>
)
}上面代码中,点击了按钮后,任何依赖rootState.a的组件都会更新
createState
你可能会问,rootState究竟从哪里来呢?
reactivity提供了一个注册rootState的方法
setRootState(
createState({
a:1,
b:2,
c:{d:[1,2,3],e:1},
f:{a:1,b:2}
})
)事实上,createState注册了一个reactivity概念上的State,然后通过
setRootState将它注册到rootState上.
createRef
当然,如果你想像vue3一样创建一个单值属性,你也可以使用createRef
注意,你需要使用value来访问它。具体的,你可以参考call by value和call by reference的区别。
function App() {
const ref = createRef(1)
useEffect(() => {
console.log(ref.value)
})
return <Son state={ref}/>
}createWatcher
作为响应式框架,watcher应该是很常见的功能。
它随着一个响应式变量的更新而触发提前定义好的响应操作。
function App() {
const state = createState({
userId: 'init'
})
const watch = createWatcher(state,'userId',(newState,oldId,newId) => {
localStorage.setItem('id',newId)
})
}createWatcher的签名如下:
watcher:(state:State,key:any,onChange:IOnChange)
IOnChange:(newState: object, oldValue ?: any, newValue ?: any, changeKey ?: any) => voidcreateCompued
同时,reactivity提供了计算功能,你可以像任何响应式框架(vue,mobx)一样使用computed
function App(props) {
const computed = createComputed(() => props.state.a * props.state.b)
}注意,createComputed需要传入一个函数
anyState
是的,单一的rootState非常难以管理,我们希望在任何位置都能快速的创建一个
State,并提供响应式的能力,参考以下代码:
function App(props) {
const A = createState({aa:1})
return (
<OtherState otherState={A}/>
)
}此时,你可以在OtherState里直接修改A的任意值,它也会触发响应式更新.
computed
watcher
当你在注册一个State时,实际上就是对一个object进行观察,同时reactivity也暴露出了一个watcher的API
let obj = {a:1,b:2}
obj = watcher(obj,() => console.log('i am not side-effect'))修改
你可以对任意的State(不论是自动注入,或是手动创建的)进行修改.
Array
const A = createState([1,2,3])
A.length = 4
A[2] = 0
A.push(3)上面的每一项都会触发响应式的更新,无需其他的心智负担.
nest-object
同样的,即使在深层次的嵌套对象的情况下,它也会完美的进行响应.
const A = {
a: {
b: {
c
}
}
}
A.a.b.c = 3性能
one but only one update once
reactivity保证了当父子组件都需要更新的时候,但子组件先于父组件更新时,父组件不会再无效的reconcile子组件。
如何定义无效更新
实际上,父组件传入的props不发生变化时,子组件不应该出现任何由父组件更新导致的变化。所以reactivity把shallowequal作为是否有效更新的判断标准。
更新顺序
实际上,更新顺序在开启schedule后是由react决定的,父子组件在updateQueue中的位置并不由reactivity计算,但框架保证了必要情况下节省无效更新。
内存泄露
reactivity拥有非常好的依赖收集性能,它没有潜在的引用逃逸和闭包等问题,
旧的依赖都会在下次收集时被清除,无需担心依赖储存会导致内存泄漏.
依赖收集
很不幸的是,目前的响应式框架都存在非常大的问题: 轻量级的依赖收集在组件规模增大后,却会成为一个非常耗时的工作.
reactivity并没有办法解决它,但是有很多小技巧可以提升依赖收集过程的性能
.一个很常见的方法就是,不要使用嵌套层级太深的数据结构作为State,你应该打平而不是嵌套他们.
// good
const A = createState({a:1})
const B = createState({b:2})
const C = createState({c:3})
// bad
const nest = createState({a:{b:c}})原因在于,你可以在任何地方创建State,所以对嵌套对象的依赖就没有那么必要了.
依赖清除
上面已经提到过,依赖清除会在第二次收集时进行.它的实现是标准的double buffer,具体细节不必深究.
React生态
自动优化
在集成reactivity后,你的React应用不再需要使用繁琐的memo或者PureComponent来进行优化了,任何组件都会被自动的shallowEqual优化.
精确更新
使用响应式框架最诱人的一点就是,你不再需要使用redux里繁琐的reselect来进行性能提升,也不需要在每个层级组件上使用useCallback来增加额外的心智负担.任何组件都只会在你需要的时候被精确更新!
调度
reactivity不会干扰任何的schedule过程,也不会涉及到任何内部优先级的计算(统一为forceUpdate优先级)。
实际上,框架的实现并不是表面上的mutable,所以调度涉及到的多次更新不会影响到框架的运行,无需担心调度被框架干扰。
调试工具
reactivity提供了History进行时光回溯,并且提供标准的chrome插件,但目前暂未开源.
