React+Mobx

Mobx中文文档

项目搭建

npx create-react-app katsuki的基础上安装依赖

路由安装

yarn add react-router-dom --save

Mobx安装

yarn add mobx --save
yarn add mobx-react --save

@装饰器语法

安装decorators@依赖配合mobx使用

npm install --save-dev @babel/plugin-proposal-decorators

释放配置文件

npm run eject

出错,提示需要提交,执行下面操作
git add .
git commit -m "katsuki"

修改package.json文件

"babel": {
    "presets": [
      "react-app"
    ],
+    "plugins": [
+      [
+        "@babel/plugin-proposal-decorators",
+        {
+          "legacy": true
+        }
+      ]
+    ]
+  }

建议执行eject后再yarn安装依赖,若先前执行过yarn则要删除整个node_modules重新执行yarn
使用css文件时,需要命名为styles.module.css,因运行eject配置文件发生变化

装饰器调用报错

ctrl+shift+p打开命令,输入Preferences: Open User Settings,在设置中搜索experimentalDecorators,改为true

使用@方式引入文件

添加jsconfig.jsonpackage.json同级目录

jsconfig.json
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    },
    "jsx": "react"
  },
  "exclude": ["node_modules", "build", "config", "scripts"]
}

./config/webpack.config.jsalias配置添加

alias: {
+ '@': path.resolve(__dirname, '../src'),
  ...
  ),
}

项目文件修改

src
	pages
		KatsukiPage
			index.jsx
			styles.css
	store
		index.js
		katsuki.js
	utils
		mobx-store.js
	App.js
	index.js

src\utils\mobx-store.js:封装的store集合处理,可在任意文件引入调用store中文件

/**
 * @name Store
 * @desc store 构造器
 * @author daecrand
 */

// 根级store
class Store {
  constructor(storeConstructors) {
    // storeConstructors 所有传入的仓库
    /* storeConstructors结构
      {
        katsuki: ƒ katsuki()
        katsukichan: ƒ katsukichan()
      }
    */
    if (storeConstructors) {
      for (const key in storeConstructors) {
        // 每个传入的store作为构造函数,往其添加$getStores方法
        // 再经过之后的处理就能在当前仓库获取到stores,以调用其它仓库,目前还不能
        // 把新的storeInstance实例存入stores
        const S = storeConstructors[key]
        if (S && typeof S === 'function') {
          // 方式1
		  // const storeInstance = new S()
          // Object.defineProperty(storeInstance, '$getStores', {
          //   value: () => this.stores,
          // })

		  // 方式2
          const storeInstance = new S({
            $getStores: () => this.stores,
          })
          this.stores[key] = storeInstance
        }
      }
      return this.stores
    }
  }

  // store集合
  stores = {}
}

export default Store

src\store\katsuki.js:一个Mobx单文件,可被页面@inject('katsuki')调用

import { observable } from 'mobx'

class katsuki {
    @observable name = 'katsuki';
    @observable age = 18;
}

export default katsuki

src\store\index.js:导入封装文件和各个Mobx单文件,导出,作为rootStore

import Store from '@/utils/mobx-store'
import katsuki from './katsuki'

export default new Store({ katsuki })

src\App.js:配置页面路由

import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom'
import KatsukiPage from '@/pages/KatsukiPage'
import './App.css';

function App() {
  return (
    <>
      <Switch>
        <RouteWithProps exact path="/" component={KatsukiPage} />
        <Redirect to="/" />
      </Switch>
    </>
  );
}

/**
 * @description 接收RouterProps
 * @param {React.ComponentClass} component
 */
function RouteWithProps({ component, ...rest }) {
  const WrapperComponent = component
  return <Route {...rest} render={props => <WrapperComponent {...props} />} />
}

export default App;

src\index.js:入口文件,将引入的内容包含


import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { BrowserRouter } from 'react-router-dom'
import { Provider as MobxProvider } from 'mobx-react'

import App from './App';
import * as serviceWorker from './serviceWorker';

import rootStore from './store'

ReactDOM.render(
    <MobxProvider {...rootStore}>
        <BrowserRouter>
            <App />
        </BrowserRouter>
    </MobxProvider>, 
    document.getElementById('root')
);

serviceWorker.unregister();

src\pages\KatsukiPage\index.jsx:一个调用katsuki库的文件

import React, { Component } from 'react'

import styles from './styles.css'
import { inject, observer } from 'mobx-react'

// 注入多个store模块
// @inject('katsuki', 'katsukichan', ...) 多个引入
@inject('katsuki')

@observer
class Katsuki extends Component {
    
    render() {
        const { name, age } = this.props.katsuki
        return (
            <>
                <div>name:{name}</div>
                <div>age:{age}</div>
            </>
        )
        
    }
}

export default Katsuki

使用

mobx

observable:监测的数据,可以是JS基本数据类型、引用类型、普通对象、类实例、数组和映射

action:修改observable的值,函数逻辑处理

runInAction:处理异步操作,如需要请求完成后执行的操作

autorun:监测的observable值发生变化执行,如改变的observable值不包含在autorun中,则不会执行,用于调试

computed:计算属性,如observable值做计算处理,不建议在这里写多的逻辑

mobx-react

inject:将组件连接到提供的 stores

observer:可观察属性,引用的组件变成响应式组件,能监听到observable值的变化重新render,并只会执行componentWillUpdatecomponentDidUpdate生命周期函数

在上面的基础上继续操作
创建新的Mobx库:src\store\katsukichan.js

import { observable, action, runInAction, autorun, computed } from 'mobx'

class katsukichan {
    // 变量
    @observable name = 'katsukichan';
    @observable salar = 100;
    @observable katsukiArr = ['katsuki'];

	constructor() {
		// 除 observable salar值发生改变触发,其他observable变量改变不触发
        autorun(() => console.log('salar', this.salar))
    }
    
	// 修改observable的函数
    @action
    reSetSalar = value => {
        this.salar = value
    }

    @action
    addSalarSoon = () => {
        this.salar = this.salar + 100
    }

    @action
    addSalarLatter = async () => {
        await delay()
        // 处理异步操作
        runInAction(() => {
            this.salar = this.salar + 1000
        })
    }

	// 展示的observable处理
    @computed get myPoorSalar() {
        return this.name + ' salar: ' + this.salar
    }
}

function delay(ms = 1000) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve()
        },ms)
    })
}

export default katsukichan

导入到导出封装

import Store from '@/utils/mobx-store'
import katsuki from './katsuki'
import katsukichan from './katsukichan'

export default new Store({ katsuki, katsukichan })

创建新的文件引用:src\pages\KatsukiChan\index.jsx

import React, { Component } from 'react'
import styles from './styles.module.css'
import { inject, observer } from 'mobx-react'

@inject('katsuki', 'katsukichan')
@observer
class KatsukiChan extends Component {
    constructor(props) {
        super(props)
        this.props = props
    }

    render() {
        const { age } = this.props.katsuki
        const { name, salar, reSetSalar, addSalarSoon, addSalarLatter, myPoorSalar, katsukiArr } = this.props.katsukichan
		// 从mobx仓库中获取到的数组或对象,是个观察者对象,不是原本的数组或对象形式
		// 网络查找基本都是数组进行 split转化为正常数组,但对象就无法处理
		console.log('katsukiArr', katsukiArr)
        return (
            <div className={styles.container}>
                <p className={styles.font_pink}>{name} {age}</p>
                <p className={styles.pointer} onClick={() => reSetSalar(100)}>click to reSet</p>
                <p className={styles.pointer} onClick={addSalarSoon}>click to plus salar now</p>
                <p className={styles.pointer} onClick={addSalarLatter}>click to plus salar latter</p>
                <p>{myPoorSalar}</p>
            </div>
        )
    }
}

export default KatsukiChan

对应样式文件:src\pages\KatsukiChan\styles.module.css

.container {
    width: 1200px;
    margin: 0 auto;
    background-color: gray;
}

.font_pink {
    color: pink;
}

.pointer {
    cursor: pointer;
}

.pointer:hover {
    background-color: pink;
}

mobx获取数组或对象为观察者对象问题

mobx仓库获取数组(array)对象(object)时,是一个观察者对象,而不是正常的数组(array)对象(object),所以进行以下处理。
src\utils\mobx-store.js修改

/**
 * @name Store
 * @desc store 构造器
 * @author daecrand
 */
import { action, isComputedProp, isObservableProp, set } from 'mobx'

// 根级store
class Store {
  constructor(storeConstructors) {
    if (storeConstructors) {
      for (const key in storeConstructors) {
        const S = storeConstructors[key]
        if (S && typeof S === 'function') {
          const storeInstance = new S({
            $getStores: () => this.stores,
          })
          this.stores[key] = storeInstance
        }
      }
      return this.stores
    }
  }

  // store集合
  stores = {}
}

// 所有store的基础类
class StoreModule {
  constructor({ $getStores }) {
    // 初始化时,赋值
    this.$getStores = $getStores
  }

  /**
   * @description 用于获取当前stroe模块所在的根级store中的所有子stroe模块
   * @description 用法: const {storeA, storeB} = this.$getStores()
   */
  $getStores() {}

  /**
   * @description 用于更新store模块中的值
   * @param {object} nextState 需要更新的值,其中的key必须存在于store中
   *
   * @description 由于此更新方法支持异步,所以不需要在'runInAction'中执行
   * @description 用法: this.$set({ a:"", b:0 })
   */
  $set(nextState) {
    if (typeof nextState !== 'object') {
      console.error(new Error('mobx action "$set": 参数类型必须为 "object"'))
      return
    }

    action(state => {
      for (const key in state) {
      	// 这里使用mobx提供的set类似es6的map映射的set,this指当前store
        if (isObservableProp(this, key) && !isComputedProp(this, key)) {
          set(this, key, state[key])
        } else {
          console.error(
            new Error(
              `mobx action "$set": 当前 store 实例中不存在 "${key}", 或者 "${key}" 不是一个可观察属性(observable)`
            )
          )
        }
      }
    })(nextState)
  }
}

export { Store, StoreModule }

src\store\katsukichan.js使用src\store\katsuki.js示例

import { observable, action, runInAction, autorun, computed } from 'mobx'

class katsukichan extends StoreModule {
    // 变量
    @observable name = 'katsukichan';
    @observable salar = 100;
    @observable katsukiArr = ['katsuki'];
    
    @action
    reSetSalar = value => {
    	// 获取 katsuki仓库
    	const { katsuki } = this.$getStores()
    	console.log('age', katsuki.age) // 18
    	// 更新mobx仓库值
    	// 设置后的数组为正常数组,对象同理
    	this.$set({ salar: value, katsukiArr = ['katsukichan'] }) 
    }
    
export default katsukichan

你可能感兴趣的:(React)