一、mobx入門
https://cn.mobx.js.org/
一、observable可觀察的狀态
https://cn.mobx.js.org/refguide/observable.html
map
import {observable} from 'mobx'
// 聲明
const map = observable.map({a: 1, b: 2});
// 設定
map.set('a', 11);
// 擷取
console.log(map.get('a'));//11
console.log(map.get('b'));//2
// 删除
map.delete('a');
console.log(map.get('a'));//undefined
// 判斷是否存在屬性
console.log(map.has('a'));//false
object
import {observable} from 'mobx'
// 聲明
const obj = observable({a: 1, b: 2});
// 修改
obj.a = 11;
// 通路
console.log(obj.a, obj.b);//11 2
array
import {observable} from 'mobx'
const arr = observable(['a', 'b', 'c', 'd']);
// 通路
console.log(arr[0], arr[10]);// "a" undefined
// 操作
arr.pop();
arr.push('e');
列印數組的話是個proxy , 同時也具有數組的方法

基礎類型
import {observable} from 'mobx'
const num = observable.box(10);
const str = observable.box('hello');
const bool = observable.box(true);
// 獲得值
console.log(num.get(), str.get(), bool.get());//10 hello true
// 修改值
num.set(100);
str.set('hi');
bool.set(false);
console.log(num.get(), str.get(), bool.get());//100 hi false
observable裝飾器
安裝
npm install --save-dev @babel/plugin-propsal-decorators @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime
每種類型的資料都需要用observable設定 , 這樣難免會很麻煩 , 這時候我們就可以用裝飾器
import {observable} from 'mobx'
// observable這個函數可以識别當成普通函數調用還是裝飾器調用
// 如果是裝飾器,會自動識别資料類型,使用不同的包裝轉換方案。
class Store{
@observable arr = [];
@observable obj = {a: 1};
@observable map = new Map();
@observable str = 'hello';
@observable num = 123;
@observable bool = false;
}
const store = new Store();
console.log(store);
console.log(store.obj.a);
注意:vscode編譯器中,js檔案使用裝飾器會報紅。解決方式:
在根目錄編寫jsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"experimentalDecorators": true
},
"include": ["src/**/*"]
}
二、對 observables 作出響應
1. computed
計算值是可以根據現有的狀态或其它計算值衍生出的值, 跟vue中的computed非常相似。
import {observable,computed} from 'mobx'
class Store{
@observable arr = [];
@observable obj = {a: 1};
@observable map = new Map();
@observable str = 'hello';
@observable num = 123;
@observable bool = false;
}
const store = new Store();
const result = computed(()=>{
console.log(1)//初始化會執行一次 修改也會執行
store.str + store.num
})
console.log(result.get());
// 監聽資料的變化
result.observe((change)=>{
console.log('result:', change);//列印兩次 因為store修改了兩次
})
//兩次對store屬性的修改都會引起result的變化
store.str = 'world';
store.num = 220;
computed可作為裝飾器, 将result的計算添加到類中:
class Store{
@observable arr = [];
@observable obj = {a: 1};
@observable map = new Map();
@observable str = 'hello';
@observable num = 123;
@observable bool = false;
@computed get result(){
return this.str + this.num;
}
}
2. autorun
當你想建立一個響應式函數,而該函數本身永遠不會有觀察者時,可以使用
mobx.autorun
所提供的函數總是立即被觸發一次,然後每次它的依賴關系改變時會再次被觸發。
經驗法則:如果你有一個函數應該自動運作,但不會産生一個新的值,請使用autorun。 其餘情況都應該使用 computed。
//aotu會立即觸發一次
autorun(()=>{
console.log(store.str + store.num);
})
autorun(()=>{
console.log(store.result);
})
//兩次修改都會引起autorun執行
store.num = 220;
store.str = 'world';
3. when
when(predicate: () => boolean, effect?: () => void, options?)
when 觀察并運作給定的 predicate,直到傳回true。 一旦傳回 true,給定的 effect 就會被執行,然後 autorunner(自動運作程式) 會被清理。 該函數傳回一個清理器以提前取消自動運作程式。
對于以響應式方式來進行處理或者取消,此函數非常有用。
when(()=>store.bool, ()=>{
console.log('when function run.....');
})
store.bool = true;
4. reaction
用法:
reaction(() => data, (data, reaction) => { sideEffect }, options?)
。
autorun 的變種,對于如何追蹤 observable 賦予了更細粒度的控制。
它接收兩個函數參數,第一個(資料 函數)是用來追蹤并傳回資料作為第二個函數(效果 函數)的輸入。
不同于 autorun 的是當建立時效果 函數不會直接運作,隻有在資料表達式首次傳回一個新值後才會運作。 在執行 效果 函數時通路的任何 observable 都不會被追蹤。
// reaction 第一個參數的傳回值作為第二個參數的輸入
reaction(()=>[store.str, store.num], (arr)=>{
console.log(arr.join('/')); // world/220
})
//隻要[store.str, store.num]中任意一值發生變化,reaction第二個函數都會執行
store.num = 220;
store.str = 'world';
三、改變 observables狀态
1. action
接上面案例,添加action到類中:
class Store{
@observable arr = [];
@observable obj = {a: 1};
@observable map = new Map();
@observable str = 'hello';
@observable num = 123;
@observable bool = false;
//擷取資料@computed
@computed get result(){
return this.str + this.num;
}
//修改資料@action
@action bar(){
this.str = 'world';
this.num = 40;
}
}
const store = new Store();
//調用action,隻會執行一次
store.bar();
2. action.bound
action.bound
可以用來自動地将動作綁定到目标對象。
class Store{
@observable arr = [];
@observable obj = {a: 1};
@observable map = new Map();
@observable str = 'hello';
@observable num = 123;
@observable bool = false;
@computed get result(){
return this.str + this.num;
}
@action bar(){
this.str = 'world';
this.num = 40;
}
//this 永遠都是正确的
@action.bound foo(){
this.str = 'world';
this.num = 40;
}
}
const store = new Store();
setInterval(store.foo, 1000)
3. runInAction
action
隻能影響正在運作的函數,而無法影響目前函數調用的異步操作。如果你使用async function來處理業務,那麼我們可以使用
runInAction
這個API來解決這個問題。
@action async fzz() {
await new Promise((resolve) => {
setTimeout(() => {
resolve(0)
}, 1000)
})
runInAction(()=>{
store.num = 220
store.str = 'world'
})
}
四、react-native + mobx案例 todolist
實作效果
1、 安裝 mobx
、 mobx-react
(将react和mobx銜接)以及配置裝飾器
mobx
mobx-react
npm install mobx mobx-react --save-dev
配置裝飾器具體步驟https://blog.csdn.net/weixin_44157964/article/details/108292457
2、建立一個store/index.js
3、 App.js
中導入 store/index.js
, 并且導入 mobx-react
, 使用 Provider
包裹元件
App.js
store/index.js
mobx-react
Provider
4、頁面中使用mobx
inject
把
store
注入到目前類元件的屬性中
observer
觀測
render
函數中用了哪些可觀察狀态 , 如果
store
中的值有變化 ,
render
函數就會重新渲染
二、webpack + mobx 環境配置
mkdir mobx-demo
cd mobx-demo
npm init -y
npm i webpack webpack-cli webpack-dev-server -D
npm i html-webpack-plugin -D
npm i babel-loader @babel/core @babel/preset-env -D
npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
npm i @babel/plugin-transform-runtime -D
npm i @babel/runtime -S
npm i mobx -S
mkdir src
touch index.html
touch src/index.js
編寫webpack.config.js
const path = require('path')
const htmlwebpackplugin = require('html-webpack-plugin')
module.exports = {
mode:"development",
devtool:"inline-source-map",
entry:"./src/index.js",
output:{
path:path.resolve(__dirname,'./dist'),
filename:'[name]-[hash].js'
},
devServer:{
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
open:true
},
module:{
rules:[
{
test:/\.js$/,
exclude:/node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
"plugins": [
//支援裝飾器
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }],
["@babel/plugin-transform-runtime"]
]
}
}
}
]
},
plugins:[new htmlwebpackplugin({
filename:'index.html',
template:'./index.html'
})]
}
編寫index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
</html>