一、相关知识补充
1、什么是decorator
- decorator是在声明阶段实现类和类成员注解的一种语法。
2、decorator修饰类
decorator有一个参数target,代表被修饰的类。例如:在调用add方法前后打印东西,可以写一个类修饰器。
function log(target){
const desc=Object.getOwnPropertyDescriptors(target.prototype);// 获取自身的所有属性描述,背后的赋值方法或者取值方法一同拷贝过来。转成对象方便后边遍历。
for(const key of Object.keys(desc)){
if(key==='constructor'){
continue;
}
const func=desc[key].value;
if('function'===typeof func){
Object.defineProperty(target.prototype, key, { // 改写add方法
value(...arg){
console.log(key, '之前');
const res=func.apply(this, arg);
console.log(key, '之后');
return res;
}
})
}
}
}
@log
class Parent{
constructor(){
this.pi=3.1415926;
}
add(...numbers){
return numbers.reduce((total, num)=>total+num);
}
}
对类成员的修饰,修饰基本类型。
function myreadonly(target, key, descriptor){
// target 修饰的目标对象
// name 方法名
// descriptor对象值如下
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
descriptor.writable=false;//不允许修改,修改就会报错
}
class Parent{
constructor(){
@readonly this.pi=3.1415926;
}
add(...numbers){
return numbers.reduce((total, num)=>total+num);
}
}
修饰方法,假如,检验输入值类型为数值
function validate(target, key, descriptor){
const func=descriptor.value;
descriptor.value=function(...arg){
for(let num of arg){
if(typeof num!=='number'){
throw Error(`${num} is not a number`);
}
}
return func.apply(this, arg);
}
}
class Parent{
@validate
add(...numbers){
return numbers.reduce((total, num)=>total+num);
}
}
二、mobx基础语法
1、可观察数据 observable
对于基本数据类型需要使用observable.box包裹来将数据变成可观察对象,其他数据类型使用observable包裹转成可观察对象。
(1)、数组
import {observable, isObservableArray} from 'mobx'
const arr=observable([1, 2, 3]);
console.log(arr);//Proxy {0: 1, 1: 2, 2: 3, Symbol(mobx administration): ObservableArrayAdministration}
console.log(arr[0]);// 1
console.log(isObservableArray(arr));// true
仍然可以使用下标来访问数组成员。类型判断需要使用isObservableArray来判断是不是数组。
- push和pop方法也可以使用。
- 不要越界访问数组,会警告。
- 可以使用for, forEach遍历数组。
(2)、对象
const obj=observable({name: 'ling', age: 16});
console.log(obj);// Proxy {Symbol(mobx administration): ObservableObjectAdministration}
console.log(obj.name);// 'ling'
- 可以直接访问对象属性。
(3)、map
- 可以使用set设置值
- 使用has判断存在,get获取值, delete删除值。
(4)、基本数据类型
number,boolean, string都可以使用
const num=observable.box(20);
console.log(num);// ObservableValue {name_: "ObservableValue@3", isPendingUnobservation_: false, isBeingObserved_: false, observers_: Set(0), diffValue_: 0, …}
num.set(9);
console.log(num.get()); // 9
- 被转换可观察对象,想要获取原始值,调用get方法。
- 修改原始值使用set方法。
2、观察数据变化的方式
(1)、computed
将其他可观察数据,计算后输出新的可观察数据。对于其他数据来说就是对可观察数据造成反应。对自己来说就是可观察数据。
可以将多个可观察数据组合成一个可观察数据。
class Store{
@observable name='ling';
@observable boolean=true;
@observable age=19;
}
const stores=new Store();
const foo=computed(function(){
return stores.name+stores.age;
});
foo.observe_(function(change){ // 查看computed做出变化。
console.log(change, '改变');
})
console.log(foo.age=20); //修改可观察对象
console.log(foo, '结果', foo.get());
当computed作为类成员修饰时候,返回计算后的值,因此,使用收到局限性。
class Store{
@observable name='ling';
@observable boolean=true;
@observable age=19;
@computed get mix(){
return stores.name+stores.age;
}
}
const stores=new Store();
console.log(stores.mix);// ling19
(2)、autorun
修改autorun中引用的可观察数据,就会触发autorun运行。autorun加载时候会执行一次,好知道那些被引用了。
class Store{
@observable name='ling';
@observable boolean=true;
@observable age=19;
}
const stores=new Store();
autorun(()=>{
console.log(stores.name, stores.age);
});
stores.name='ge';
stores.age=29;
条件成立才触发,需要自己判断。
(3)、when
有两个参数,第一个是返回布尔值,第二个是第一个可观察对象为true执行代码。
class Store {
@observable name = "ling";
@observable boolean = true;
@observable age = 19;
}
const stores = new Store();
when(
() => stores.boolean,
() => console.log("boolean is a true")
);
(4)、reaction
当数据变化后才触发更新
class Store {
@observable name = "ling";
@observable boolean = true;
@observable age = 19;
}
const stores = new Store();
reaction(()=>[stores.name, stores.age], (data)=>console.log(data));
stores.name="lingge";
(5)、action
每次修改都会触发更新,这样性能不太好,不利于使用。因此,使用action将每次更新合并执行。
class Store {
@observable name = "ling";
@observable boolean = true;
@observable age = 19;
@action
change(){
this.name='ge';
this.age=20;
}
}
const stores = new Store();
reaction(()=>[stores.name, stores.age], (data)=>console.log(data.name, data.age));
stores.change();
也可以使用bund全局使用上下问,传递函数在其他地方使用。
class Store {
@observable name = "ling";
@observable boolean = true;
@observable age = 19;
@action.bound
change(){
this.name='ge';
this.age=20;
}
}
const stores = new Store();
reaction(()=>[stores.name, stores.age], (data)=>console.log(data.name, data.age));
const bar=stores.bar;
bar();
action建议使用,性能较好
三、mobx在react中使用
1、组件修饰器
为了让mobx观察数据变化更新视图,要给使用到可观察对象变化组件加类修饰。
import { observer } from "mobx-react";
@observer
class Parent extends Component{
render(){
return (<div>123</div>)
}
}
对于函数编程,使用observer将hooks组件包裹起来。
import { observable } from "mobx";
import { observer } from "mobx-react";
appState=observable({ timer: 0 });
export default App=observer(({store})=>(<div>我是内容</div>))
ReactDOM.render(<App store={appState} />, root);
四、类组件中初探使用mobx完成数据管理
1、创建可观察数据store
import {observable, computed, action} from 'mobx';
class Store{
@observable list=[];
@observable id=20;
@computed get getList(){
return this.list;
}
@action addList(item){
this.list.push(item);
};
@action addId(item){
this.id=item;
}
}
const store=new Store();
export default store;
2、创建父组件
import { Component } from "react";
import { Provider, observer } from "mobx-react";
import "./App.css";
import store from './store/index';
import MyHeader from './components/MyHeader';
import Footer from './components/Footer';
@observer
class App extends Component {
render() {
return (
<Provider className="App" store={store}>
<MyHeader />
<Footer />
</Provider>
);
}
}
export default App;
3、footer组件
import { Component, React } from "react";
import { inject, observer } from "mobx-react";
@inject("store")
@observer
class MyHeader extends Component {
render() {
return (
<div>
{this.props.store.list &&
this.props.store.list.map((item) => {
return (
<div>
<span>{item.name}</span>
<span>{item.age}</span>
</div>
);
})}
</div>
);
}
}
export default MyHeader;
4、header组件
import { Component } from "react";
import { inject, observer } from "mobx-react";
@inject("store")
@observer
class MyHeader extends Component {
constructor(props){
super(props);
this.handleChange=this.handleChange.bind(this);
}
handleChange(){
this.props.store.addList({name: 'linge', age: this.props.store.id});
this.props.store.addId(this.props.store.id+1);
}
render() {
return (
<div>
<button onClick={this.handleChange}>点击</button>
</div>
);
}
}
export default MyHeader;
5、目录结构
注意:由于我用的是react17,因此,无需引入React