Mobx 学习笔记

最简单的版本

第一步:初始化容器仓库

import {observable} from "mobx"
class Store
{
  @observable count = 0;//将普通数据变为可被观测的数据
  
}
import {observable,action} from "mobx"
class Store
{
  @observable count = 0;//将普通数据变为可被观测的数据
  @action.bound increment(){ //添加一个action,修改数据状态
     this.count++
  }
  constructor()
  {
    makeObservable(this);
  }
}

//参考写法:https://github.com/mobxjs/mobx/issues/2468

第二步:在组件中使用mobx容器状态

初始版本

//1. 定义组件
class App extends React.Component{
  render(){
    return <div>
      <h1>App Component</h1>
    </div>
  }
}
//2. 渲染组件
ReactDOM.render(<App />,document.getElementById('root'));

第二版 使用状态数据

//1. 定义组件
class App extends React.Component{
  render(){
    const {count} = this.props;
    return <div>
      <h1>App Component</h1>
      <p>{store.count}</p>
    </div>
  }
}
//2. 渲染组件
//传递store对象,当做props,这样就可以在render函数中使用了
ReactDOM.render(<App store= {new Store()}/>,document.getElementById('root'));

第三版 使用action

//1. 定义组件
class App extends React.Component{
  render(){
    const {count} = this.props;
    return <div>
      <h1>App Component</h1>
      <p>{store.count}</p>
      <button onClick= {store.increment}> Increment  </button>
    </div>
  }
}
//2. 渲染组件
//传递store对象,当做props,这样就可以在render函数中使用了
ReactDOM.render(<App store= {new Store()}/>,document.getElementById('root'));

第四版 关联mobx和react

//1. 定义组件
import {observer} from "mobx-react"
@observer  //修改的是这里
class App extends React.Component{
  render(){
    const {count} = this.props;
    return <div>
      <h1>App Component</h1>
      <p>{store.count}</p>
      <button onClick= {store.increment}> Increment  </button>
    </div>
  }
}
//2. 渲染组件
//传递store对象,当做props,这样就可以在render函数中使用了
ReactDOM.render(<App store= {new Store()}/>,document.getElementById('root'));

装饰器的种类

1. 类修饰器

//不带参数的版本
function fn(target){ //target为类定义
  target.foo = "bar";
}
@fn
class MyClass {

}
console.log(MyClass.foo) // => bar
//带参数的版本
function fn2(value){
  return function(target){
    target.count = value
  }
}
@fn2(10)
class MyClass {

}
console.log(MyClass.count) // => 10

2. 类实例对象修饰器

function fn3(target){
  target.prototype.foo= "bar";
}
@fn3
class MyClass {

}
let obj = new MyClass();
console.log(MyClass.foo) // => bar

3. 类实例属性修饰器

function fn4(target,name,descriptor){
  console.log(target);//target为目标类的prototype
  //=> {constructor:f}
  console.log(name);//name 被修饰的类成员名称
  //=> message
  console.log(descriptor);//descriptor 被修饰的类成员的描述对象 
  //=> {configurable:true,enumerable:true,writable:true,initializer:f}
  descriptor.writable = false;//修改为不可写,只读
}
class MyClass {
  @fn4 message = "hello";
}

4. 类实例方法修饰器和类实例属性修饰器是一样的

function fn4(target,name,descriptor){
  console.log(target);//target为目标类的prototype
  //=> {constructor:f}
  console.log(name);//name 被修饰的类成员名称
  //=> message
  console.log(descriptor);//descriptor 被修饰的类成员的描述对象 
  //=> {configurable:true,enumerable:true,writable:true,initializer:f}
  descriptor.writable = false;//修改为不可写,只读
}
class MyClass {
  @fn4 test (){
    console.log("hello");
  }
}

理解 observable

状态改变触发事务更新

import {autorun} from 'mobx'
//autorun 根据依赖决定观测后的行为处理
const store = new Store()
//autorun 一上来会自动执行一次,然后autorun就知道了自己依赖了store.count
//当store.count发生改变,autorun会自动执行; 这里就要求store.count是可观测的
//对于不可观测的属性,只会一上来制动执行一次,后续都不会执行,因为观测不到属性的状态修改.
autorun( 
  ()=>{
    console.log(store.count);
  }
);
store.count = 1; //手动修改状态,另外一种方式是通过action

理解computed


@observer
Class App extends React.Component{
   render()
   {
      return (<p> Total: {store.price * store.count}</p>)
   }
}
//该方法的缺点,计算每次渲染都会执行
//而实际上只需要依赖的数据改变,执行一次即可(缓存起来); 这里是store.price和store.count的数据改变

import {computed} from "mobx"
class Store{
  @observable price = 10;
  @observable count = 5;
  @computed get totalPrice(){
    return this.price * this.count
  }
}
@observer
Class App extends React.Component{
   render()
   {
      return (<p> Total: {store.totalPrice}</p>)
   }
}

理解action

//可以手动修改observable属性
import {computed} from "mobx"
class Store{
  @observable price = 10;
  @observable count = 5;
  @observable foo = 1;
}
const store = new Store();
autorun( ()=>{
   console.log('autorun:',store.count,store.foo,store.price)
}
)
//autorun 依赖了3个属性,下面的3次修改每一次都会触发一次autorun; 
store.count = 1;// 这是直接修改的方式 , 触发了一次autorun
store.foo = 2;//触发了一次autorun
store.price = 12;//触发了一次autorun
//如何做到这3个属性的修改,只触发一次autorun呢?类似于数据库中事务的概念
//可以手动修改observable属性
import {computed} from "mobx"
class Store{
  @observable price = 10;
  @observable count = 5;
  @observable foo = 1;
  //答案是使用action,然后在函数中修改3个变量; 这样调用change,只会触发一次autorun
  @action change()
  {
    this.count = 2;
    this.price = 12;
    this.foo = 3;
  }
}
const store = new Store();
autorun( ()=>{
   console.log('autorun:',store.count,store.foo,store.price)
}
)
store.change()//这是通过action的修改方式

屏蔽直接修改

import {configure} from "mobx"
//修改observed属性时,必须通过action修饰的函数来进行
configure({
  enforceActions:'observed'
})
//直接修改会报错
store.count = 22; 
// => changing observed observable values outside actions is not allowd.

action.bound的作用:

为成员函数绑定this

class Store{
  @observable price = 10;
  @observable count = 5;
  @observable foo = 1;
  //答案是使用action,然后在函数中修改3个变量; 这样调用change,只会触发一次autorun
  @action change()
  {
    console.log(this);
    this.count = 2;
    this.price = 12;
    this.foo = 3;
  }
}
const change = store.change;
change(); // => 输出undefined,因为对于const change 来说,this的指向window;严格模式为undefined

runInAction 的作用

可以将多个observable成员变量的数据进行修改,但只触发一次autorun

import {runInAction} from "mobx"
runInAction( ()=>{
    store.count = 111;
    store.price = 222;
    store.foo   = 333;
  }
);

异步action

class Store{
  @observable price = 10;
  @observable count = 5;
  @observable foo = 1;

  @action asyncChange()
  {//默认不允许在异步操作执行对observable数据的修改
   //因为默认行为是异步操作中的动作和action修饰的函数没有任何关系
    setTimeout(()=>{
      this.count = 222;
    },100);
  }
}
const store = new Store();
store.asyncChange(); //会直接报错
class Store{
  @observable price = 10;
  @observable count = 5;
  @observable foo = 1;

  //解决异步action方式一,添加新的action函数
  @action.bound changeCount(){
    this.count = 222;
  }
  @action.bound asyncChange()
  {
    setTimeout(this.changeCount(),100); //将回调函数指定为action函数
  }
}
const store = new Store();
store.asyncChange(); //会直接报错
class Store{
  @observable price = 10;
  @observable count = 5;
  @observable foo = 1;

  //解决异步action方式二,使用action函数
  @action.bound asyncChange()
  {
    setTimeout(()=>{
      action('changeFoo',()=>{
        this.foo = "hello";
      }) ();//定义一个叫changeFoo的函数,然后立即调用
    },100); 
  }
}
const store = new Store();
store.asyncChange(); //会直接报错
class Store{
  @observable price = 10;
  @observable count = 5;
  @observable foo = 1;

  //解决异步action方式三,使用runInAction函数
  @action.bound asyncChange()
  {
    setTimeout(()=>{
      runInAction(()=>{
        this.count = 222;  
      });
    },100); 
  }
}
const store = new Store();
store.asyncChange(); //会直接报错

监测数据的几种方式

  • @computed 前面已经接触过
  • autorun //前面有讲过,一开始会执行一次,然后依赖的数据发生改变,重新出发执行
  • when 条件触发
  • reaction 当被观测的数据发生改变的时候,依次执行第一个函数,将第一个函数的返回值传给第二个函数; 与when的区别,reaction每次修改都会触发;when只执行一次; reaction不会一开始就执行一次
import {when} from "mobx"
//当count>100,只执行一次自定义逻辑
when( ()=>{
  //第一个参数为函数,返回true或者false
  return store.count>100;
},()=>{ //只有第一个参数的返回值为ture 的时候才执行
  console.log('when:',store.count);
} )
reaction(
  ()=>{
    //执行一些业务逻辑,返回数据给下一个函数使用
    return store.count
  },
  (data,reaction)=>{//第一个参数为上一个函数的返回值;第二个参数为reaction函数本身
     console.log("reaction :",data);
     //模拟出when的操作
     reaction.dispose();//手动停止当前reaction的监听
  }
)

总结: 只有computed会返回数据,其他的都是用于可观测数据发生改变时,做一些业务逻辑,比如发消息给服务器.
十分钟入门mobx

posted @ 2021-10-07 16:26  QQLQ  阅读(179)  评论(0编辑  收藏  举报