React.js學(xué)習(xí)筆記(17) Mobx

(1) Decorator 裝飾器

Decorator是一個(gè)函數(shù),用來(lái)修改類或者類的屬性的行為颊埃。
說(shuō)的直白點(diǎn)decorator就是給類添加或者修改類的變量與方法的。

  • ES7 中的 decorator 是個(gè)語(yǔ)法糖蝶俱,不過(guò)依賴于 ES5 的Object.defineProperty 方法 班利。

(1) Object.defineProperty

  • defineProperty 所做的事情就是,為一個(gè)對(duì)象增加新的屬性榨呆,或者更改對(duì)象某個(gè)已存在的屬性罗标。
  • 調(diào)用方式是 Object.defineProperty(obj, prop, descriptor),這 3 個(gè)參數(shù)分別代表:
    obj: 目標(biāo)對(duì)象
    prop: 屬性名
    descriptor: 針對(duì)該屬性的描述符
    有意思的是 descriptor 參數(shù)积蜻,它其實(shí)也是一個(gè)對(duì)象闯割,其字段決定了 obj 的prop 屬性的一些特性。比如 enumerable 的真假就能決定目標(biāo)對(duì)象是否可枚舉(能夠在 for…in 循環(huán)中遍歷到竿拆,或者出現(xiàn)在 Object.keys 方法的返回值中)宙拉,writable 決定目標(biāo)對(duì)象的屬性是否可以更改,等等丙笋。

(2) Decorator 例子:

(1)
class Dog {
  bark () {
    return 'wang!wang!'
  }
}


-------------------------------------------------

如果我們想讓 bark 這個(gè)方法成為一個(gè)只讀的屬性谢澈,那么可以定義一個(gè)readonly 的 decorator:

(2)
// 注意這里的 `target` 是 `Dog.prototype`
function readonly(target, key, descriptor) {
  descriptor.writable = false
  return descriptor
}

可以看到,decorator 就是一個(gè)普通函數(shù)御板,只不過(guò)它接收 3 個(gè)參數(shù)锥忿,與Object.defineProperty 一致。

具體在這里怠肋,我們就是把 descriptor 的writable 字段設(shè)為 false敬鬓。

然后把 readonly 作用到 bark 方法上:

(3)
class Dog {
  @readonly
  bark () {
    return 'wang!wang!'
  }
}

let dog = new Dog()
dog.bark = 'bark!bark!'
// Cannot assign to read only property 'bark' of [object Object]




總結(jié):

ES7 的 decorator,作用就是返回一個(gè)新的 descriptor笙各,并把這個(gè)新返回的 descriptor 應(yīng)用到目標(biāo)方法上钉答。

https://zhuanlan.zhihu.com/FrontendMagazine/20139834







(2) Mobx

(1) observable 被觀察者

作用:存儲(chǔ)狀態(tài)(Store state),可以是任何的數(shù)據(jù)結(jié)構(gòu)酪惭,observable可以用來(lái)觀測(cè)一個(gè)數(shù)據(jù)希痴,這個(gè)數(shù)據(jù)可以數(shù)字、字符串春感、數(shù)組砌创、對(duì)象等類型

  • 用法:observable(value) 或者 @observable classProperty = value

@observable classProperty = value


---------------------------------------------

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

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

(2) observer 觀察者

使用 @observer 虏缸,將組件變?yōu)橛^察者,響應(yīng) todos 狀態(tài)變化嫩实。
當(dāng)狀態(tài)變化時(shí)刽辙,組件也會(huì)做相應(yīng)的更新。

// 這里引入的是 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>
    )
  }
}

(3) action

首先在 Store 中甲献,定義一個(gè) action宰缤。

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



----------------------------------------------------------------

在 Component 中調(diào)用,這樣通過(guò) action 的方法晃洒,就避免了直接修改 props 的問(wèn)題慨灭。

<input type="button" onClick={() => {
  this.props.store.changeTodoTitle({index:0,title:"修改后的todo標(biāo)題"});
}} value="點(diǎn)我"/>'



嚴(yán)格模式:
可以通過(guò)引入 mobx 定義的嚴(yán)格模式,強(qiáng)制使用 action 來(lái)修改狀態(tài)球及。

import {useStrict} from 'mobx';

useStrict(true);

(4) computed 計(jì)算屬性

import {observable, computed} from "mobx";

class OrderLine {
    @observable price = 0;
    @observable amount = 1;

    constructor(price) {
        this.price = price;
    }

    @computed get total() {
        return this.price * this.amount;
    }
}

(5) autorun

var numbers = observable([1,2,3]);
var sum = computed(() => numbers.reduce((a, b) => a + b, 0));

var disposer = autorun(() => console.log(sum.get()));
// 輸出 '6'
numbers.push(4);
// 輸出 '10'

disposer();
numbers.push(5);
// 不會(huì)再輸出任何值氧骤。`sum` 不會(huì)再重新計(jì)算。

(6) mobx在react中的使用流程:

  • 安裝mobx
  • 安裝mobx-react
  • 安裝babel-preset-stage-1 插件
  • 安裝transform-decorators-legacy 插件
    transform-decorators-legacy是目前babel插件轉(zhuǎn)換decorator的
安裝:
cnpm install mobx --sava
cnpm install mobx-react -S  // --save的簡(jiǎn)寫(xiě)形式
cnpm install babel-plugin-transform-decorators-legacy --save-dev
cnpm install babel-preset-stage-1 -D   //--save-dev的簡(jiǎn)寫(xiě)形式


---------------------------------------------------

使用:
.babelrc文件

{
    "presets":[
        ["es2015", {"loose": true}],
        "stage-1",    //省事就安裝"stage-0",因?yàn)樗瑂tage-1, stage-2以及stage-3的所有功能
        "react"
    ],
    "plugins": ["transform-decorators-legacy","react-hot-loader/babel"]
}

注意: transform-decorators-legacy 一定要寫(xiě)在最前面
  • 聲明store 定義state
store文件中的 app-state.js 文件




import  {observable, computed, autorun, action} from 'mobx';

export class AppState {
    @observable count = 0;     // 被觀察的變量
    @observable name = 'woow';
    @computed get msg() {      // 計(jì)算屬性
        return `${this.name} say count is ${this.count}`
    };
    @action add() {
        this.count += 1;
    }
}


const appState = new AppState();     // 實(shí)例化

autorun(() => {
    console.log(appState.msg)    // 注意:computed調(diào)用的時(shí)候是屬性吃引,不是方法
})                               // 比如: console.log(appState.msg())就會(huì)報(bào)錯(cuò)筹陵,計(jì)算屬性!!

setInterval(()=>{
    appState.add();    // 調(diào)用add() action
},1000)

export default appState;


  • 把store連接到應(yīng)用上
import React from 'react';
import ReactDom from 'react-dom';
import App from './view/App.jsx';
import {AppContainer} from 'react-hot-loader';
import {BrowserRouter} from 'react-router-dom';

import {Provider} from 'mobx-react';   // 引入Provider,儲(chǔ)存store給子組件镊尺,和react-redux一樣的
import appStateA from './store/app-state.js';   // 引入app-state.js中export default的內(nèi)容

const root = document.getElementById('root');

const render = Component => {
    ReactDom.hydrate(
        <AppContainer>                             // react-hot-loader規(guī)定必須放在最外層
            <Provider  appState={appStateA}>   // Provider儲(chǔ)存store中的數(shù)據(jù)朦佩,給子組件使用
                <BrowserRouter>
                    <Component />
                </BrowserRouter>
            </Provider>
        </AppContainer>, 
        root
    )
}

render(App)

if(module.hot) {
    module.hot.accept('./view/App.jsx', ()=> {
        const NextApp = require('./view/App.jsx').default;
        render(NextApp)
    })
}

  • 子組件 得到數(shù)據(jù)
import React,  {Component} from 'react';
import {observer, inject} from 'mobx-react'  // inject是注入的意思
import PropTypes from 'prop-types';
import {AppState} from '../../store/app-state.js';


@inject('appState') @observer     // 在Provider上定義的屬性
export default class TopicList extends Component {
    static propTypes = {
        appState: PropTypes.instanceOf(AppState).isRequired
    }
    changeName = (e) => {
        this.props.appState.name = e.target.value
    }
    render() {
        return (
            <div>
                topic-list
                <div>
                    <input type="text" onChange={this.changeName} />
                 // <input type="text" onChange={e => this.changeName(e)} />這樣也行
                </div>
                <div>
                    {this.props.appState.msg}
                </div>
            </div>
        )
    }
}


--------------------------------------------------------------------

以上改變name的方法不推薦,以為不好統(tǒng)一管理:

一般都要在store中定義好@action 讓后調(diào)用即可



例如:
在store文件中的app-state.js文件中


import  {observable, computed, autorun, action} from 'mobx';

export class AppState {
    @observable count = 0;
    @observable name = 'woow';
    @computed get msg() {
        return `${this.name} say count is ${this.count}`
    };
    @action add() {
        this.count += 1;
    }
    @action chageName(name) {     // 定義好改變name的action
        this.name = name;
    }
}

const appState = new AppState();

autorun(() => {
    console.log(appState.msg)
})

setInterval(()=>{
    appState.add();
},1000)

export default appState;



------------------------------------------------------------------

在子組件中調(diào)用:


import React,  {Component} from 'react';
import {observer, inject} from 'mobx-react'
import PropTypes from 'prop-types';
import {AppState} from '../../store/app-state.js';


@inject('appState') @observer
export default class TopicList extends Component {
    static propTypes = {
        appState: PropTypes.instanceOf(AppState).isRequired
    }
    changeName = (e) => {
        this.props.appState.name = e.target.value
    }
    changeName2 = (e) => {     // 這樣方式才正確合理
        this.props.appState.chageName(e.target.value)
    }
    render() {
        return (
            <div>
                topic-list
                <div>
                    <input type="text" onChange={e => this.changeName(e)} />
                </div>
                <div>
                    <input type="text" onChange={this.changeName2}/>   // 這種方式才正確合理

                </div>
                <div>
                    {this.props.appState.msg}
                </div>
            </div>
        )
    }
}


http://blog.csdn.net/u012125579/article/details/69400169







其他

ps: vscode編輯器使用mobx中 修飾器 相關(guān)語(yǔ)法報(bào)錯(cuò):
配置: "javascript.implicitProjectConfig.experimentalDecorators": true,

router

  • exact
import React from 'react';

import TopicLsit from '../views/top-list/index.jsx';
import TopicDetail from '../views/topic-detail/index.jsx';
import {Route} from 'react-router-dom';


export default () => [
    <Route path="/" component={TopicLsit} />,   
    <Route path="/detail" component={TopicDetail} />
]

輸入路由‘/detail’庐氮,會(huì)匹配‘/’语稠,‘/list’兩個(gè),所以加上exact="true",就只會(huì)匹配‘/detail’,不再匹配‘/’


例如:
export default () => [
    <Route path="/" component={TopicLsit}  exact={true}/>,   
    <Route path="/detail" component={TopicDetail} />
]
輸入路由‘/detail’就只會(huì)匹配‘/detail’,而不會(huì)再匹配上‘/’



簡(jiǎn)寫(xiě)形式: :<Route path="/list" component={TopicLsit}  exact />,  

  • Redirect 重定向

export default () => [
    <Route path="/" render={() => <Redirect to="/detail" />} exact={true}/>,
    <Route path="/list" component={TopicLsit} />,
    <Route path="/detail" component={TopicDetail} />
] 

 ‘/’路由重定向到‘/detail’


---------------------------------------------------

注意:Redirect可以寫(xiě)成下面這樣旭愧,這種情況一般都要配合Switch標(biāo)簽


<Switch>
  <Redirect from='/old-path' to='/new-path'/>
  <Route path='/new-path' component={Place}/>
</Switch>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末颅筋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子输枯,更是在濱河造成了極大的恐慌议泵,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桃熄,死亡現(xiàn)場(chǎng)離奇詭異先口,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)瞳收,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)碉京,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人螟深,你說(shuō)我怎么就攤上這事谐宙。” “怎么了界弧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵凡蜻,是天一觀的道長(zhǎng)搭综。 經(jīng)常有香客問(wèn)我,道長(zhǎng)划栓,這世上最難降的妖魔是什么兑巾? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮忠荞,結(jié)果婚禮上蒋歌,老公的妹妹穿的比我還像新娘。我一直安慰自己委煤,他們只是感情好堂油,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著碧绞,像睡著了一般称诗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上头遭,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音癣诱,去河邊找鬼计维。 笑死,一個(gè)胖子當(dāng)著我的面吹牛撕予,可吹牛的內(nèi)容都是我干的鲫惶。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼实抡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼欠母!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起吆寨,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤赏淌,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后啄清,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體六水,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年辣卒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掷贾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荣茫,死狀恐怖想帅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情啡莉,我是刑警寧澤港准,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布旨剥,位于F島的核電站,受9級(jí)特大地震影響叉趣,放射性物質(zhì)發(fā)生泄漏泞边。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一疗杉、第九天 我趴在偏房一處隱蔽的房頂上張望阵谚。 院中可真熱鬧,春花似錦烟具、人聲如沸梢什。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嗡午。三九已至,卻和暖如春冀痕,著一層夾襖步出監(jiān)牢的瞬間荔睹,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工言蛇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留僻他,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓腊尚,卻偏偏與公主長(zhǎng)得像吨拗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子婿斥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容