【unibest文档】九、状态篇

1,289 阅读4分钟

状态篇

unibest - 官方文档地址unibest.tech

本文主要介绍了全局状态管理 pinia 和 简单状态 ref + reactive

pinia

unibest 已经内置了 Pinia + pinia-plugin-persistedstate(数据持久化插件),并提供了开箱即用的示例。

兼容性处理

本身 pinia-plugin-persistedstate 是不支持 uniapp 的,但是 pinia-plugin-persistedstate 提供了修改 storage 存储 API 的方式(默认是 localStorage,是一个 WEB API非H5端 不支持),目前 unibest 已经处理好了。关键代码如下:

import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持久化

const store = createPinia()
store.use(
  createPersistedState({
    storage: {
      getItem: uni.getStorageSync, // 看这里
      setItem: uni.setStorageSync, // 看这里
    },
  }),
)

定义 pinia 全局状态

src/store/xxx.ts 里面编写代码,如下是 src/store/count.ts 文件。

注意 defineStore 第三个参数可以设置是否需要持久化,默认不需要。

import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useCountStore = defineStore(
  'count',
  () => {
    const count = ref(0)
    const increment = () => {
      count.value++
    }
    const decrement = () => {
      count.value--
    }
    const reset = () => {
      count.value = 0
    }
    return {
      count,
      decrement,
      increment,
      reset,
    }
  },
  {
    // 如果需要持久化就写 true, 不需要持久化就写 false(或者去掉这个配置项)
    persist: true,
  },
)

请不要随意把数据丢到 pinia,能不用就不用。简单状态尽量使用 ref 或者 reactive

使用 pinia 全局状态

vue 文件中就可以使用了,如下是 src/pages/demo.vue 文件:

<template>
  <view class="flex justify-center items-center text-blue-500 mt-4 mb-4">
    <view class="w-20">Count: {{ countStore.count }}</view>
    <button class="ml-2 mr-2" @click="countStore.decrement">-1</button>
    <button class="ml-2 mr-2" @click="countStore.increment">+1</button>
    <button class="ml-2 mr-2" @click="countStore.reset">重置</button>
  </view>
</template>

<script lang="ts" setup>
import { useCountStore } from '@/store'

const countStore = useCountStore()
</script>

简单状态

你可以直接使用 Vue 提供的 refreactive 方法来做简单状态管理。

ref

如下是 src/pages/demo/useCount.ts 文件,定义简单状态。

// 全局状态
const globalCount = ref(1)
export function useCount() {
  // 本地状态
  const localCount = ref(1)
  function increment() {
    globalCount.value++
    localCount.value++
  }
  return {
    globalCount,
    localCount,
    increment,
  }
}

如下是 src/pages/demo/index.vue,与 ref 简单状态文件放到同一个目录下,方便管理。

<script setup lang="ts">
import useCount from './useCount.ts'
const { globalCount, localCount, increment } = useCount()
</script>

<template>
  <button @click="increment()">
    {{ globalCount }}
    {{ localCount }}
  </button>
</template>

reactive

reactiveref 类似。

如下是 src/pages/demo/count.ts 文件,定义状态。

export const countStore = reactive({
  count: 0,
  increment() {
    this.count++
  },
})

如下是 src/pages/demo/index.vue,与 reactive 简单状态文件放到同一个目录下,方便管理。

<script setup lang="ts">
import { countStore } from './count.ts'
</script>

<template>
  <button @click="countStore.increment()">
    {{ countStore.count }}
  </button>
</template>

unibest 内置代码(选读)

  • src/store/user.ts

内置用户状态

import { defineStore } from 'pinia'
import { ref } from 'vue'

const initState = { nickname: '', avatar: '' }

export const useUserStore = defineStore(
  'user',
  () => {
    const userInfo = ref<IUserInfo>({ ...initState })

    const setUserInfo = (val: IUserInfo) => {
      userInfo.value = val
    }

    const clearUserInfo = () => {
      userInfo.value = { ...initState }
    }
    // 一般没有reset需求,不需要的可以删除
    const reset = () => {
      userInfo.value = { ...initState }
    }
    const isLogined = computed(() => !!userInfo.value.token)

    return {
      userInfo,
      setUserInfo,
      clearUserInfo,
      isLogined,
      reset,
    }
  },
  {
    persist: true,
  },
)
  • src/store/index.ts

全局状态统一输出

import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持久化

const store = createPinia()
store.use(
  createPersistedState({
    storage: {
      getItem: uni.getStorageSync,
      setItem: uni.setStorageSync,
    },
  }),
)

export default store

// 模块统一导出
export * from './user'

  • src/interceptors/request.ts

请求拦截器里面有用到用户状态

/* eslint-disable no-param-reassign */
import qs from 'qs'
import { useUserStore } from '@/store'
import { platform } from '@/utils/platform'

export type CustomRequestOptions = UniApp.RequestOptions & {
  query?: Record<string, any>
  /** 出错时是否隐藏错误提示 */
  hideErrorToast?: boolean
} & IUniUploadFileOptions // 添加uni.uploadFile参数类型

// 请求基准地址
const baseUrl = import.meta.env.VITE_SERVER_BASEURL

// 拦截器配置
const httpInterceptor = {
  // 拦截前触发
  invoke(options: CustomRequestOptions) {
    // 接口请求支持通过 query 参数配置 queryString
    if (options.query) {
      const queryStr = qs.stringify(options.query)
      if (options.url.includes('?')) {
        options.url += `&${queryStr}`
      } else {
        options.url += `?${queryStr}`
      }
    }
    // 非 http 开头需拼接地址
    if (!options.url.startsWith('http')) {
      // #ifdef H5
      console.log(__VITE_APP_PROXY__)
      if (JSON.parse(__VITE_APP_PROXY__)) {
        // 啥都不需要做
      } else {
        options.url = baseUrl + options.url
      }
      // #endif
      // 非H5正常拼接
      // #ifndef H5
      options.url = baseUrl + options.url
      // #endif
      // TIPS: 如果需要对接多个后端服务,也可以在这里处理,拼接成所需要的地址
    }
    // 1. 请求超时
    options.timeout = 10000 // 10s
    // 2. (可选)添加小程序端请求头标识
    options.header = {
      platform, // 可选,与 uniapp 定义的平台一致,告诉后台来源
      ...options.header,
    }
    // 3. 添加 token 请求头标识
    const userStore = useUserStore()
    const { token } = userStore.userInfo as unknown as IUserInfo
    if (token) {
      options.header.Authorization = `Bearer ${token}`
    }
  },
}

export const requestInterceptor = {
  install() {
    // 拦截 request 请求
    uni.addInterceptor('request', httpInterceptor)
    // 拦截 uploadFile 文件上传
    uni.addInterceptor('uploadFile', httpInterceptor)
  },
}

其他可以看 unibest 代码,注释较多,简单易懂。

总结

本文介绍了 unibest 里面状态管理的 2 种方式:pinia 全局状态 和 ref\reactive 简单状态,分别演示了如何定义状态和使用状态。

注意需要灵活使用 pinia简单状态,局部的状态尽量使用 简单状态 的方式来处理,减少 pinia 里面全局变量的数量。

全文完~

unibest 链接地址

最后贴几个链接,方便大家。

文档地址:codercup.github.io/unibest-doc…

github 地址:github.com/codercup/un…

gitee 地址:gitee.com/codercup/un…

微信交流群 因不能贴引流二维码,有需要的同学请看官方文档微信群 传送门