(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>