zoukankan      html  css  js  c++  java
  • Mobx是如何工作的

    mobx工作原理

    推荐版本: "mobx-react": "4.1.0", "mobx": "^2.7.0 || ^3.0.0"

    1 Mobx 要点

    1.1 定义状态并使其可观察

    可以任何数据结构来存储状态,如对象、数组、类,打上mobx的标记会变为可观察。

    import { observable } from 'mobx';
    var appStore = observable({
        timer: 0;
    });
    

    1.2 创建视图响应状态变化

    mobx 以最小限度更新视图,任何函数都可以成为响应式视图观察自身数据。 

    import {observer} from 'mobx-react';
    @observer
    class TimerView extends React.Component {
        render() {
            return (
                <button onClick={this.onReset.bind(this)}>
                    Seconds passed: {this.props.appState.timer}
                </button>
            );
        }
        onReset() {
            this.props.appState.resetTimer();
        }
    };
    ReactDOM.render(<TimerView appState={appStore} />, document.body);
    

    1.3 更改状态

    mobx会用简单直观的方式更改状态,使用action(可以配置Mobx强制使用action更新)或者直接修改

    2 概念及原则

    2.1 State 状态

    状态是驱动应用的数据。像待办事务列表的特定状态,还有像当前已选元素的视图状态。 
    状态就像有数据的 Excel表格。

    2.2 derivations 衍生

    什么是衍生, 源自状态并且不会再有进一步的相互作用的东西就是衍生。 

    • 用户界面 
    • 衍生数据,剩下的待办事项 
    • 后端集成,比如吧变化发送到服务器端 
      mobx 集成了两种类型的衍生

    Computed values计算属性值

    使用纯函数从当前可观察状态中衍生出的值。 

    Reactions 反应

    是当 State 改变时需要自动发生的副作用。。需要有一个桥梁链接 函数式编程 和 响应式编程 
    初次使用mobx会经常使用reactions,但是推荐使用computed,回到表格的概念,公式是计算值的衍生。 

    Actions 动作

    动作 任意一段可以改变状态的代码。 用户操作,后端数据推送,预定事件等。 
    动作 类似在表格单元格中输入一个新值。 mobx中可以显示的定义动作,@action . 

    3 原则

    mobx支持单向的数据流,动作来改变状态,从而 状态State 的改变会更新受影响的视图。 
    action ---> state ---> view 
    当 State 改变时,所有衍生都会进行原子级自动更新 因此不可能观察到中间值 
    全部的 衍生 默认都是同步更新 因此在动作之后,可以安全的检查计算值 
    计算值 是延迟更新的。 任何不在使用状态的计算值都不会更新,直到需要时会进行 副作用(IO),不使用时会自动垃圾回收 
    计算值 不应该去改变状态,应该是一个纯洁的副作用。 

    4 核心API

    主要api: Computed 、 observable 、 reactions 、 actions  
    

    4.1 observable

    observable(value);
    @observable property = value;
    

    Observable 观察的值可以是基本类型、引用类型、普通对象、类实例、数组和映射。 
    主要类型转换规则,或者通过装饰器微调(修饰class,函数)

    • 如果被观察 value 是ES6实例,会返回一个新的Observe Map,基于ES6。如果不只是在更改某个entry时修改 
      而是,在添加或删除其他entry时做出反应,Observe Map 会很有用。 
    • 如果被观察 value 是数组,会返回一个 Observe Array 
    • 如果 value 是么有原型的对象(对象可以灭有原型)或者原型是 Object.prototype ,对象会被克隆并且所有属性会被转换成可观察的 Observe Object 
    • 如果 value 是有原型的,例如函数,数组,可以有4中方法处理 Boxed Observer
      • 显示的调用 observable.box(value) 有点神奇
      • @observable 常用
      • 调用 decorate()
      • 类中引入 extendObservable() 来引入属性 可用 装饰器默认是有感染的,observalbe 被自动应用于数据结构包含的任何值,
        observable 是 extendObservable(this, {prototype: value}) 的语法糖 observable.object(obj, decorator, option) 默认这些值都会转换成可观察 
        observable.array(obj, option) 会生成一个observable 数组,如果不想每个值都被观测,可设置 {deep: false} 
        observable.map(obj, option) 无需局限于字符串

    4.2 装饰器 Decorator

    可用装饰的列表是这些:

    • observable.deep 默认的 observable 装饰器 
    • computed 创建一个衍生,就是能自动获取已修改值的函数并返回新值 
    • action 创建 动作 
    • action.bound 创建有范围的动作 
    class TodoList {
        todos = {}
        get unfinishedTodoCount() {
            return values(this.todos).filter(todo => !todo.finished).length
        }
        addTodo() {
            const t = new Todo()
            t.title = 'Test_' + Math.random()
            set(this.todos, t.id, t)
        }
    }
    decorate(TodoList, {
        todos: observable,
        unfinishedTodoCount: computed,
        addTodo: action.bound
    })   // 对类 Observable 转换 
    

    4.3 计算属性 Computed

    用法好几种,看起来只有一些细微的差别:

    • computed( () => expression) 
    • computed( () => expression, (newvalue) => void ) 
    • computed( () => expression, option ) 
      @computed({ equals: compareFn }) get property() { return expression; } 
      @computed get classProperty() { return expression; } 

    Computed 自带很多操作属性 控制 Computed 行为 

    • 比较器算法 equals: (value, value) => boolean 用来重载默认检测规则的比较函数。 内置比较器有: comparer.identity, comparer.default, comparer.structural 
    • 追踪 其他observable 类型属性值,等待返回之后在做计算 requiresReaction: boolean 在重新计算衍生属性之前,等待追踪的 observables 值发生变化

    - get: () => value 
    - set: (value) => void 
    - keepAlive: boolean 保持计算值活动,不光是在值发生变化之后。 

    4.4 动作 Actions

    任何用来 修改状态 的东西 
    建议在任何更改 observable 或者有副作用的函数上进行 Actions修饰 

    4.5 流处理 Flow

    flow(function* (args) {}) 
    flow() 接收 generator 函数作为他的唯一输入 
    flow 的关键作用是 处理异步代码时确保代码被action包装 ,因为正常的 observable state 对异步操作无法通过 enforceActions 检查。 
    神奇的flow可以解决这个异步不跟踪的问题 
    注意,异步函数必须是 generator ,而且在内部只能 yield promises 

    import { configure, flow } from 'mobx';
    
    // 不允许在动作外部修改状态 严格模式的意思
    configure({ enforceActions: true });
    
    class Store {
        @observable githubProjects = [];
        @observable state = "pending"; // "pending" / "done" / "error"
    
    
        fetchProjects = flow(function* fetchProjects() { // <- 注意*号,这是生成器函数!
            this.githubProjects = [];
            this.state = "pending";
            try {
                const projects = yield someAsyncProcess(); // 用 yield 代替 await
                const filteredProjects = somePreprocessing(projects);
    
                // 异步代码自动会被 `action` 包装
                this.state = "done";
                this.githubProjects = filteredProjects;
            } catch (error) {
                this.state = "error";
            }
        })
    }
    

    Flows 可以撤销,调用promise的cancel() 方法会停止异步状态取值, 会继续执行 finally 子句 。

    5 observables 做出响应

    5.1 computed

    计算值是可以根据现有的状态或其他计算值衍生的值。 
    概念上来讲,他们和表格中的值十分相似,比如汇总80分以上的同学。 
    计算属性 可以使实际可修改的值尽可能的小,计算属性也是高度优化过的,可以多用 

    5.2 computed & autorun

    声明式的创建计算属性,可以在类任意的属性上使用装饰器

    import { observable, computed } from 'mobx';
    
    class orderline {
        @observable price = 10;
        @observable amount = 1;
    
        constructor(price) {
            this.price  = price;
        }
        @computed get total() {
            return this.price * this.amount;
        }
    
        
    }
    import { observable, autorun } from 'mobx';
    
    const value = observable(0);
    const number = observable(100);
    
    autorun(() => {
      console.log(value.get());
    });
    
    value.set(1);
    value.set(2);
    number.set(101); // 0 1  2  不打印 101 yinwei number 未在autorun内部执行 number.get()/
    Mobx 学习 基本写法 * 此处声明式的监控变量,与 ES6 的类修饰不同。 

    import { observable, action, computed, toJS } from 'mobx' import { observer } from 'mobx-react'

    export default class InstanceStore { @observable value = 1

    @action
    modifyValue(v) {
        this.value = v
    }
    @computed get getValue() {
        return this.value * 10;
    }
    

    }

    computed 直接获取一个计算后的值。

    如果一个值需要根据某个state计算,并且也需要被观察则可以使用 @computed autorun 类似

    autorun 用于执行一些和值变化有关的操作,比如异步请求,数据处理等

    computed 用于根据已有的值,计算出新的值返回一个对观察值追踪的结果 var ins = new InstanceStore(); console.log('value form mobx computed', toJS(ins.getValue()))

    autorun 在不需要继续使用的情况可以进行垃圾回收

    var numbers = observable([1,2,3]);

    var sum = computed(() => numbers.reduce((a, b) => a + b, 0));

    var disposer = autorun(() => console.log(sum.get())); // '6'

    numbers.push(4);                                                  // '10'

    disposer();

    numbers.push(5); // 什么也不打印,因为disposer执行是不再对autorun reaction

    过期状态值方式如下

    var ins = new InstanceStore();

    console.log(toJS(ins.value),'get value from mobx');

    dispatch 修改值 var ins = new InstanceStore(); ins.modifyValue(1000);

    在组件内可以使用观察者模式 @observer class routeCreate extends Component { constructor(props) { super(props); this.store = new InstanceStore(); } ... }

    使用 observer 修饰组件,并且在render内部有 mobx 值的引用,组件会多一个生命周期 componentWillReact // redux 改变值的方式是通过拷贝原来的对象生成新的对象,触发组件的componentWillReceiveProps // mobx 是以原始值的基础上生成新的对象,之前的引用不变所以mobx 不会触发ReceiveProps周期

    
    异步处理
    mobx 状态值为同步更新。
    

    export default class InstanceStore { @observable value = 1

    @action
    modifyValue(v) {
        this.value = v;
        setTimeout(this.valueAdd, 100);
    }
    @action.bound
    valueAdd(v) {
        this.value = v + 20;
    }
    

    }

    // .bound 是js执行环境语法糖
    // 过多action ? 需要简化写法
    // mobx 自身提供了一个工具函数帮助跟新对应action中的值 runInAction
    

    export default class InstanceStore {

       @observable value = 1

    @action
    asyncModifyValue(v) {
        this.value = v;
        setTimeout(action('valueAdd', () => {
            this.value = v + 20
        }), 100);
    }
    
    @action
    asyncModify(v) {
        this.value = v;
        setTimeout(runInAction(() => {
            this.value = v + 20
        }), 100);
    }
    

    }

    //  异步action,action可以这样写
    
    @asyncAction
    changeValue() {
        this.value = 0;
        const data = yield Promise.resolve(1)
        this.value = data;
    }
    
    
    
    toJS 将mobx state 序列转换为js可识别类型?
    
    更新action的约束
    mobx 非强制使用action改变state;如果要加强制action触发state可以通过 
    Mobx.configure({enforceActions: true}) 加限制条件,强制通过action更新,适用于大型项目
    
    
    
    以下是遗留问题
    * 1, mobx 是否是同步更新 是
    * 2, mobx toJS是如何实现的  
    * 3,store对应单个变量,会按照类型预留数组空间,是什么原因
    * 4,使用toJS获取数据,需要在class名称上面加 @observer 吗
    * 5,为什么mobx取值,是如此的简介?,而且是支持多状态
    * 6,extendObservable 可以按照扩展的方式 装饰函数或class里的对象
  • 相关阅读:
    第二章 数据类型、变量、和运算符
    第一章
    ActiveMQ点对点的发送和接收
    初探设计模式(1)——工厂模式
    IDEA使用switch传入String编译不通过
    MyBatis日期转换报错
    HTML页面传值问题
    maven配置本地仓库
    Maven的下载及安装
    PHP实现获得一段时间内所在的所有周的时间
  • 原文地址:https://www.cnblogs.com/the-last/p/11221076.html
Copyright © 2011-2022 走看看