React 和 Vue一樣都屬於單向數據流,為了更好的進行狀態和數據管理官方和第三方也有配套的Redux等插件,本文介紹一個個人覺得更易用使用的組件 Mobx
核心概念
MobX 處理你的應用程序狀態如下圖所示
常用的幾個裝飾器 (裝飾器解釋
)
-
Actions: 改變state的操作。
-
ObservableState:應用的可被觀察的數據狀態。
-
Computed: 從state中通過純函數的操作衍生出的值,state變化它也會跟着變化。
-
Reactions:需要對state變化動態作出反應的東西,它包含不同的概念,基於被觀察數據的更新導致某個計算值,或者是發送網絡請求以及更新視圖等,都屬於響應的范疇,這也是響應式編程在 JavaScript 中的一個應用。
-
Autorun: 依賴收集,監聽觸發,autorun 背后由 reaction 實現。由於 autorun 與 view 的 render 函數很像,我們在 render 函數初始化執行時,使其包裹在 autorun 環境中,第 2 次 render 開始遍剝離外層的 autorun,保證只綁定一遍數據。這樣 view 層在原本 props 更新機制的基礎上,增加了 autorun 的功能,實現修改任何數據自動更新對應 view 的效果。(ps:使用autoRun實現Mobx-react非常簡單,核心思想是將組件外面包上autoRun,這樣代碼中用到的所有屬性都會像上面Demo一樣,與當前組件綁定,一旦任何值發生了修改,就直接forceUpdate,而且精確命中,效率最高。)
React項目中Mobx的安裝
npm或yarn安裝插件
npm install mobx-react --save
或者
yarn add mobx-react --save
裝飾器的啟用,裝飾器目前仍處於提案階段因此需要做特殊處理
修改package.json添加如下引用,這個和npm手動安裝一個道理
"@babel/core": "^7.1.0",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-decorators": "^7.1.0",
"@babel/preset-env": "^7.1.0"
使用npm run eject確認顯示所有隱藏的配置文件
注意此步驟不可逆
在根目錄創建.babelrc文件,添加如下內容
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }]
]
}
刪除package.json中babel配置
幾個核心裝飾器的解釋
@observable 創建需要被監聽的應用狀態
通過對Class的屬性簡單的使用@observable修飾符,就定義了一個需要被監聽的應用狀態變量;然后直接在類中定義對應用狀態變量的操作;我們就實現了一個靈活的Store層
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import TodoBox from './Welcome';
import {observable, action} from 'mobx';
class Store{
@observable todos=[{
key:1,
title: "todo標題",
done: false,
},{
key:2,
title: "todo-2",
done: false,
}];
}
如上面代碼所示,Store是一個store層的類,用來對狀態進行管理
@observer 創建應用狀態的監聽者
有多種方式可以創建應用狀態的監聽者(Reactions),包括autorun、reaction、@observer等
此處示例顯示如何用@observer來監聽
import React, {Component} from 'react';
import {observer} from "mobx-react";
import {action} from "mobx";
@observer
class TodoBox extends Component {
/**
*
* @param props
*/
constructor(props) {
super(props);
}
render() {
const store=this.props.store;
return (
<div>
<ul>
{store.todos.map((todo,_) => <li>{todo.title}</li>)}
</ul>
</div>
)
}
}
export default TodoBox;
@action
可以看到之前的監聽模式下,其實完全可以直接通過store層對數據進行讀取和寫入,雖然很便捷但卻打破了單向數據流向的原則,Mobx也考慮到這點所以特意設計了action,並且要求用戶盡可能的使用action來設定更改狀態和數據的方法
class Store{
@observable todos=[{
key:1,
title: "todo標題",
done: false,
},{
key:2,
title: "todo-2",
done: false,
}];
@action
changeTitle(){
// 直接修改倉庫中的狀態值
this.todos[0].title = "修改后的todo標題"
}
}
完整Demo
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import TodoBox from './Welcome';
import {observable, action} from 'mobx';
class Store{
@observable todos=[{
key:1,
title: "todo標題",
done: false,
},{
key:2,
title: "todo-2",
done: false,
}];
@action
changeTitle(){
// 直接修改倉庫中的狀態值
this.todos[0].title = "修改后的todo標題"
}
}
const store1=new Store();
ReactDOM.render(<TodoBox store={store1} />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();
import React, {Component} from 'react';
import {observer} from "mobx-react";
import {action} from "mobx";
@observer
class TodoBox extends Component {
/**
*
* @param props
*/
constructor(props) {
super(props);
}
render() {
const store=this.props.store;
return (
<div>
<ul>
{store.todos.map((todo,_) => <li>{todo.title}</li>)}
</ul>
<div>
<input type="button" onClick={() => {
this.props.store.changeTitle();
// // 直接修改倉庫中的狀態值
// this.props.store.todos[0].title = "修改后的todo標題"
}} value="點我"/>
</div>
</div>
)
}
}
export default TodoBox;