MobX
1. 介绍1.1. 原理React的render是 状态 转化为树状结构的渲染组件的方法 1.2. 安装MobX: npm install mobx --save React bindings: npm install mobx-react --save 1.3. 要点MobX看起来很复杂的样子,其实是用它只需要三步
import {observable} from 'mobx' let appState = observable({ timer: 0 })
import {observer} from 'mobx-react'; @observer class TimerView extends React.Component { render() { return (<button onClick={this.onReset.bind(this)}> Seconds passed: {this.props.appState.timer} </button>); } onReset () { //appState.resetTimer会在下一节完成 this.props.appState.resetTimer(); } }; React.render(<TimerView appState={appState} />,document.body);
appState.resetTimer = action(function reset() { appState.timer = 0; }); setInterval(action(function tick() { appState.timer += 1; }),1000); 其中action包装用法只能在strict模式下使用,请记得在你的javascript文件头写上:'use strict'。 2. API从上面的例子可以看到,MobX的API其实不多:observable,computed,reactions,actions 2.1. observable(value)其中的value可以是JS原定的数据结构,引用,对象,数组,ES6的map
以下是一些例子: const map = observable(asMap({ key: "value"})); map.set("key","new value"); const list = observable([1,2,4]); list[2] = 3; const person = observable({ firstName: "Clive Staples",lastName: "Lewis" }); person.firstName = "C.S."; const temperature = observable(20); temperature.set(25); 2.2. @observableimport {observable} from "mobx"; class OrderLine { @observable price:number = 0; @observable amount:number = 1; constructor(price) { this.price = price; } //这里在下一节会说到 @computed get total() { return this.price * this.amount; } } const line = new OrderLine(); console.log("price" in line); // true //hasOwnProperty:判断一个对象是否有你给出名称的属性或对象。需要注意,此方法无法检查该对象的原型链中是否具有该属性 console.log(line.hasOwnProperty("price")); //false 如果你的环境不支持ES6/7的语法的话,其实@observable key = value; 只是extendObservable(this,{ key: value })的语法糖。因此在ES5环境下你也能使用 2.3. (@)computedComputed values 就像一个算术公式一样去从现有的状态或其他值去计算出需要的值。计算的耗费是不可低估的。Computed尽可能帮你减少其中的耗费。它们是高度优化的,请把它用在可能用到的地方。 不要混淆下一节说到的autorun。虽然他们都是被动调用的表达式。但是…… 如果Computed values不再是观察者(observed),那么在UI上也会把它除掉,MobX能自动做垃圾回收。autorun则需要你自己手动去处理。如果参与计算的值不再被使用,是不会缓存Computed的,所以重新计算是需要的。这个是最理想的默认情况。如果你想保留,可以了解一下keepalive和observe。 例子1: 在2.2的例子。@computed get 例子2: @computed set class Foo { @observable length: 2,@computed get squared() { return this.length * this.length; } set squared(value) { //this is automatically an action,no annotation necessary this.length = Math.sqrt(value); } } 需要注意的是:setter并非用于直接改变参数计算的值,如例子中的length。而是作为一个逆推导。 2.4. AutorunAutorun是用在一些你想要产生一个不用观察者参与的被动调用函数里面。当autorun被使用的时候,一旦依赖项发生变化,autorun提供的函数就会被执行。与之相反的是,computed提供的函数只会在他有自己的观察员(observers)的时候才会评估是否重新执行,否则它的值被认为是无用的。 根据这些经验:如果你需要一个自动运行但却不会产生任何新的值的结果的函数,那么请使用Autorun。其他情况请使用computed。Autorun只是作用于如果达到某个效果或者功能,而不是计算某些值。如果有一个字符串作为第一个参数存入Autorun,那么它将成为一个调试名称。 var numbers = observable([1,3]); var sum = computed(() => numbers.reduce((a,b) => a + b,0)); var disposer = autorun(() => console.log(sum.get())); // prints '6' numbers.push(4); // prints '10' 2.5. @observer
import {observer} from "mobx-react"; var timerData = observable({ secondsPassed: 0 }); setInterval(() => { timerData.secondsPassed++; },1000); @observer class Timer extends React.Component { render() { return (<span>Seconds passed: { this.props.timerData.secondsPassed } </span> ) } }); React.render(<Timer timerData={timerData} />,document.body); tips: 如果还有其他的decorators一起或者高阶组件的存在,请确保observer为最内层(优先应用)的修饰器。否则它可能无法工作。如果你只在ES5的环境下工作:其实observer不过是observer(class Timer ... { }) 的语法糖。
React.render(<Timer timerData={timerData.secondsPassed} />,document.body) 这时候程序并不会工作了。传入组件的只是timerData里面secondsPassed的当前值。在组件里面,它是不可变的。
import {observer} from "mobx-react" import {observable} from "mobx" @observer class Timer extends React.Component { @observable secondsPassed = 0 componentWillMount() { setInterval(() => { this.secondsPassed++ },1000) } render() { return (<span>Seconds passed: { this.secondsPassed } </span> ) } }) React.render(<Timer />,document.body)
const colors = observable({ foreground: '#000',background: '#fff' }); const App = () => <Provider colors={colors}> <app stuff... /> </Provider>; const Button = observer(["colors"],({ colors,label,onClick }) => <button style={{ color: colors.foreground,backgroundColor: colors.background }} onClick={onClick} >{label}<button> ); // later.. colors.foreground = 'blue'; // all buttons updated
import {observer} from "mobx-react"; @observer class TodoView extends React.Component { componentWillReact() { console.log("I will re-render,since the todo has changed!"); } render() { return <div>this.props.todo.title</div>; } } componentWillReact没有任何参数,而且不会在render初始化之前执行(componentWillMount的区别)。而当接收新的属性或者setState之后,它会被调用。 2.6. action
action(fn) action(name,fn) @action classMethod() {} @action(name) classMethod () {} @action boundClassMethod = (args) => { body } @action(name) boundClassMethod = (args) => { body } @action.bound classMethod() {} @action.bound(function() {}) @action createRandomContact() { this.pendingRequestCount++; superagent .get('https://randomuser.me/api/') .set('Accept','application/json') .end(action("createRandomContact-callback",(error,results) => { if (error) console.error(error) else { const data = JSON.parse(results.text).results[0]; const contact = new Contact(this,data.dob,data.name,data.login.username,data.picture) contact.addTag('random-user'); this.contacts.push(contact); this.pendingRequestCount--; } } ))}
@action /*optional*/ updateDocument = async () => { const data = await fetchDataFromUrl(); /* required in strict mode to be allowed to update state: */ runInAction("update state after fetching data",() => { this.data.replace(data); this.isSaving = true; }) }
class Ticker { @observable this.tick = 0 @action.bound increment() { this.tick++ // 'this' will always be correct } } const ticker = new Ticker() setInterval(ticker.increment,1000) 后记启动例子项目:进入
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |