> 웹 프론트엔드 > View.js > Vue3 상태 관리 라이브러리 Pinia 사용법에 대한 자세한 설명

Vue3 상태 관리 라이브러리 Pinia 사용법에 대한 자세한 설명

青灯夜游
풀어 주다: 2022-10-20 19:29:24
앞으로
3004명이 탐색했습니다.

피니아란 무엇인가요? 사용하는 방법? 이 기사는 Vue의 차세대 상태 관리 라이브러리인 Pinia에 대해 알아보는 데 도움이 되기를 바랍니다.

Vue3 상태 관리 라이브러리 Pinia 사용법에 대한 자세한 설명

2020년 9월 Vue 3 공식 버전이 출시된 후 Vuex도 2021년 2월 Vue 3에 적용된 4.0 버전을 출시했습니다. 그러나 2021년 8월 말에는 주로 Eduardo, Vue 핵심 팀의 구성원입니다. 새로운 Vue 상태 공유 라이브러리 출시 버전 2.0이었으며 같은 해 11월 Youda는 공식적으로 Pinia를 Vue의 공식 상태 라이브러리로 지정했습니다(현재 Vue 공식 웹사이트는 Vuex를 Pinia로 대체했습니다). ). (학습 영상 공유: vue 영상 튜토리얼)

Pinia란 무엇입니까

Pinia는 Vuex와 마찬가지로 Vue의 "상태 저장소"로, 교차 페이지/구성 요소 형태의 데이터 상태를 구현하는 데 사용됩니다. 공유.

일반적인 개발 과정에서 Vue 구성 요소는 PropsEvents를 통해 서로 통신할 수 있습니다. 크로스 레벨 구성 요소의 경우 EventBus를 통해서도 통신할 수 있습니다. 그러나 대규모 프로젝트에서는 일반적으로 여러 데이터와 상태를 브라우저에 저장해야 하며 Props/Events 또는 EventBus를 사용하는 것은 유지 관리 및 확장이 어렵습니다. 따라서 Vuex와 Pinia가 있습니다.

Vuex를 Pinia로 대체할 수 있는 이유

Vue 개발자로서 우리 모두는 Vue의 오래된 공식 상태 라이브러리인 Vuex가 오랫동안 Vue와 함께 존재했다는 것을 알고 있습니다. 왜 지금은 Pinia로 대체됩니까?

공식 성명은 주로 다음과 같습니다:

  • Cancel

    mutations. 대부분의 개발자의 눈에 돌연변이는 상태 데이터의 동기 수정만 지원하고 actions비동기를 지원하지만 상태를 수정하려면 여전히 내부적으로 돌연변이를 호출해야 하기 때문에 이는 의심할 여지 없이 매우 번거롭고 중복됩니다

  • 모든 코드는 TypeScript로 작성되었으며 모든 인터페이스는 TypeScript를 지원하기 위해 사용자 정의 TS 래퍼가 필요한 Vuex와 달리 TypeScript의

    유형 추론을 최대한 활용합니다

  • Vuex는 인스턴스/Vue에 상태 종속성을 주입해야 합니다. 프로토타입이지만 상태 모듈을 직접 도입하고 getter/actions 함수를 호출하여 상태 업데이트 획득을 완료합니다. TypeScript 및 유형 추론에 대한 우수한 지원으로 인해 개발자는 매우 뛰어난 코드 팁을 즐길 수 있습니다

  • 상태 데이터 사전 등록, 기본적으로 코드 로직에 따라 자동으로 처리되며 사용 중에 언제든지 새 상태를 등록할 수 있습니다

  • Vuex 모듈 내장 세트 구조가 없으며 모든 상태가 평면적으로 관리됩니다 . pinia에 의해 등록된 상태는 vuex의 모듈과 유사하다는 것도 이해할 수 있습니다. 단, pinia는 모든 상태 모듈을 등록하기 위해 통일된 입구가 필요하지 않습니다

  • 플랫 구조이지만 여전히 상호 참조를 지원합니다. 각 상태 및 중첩

  • 은 네임스페이스가 필요하지 않으며 플랫 구조의 이점을 제공합니다. 각 상태가 등록 시 상태 모듈 이름을 선언하지 않더라도 pinia는 기본적으로 이를 처리합니다.
요약하자면:
Vuex 전역 상태 공유 기능 실현을 전제로 Pinia는 상태 저장 구조를 개선하고 사용을 최적화하며 API 디자인 및 사양을 단순화하고 TypeScript 유형 추론을 기반으로 개발자에게 우수한 TypeScript 지원 및 코드 팁을 제공합니다.

사용 방법

프로젝트에서 Pinia 설치는 패키지 관리 도구를 통해 직접 설치할 수 있다는 점을 다들 아셔야 합니다.

1. Pinia 인스턴스 등록

Vue 3 프로젝트를 예로 들면, 항목 파일

main.ts

에 이를 도입하기만 하면 Pinia 등록이 완료됩니다.

import { createApp } from 'vue'
import { createPinia } from 'pinia'

const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
로그인 후 복사

물론, createApp은
체인 콜

을 지원하기 때문에 .createApp(App).use(createPinia()).mount('#app')

로 직접 작성할 수도 있습니다. 이때
createPinia()

app.use가 실행될 때 루트 인스턴스를 생성합니다. 인스턴스가 앱에 삽입되고 app.config.globalProperties.$pinia도 인스턴스를 가리키도록 구성됩니다.

2. Define state Store

피니아 상태 모듈을 등록할 때

defineStore

메소드를 통해 상태 모듈 함수를 생성할 수 있습니다. (함수인 이유는 나중에 함수 형식) 내부에서 상태를 가져옵니다. deineStore 함수의 TypeScript 정의는 다음과 같습니다.

function defineStore<Id, S, G, A>(id, options): StoreDefinition<Id, S, G, A>
function defineStore<Id, S, G, A>(options): StoreDefinition<Id, S, G, A>
function defineStore<Id, SS>(id, storeSetup, options?): StoreDefinition<Id, _ExtractStateFromSetupStore<SS>, _ExtractGettersFromSetupStore<SS>, _ExtractActionsFromSetupStore<SS>>

type Id = ID extends string
type storeSetup = () => SS
type options = Omit<DefineStoreOptions<Id, S, G, A>, "id"> | DefineStoreOptions<Id, S, G, A> | DefineSetupStoreOptions<Id, _ExtractStateFromSetupStore<SS>, _ExtractGettersFromSetupStore<SS>, _ExtractActionsFromSetupStore<SS>>
로그인 후 복사

可以看到该函数最多接收 3个参数,但是我们最常用的一般都是第一种或者第二种方式。这里以 第一种方式 例,创建一个状态模块函数:

// 该部分节选字我的开源项目 vite-vue-bpmn-process
import { defineStore } from 'pinia'
import { defaultSettings } from '@/config'
import { EditorSettings } from 'types/editor/settings'

const state = {
  editorSettings: defaultSettings
}

export default defineStore('editor', {
  state: () => state,
  getters: {
    getProcessDef: (state) => ({
      processName: state.editorSettings.processName,
      processId: state.editorSettings.processId
    }),
    getProcessEngine: (state) => state.editorSettings.processEngine,
    getEditorConfig: (state) => state.editorSettings
  },
  actions: {
    updateConfiguration(conf: Partial<EditorSettings>) {
      this.editorSettings = { ...this.editorSettings, ...conf }
    }
  }
})
로그인 후 복사

其中的 options 配置项包含三个部分:

  • state:状态的初始值,推荐使用的是一个 箭头函数,方便进行类型推断
  • getters:状态的获取,是一个对象格式;推荐配置为每个 getters 的对象属性为 箭头函数,方便进行类型推断;在使用时等同于获取该函数处理后的 state 状态结果;并且与 Vue 的计算属性一样,该方法也是惰性的,具有缓存效果
  • actions:类似 Vue 中的 methods 配置项,支持异步操作,主要作用是 处理业务逻辑并更新状态数据;另外,此时的 actions 是一个 函数集合对象,与 getters 不同的是 不建议使用箭头函数并且函数内部的 this 就指向当前 store 的 state。

注意:getters 的函数定义中 第一个参数就是当前 store 的状态数据 state,而 actions 中的函数参数为 实际调用时传递的参数,可以传递多个,内部通过 this 上下文 直接访问 state 并进行更新。

3. 组件使用(配合 setup)

众所周知,vue 3 最大的亮点之一就是 组合式API(Composition API),所以我们先以组件配合 setup 使用。

import { defineComponent, ref, computed } from 'vue'
import { storeToRefs } from 'pinia'
import { EditorSettings } from 'types/editor/settings'
import editorStore from '@/store/editor'

export default defineComponent({
  setup(props) {
    const editor = editorStore()
    
    // 直接获取 state 状态
    const { editorSettings } = storeToRefs(editor)
    
    // 使用 computed
    const editorSettings = computed(() => editor.editorSettings)

    // getters
    const prefix = editor.getProcessEngine
    
    // 更新方式 1:调用 actions
    editorStore.updateConfiguration({})
    
    // 更新方式 2:直接改变 state 的值
    editorStore.editorSettings = {}
    
    // 更新方式 3:调用 $patch
    editorStore.$patch((state) => {
      state.editorSettings = {}
    })

    return {
      editorStore
    }
  }
})
로그인 후 복사

这里对以上几种处理方式进行说明:

获取值:

  • 可以通过 解构 获取 state 定义的数据,但是 解构会失去响应式,所以需要用 storeToRefs 重新对其进行响应式处理

  • 通过 computed 计算属性,好处是 可以对 state 中的状态数据进行组合

  • 通过定义的 getters 方法来获取值,这种方式获取的结果本身就是 响应式的,可以直接使用

更新值:

  1. 首先是可以 直接改变 state 的状态值,缺点是多次使用容易有重复代码,且不好维护;也会影响代码的可读性
  2. 通过定义的 actions 更新,也算是推荐方法之一;在后续迭代和扩展中,只需要维护好 store 中的代码即可
  3. $patch: 这个方式 可以接收一个对象或者函数,但是 推荐使用箭头函数(函数参数为状态数据 state);因为如果是对象,则需要根据新数据和当前状态 重建整个 state,增加了很多的性能损耗;而使用箭头函数,其实就与 actions 中的方式类似,可以 按代码逻辑修改指定的状态数据

4. 组件使用(没有 setup)

而在传统的 optionsAPI 模式的组件中(也没有配置 setup),Pinia 也提供了与 Vuex 一致的 API:mapState,mapGetters,mapActions,另外还增加了 mapStores 用来访问所有已注册的 store 数据,新增了 mapWritableState 用来 定义可更新状态;也因为 pinia 没有 mutations,所以也取消了 mapMutations 的支持。

mapGetters 也只是为了方便迁移 Vuex 的组件代码,后面依然建议 使用 mapState 替换 mapGetters

<template>
	<div>
    <p>{{ settings }}</p>
    <p>{{ processEngine }}</p>
    <button @click="updateConfiguration({})">调用 action</button>
    <button @click="update">调用 mapWritableState</button>
  </div>
</template>
<script>
  import { defineComponent, ref, storeToRefs } from 'vue'
  import { mapState, mapActions, mapWritableState } from 'pinia'
  import editorStore from '@/store/editor'
  
  export default defineComponent({
    computed: {
      ...mapState(editorStore, {
        settings: 'editorSettings',
        processEngine: (state) => `This process engine is ${state.editorSettings.processEngine}`
      }),
      ...mapWritableState(editorStore, ['editorSettings'])
    },
    methods: {
      ...mapActions(editorStore, ['updateConfiguration']),
      update() {
        this.editorSettings.processEngine = "xxx"
      }
    }
  })
</script>
로그인 후 복사

mapStores 用来访问 所有已注册 store 状态。假设我们除了上文定义的 editor,还定义了一个 id 为 modeler 的 store,则可以这么使用:

import editor from '@/store/editor'
import modeler from '@/store/modeler'
export default defineComponent({
  computed: {
    ...mapStores(editor, modeler)
  },
  methods: {
    async updateAll() {
      if (this.editorStore.processEngine === 'camunda') {
        await this.modelerStore.update()
      }
    }
  }
})
로그인 후 복사

其中引用的所有 store,都可以通过 id + 'Store' 的形式在 Vue 实例中访问到。

5. 互相引用

因为 Pinia 本身是支持各个 store 模块互相引用的,所以在定义的时候可以直接引用其他 store 的数据进行操作。

例如我们这里根据 editor store 创建一个 modeler store

import { defineStore } from 'pinia'
import editor from '@/store/editor'

export default defineStore('editor', {
  state: () => ({
    element: null,
    modeler: null
  }),
  actions: {
    updateElement(element) {
      const editorStore = editor()
      if (!editorStore.getProcessEngine) {
        editorStore.updateConfiguration({ processEngine: 'camunda' })
      }
      this.element = element
    }
  }
})
로그인 후 복사

6. 脱离 store 模块和组件使用

因为 Pinia 的每个 store 模块都是依赖 vue 应用和 pinia 根实例的,在组件内部使用时因为 Vue 应用和 pinia 根实例肯定都已经是 注册完成处于活动状态中的,所以可以直接通过调用对应的 store 状态模块函数即可。

但是在脱离 store 模块与组件,直接在外部的纯函数中使用时,则需要注意 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'
})
로그인 후 복사

直接在js模块的执行中 直接调用是可能会报错的,因为此时可能在 import router 的时候 还没有调用 createApp 和 createPinia 创建对应的应用实例和 pinia 根实例,所以无法使用。

而在路由导航的拦截器中使用时,因为 路由拦截触发时,应用和 pinia 根实例肯定已经全部实例化完毕,才可以正常使用。

所以 如果是在外部的 hooks 函数或者 utils 工具函数等纯函数模块中使用 store 数据时,最好是定义一个函数方法导出,在组件或者 store 模块中调用该方法,保证此时能正确执行

最后

总的来说,Pinia 作为 Vue 官方推荐的状态库,配合 Vue 3 的组合式 API,可以更好的实现项目中各种数据状态的管理,而不是像以前使用 Vuex 一样通过 modules 的形式注册各种状态。Pinia 对于抽离逻辑进行复用(hooks),简化使用方式来说,比之前的 Vuex 好了很多倍;加上良好的类型支持与代码提示,让我们在开发过程中可以省去很多前置工作,也是对我们的开发效率的一种提升吧。

当然,、Vue DevTools 在更新之后,也实现了对 Pinia 的支持。

【相关视频教程推荐:vuejs入门教程web前端入门

위 내용은 Vue3 상태 관리 라이브러리 Pinia 사용법에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:juejin.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿