likes
comments
collection
share

手写状态管理库: Mobx

作者站长头像
站长
· 阅读数 40

mobx是一个状态管理库,类似于redux或者vuex完成状态观察相应相关的处理。mobx使得状态管理更加的简单和透明。由于这篇文章时mobx原理解析,所以关于mobx的使用就不在这里记录。

注: 在开始之前,需要先搭建起支持Es7语法的装饰器开发的webpack等开发环境. 这里主要实现Mobx的observable, autorun两个方法.

Mbox的observable的方法主要实现的是对状态的Proxy深度代理,使用Proxy对象代理状态对象的属性,同时在autorun方法使用的时候,会在这个代理过程中执行状态的依赖收集的相关操作.

首先这里给一个简单的测试例子:

// 测试 Mobx 库用例
import { observable, autorun } from "./mobx" 
// observable方法用于完成对状态对象的Proxy的代理,将状态变为可观察对象
const o = observable({ name: "chensir" });
// autorun方法类似于vue中的watcher,其中传递进去一个handler Function,这个回调函数会在初始化的时候被执行一次,之后每次内部相关的observable中的依赖发生变动时被再次调用
autorun(() => {
    console.log(o.name)
})
// 直接设置 o.name 值
o.name = "chenSirLaiLe"
// 测试用例会打印: chensir \n chenSirLaiLe

实现observable方法:

// 这里后面的两个参数: key 和 descriptor主要用于之后的装饰器实现
export defualt function observable(target, key, descriptor){
    // 这里支持装饰器模式的observable写法:
    if(typeof key === "string"){
        // 如果是作为装饰器装饰属性进行监听,先将装饰的对象进行深度代理
        let v = descriptor.initializer();
        v = createObservable(v);
        // 这里执行依赖搜集: 使用的Reaction类会在之后实现
        let reaction = new Reaction();
        // 返回描述器
        return {
            enumerable: true,
            configurable: true,
            get(){
                reaction.collect();  // 再获取target属性时进行autorun中的handler的依赖搜集
                return v;
            },
            set(value){
                v = value;
                reaction.run();  // 在每次更新target中的属性时执行autorun中的依赖
            }
        }
    }
    // 如果不是装饰器写法,则创建Proxy代理
    return createObservable(target);
}

创建代理对象

function createObservable(val){
    // 用于生成代理对象的控制器:
    const handler = () => {
        // 实例化Reaction在autorun获取属性的时候进行依赖搜集
        let reaction = new Reaction();
        return {
            set(target, key, value){
                // 对于数组的值设置处理: 当对数组进行观察监听时,由于对数组的操作会有两步执行:
                // 更新数组元素值
                // 更改数组的length属性,所以需要将更改length属性的操作给拦截,避免一次操作数组,多次触发handler
                if(key === "length"){
                    return true;
                }
                // 执行搜集绑定, 此时修改值需要先执行,这样在autorun中的handler中才能拿到最新的值
                let r = Reflect.set(target, key, value)
                reaction.run();
                return r;
            },
            get(target, key){
                // 在获取属性值的时候进行依赖搜集
                reaction.collect()
                return Reflect.get(target, key);
            }
        }
    }
    // 进行深层Proxy代理返回: 针对如: {name: "chensir", age: {num: 21}}这样的对象
    return deepProxy(val, handler)
}
​
// 深度设置Proxy对象代理
function deepProxy(val, handler){
    if(typeof val !== "object"){
        return val;
    }
    // 深度递归进行Proxy代理,此时的递归树相当于是后序遍历进行代理
    for(let key in val){
        val[key] = deepProxy(val[key], handler);
    }
    return new Proxy(val, handler);
}

实现Reaction类进行状态搜集,作为abservable和autorun之间的桥梁:

// 定义两个全局变量,这里是简单实现,所以和实际的源码实现有一定的区别
let nowFn = null;  // 这个表示当前的autorun中的handler方法
let counter = 0;  // 这里使用counter记录一个计数器值作为每个observable属性的id值进行和nowFn进行绑定class Reaction {
    constructor(){
        // 标识每一个proxy对象
        this.id = ++counter;  // 这里采用一个比较low的方法简易实现的,在每次对observable属性进行Proxy的时候,对Proxy进行标记
        this.store = {};  // 存储当前可观察对象对应的nowFn, 写入的形式如: {id: [nowFn]}
    }
    collect(){
        // 进行依赖搜集,只当当前有autorun绑定了相关属性观察后才会进行绑定
        if(nowFn){   // 通过这个判断主要是因为只有在调用autorun绑定的时候才会设置这里的nowFn
            this.store[this.id] = this.store[this.id] || [];
            this.store[this.id].push(nowFn);    
        }
    }
    run(){
        // 运行依赖函数
        if(this.store[this.id]){
            this.store[this.id].forEach(fn => {
                fn()
            })
        }
    }
    // 定义两个静态方法,用于在调用autorun方法时候对nowFn进行设置和消除
    static start(handler){
        nowFn = handler;
    }
    // 在注册绑定这个就要清空当前的nowFn,用于之后进行进行搜集绑定
    static end(){
        nowFn = null;
    }
}

实现autorun方法,进行简单的依赖搜集

export default function autorun(handler){
    if(typeof handler !== "function"){
        throw new TypeError(`autorun function expect a function but get a ${typeof handler}`)
    }
    // 开始搜集依赖,设置Reaction中的nowFn
    Reaction.start(handler)
    // 执行一次handler,在handler中有对于相应属性的getter获取,此时就可以设置改属性的Proxy的Reaction状态依赖
    handler()
    // 清除nowFn
    Reaction.end()
}

这里简单实现了Mobx的核心,当然和源码还是有一定的区别,所以不要将此处的代码和源码进行比对,我们只是希望去理解Mobx的状态观察搜集的原理而已。除此之外,Mobx还有一些其他的方法大家可以去了解了解。除此之外,Mobx在React框架中使用时,需要使用一个Mobx-react的库,这个库的实现原理也比骄简单这里提一下思路原理: Mobx-React这个库在React Component组件中的state发生变动时手动调起组件的render方法和forceUpdate()对外部mobx更新后的状态在Component中进行强制刷新。

原文链接:blog.csdn.net/qq_44746132…

转载自:https://juejin.cn/post/7076329672706883591
评论
请登录