0
点赞
收藏
分享

微信扫一扫

对象存储:阿里云OSS、腾讯云COS与七牛云KODO的深度解析

辰鑫chenxin 2024-07-24 阅读 29

Pinia(Vue 的专属状态管理库)

Vue全家桶 - pinia 的理解和学习1(Pinia 核心概念的 Store、State、Getter、Action) https://blog.csdn.net/weixin_54092687/article/details/140520675

插件

// 通过 pinia.use() 添加插件到 pinia 实例的。
import { createPinia } from 'pinia'
function SecretPiniaPlugin() { return { secret: 'the cake is a lie' } }
const pinia = createPinia()
pinia.use(SecretPiniaPlugin)

// 在组件中使用
const store = useStore()
store.secret // 'the cake is a lie'
export function myPiniaPlugin(context) {
  context.pinia // 用 `createPinia()` 创建的 pinia。
  context.app // 用 `createApp()` 创建的当前应用(仅 Vue 3)。
  context.store // 该插件想扩展的 store
  context.options // 定义传给 `defineStore()` 的 store 的可选对象。
  // ...
}

pinia.use(myPiniaPlugin)

扩展 Store

// 方式1 -- 可以直接通过在一个插件中返回包含特定属性的对象来为每个 store 都添加上特定属性。
pinia.use(() => ({ hello: 'world' }))
// 方式2 -- 可以直接在 store 上设置该属性,最好使用返回对象的方法。
pinia.use(({ store }) => { store.hello = 'world' })

// 想在 devtools 中调试 hello 属性,为了使 devtools 能追踪到 hello。
pinia.use(({ store }) => {
  store.hello = 'world'
  if (process.env.NODE_ENV === 'development') {
    store._customProperties.add('hello') // 添加你在 store 中设置的键值
  }
})

// 每个 store 都被 reactive 包装过,所以可以自动解包任何它所包含的 Ref(ref()、computed()...)。
const sharedRef = ref('shared')
pinia.use(({ store }) => {
  store.hello = ref('secret') // 每个 store 都有单独的 `hello` 属性,它会被自动解包
  store.hello // 'secret'
  
  store.shared = sharedRef // 所有的 store 都在共享 `shared` 属性的值
  store.shared // 'shared'
})
添加新的 state
import { toRef, ref } from 'vue'

pinia.use(({ store }) => {
  // 为了正确地处理 SSR,我们需要确保我们没有重写任何一个现有的值
  if (!store.$state.hasOwnProperty('hasError')) {
    // 在插件中定义 hasError,因此每个 store 都有各自的 hasError 状态
    const hasError = ref(false)
    // 在 `$state` 上设置变量,允许它在 SSR 期间被序列化。
    store.$state.hasError = hasError
  }
  // 我们需要将 ref 从 state 转移到 store
  // 这样的话,两种方式:store.hasError 和 store.$state.hasError 都可以访问
  // 并且共享的是同一个变量
  // 查看 https://cn.vuejs.org/api/reactivity-utilities.html#toref
  store.hasError = toRef(store.$state, 'hasError')

  // 在这种情况下,最好不要返回 `hasError`
  // 因为它将被显示在 devtools 的 `state` 部分
  // 如果我们返回它,devtools 将显示两次。
})
重置插件中添加的 state
import { toRef, ref } from 'vue'
pinia.use(({ store }) => {
  if (!store.$state.hasOwnProperty('hasError')) {
    const hasError = ref(false)
    store.$state.hasError = hasError
  }
  store.hasError = toRef(store.$state, 'hasError')

  // 确认将上下文 (`this`) 设置为 store
  const originalReset = store.$reset.bind(store)

  // 覆写其 $reset 函数
  return {
    $reset() {
      originalReset()
      store.hasError = false
    }
  }
})

添加新的外部属性

import { markRaw } from 'vue'
import { router } from './router'
pinia.use(({ store }) => { store.router = markRaw(router) })

在插件中调用 $subscribe

pinia.use(({ store }) => {
  store.$subscribe(() => { }) // 响应 store 变化
  store.$onAction(() => { }) // 响应 store actions
})

添加新的选项

// 创建一个 debounce 选项,允许让任何 action 实现防抖。
defineStore('search', {
  actions: {
    searchContacts() { }
  },
  // 这将在后面被一个插件读取
  debounce: {
    searchContacts: 300 // 让 action searchContacts 防抖 300ms
  }
})

// 然后,该插件可以读取该选项来包装 action,并替换原始 action
import debounce from 'lodash/debounce' // 使用任意防抖库
pinia.use(({ options, store }) => {
  if (options.debounce) {
    // 我们正在用新的 action 来覆盖这些 action
    return Object.keys(options.debounce).reduce((debouncedActions, action) => {
      debouncedActions[action] = debounce(
        store[action],
        options.debounce[action]
      )
      return debouncedActions
    }, {})
  }
})

// 注意,在使用 setup 语法时,自定义选项作为第 3 个参数传递:
defineStore( 'search',  () => { }, {
    debounce: { searchContacts: 300 }
  }
)

TypeScript

标注插件类型
// 一个 Pinia 插件可按如下方式实现类型标注:
import { PiniaPluginContext } from 'pinia'
export function myPiniaPlugin(context: PiniaPluginContext) { }
为新的 store 属性添加类型
// 当在 store 中添加新的属性时,你也应该扩展 PiniaCustomProperties 接口。
import 'pinia'
declare module 'pinia' {
  export interface PiniaCustomProperties {
    set hello(value: string | Ref<string>) // 通过使用一个 setter,我们可以允许字符串和引用。
    get hello(): string
    simpleNumber: number // 你也可以定义更简单的值
    router: Router // 你也可以定义更简单的值
  }
}

// 然后,就可以安全地写入和读取它了:
pinia.use(({ store }) => {
  store.hello = 'Hola'
  store.hello = ref('Hola')
  store.simpleNumber = Math.random()
  // @ts-expect-error: we haven't typed this correctly
  store.simpleNumber = ref(Math.random())
})
// 如果把初始选项复制成 $options(这只对 option store 有效),如何标注类型:
pinia.use(({ options }) => ({ $options: options }))
// 可以通过使用 PiniaCustomProperties 的4种通用类型来标注类型:
import 'pinia'
declare module 'pinia' {
  export interface PiniaCustomProperties<Id, S, G, A> {
    $options: {
      id: Id
      state?: () => S
      getters?: G
      actions?: A
    }
  }
}
为新的 state 添加类型
import 'pinia'
declare module 'pinia' {
  export interface PiniaCustomStateProperties<S> {
    hello: string
  }
}
为新的定义选项添加类型
import 'pinia'
declare module 'pinia' {
  export interface DefineStoreOptionsBase<S, Store> {
    // 任意 action 都允许定义一个防抖的毫秒数
    debounce?: Partial<Record<keyof StoreActions<Store>, number>>
  }
}

在组件外使用 store

单页面应用

import { useUserStore } from '@/stores/user'
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue'

// ❌  失败,因为它是在创建 pinia 之前被调用的
const userStore = useUserStore()

const pinia = createPinia()
const app = createApp(App)
app.use(pinia)

// ✅ 成功,因为 pinia 实例现在激活了
const userStore = useUserStore()
// 在 Vue Router 的导航守卫中使用 store 的例子。
import { createRouter } from 'vue-router'
const router = createRouter({
  // ...
})

// ❌ 由于引入顺序的问题,这将失败
const store = useStore()

router.beforeEach((to, from, next) => {
  // 我们想要在这里使用 store
  if (store.isLoggedIn) next()
  else next('/login')
})

router.beforeEach((to) => {
  // ✅ 这样做是可行的,因为路由器是在其被安装之后开始导航的,
  // 而此时 Pinia 也已经被安装。
  const store = useStore()

  if (to.meta.requiresAuth && !store.isLoggedIn) return '/login'
})

服务端渲染应用

<script setup>
// 这是可行的,
// 因为 pinia 知道在 `setup` 中运行的是什么程序。
  const main = useMainStore()
</script>
在 setup() 外部使用 store
const pinia = createPinia()
const app = createApp(App)

app.use(router)
app.use(pinia)

router.beforeEach((to) => {
  // ✅这会正常工作,因为它确保了正确的 store 被用于
  // 当前正在运行的应用
  const main = useMainStore(pinia)

  if (to.meta.requiresAuth && !main.isLoggedIn) return '/login'
})
export default {
  serverPrefetch() {
    const store = useStore(this.$pinia)
  },
}
State 激活
import devalue from '@nuxt/devalue'
import { createPinia } from 'pinia'
// 检索服务端的 rootState
const pinia = createPinia()
const app = createApp(App)
app.use(router)
app.use(pinia)

// 渲染页面后,rootState 被建立,
// 可以直接在 `pinia.state.value`上读取。

// 序列化,转义(如果 state 的内容可以被用户改变,这点就非常重要,几乎都是这样的)
// 并将其放置在页面的某处
// 例如,作为一个全局变量。
devalue(pinia.state.value)
// 在 vite-ssr中你可以使用transformState 选项 以及 @nuxt/devalue:
import devalue from '@nuxt/devalue'
export default viteSSR(
  App,
  {
    routes,
    transformState(state) {
      return import.meta.env.SSR ? devalue(state) : state
    },
  },
  ({ initialState }) => {
    // ...
    if (import.meta.env.SSR) {
      // 序列化并设置为 window.__INITIAL_STATE__
      initialState.pinia = pinia.state.value
    } else {
      // 在客户端,我们恢复 state
      pinia.state.value = initialState.pinia
    }
  }
)
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)

// 必须由用户设置
if (isClient) {
  pinia.state.value = JSON.parse(window.__pinia)
}
举报

相关推荐

0 条评论