React.js学习笔记(17) Mobx

(1) Decorator 装饰器

Decorator是一个函数,用来修改类或者类的属性的行为。
说的直白点decorator就是给类添加或者修改类的变量与方法的。

  • ES7 中的 decorator 是个语法糖,不过依赖于 ES5 的Object.defineProperty 方法 。

(1) Object.defineProperty

  • defineProperty 所做的事情就是,为一个对象增加新的属性,或者更改对象某个已存在的属性。
  • 调用方式是 Object.defineProperty(obj, prop, descriptor),这 3 个参数分别代表:
    obj: 目标对象
    prop: 属性名
    descriptor: 针对该属性的描述符
    有意思的是 descriptor 参数,它其实也是一个对象,其字段决定了 obj 的prop 属性的一些特性。比如 enumerable 的真假就能决定目标对象是否可枚举(能够在 for…in 循环中遍历到,或者出现在 Object.keys 方法的返回值中),writable 决定目标对象的属性是否可以更改,等等。

(2) Decorator 例子:

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


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

如果我们想让 bark 这个方法成为一个只读的属性,那么可以定义一个readonly 的 decorator:

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

可以看到,decorator 就是一个普通函数,只不过它接收 3 个参数,与Object.defineProperty 一致。

具体在这里,我们就是把 descriptor 的writable 字段设为 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]




总结:

ES7 的 decorator,作用就是返回一个新的 descriptor,并把这个新返回的 descriptor 应用到目标方法上。

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







(2) Mobx

(1) observable 被观察者

作用:存储状态(Store state),可以是任何的数据结构,observable可以用来观测一个数据,这个数据可以数字、字符串、数组、对象等类型

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

@observable classProperty = value


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

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

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

(2) observer 观察者

使用 @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>
    )
  }
}

(3) action

首先在 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);

(4) computed 计算属性

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);
// 不会再输出任何值。`sum` 不会再重新计算。

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

  • 安装mobx
  • 安装mobx-react
  • 安装babel-preset-stage-1 插件
  • 安装transform-decorators-legacy 插件
    transform-decorators-legacy是目前babel插件转换decorator的
安装:
cnpm install mobx --sava
cnpm install mobx-react -S  // --save的简写形式
cnpm install babel-plugin-transform-decorators-legacy --save-dev
cnpm install babel-preset-stage-1 -D   //--save-dev的简写形式


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

使用:
.babelrc文件

{
    "presets":[
        ["es2015", {"loose": true}],
        "stage-1",    //省事就安装"stage-0",因为它包含stage-1, stage-2以及stage-3的所有功能
        "react"
    ],
    "plugins": ["transform-decorators-legacy","react-hot-loader/babel"]
}

注意: transform-decorators-legacy 一定要写在最前面
  • 声明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() {      // 计算属性
        return `${this.name} say count is ${this.count}`
    };
    @action add() {
        this.count += 1;
    }
}


const appState = new AppState();     // 实例化

autorun(() => {
    console.log(appState.msg)    // 注意:computed调用的时候是属性,不是方法
})                               // 比如: console.log(appState.msg())就会报错,计算属性!!

setInterval(()=>{
    appState.add();    // 调用add() action
},1000)

export default appState;


  • 把store连接到应用上
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,储存store给子组件,和react-redux一样的
import appStateA from './store/app-state.js';   // 引入app-state.js中export default的内容

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

const render = Component => {
    ReactDom.hydrate(
        <AppContainer>                             // react-hot-loader规定必须放在最外层
            <Provider  appState={appStateA}>   // Provider储存store中的数据,给子组件使用
                <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)
    })
}

  • 子组件 得到数据
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的方法不推荐,以为不好统一管理:

一般都要在store中定义好@action 让后调用即可



例如:
在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;



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

在子组件中调用:


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中 修饰器 相关语法报错:
配置: "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’,会匹配‘/’,‘/list’两个,所以加上exact="true",就只会匹配‘/detail’,不再匹配‘/’


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



简写形式: :<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可以写成下面这样,这种情况一般都要配合Switch标签


<Switch>
  <Redirect from='/old-path' to='/new-path'/>
  <Route path='/new-path' component={Place}/>
</Switch>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容