Mobx 学习笔记
最简单的版本
第一步:初始化容器仓库
import {observable} from "mobx"
class Store
{
@observable count = 0;//将普通数据变为可被观测的数据
}
import {observable,action} from "mobx"
class Store
{
@observable count = 0;//将普通数据变为可被观测的数据
@action.bound increment(){ //添加一个action,修改数据状态
this.count++
}
constructor()
{
makeObservable(this);
}
}
//参考写法:https://github.com/mobxjs/mobx/issues/2468
第二步:在组件中使用mobx容器状态
初始版本
//1. 定义组件
class App extends React.Component{
render(){
return <div>
<h1>App Component</h1>
</div>
}
}
//2. 渲染组件
ReactDOM.render(<App />,document.getElementById('root'));
第二版 使用状态数据
//1. 定义组件
class App extends React.Component{
render(){
const {count} = this.props;
return <div>
<h1>App Component</h1>
<p>{store.count}</p>
</div>
}
}
//2. 渲染组件
//传递store对象,当做props,这样就可以在render函数中使用了
ReactDOM.render(<App store= {new Store()}/>,document.getElementById('root'));
第三版 使用action
//1. 定义组件
class App extends React.Component{
render(){
const {count} = this.props;
return <div>
<h1>App Component</h1>
<p>{store.count}</p>
<button onClick= {store.increment}> Increment </button>
</div>
}
}
//2. 渲染组件
//传递store对象,当做props,这样就可以在render函数中使用了
ReactDOM.render(<App store= {new Store()}/>,document.getElementById('root'));
第四版 关联mobx和react
//1. 定义组件
import {observer} from "mobx-react"
@observer //修改的是这里
class App extends React.Component{
render(){
const {count} = this.props;
return <div>
<h1>App Component</h1>
<p>{store.count}</p>
<button onClick= {store.increment}> Increment </button>
</div>
}
}
//2. 渲染组件
//传递store对象,当做props,这样就可以在render函数中使用了
ReactDOM.render(<App store= {new Store()}/>,document.getElementById('root'));
装饰器的种类
1. 类修饰器
//不带参数的版本
function fn(target){ //target为类定义
target.foo = "bar";
}
@fn
class MyClass {
}
console.log(MyClass.foo) // => bar
//带参数的版本
function fn2(value){
return function(target){
target.count = value
}
}
@fn2(10)
class MyClass {
}
console.log(MyClass.count) // => 10
2. 类实例对象修饰器
function fn3(target){
target.prototype.foo= "bar";
}
@fn3
class MyClass {
}
let obj = new MyClass();
console.log(MyClass.foo) // => bar
3. 类实例属性修饰器
function fn4(target,name,descriptor){
console.log(target);//target为目标类的prototype
//=> {constructor:f}
console.log(name);//name 被修饰的类成员名称
//=> message
console.log(descriptor);//descriptor 被修饰的类成员的描述对象
//=> {configurable:true,enumerable:true,writable:true,initializer:f}
descriptor.writable = false;//修改为不可写,只读
}
class MyClass {
@fn4 message = "hello";
}
4. 类实例方法修饰器和类实例属性修饰器是一样的
function fn4(target,name,descriptor){
console.log(target);//target为目标类的prototype
//=> {constructor:f}
console.log(name);//name 被修饰的类成员名称
//=> message
console.log(descriptor);//descriptor 被修饰的类成员的描述对象
//=> {configurable:true,enumerable:true,writable:true,initializer:f}
descriptor.writable = false;//修改为不可写,只读
}
class MyClass {
@fn4 test (){
console.log("hello");
}
}
理解 observable
状态改变触发事务更新
import {autorun} from 'mobx'
//autorun 根据依赖决定观测后的行为处理
const store = new Store()
//autorun 一上来会自动执行一次,然后autorun就知道了自己依赖了store.count
//当store.count发生改变,autorun会自动执行; 这里就要求store.count是可观测的
//对于不可观测的属性,只会一上来制动执行一次,后续都不会执行,因为观测不到属性的状态修改.
autorun(
()=>{
console.log(store.count);
}
);
store.count = 1; //手动修改状态,另外一种方式是通过action
理解computed
@observer
Class App extends React.Component{
render()
{
return (<p> Total: {store.price * store.count}</p>)
}
}
//该方法的缺点,计算每次渲染都会执行
//而实际上只需要依赖的数据改变,执行一次即可(缓存起来); 这里是store.price和store.count的数据改变
import {computed} from "mobx"
class Store{
@observable price = 10;
@observable count = 5;
@computed get totalPrice(){
return this.price * this.count
}
}
@observer
Class App extends React.Component{
render()
{
return (<p> Total: {store.totalPrice}</p>)
}
}
理解action
//可以手动修改observable属性
import {computed} from "mobx"
class Store{
@observable price = 10;
@observable count = 5;
@observable foo = 1;
}
const store = new Store();
autorun( ()=>{
console.log('autorun:',store.count,store.foo,store.price)
}
)
//autorun 依赖了3个属性,下面的3次修改每一次都会触发一次autorun;
store.count = 1;// 这是直接修改的方式 , 触发了一次autorun
store.foo = 2;//触发了一次autorun
store.price = 12;//触发了一次autorun
//如何做到这3个属性的修改,只触发一次autorun呢?类似于数据库中事务的概念
//可以手动修改observable属性
import {computed} from "mobx"
class Store{
@observable price = 10;
@observable count = 5;
@observable foo = 1;
//答案是使用action,然后在函数中修改3个变量; 这样调用change,只会触发一次autorun
@action change()
{
this.count = 2;
this.price = 12;
this.foo = 3;
}
}
const store = new Store();
autorun( ()=>{
console.log('autorun:',store.count,store.foo,store.price)
}
)
store.change()//这是通过action的修改方式
屏蔽直接修改
import {configure} from "mobx"
//修改observed属性时,必须通过action修饰的函数来进行
configure({
enforceActions:'observed'
})
//直接修改会报错
store.count = 22;
// => changing observed observable values outside actions is not allowd.
action.bound的作用:
为成员函数绑定this
class Store{
@observable price = 10;
@observable count = 5;
@observable foo = 1;
//答案是使用action,然后在函数中修改3个变量; 这样调用change,只会触发一次autorun
@action change()
{
console.log(this);
this.count = 2;
this.price = 12;
this.foo = 3;
}
}
const change = store.change;
change(); // => 输出undefined,因为对于const change 来说,this的指向window;严格模式为undefined
runInAction 的作用
可以将多个observable成员变量的数据进行修改,但只触发一次autorun
import {runInAction} from "mobx"
runInAction( ()=>{
store.count = 111;
store.price = 222;
store.foo = 333;
}
);
异步action
class Store{
@observable price = 10;
@observable count = 5;
@observable foo = 1;
@action asyncChange()
{//默认不允许在异步操作执行对observable数据的修改
//因为默认行为是异步操作中的动作和action修饰的函数没有任何关系
setTimeout(()=>{
this.count = 222;
},100);
}
}
const store = new Store();
store.asyncChange(); //会直接报错
class Store{
@observable price = 10;
@observable count = 5;
@observable foo = 1;
//解决异步action方式一,添加新的action函数
@action.bound changeCount(){
this.count = 222;
}
@action.bound asyncChange()
{
setTimeout(this.changeCount(),100); //将回调函数指定为action函数
}
}
const store = new Store();
store.asyncChange(); //会直接报错
class Store{
@observable price = 10;
@observable count = 5;
@observable foo = 1;
//解决异步action方式二,使用action函数
@action.bound asyncChange()
{
setTimeout(()=>{
action('changeFoo',()=>{
this.foo = "hello";
}) ();//定义一个叫changeFoo的函数,然后立即调用
},100);
}
}
const store = new Store();
store.asyncChange(); //会直接报错
class Store{
@observable price = 10;
@observable count = 5;
@observable foo = 1;
//解决异步action方式三,使用runInAction函数
@action.bound asyncChange()
{
setTimeout(()=>{
runInAction(()=>{
this.count = 222;
});
},100);
}
}
const store = new Store();
store.asyncChange(); //会直接报错
监测数据的几种方式
- @computed 前面已经接触过
- autorun //前面有讲过,一开始会执行一次,然后依赖的数据发生改变,重新出发执行
- when 条件触发
- reaction 当被观测的数据发生改变的时候,依次执行第一个函数,将第一个函数的返回值传给第二个函数; 与when的区别,reaction每次修改都会触发;when只执行一次; reaction不会一开始就执行一次
import {when} from "mobx"
//当count>100,只执行一次自定义逻辑
when( ()=>{
//第一个参数为函数,返回true或者false
return store.count>100;
},()=>{ //只有第一个参数的返回值为ture 的时候才执行
console.log('when:',store.count);
} )
reaction(
()=>{
//执行一些业务逻辑,返回数据给下一个函数使用
return store.count
},
(data,reaction)=>{//第一个参数为上一个函数的返回值;第二个参数为reaction函数本身
console.log("reaction :",data);
//模拟出when的操作
reaction.dispose();//手动停止当前reaction的监听
}
)
总结: 只有computed会返回数据,其他的都是用于可观测数据发生改变时,做一些业务逻辑,比如发消息给服务器.
十分钟入门mobx