mobx——rudex的簡單替代品

mobx 能幹什麼

使用 react 寫小型應用,數據、業務邏輯和視圖的模塊劃分不是很細是沒有問題的。在這個階段,引入任何狀態管理庫,都算是奢侈的。可是隨着頁面邏輯的複雜度提高,在中大型應用中,數據、業務邏輯和視圖,若是不能很好的劃分,就頗有可能出現維護難、性能低下的問題。javascript

業內比較成熟的解決方案有 redux,可是 redux 使用過程當中,給個人感受是太複雜和繁瑣。那麼爲何不簡單一點呢?mobx 的核心理念是 簡單、可擴展的狀態管理庫。這可能正是你想要的。java

react 關注的狀態(state)到視圖(view)的問題。而 mobx 關注的是狀態倉庫(store)到的狀態(state)的問題。react

核心的概念1

mobx 最最核心的概念只有2個。 @observable@observer ,它們分別對應的是被觀察者和觀察者。這是你們常見的觀察者模式,不過這裏使用了,ES7 中的 裝飾器es6

使用 @observable 能夠觀察類的值。redux

這裏使用 @observable 將 Store 的 todos 變爲一個被觀察的值。瀏覽器

observable

倉庫緩存

// 這裏引入的是 mobx
import {observable} from 'mobx';

class Store {
  @observable todos = [{
    title: "todo標題",
    done: false,
  }];
}

observer

mobx 組件dom

而後再使用 @observer ,將組件變爲觀察者,響應 todos 狀態變化。
當狀態變化時,組件也會作相應的更新。函數

// 這裏引入的是 mobx-react
import {observer} from 'mobx-react';

@observer
class TodoBox extends Component  {
  render() {
    return (
      <ul>
        {this.props.store.todos.map(todo => <li>{todo.title}</li>)}
      </ul>
    )
  }
}

完整的 demo 以下。性能

import React, {Component} from 'react';
import { render } from 'react-dom';
import {observable} from 'mobx';
import {observer} from 'mobx-react';

// 最簡單的 mobx 就是一個觀察者模式

class Store {
  // 被觀察者
  @observable todos = [{
    title: "完成 Mobx 翻譯",
    done: false,
  }];
}

// 觀察者
@observer
class TodoBox extends Component  {
  render() {
    return (
      <ul>
        {this.props.store.todos.map(todo => <li>{todo.title}</li>)}
      </ul>
    )
  }
}


const store = new Store();

render(
  <TodoBox store={store} />,
  document.getElementById('root')
);

經過以上的簡單的例子,展示了 mobx 分離數據、視圖的能力。

核心概念2

這一小節要介紹的兩個概念雖然也是核心概念,可是是可選的。

前面例子,只講了狀態的讀取,那麼狀態應該如何寫入呢?

答案是直接寫入!

@observer
class TodoBox extends Component  {
  render() {
    return (
      <div>
        <ul>
          {this.props.store.todos.map(
            (todo,index) => <li key={index}>{todo.title}</li>
          )}
        </ul>
        <div>
          <input type="button" onClick={() => {
            // 直接修改倉庫中的狀態值
            this.props.store.todos[0].title = "修改後的todo標題"
          }} value="點我"/>
        </div>
      </div>
    )
  }
}

細心的朋友必定發現了奇怪的地方,react 官方說過 props 值不能直接修改,可是引入 mobx 後 props 能夠直接修改了,這太奇怪了!

解決辦法就是 mobx 的下一個概念 action

actions

首先在 Store 中,定義一個 action。

class Store {
  @observable todos = [{
    title: "todo標題",
    done: false,
  }];
  @action changeTodoTitle({index,title}){
    this.todos[index].title = title
  }
}

在 Component 中調用,這樣經過 action 的方法,就避免了直接修改 props 的問題。

<input type="button" onClick={() => {
  this.props.store.changeTodoTitle({index:0,title:"修改後的todo標題"});
}} value="點我"/>

能夠經過引入 mobx 定義的嚴格模式,強制使用 action 來修改狀態。

import {useStrict} from 'mobx';

useStrict(true);

computed values

在有些時候,state 並不必定是咱們須要的最終數據。例如,全部的 todo 都放在 store.todos 中,而已經完成的 todos 的值(store.unfinishedTodos),能夠由 store.todos 衍生而來。

對此,mobx 提供了 computed 裝飾器,用於獲取由基礎 state 衍生出來的值。若是基礎值沒有變,獲取衍生值時就會走緩存,這樣就不會引發虛擬 DOM 的從新渲染。

經過 @computed + getter 函數來定義衍生值(computed values)。

import { computed } from 'mobx';

class Store {
  @observable todos = [{
    title: "todo標題",
    done: false,
  },{
    title: "已經完成 todo 的標題",
    done: true,
  }];

  @action changeTodoTitle({index,title}){
    this.todos[index].title = title
  }

  @computed get finishedTodos () {
    return  this.todos.filter((todo) => todo.done)
  }
}

mobx 有一套機制,若是衍生值(computed values)所依賴的基礎狀態(state)沒有發生改變,獲取衍生值時,不會從新計算,而是走的緩存。所以 mobx 不會引發過分渲染,從而保障了性能。

當渲染的值爲 finishedTodos ,點擊修改標題,不會在控制檯打印 "render";
換成 todos,就會打印 "render".
這是因爲已完成的 todos 值沒有改變,因此不會從新計算,而是走的緩存。所以不會調用 render 方法。

完整 demo 以下

import React, {Component} from 'react';
import { render } from 'react-dom';
import {observable, action, computed,useStrict} from 'mobx';
import {observer} from 'mobx-react';

useStrict(true);


class Store {
  @observable todos = [{
    title: "todo標題",
    done: false,
  },{
    title: "已經完成 todo 的標題",
    done: true,
  }];

  @action changeTodoTitle({index,title}){
    this.todos[index].title = title
  }

  @computed get unfinishedTodos () {
    return  this.todos.filter((todo) => todo.done)
  }
}


@observer
class TodoBox extends Component  {

  render() {
    console.log('render');
    return (
      <div>
        <ul>
          { /* 把 unfinishedTodos 換成 todos,點擊修改標題就會在控制檯打印 "render".*/ }
          {this.props.store.unfinishedTodos.map(
            (todo,index) => <li key={index}>{todo.title}</li>
          )}
        </ul>
        <div>
          <input type="button" onClick={() => {
            this.props.store.changeTodoTitle({index:0,title:"修改後的todo標題"});
          }} value="修改標題"/>
        </div>
      </div>
    )
  }
}

const store = new Store();

render(
  <TodoBox store={store} />,
  document.getElementById('root')
);

小結

翻譯了官網的一段文章,就拿過來作小結了。

mobx 是一個的簡單、可擴展的狀態管理庫。它背後的哲學很是簡單:

應用程序 state 是最基礎的數據。任何能夠從 state 中衍生出來的數據,都應該自動的被衍生出。

img

actions 是惟一可以改變 state 的方法。

state 是最基礎的數據,它不該該包含冗餘的和派生的數據。

computed values 派生值是經過純函數從 state 中派生而來的。當派生值依賴的狀態發生變化了,Mobx 將會自動更新派生值。若是依賴的狀態沒有改變,mobx 會作優化處理。

reactions 也是派生數據,是從 state 中派生而來的。它的反作用是自動更新 UI。(注:mobx 有一個 reaction 接口,當 state 改變時,就會調用它的回調。UI 是經過 reaction 更新的。)

React 和 MobX 是很是強大的組合。React 提供了將應用狀態映射爲可渲染的組件樹的機制。MobX 提供存儲和更新應用狀態的機制,供 React 使用。

React 和 MobX 提供了開發過程當中常見問題的解決方案。 React 經過使用虛擬 DOM,減小了對瀏覽器 DOM 的操做。MobX 經過使用了響應式虛擬依賴狀態圖(reactive virtual dependency state graph) ,提供了應用程序狀態與 React 組件同步的機制,這樣 state 只會在須要時更新纔會更新。(譯者注:這段有點難理解,大概的意思是 Mobx 關注的是 store 到 state 的過程,React 關注的是 state 到 view 的過程)。

輔助函數

在實際開發中,須要用到很多 mobx 的輔助函數,這些輔助函數一共 14 個,挑了一些列舉以下。

autorun
observable 的值初始化或改變時,自動運行。

trasaction
批量改變時,經過 trasaction 包裝,只會觸發一次 autorun。

extendsObservable
對類的屬性或實例,進行監聽。

observable
對普通對象進行監聽。

map
使用 asMap 將對象轉化爲 map。

action-strict
在 mobx.usrStrict(true)時,只能經過 action 觸發值的改變。

when
相似 autorun.

mobx.when 第一個參數是一個函數,初始化時也會自動執行。該函數返回一個 boolean 值,當返回值爲 true 的時候,纔會繼續觸發第一個函數。當返回值爲 flase 時,再也不繼續監聽。這時會執行 mobx.when 的第二個參數,這個參數也是一個函數。

reaction
相似 autorun.

reaction 不會在初始化時執行,只會在值改變的時候執行。

該函數有 2 個值,第一個參數是一個函數,返回監聽的值.
第二個參數,也是一個函數,會在值改變的時候執行。

spy
相似 aoturun.

監聽全部 mobx 的事件。

包含一個 type ,該值用來區分執行事件的類型。

whyRun用於調試,打印 autorun 爲何會觸發。

相關文章
相關標籤/搜索