1. mobx #

mobx是一个简单可扩展的状态管理库

2. mobx vs redux #

mobx学习成本更低,性能更好的的状态解决方案

3. 核心思想 #

状态变化引起的副作用应该被自动触发

flow

4.环境配置 #

create-vite 
npm install mobx mobx-react --save

5. mobx #

observable

5.1 引用类型 (observable) #

import { observable } from 'mobx';
const p1 = observable([1, 2, 3]);
p1.push(4);
p1.pop();
console.log(p1);
console.log(Array.isArray(p1));

5.2 基本类型(observable.box) #

类型 描述
String 字符串
Boolean 布尔值
Number 数字
Symbol 独一无二的值
import { observable } from 'mobx';
let num = observable.box(10);
let str = observable.box('hello');
let bool = observable.box(true);
console.log(num.get(), str.get(), bool.get());
num.set(100);
str.set('world');
bool.set(false);
console.log(num.get(), str.get(), bool.get());

5.1.3 decorator #

import { observable } from 'mobx';
class Store {
    @observable name='zfpx';
    @observable age=9;
    @observable isMarried=false;

    @observable hobbies=[];
    @observable home={name:'北京'};
    @observable skills=new Map();
}

6. 使用对可观察对象做出响应 #

6.1 computed #


let {observable,computed} = require('mobx');
class Store {
    @observable name='zfpx';
    @observable age=9;
    @observable area='010';
    @observable number="18910092296"

    @observable province="广东";
    @observable city="东莞";
    @computed get home() {
        return this.province+this.city;
    }
}

let store=new Store();
let cell = computed(function () {
    return store.area+'-'+store.number;
});
cell.observe(change=>console.log(change));
console.log(cell.get());
store.area='020';
store.number='15718856132';
console.log(cell.get());
console.log(store.home);
store.province='山东';
store.city='济南';
console.log(store.home);

6.2 autorun #

autorun(() => {
    //console.log(store.province,store.city);
    console.log(store.home);
});

store.province='山东';
store.city='济南';

6.3 when #

when(predicate: () => boolean, effect?: () => void, options?)
let dispose = when(() => store.age>=18,()=>{
    console.log('你已经成年了!')
});
dispose();
store.age=10;
store.age=20;
store.age=30;

6.4 reaction #

reaction(() => [store.province,store.city],arr => console.log(arr.join(',')));
store.province='山东';
store.city='济南';

7. action #

let {observable,computed,autorun,when,reaction,action} = require('mobx');
class Store {
    @observable province="广东";
    @observable city="东莞";
    @action moveHome(province,city) {
         this.province=province;
         this.city=city;
    }
}
let store=new Store();
reaction(() => [store.province,store.city],arr => console.log(arr.join(',')));
store.moveHome('山东','济南');

7.2 action.bound #

let {observable,computed,autorun,when,reaction,action} = require('mobx');
class Store {
    @observable province="广东";
    @observable city="东莞";
    @action.bound moveHome(province,city) {
         this.province=province;
         this.city=city;
    }
}
let store=new Store();
reaction(() => [store.province,store.city],arr => console.log(arr.join(',')));
let moveHome=store.moveHome;
moveHome('山东','济南');

7.3 runInAction #

runInAction(() => {
    store.province='山东';
    store.city='济南';
});

8. mobx应用 #

8.1 计数器 #

import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import {observable,action} from 'mobx';
import PropTypes from 'prop-types';
import {observer} from 'mobx-react';

class Store {
    @observable number=0;
    @action.bound add() {
        this.number++;
    }
}
let store=new Store();

@observer
class Counter extends Component{
    render() {
        return (
            <div>
                <p>{store.number}</p>
                <button onClick={store.add}>+</button>
            </div>
        )
    }
}
ReactDOM.render(<Counter/>,document.querySelector('#root'));
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import {observable,action} from 'mobx';
import PropTypes from 'prop-types';
import {observer} from 'mobx-react';

class Store {
    @observable counter={number:0};
    @action.bound add() {
        this.counter.number++;
    }
}
let store=new Store();
@observer
class Counter extends Component{
    render() {
        return (
            <div>
                <p>{this.props.counter.number}</p>
                <button onClick={this.props.add}>+</button>
            </div>
        )
    }
}
ReactDOM.render(<Counter
    counter={store.counter}
    add={store.add}
/>,document.querySelector('#root'));

8.2 TODO #

import React,{Component,Fragment} from 'react';
import ReactDOM from 'react-dom';
import {observable,action, computed} from 'mobx';
import PropTypes from 'prop-types';
import {observer,PropTypes as ObservablePropTypes} from 'mobx-react';
class Todo{
    id=Math.random();
    @observable text='';
    @observable completed=false;
    constructor(text) {
        this.text=text;
    }
    @action.bound toggle() {
        this.completed=!this.completed;
    }
}
class Store{
    @observable todos=[];
    @computed get left() {
        return this.todos.filter(todo=>!todo.completed).length;
    }
    @computed get filterTodos() {
        return this.todos.filter(todo => {
            switch (this.filter) {
                case 'completed':
                    return todo.completed;
                case 'uncompleted':
                    return !todo.completed;
                default:
                    return true;
            }
        });
    }
    @observable filter='all';
    @action.bound changeFilter(filter) {

        this.filter=filter;
        console.log(this.filter);
    }
    @action.bound addTodo(text) {
        this.todos.push(new Todo(text));
    }
    @action.bound removeTodo(todo) {
        this.todos.remove(todo);
    }
}
@observer
class TodoItem extends Component{
    static porpTypes={
        todo: PropTypes.shape({
            id: PropTypes.number.isRequired,
            text: PropTypes.string.isRequired,
            completed:PropTypes.bool.isRequired
        }).isRequired
    }
    render() {
        let {todo}=this.props;
        return (
            <Fragment>
                <input
                    type="checkbox"
                    onChange={todo.toggle}
                    checked={todo.completed} />
                <span className={todo.completed? 'completed':''}>{todo.text}</span>

            </Fragment>
        )
    }
}
@observer
class TodoList extends Component{
    static propsTypes={
        store: PropTypes.shape({
            addTodo:PropTypes.func,
            todos:ObservablePropTypes.observableArrayOf(ObservablePropTypes.observableObject)
        }).isRequired
    };
    state={text:''}
    handleSubmit=(event) => {
        event.preventDefault();
        this.props.store.addTodo(this.state.text);
        this.setState({text:''});
    }
    handleChange=(event) => {
        this.setState({text:event.target.value});
    }
    render() {
        let {filterTodos,left,removeTodo,filter,changeFilter}=this.props.store;
        return (
            <div className="todo-list">
                <form onSubmit={this.handleSubmit}>
                    <input placeholder="请输入待办事项" type="text" value={this.state.text} onChange={this.handleChange}/>
                </form>
                <ul>
                    {
                        filterTodos.map(todo => (
                            <li key={todo.id}>
                                <TodoItem todo={todo} />
                                <button onClick={()=>removeTodo(todo)}>X</button>
                            </li>
                        ))
                    }
                </ul>
                <p>
                    <span>你还有{left}件待办事项!</span>
                    <button
                        onClick={()=>changeFilter('all')}
                        className={filter==='all'?'active':''}>全部</button>
                    <button onClick={() => changeFilter('uncompleted')}
                        className={filter==='uncompleted'?'active':''}>未完成</button>
                    <button
                        onClick={()=>changeFilter('completed')}
                        className={filter==='completed'?'active':''}>已完成</button>
                </p>
            </div>
        )
    }
}
let store=new Store();
ReactDOM.render(<TodoList store={store}/>,document.querySelector('#root'));

9.优化 #

9.1 observe #

    constructor() {
        observe(this.todos,change => {
            console.log(change);
            this.disposers.forEach(disposer => disposer());
            this.disposers=[];
            for (let todo of change.object) {
                this.disposers.push(observe(todo,change => {
                    this.save();
                    //console.log(change)
                }));
            }
            this.save();
        });
    }

9.2 spy #

spy(event => {
    //console.log(event);
})

9.3 toJS #

    constructor() {
        observe(this.todos,change => {
            console.log(change);
            this.disposers.forEach(disposer => disposer());
            this.disposers=[];
            for (let todo of change.object) {
                this.disposers.push(observe(todo,change => {
                    this.save();
                    //console.log(change)
                }));
            }
            this.save();
        });
    }
    save() {
        localStorage.setItem('todos',JSON.stringify(toJS(this.todos)));
    }

9.4 trace #

trace

10. 优化 #