热搜:fiddler git ip ios 代理
历史搜索

前端实现Mobx+TypeScript+持久化存储

游客2024-08-01 15:03:01
目录文章目录
  1. 封装 Store
  2. 导入使用
  3. 结语

Mobx+Ts+持久化存储是一种强大的前端开发组合,它结合了 Mobx 状态管理、TypeScript 类型检查和持久化存储技术。Mobx 是一款功能丰富且易于使用的状态管理库,通过提供响应式的数据流和简洁的语法,有效解决了复杂应用中的数据管理问题。TypeScript 作为静态类型检查工具,可以帮助开发者在编码阶段捕获类型错误和提供智能提示,提高代码的可维护性和可读性。而持久化存储技术则可以将应用的数据长期保存,保证用户数据的可靠性和持续性。通过结合这三者,开发者可以构建出更健壮、可靠的前端应用。

封装 Store

HomeStore 示例

import { makeAutoObservable, runInAction } from "mobx";
import { setResult } from "@utils/util";
import { getTaskPendingApi } from "@/apis/modules/firstPage/firstPage.api";
import { toJS } from "mobx";

class HomeStore {
  taskPendingList = {
    count: 0,
    list: [],
  };
  reminderCount: any = {
    total: 0,
  };

  constructor() {
    makeAutoObservable(this);
  }

  // 获取待办列表
  async getTaskPending(data) {
    const resultObj = {
      api: getTaskPendingApi,
      apiParams: data,
      loading: "riskLoading",
      result: "taskPendingList",
    };
    await setResult.call(this, resultObj);
  }

  // 清空消息数量
  countEmpty() {
    runInAction(() => {
      this.reminderCount = {};
      console.log("清空消息数量", toJS(this.reminderCount));
    });
  }
}

export default new HomeStore();

在构造函数中调用了makeAutoObservable(this),这个函数来自 MobX 库,用于将类实例中的属性和方法转换为可观察的对象,使其能够被 MobX 追踪并自动更新相关组件。

setResult 方法

/*
 * 处理 store
 * @param api 调用后端 api 接口
 * @param apiParams 接口需要传递的参数
 * @param loading 涉及到的 loading 状态属性名称
 * @param result 需要修改的结果属性名称
 * @param msg 调用接口成功提示信息
 * @param ifConsole 是否打印调用接口信息到控制台
 * @param callback 回调函数,用于处理接口返回结果
 * @returns {Promise<any>}
 */
export async function setResult(this: any, {
  api,
  apiParams,
  loading,
  result,
  msg,
  ifConsole = false,
  callback
}: any): Promise<any> {
  if (typeof loading === "string" && loading.length > 0) {
    this[loading] = true;
  }

  try {
    const res = await api(apiParams);

    runInAction(() => {
      if (typeof loading === "string" && loading.length > 0) {
        this[loading] = false;
      }
      if (typeof result === "string" && result.length > 0) {
        this[result] = callback ? callback(res) : res;
      }
    });

    if (ifConsole) {
      console.log({ api, res });
    }
    if (msg) {
      message.success(msg);
    }

    return await Promise.resolve(res)
  } catch (e) {
    runInAction(() => {
      if (typeof loading === "string" && loading.length > 0) {
        this[loading] = false;
      }
    });

    if (ifConsole) {
      console.log({ api, e });
    }

    await Promise.reject(e)
  }
}

函数setResult接收一个对象作为参数,包含了调用后端 API 所需的各种信息,如 API 函数、参数、loading 状态属性名称、结果属性名称等。具体来说,它的参数如下:

  • api:后端 API 接口函数。
  • apiParams:调用 API 时传递的参数。
  • loading:涉及到的 loading 状态属性的名称。
  • result:需要修改的结果属性的名称。
  • msg:调用接口成功时的提示信息。
  • ifConsole:一个布尔值,指示是否将调用接口信息打印到控制台。
  • callback:一个回调函数,用于处理接口返回结果。

函数首先根据传入的loading参数设置相应的 loading 状态属性,然后调用后端 API,并在获取到结果后使用runInAction函数修改状态属性。如果设置了msg,则会显示成功提示信息。最后,根据ifConsole参数决定是否将调用接口信息打印到控制台。

如果调用过程中出现了错误,函数会在catch块中处理,并同样根据需要修改 loading 状态属性和打印错误信息。

这个函数是一个通用的、用于调用后端 API 并处理结果的辅助函数。

持久化存储示例

import { makeAutoObservable, runInAction } from "mobx";
import { makePersistable } from "mobx-persist-store";
import { getEnumApi } from "@apis/common.api";
import { formatEnumList, setResult, handleLocalForage } from "@utils/util";

class CommonStore {
  enumList: any = {};
  appProvider: any = {};

  constructor() {
    makeAutoObservable(this, {
      isModuleEnum: false,
    });
    //持久化存储
    makePersistable(this, {
      name: "enumList",
      properties: ["enumList"],
      storage: handleLocalForage,
    });
  }

  get isModuleEnum() {
    const list = window.location.pathname.split("/");
    const pathname = `/${list[1]}`;
    return formatEnumList(this.enumList.data[pathname]);
  }

  saveAppProvider(appProvider: any) {
    runInAction(() => (this.appProvider = appProvider));
  }

  async getEnumList(data: any) {
    const resultObj = {
      api: getEnumApi,
      results: "enumList",
      apiParams: data,
    };
    await setResult.call(this, resultObj);
  }
}
export default new CommonStore();
  • akeAutoObservable函数用于将类实例的属性和方法转换为可观察的,并自动追踪其变化。在这个例子中,makeAutoObservable被调用时,第一个参数是当前类的实例this,而第二个参数是一个对象,用于指定哪些属性或方法不应该被自动转换为可观察的。
  • makePersistable是 MobX Persist Store 库中提供的一个函数,用于将 MobX store 中的特定属性持久化到本地存储中,以便在应用程序重新加载时恢复它们的状态。它的主要作用是确保应用程序在重新加载后能够保持之前的状态,并且不会丢失任何重要的数据。
    • name:保存的 name,用于在 storage 中的名称标识,只要不和 storage 中其他名称重复就可以
    • properties:要保存的字段,这些字段会被保存在 name 对应的 storage 中,注意:不写在这里面的字段将不会被保存,刷新页面也将丢失:get 字段例外。get 数据会在数据返回后再自动计算
    • storage:保存的位置:看自己的业务情况选择,可以是 localStorage,sessionstorage

封装 useStores

import React from "react";
import { MobXProviderContext } from "mobx-react";

interface ContextType {
  stores: StoreType;
}

//函数声明,重载
function useStores(): StoreType;
function useStores<T extends keyof StoreType>(storeName: T): StoreType[T];
/*
 *获取根 store 或者指定 store 名称数据
 *@param storeName 指定了 store 名称
 *@returns typeof StoreType[storeName]
 */

function useStores<T extends keyof StoreType>(storeName?: T) {
  const rootStore = React.useContext(MobXProviderContext);
  const { stores } = rootStore as ContextType;
  return storeName ? stores[storeName] : stores;
}

export { useStores };
/**
 *获取所有模块 store
 */
const files = Object.entries(
  import.meta.glob("./modules/**/*.ts", { eager: true })
);
const _store: any = {};
files.forEach((item: any) => {
  const key = `${item[0].split("/").at(-1).split(".")[0]}Store`;
  _store[key] = item[1].default;
});

export type StoreType = typeof _store;
export default _store;

Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块,并且在构建时将它们分离为独立的 chunk。这种方式默认是异步加载模块的,通过动态导入实现。你可以遍历生成的 modules 对象的键来访问相应的模块。

如果你希望直接引入所有的模块(同步加载使用),你可以传入 { eager: true } 作为第二个参数。这样,模块会被转译为直接引入的形式。

示例:

下面是你提供的示例代码以及它们在构建时被转译成的代码:

// 原始代码示例
const modules = import.meta.glob('./dir/*.js')

for (const path in modules) {
  modules[path]().then((mod) => {
    console.log(path, mod)
  })
}

在构建时,上述代码会被转译成:

// 转译后的代码示例
const modules = {
  './dir/foo.js': () => import('./dir/foo.js'),
  './dir/bar.js': () => import('./dir/bar.js')
}
​
for (const path in modules) {
  modules[path]().then((mod) => {
    console.log(path, mod)
  })
}

另外,如果你希望以同步加载的形式引入所有的模块,你可以这样写:

javascriptCopy Code// 原始代码示例
const modules = import.meta.glob('./dir/*.js', { eager: true })
​
// 转译后的代码示例
import * as __glob__0_0 from './dir/foo.js'
import * as __glob__0_1 from './dir/bar.js'
const modules = {
  './dir/foo.js': __glob__0_0,
  './dir/bar.js': __glob__0_1
}

这样,所有模块会被转译成直接引入的形式,而不是通过动态导入异步加载。

导入使用

main.js 全局导入

import { observer, Provider } from 'mobx-react'

import stores from '@/stores'

function Main() {
  return <Provider stores={ stores }> {/* 应用的其余部分 */ } < /Provider>
}

export default observer(Main)

组件中使用

import { observer } from "mobx-react";
import { useEffect } from "react";
import { useStores } from "@/stores";

const FirstPage = () => {
  const { homePageStore } = useStores();

  useEffect(() => {
    homePageStore.getEnumsList();
  }, []);

  return <></>;
};

export default observer(FirstPage);

结语

以上就是关于 Mobx+Ts+持久化存储的全部内容,希望对大家有用,更多精彩内容请关注。