关于vue.js:Vue-第二章-Pinia

32次阅读

共计 6140 个字符,预计需要花费 16 分钟才能阅读完成。

  • 本文来源于 Home | Pinia 中文文档 (web3doc.top)

1、概述

  • Pinia 是 Vue 的存储库,它容许您跨组件 / 页面共享状态。

1)比照 Vuex

  • 更小,只有 1kb;
  • 更简略,actions 能够同时解决同步工作和异步工作;
  • 不再有命名空间模块,然而各个 store 之间互相独立并且能够相互援用。
  • 组合式 API 格调和良好的 TS 反对。

2)装置

  • Vue3 创立一个 pinia(根存储)并将其传递给应用程序

    import {createPinia} from 'pinia'
    
    app.use(createPinia())
  • Vue 2,您还须要装置一个插件并将创立的 pinia 注入应用程序的根目录
  • 须要装置组合 API:@vue/composition-api
import {createPinia, PiniaVuePlugin} from 'pinia'

Vue.use(PiniaVuePlugin)
const pinia = createPinia()

new Vue({
  el: '#app',
  // ...
  pinia,
})

2、store

1)定义

  • Store 是应用 defineStore() 定义的,并且它须要一个 惟一 名称,作为第一个参数传递

    • 这个 name,也称为 id,是必要的,Pinia 应用它来将 store 连贯到 devtools。
    import {defineStore} from 'pinia'
    
    // useStore 能够是 useUser、useCart 之类的任何货色
    // 第一个参数是应用程序中 store 的惟一 id
    
    export const useStore = defineStore('main', {// other options...})

2)模块化

  • 能够依据须要定义任意数量的 store,并且 您应该在不同的文件中定义每个 store 以充分利用 pinia。

3、state

  • 在 Pinia 中,状态被定义为返回初始状态的函数。

    import {defineStore} from 'pinia'
    
    const useStore = defineStore('storeId', {
      // 举荐应用 残缺类型推断的箭头函数
      state: () => {
        return {
          // 所有这些属性都将主动推断其类型
          counter: 0,
          name: 'Eduardo',
          isAdmin: true,
        }
      },
    })

1)拜访“state”

  • 默认状况下,您能够通过 store 实例拜访状态来间接读取和写入状态
const store = useStore()

store.counter++

2)扭转状态

  • 除了间接用 store.counter++ 批改 store,你还能够调用 $patch 办法。它容许您应用局部“state”对象同时利用多个更改。

    store.$patch({
      counter: store.counter + 1,
      name: 'Abalam',
    })
  • $patch 办法也承受一个函数来批量批改汇合内局部对象的状况:

    cartStore.$patch((state) => {state.items.push({ name: 'shoes', quantity: 1})
      state.hasChanged = true
    })

3)重置状态

  • 能够通过调用 store 上的 $reset() 办法将状态 重置 到其初始值
const store = useStore()

store.$reset()

4)替换state

  • 能够通过将其 $state 属性设置为新对象来替换 Store 的整个状态:

    store.$state = {counter: 666, name: 'Paimon'}

5)订阅状态

  • 能够通过 store 的 $subscribe() 办法查看状态及其变动。
  • 与惯例的 watch() 相比,应用 $subscribe() 的长处是 subscriptions 只会在 patches 之后触发一次。
  • 当组件被卸载时,它们将被主动删除。如果要在卸载组件后保留它们,请将 {detached: true} 作为第二个参数传递给 detach 以后组件的 state subscription

    export default {setup() {const someStore = useSomeStore()
    
        // 此订阅将在组件卸载后保留
        someStore.$subscribe(callback, { detached: true})
    
        // ...
      },
    }

4、getters

  • Getter 齐全等同于 Store 状态 的 computed

    • 箭头函数接管 state 作为第一个参数。
    • 惯例函数通过 this 拜访到整个 store 实例,然而须要定义返回类型。
    export const useStore = defineStore('main', {state: () => ({counter: 0,}),
      getters: {
        // 主动将返回类型推断为数字
        doubleCount(state) {return state.counter * 2},
        // 返回类型必须明确设置
        doublePlusOne(): number {return this.counter * 2 + 1},
      },
    })

1)拜访其余 getter

  • 与计算属性一样,您能够组合多个 getter。通过 this 拜访任何其余 getter。
export const useStore = defineStore('main', {state: () => ({counter: 0,}),
  getters: {
    // 类型是主动推断的,因为咱们没有应用 `this`
    doubleCount: (state) => state.counter * 2,

    doubleCountPlusOne() {return this.doubleCount + 1},
  },
})

2)将参数传递给 getter

  • Getters 只是幕后的 computed 属性,因而无奈向它们传递任何参数。然而,您能够从 getter 返回一个函数以承受任何参数:

    export const useStore = defineStore('main', {
      getters: {getUserById: (state) => {return (userId) => state.users.find((user) => user.id === userId)
        },
      },
    })
    
    // App.vue
    <template>
      <p>User 2: {{getUserById(2) }}</p>
    </template>
  • 在执行此操作时,getter 不再缓存,它们只是您调用的函数。然而,您能够在 getter 自身外部利用闭包缓存一些后果。

    export const useStore = defineStore('main', {
      getters: {getActiveUserById(state) {const activeUsers = state.users.filter((user) => user.active)
          return (userId) => activeUsers.find((user) => user.id === userId)
        },
      },
    })

3)拜访其余 Store 的 getter

  • 要应用其余存储 getter,您能够间接在 getter 外部应用它
import {useOtherStore} from './other-store'

export const useStore = defineStore('main', {state: () => ({// ...}),
  getters: {otherGetter(state) {const otherStore = useOtherStore()
      return state.localData + otherStore.data
    },
  },
})

5、actions

  • Actions 相当于组件中的 methods,能够通过 this 拜访到整个 store 实例。
export const useStore = defineStore('main', {state: () => ({counter: 0,}),
  actions: {increment() {this.counter++},
    randomizeCounter() {this.counter = Math.round(100 * Math.random())
    },
  },
})
  • actions 能够是异步的,您能够在其中await 任何 API 调用甚至其余操作!
export const useUsers = defineStore('users', {state: () => ({
    userData: null,
    // ...
  }),

  actions: {async registerUser(login, password) {
      try {this.userData = await api.post({ login, password})
        showTooltip(`Welcome back ${this.userData.name}!`)
      } catch (error) {showTooltip(error)
        // 让表单组件显示谬误
        return error
      }
    },
  },
})

1)拜访其余 store 操作

  • 要应用另一个 store,您能够间接在 action 外部应用它:
import {useAuthStore} from './auth-store'

export const useSettingsStore = defineStore('settings', {state: () => ({// ...}),
  actions: {async fetchUserPreferences(preferences) {const auth = useAuthStore()
      if (auth.isAuthenticated) {this.preferences = await fetchPreferences()
      } else {throw new Error('User must be authenticated')
      }
    },
  },
})

2)订阅 Actions

  • 能够应用 store.$onAction() 订阅 action 及其后果。传递给它的回调在 action 之前执行。
  • after 解决 Promise 并容许您在 action 实现后执行函数。
  • onError 容许您在解决中抛出谬误。这些对于在运行时跟踪谬误很有用。
const unsubscribe = someStore.$onAction(
  ({
    name, // action 的名字
    store, // store 实例
    args, // 调用这个 action 的参数
    after, // 在这个 action 执行结束之后,执行这个函数
    onError, // 在这个 action 抛出异样的时候,执行这个函数
  }) => {
    // 记录开始的工夫变量
    const startTime = Date.now()
    // 这将在 `store` 上的操作执行之前触发
    console.log(`Start "${name}" with params [${args.join(',')}].`)

    // 如果 action 胜利并且齐全运行后,after 将触发。// 它将期待任何返回的 promise
    after((result) => {
      console.log(`Finished "${name}" after ${Date.now() - startTime
        }ms.\nResult: ${result}.`
      )
    })

    // 如果 action 抛出或返回 Promise.reject,onError 将触发
    onError((error) => {
      console.warn(`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
      )
    })
  }
)

// 手动移除订阅
unsubscribe()
  • 当组件被卸载时,它们将被主动删除。如果要在卸载组件后保留它们,请将 true 作为第二个参数传递给以后组件的 detach action subscription

    export default {setup() {const someStore = useSomeStore()
    
        // 此订阅将在组件卸载后保留
        someStore.$onAction(callback, true)
    
        // ...
      },
    }

6、plugins

  • 应用 pinia.use() 将插件增加到 pinia 实例中。
  • 装置此插件后创立的每个 store 增加一个名为 secret 的属性
import {createPinia} from 'pinia'

// 这可能在不同的文件中
function SecretPiniaPlugin() {return { secret: 'the cake is a lie'}
}

const pinia = createPinia()

// 将插件提供给 pinia
pinia.use(SecretPiniaPlugin)

// 在另一个文件中
const store = useStore()
store.secret // 'the cake is a lie'
  • Pinia 插件是一个函数,能够抉择返回要增加到 store 的属性。它须要一个可选参数,一个 context
export function myPiniaPlugin(context) {context.pinia // 应用 `createPinia()` 创立的 pinia
  context.app // 应用 `createApp()` 创立的以后应用程序(仅限 Vue 3)context.store // 插件正在裁减的 store
  context.options // 定义存储的选项对象传递给 `defineStore()`
  // ...
}

1)增加新的内部属性

  • 当增加内部属性、来自其余库的类实例或仅仅是非响应式的货色时,您应该在将对象传递给 pinia 之前应用 markRaw() 包装对象。这是一个将路由增加到每个 store 的示例:

    import {markRaw} from 'vue'
    // 依据您的路由所在的地位进行调整
    import {router} from './router'
    
    pinia.use(({store}) => {store.router = markRaw(router)
    })

2)在插件中调用 $subscribe

  • 您也能够在插件中应用 store.subscribestore.onAction

    pinia.use(({store}) => {store.$subscribe(() => {// 在存储变动的时候执行})
        
      store.$onAction(() => {// 在 action 的时候执行})
    })

3)引入新的 store 属性

  • 向 store 增加新属性时,您还应该扩大 PiniaCustomProperties 接口。

    import 'pinia'
    
    declare module 'pinia' {
      export interface PiniaCustomProperties {
        // 通过应用 setter,咱们能够同时容许字符串和援用
        set hello(value: string | Ref<string>)
        get hello(): string
    
        // 你也能够定义更简略的值
        simpleNumber: number
      }
    }
  • 而后能够平安地写入和读取它:

    pinia.use(({store}) => {
      store.hello = 'Hola'
      store.hello = ref('Hola')
    
      store.number = Math.random()
      // @ts-expect-error: we haven't typed this correctly
      store.number = ref(Math.random())
    })

正文完
 0