应该如何理解mobx_浅出remax+mobx+typescript(快速上手篇)

80b41754075ab9032883a0fb2740b493.png

本篇幅主要记录下近期使用remax+mobx+typescript的过程中,如何在mobx的store中使用class decorator

近期由于新项目的需求,需要重新搭建一套小程序的开发框架,之前用过taro,使用过程中唯一不适的就是,taro并不能完整的支持react的写法,在代码的编写过程中,需要注意taro在react中的一些限制(可以参考https://taro-docs.jd.com/taro/docs/react),所以秉着我还能不能好好搬砖的想法,继续摸索一下其他的框架,其实市面上也就剩下kbone、或者新出的remax可供选择,remax之前就听说,但并未仔细深入的研究,直到我打开remax的官网,发现截图中这些美妙的描述

1c38e4092743969355cad2ed1265960e.png

于是乎,果断的选择了remax
既然框架层面已经选择完成,那就开始上手撸一个ToDoList吧,得益于前端的飞速发展,任何高大上的框架,必然提供了完美的cli,remax也不例外,按着官网的步骤,npm、npm、npm...一切都已完成,最后npm run,完美启动
既然是项目需要,在react开发的基础上,少不了状态管理这一层,既然remax标榜着使用真正的react构建,那必然,市面react的成熟状态管理框架也能很好的支持,其实也就是redux、mobx之类的选择(笔者选择了后者),有人说redux不香吗?我觉得不香,主要原因是 :

  1. 我不想定义一个model,需要完成action,constant,reducer,项目过大的时候,action,constant就变得稍微难以维护
  2. 那又有人说了dva可以解决问题,从我的角度理解,dva确实解决了redux编写过程中action,constant的问题(但是我觉得写async/await逼格高点 ,),开个小玩笑,其实不愿意使用dva的原因在于,项目使用的是ts,dva中的type又只能定义成字符串类型,导致代码重构过程中,不按照规范实现的model,无法很好的使用ts提供的便利,快速的找到model定义的位置。
  3. 由于之前项目的原因,整体上都已经习惯了mobx层使用class decorator的方式进行定义

基于以上的考量,还是选择了mobx,按照remax的定义,似乎可以无缝的迁移mobx,查找了一遍官方提供的demo(https://github.com/remaxjs/examples/tree/master/wechat-mobx)可以很方便快捷的实现mobx的状态管理,至此,我可以很开心的说,一切都是这么的完美...

仔细查阅官方提供的demo,发现了store的写法似乎不符合我的预期。

5921b2b040a7a04f552697cc26047868.png

说好的class ,说好的高大上的decorator呢,在探索欲望的驱使下,我决定改造 demo中 store的写法,换成我心爱的class decorator,官方的写法,store返回的是TodoList的数据对象,在通过入口文件中的useLocalStore, 将其转换成为proxy(有兴趣的同学可以看看Proxy的定义,vue3中的改进点)

9468abfcb240947d752232d031c7eb3c.png

那问题就来了,我需要的是一个class,如何将class 的定义转换成为官方demo中提供的方式就成了解决问题的关键(其实并不是,这真是我躺过的时间最长的坑),那实现的关键方式的方法:

  1. class转换成为名副其实的object
  2. 是不是可以在定义一个object,将value值设置为class的实例

第二种方案,从当前的选择中,应该是比较简单的实现方式,想法很完美,现实却很骨感,实现的结果,转换后的store,变成了纯粹的object,不在是Proxy,导致的问题就是,无论如何更新model层的数据,始终无法触发界面的rerender,在无法解决的情况下,最好的方式就是查看useLocalStore的实现,发现在useLocalStore的hooks里,非常简单的事情,就是将store序列化后,useState了一丢丢,按照此思路,我似乎也可以实现的所谓的useClassStore,将class完美转换( 确实在去年的mobx-react issues中,发现了此问题的答案https://github.com/mobxjs/mobx-react/issues/722),此方案应该没有任何瑕疵,按照class-transform的思路,确实实现了一般class 的 方式,到此整个方案应该是落地无任何的问题了,所以我很开心的去睡了一觉。

当我造此思路,决定继续完成我的ToDoList demo,在mobx的class decorator中,有个还算实用的computed方法,但是当我在model加入此方法时,小程序开发工具的console端,无情的给了我一个红色的错误,

c09e1cceddd3efdb13f4c68f0b09dd01.png

看提示,凭着直觉,似乎computed decorator并没有按照预期的实现,而是在转换过程出了差错。decorator的转换,

  1. 在ts情况下,无非是按照ts的实现进行转换
  2. 第二种,就是babel

凭着良好的嗅觉,我觉得还是重新翻一遍remax的文档吧,终于在remax文档此页,发现了关于babel的描述,https://remaxjs.org/guide/config/babel,所以,修改tsconfig中关于decorator的配置,并不能完全的转换mobx中的decorator(文档中并没有提及mobx,虽然这一页被我看了几遍),还需要加入关于babel的配置,按照官方提供的文档信息,加入了babel的配置,重启,错误奇迹般的消息了,但是对于做class 转换这件事,我一直觉得是个繁琐的过程,既然是真正的react,就不存在mobx的class 需要手动转换的过程,所以google了不同的关键字,终于在medium的这篇文章中,发现了作者关于multi global store 和 function component的描述,https://medium.com/@suraj.kc/mobx-strategies-with-react-hooks-3de23932cb8c,所以如果使用的是class方式定义的store,在入口文件中,完全不需要provider的支持,纯粹的就是使用useContext API就可以完成全局的状态管理,可以在CodeSandBox(https://codesandbox.io/s/react-mobx-global-state-management-lxcp6?file=/src/index.tsx)看下,按照文章意思,remax中实现class 的 store,似乎也可以不按照官方提供的demo样本,使用useLocalStore,这样不仅可以减少了手动实现class transform的过程,还还原了mobx原来的味道,堪称完美,最后附上mobx class完整实现方案

一、项目加入babel的配置

module.exports = {
  presets: [
    [
      "remax",
      {
        // 是否使用 @babel/preset-typescript 转换TS代码
        // 例子:下面的 `decorators` 和 `classProperties` 可以使Mobx的装饰器能正常工作
        // @babel/plugin-proposal-decorators 的选项,详见 https://babeljs.io/docs/en/babel-plugin-proposal-decorators
        decorators: {
          legacy: true,
        },
        // @babel/plugin-proposal-class-properties 的选项,详见 https://babeljs.io/docs/en/babel-plugin-proposal-class-properties
        classProperties: {
          loose: true,
        },
      },
    ],
  ],
};

二、store class 写法(还是那个配方和味道)

import { observable, action, computed } from "mobx";

export interface ITodo {
  id: number;
  text: string;
  completed: boolean;
}

export default class TodoStore {
  private id = 5;

  @observable
  todos: ITodo[] = [
    { id: 1, text: "Learning JavaScript", completed: true },
    { id: 2, text: "Learning ES6", completed: true },
    { id: 3, text: "Learning React Native", completed: true },
    { id: 4, text: "Learning Remax", completed: false },
  ];

  @computed
  get total() {
    return this.todos.filter((item) => item.completed).length;
  }

  @action
  addTodo(text: string) {
    this.todos.push({
      id: this.id++,
      text,
      completed: false,
    });
  }

  @action
  async toggleTodo(id: number) {
    const todo = this.todos.find((todo) => todo.id === id);

    if (todo) {
      todo.completed = !todo.completed;
    }
  }
}

三、组装store createStore.ts

import ToDoStore from "./TodoStore";

const createStore = () => ({
  TodoStore: new TodoStore(),
});

export default createStore;

四、改写入口文件app.tsx

import React, { useEffect } from "react";
import axios from "@/utils/axios";

const App: React.FC = ({ children }) => {
  return children as React.ReactElement;
};

export default App;

page 写法和web上无差异,具体可以参见前面的codeSandBox的demo。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值