Detailed explanation of using Pinia in Vue3

Introduction

Pinia is a state management library designed specifically for Vue.js, which provides an easy and intuitive way to manage the state of the application. When using Pinia, it's easy to create a store that defines state and then bind it with Vue components to enable them to consume that state. Compared with Vuex mentioned in the previous blog, Pinia is easier to use, smaller in size, and has better TypeScript support and plug-in system.

On the official website of Vue.js, we can see that Pinia has replaced Vuex and become part of the Vue ecosystem.

Install and configure Pinia

Installing and configuring Pinia is very simple. Like other Vue plugins, Pinia needs to be installed through yarn or npm and bundled with the Vue application. You can use the following command to install:

yarn add pinia
# 或者使用 npm
npm install pinia

After installing the Pinia package, you need to import the createPinia function in the main.ts file and bind the Pinia plugin with the Vue application, as follows:

import {
    
     createApp } from 'vue';
import {
    
     createPinia } from 'pinia';
import App from './App.vue';

const app = createApp(App);

const pinia = createPinia();
app.use(pinia);

app.mount('#app');

Use the createPinia() function to create and initialize a Pinia plugin instance, and bind it to the Vue application using app.use(pinia). At this point, we can use Pinia to manage the state of the Vue application.

Pinia's core

Store

Store is the core concept of managing state in Pinia. It is equivalent to the state in a Vue component, but the Store is a separate module.

Store is defined with defineStore(), its first parameter requires a unique name, this name, also used as id, must be passed in, Pinia will use it to connect store and devtools. In order to develop habitual usage, naming the returned function use... is a convention that conforms to the compositional function style.

The second parameter to defineStore() can accept two types of values: a Setup function or an Option object.

Example code defining Store:

import {
    
     defineStore } from 'pinia'

// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore``useCartStore``useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useAlertsStore = defineStore('alerts', {
    
    
  // 其他配置...
})

State

State is where data is stored in the store. By defining State, data can be accessed and modified anywhere in the store.

In Pinia, state is defined as a function that returns the initial state. This allows Pinia to support both server and client.
The sample code for defining State is as follows:

import {
    
     defineStore } from 'pinia'

const useStore = defineStore('storeId', {
    
    
  // 为了完整类型推理,推荐使用箭头函数
  state: () => {
    
    
    return {
    
    
      // 所有这些属性都将自动推断出它们的类型
      count: 0,
      name: 'Eduardo',
      isAdmin: true,
      items: [],
      hasChanged: true,
    }
  },
})

Getter

Getter is used to get data derived from state, similar to computed properties in Vue components. They can be defined via the getters property in defineStore(). Arrow functions are recommended and will receive state as the first argument:

export const useStore = defineStore('main', {
    
    
  state: () => ({
    
    
    count: 0,
  }),
  getters: {
    
    
    doubleCount: (state) => state.count * 2,
  },
})

Action

Actions are equivalent to methods in components. They can be defined through the actions attribute in defineStore(); Action is a way to encapsulate asynchronous operations in the store. It is a function that can be called, and can also receive parameters and modify the state in the store. Actions should always be synchronous and return a Promise so that the results are handled nicely when processing asynchronous operations.

Actions in Pinia are created by defineStore and they can be used by defining them in actions. For example, here is an Action definition in a store:

import {
    
     defineStore } from 'pinia'

export const myStore = defineStore('myStore',{
    
     
  state: () => ({
    
    
    message: 'Hello',
  }),
  actions: {
    
    
    async fetchMessage() {
    
    
      const response = await fetch('http://127.0.0.1:5173/message')
      const data = await response.json()
      this.message = data.message
    },
  },
})

In the above example, we define an Action for myStore, fetchMessage(), which will fetch data from the background API and update the state in the store. We can then call this Action from a component or another Action:

import {
    
     useStore } from 'pinia'

export default {
    
    
  setup() {
    
    
    const store = useStore('myStore')

    function handleClick() {
    
    
      store.fetchMessage()
    }

    return {
    
    
      handleClick,
    }
  },
}

In the above code, we use the useStore hook in the component to get the store instance and then pass it to the fetchMessage() method. This method will fetch data from the app's background and update the state in memory. Finally, a handleClick() method is exposed so that the component can call it and trigger the Action.

Create and use Pinia

Create Pinias

We have installed and configured Pinia before. Before creating Pinia, for unified management and maintainability of the code, we still create a store folder first, and then create related Pinia. The specific steps are as follows

  1. Create a new store folder under the src folder, and all subsequent codes that require Pinia for state management are placed in this folder
  2. Create a new movieListStore.js file under the store folder. After the creation is complete, open the file
  3. Introduce the defineStore method in Pinia in the movieListStore.js file
import {
    
     defineStore } from 'pinia'
  1. Create a defineStore object, define a useMovieListStore to receive the object created by defineStore, and export it through export default
 const useMovieListStore = defineStore('movie',{
    
     
  state: () => ({
    
    
    isShow: true,
    movies: [],
  }),
  getters: {
    
    
    getIsShow() {
    
    
      return this.isShow
    },
    getMovies() {
    
    
      return this.movies
    },
  },
  actions: {
    
    
    setIsShow(value) {
    
    
      this.isShow = value
    },
    async fetchMovies() {
    
    
      const response = await fetch('https://api.movies.com/movies')
      const data = await response.json()
      this.movies = data
    },
  },
})
export default useMovieListStore 

In the above code, we use action to define two methods, a synchronous method setIsShow, and
an asynchronous method fetchMovies
Note:
It should be noted here that the official suggestion is that when defining the hook function, it is recommended to use the naming method at the beginning of use and at the end of Store. Name the object created above, such as useMovieListStore above

Use Pinia

We have created Pinia earlier, and then we can use it in components.
To use the store in a Vue component, we need to access the store instance through the useStore() function.
The steps to use Pinia in Vue components are as follows

  1. First use import to introduce useStore in Pinia
import {
    
     useStore } from 'pinia'
  1. Create a useStore object
const store = useStore('movie')
  1. Get the status through the store.getIsShow() defined above where you need to get the status
return {
    
    
   isShow: store.getIsShow(),
}

The complete sample code in Menu.vue is as follows:

<template>
  <nav>
    <ul>
      <li v-show="isShow">{
    
    {
    
     $route.name }} </li>
      <li><router-link to="/">Home</router-link></li>
      <li><router-link to="/movies">Movies</router-link></li>
    </ul>
  </nav>
</template>

<script>
import {
    
     defineComponent } from 'vue'
import {
    
     useStore } from 'pinia'

export default defineComponent({
    
    
  name: 'Menu',

  setup() {
    
    
    const store = useStore('movie')

    return {
    
    
      isShow: store.getIsShow(),
    }
  },
})
</script>

Pinia's Option Store method defines Store

The Option Store way to define the Store is similar to Vue's option API. We define it by passing in an Option object with state, actions and getters properties. The sample code is as follows:

export const useCounterStore = defineStore('counter', {
    
    
  state: () => ({
    
     count: 0 }),
  getters: {
    
    
    double: (state) => state.count * 2,
  },
  actions: {
    
    
    increment() {
    
    
      this.count++
    },
  },
})

We can think of the state as the store's data (data), getters as the store's computed properties (computed), and actions as the methods (methods).

Pinia's Setup Store method defines Store

The Setup Store is slightly different from the Option Store. It is similar to the setup function of the Vue composite API. We pass in a function that defines some responsive properties and methods, and returns a function with the properties and methods we want to expose. method object. The sample code is as follows:

export const useCounterStore = defineStore('counter', () => {
    
    
  const count = ref(0)
  function increment() {
    
    
    count.value++
  }

  return {
    
     count, increment }
})

In the Setup Store:

  • ref() is the state attribute
  • computed() is getters
  • function() is actions

sample code

The following is an example to fully illustrate the use of Pinia state management. Now the following effects need to be achieved:
Two functions need to be completed on the page. One function is to control the jumping of different pages by monitoring the value of isShow, and the bottom menu bar The display and hiding of buttons; another function is to connect to the network through a function to obtain the movie list and display it on the details page

In the optional API, the implementation code is as follows:

  1. Code in main.js
// main.js

import {
    
     createApp } from 'vue'
import App from './App.vue'
import {
    
     createPinia } from 'pinia'
import {
    
     movieStore } from './store/movieStore'

const app = createApp(App)

app.use(createPinia())
app.use(movieStore)

app.mount('#app')
  1. The code in movieStore.js under the store folder
// store/movieStore.js

import {
    
     defineStore } from 'pinia'

export const useMovieListStore = defineStore('movie',{
    
     
  state: () => ({
    
    
    isShow: true,
    movies: [],
  }),
  getters: {
    
    
    getIsShow() {
    
    
      return this.isShow
    },
    getMovies() {
    
    
      return this.movies
    },
  },
  actions: {
    
    
    setIsShow(value) {
    
    
      this.isShow = value
    },
    async fetchMovies() {
    
    
      const response = await fetch('https://api.movies.com/movies')
      const data = await response.json()
      this.movies = data
    },
  },
})
  1. The code of the Menu.vue file under the components folder
<!-- components/Menu.vue -->

<template>
  <nav>
    <ul>
      <li v-show="isShow">{
    
    {
    
     $route.name }} </li>
      <li><router-link to="/">Home</router-link></li>
      <li><router-link to="/movies">Movies</router-link></li>
    </ul>
  </nav>
</template>

<script>
import {
    
     defineComponent } from 'vue'
import {
    
     useStore } from 'pinia'

export default defineComponent({
    
    
  name: 'Menu',

  setup() {
    
    
    const store = useStore('movie')

    return {
    
    
      isShow: store.getIsShow(),
    }
  },
})
</script>
  1. The code of MovieList.vue under the components folder
<!-- components/MovieList.vue -->

<template>
  <ul>
    <li v-for="movie in movies" :key="movie.id">
      <router-link :to="`/movies/${
      
      movie.id}`">{
    
    {
    
     movie.title }}</router-link>
    </li>
  </ul>
</template>

<script>
import {
    
     defineComponent } from 'vue'
import {
    
     useStore } from 'pinia'

export default defineComponent({
    
    
  name: 'MovieList',

  setup() {
    
    
    const store = useStore('movie')

    store.fetchMovies()

    return {
    
    
      movies: store.getMovies(),
    }
  },
})
</script>
  1. The code of MovieDetails.vue under the views folder
<!-- views/MovieDetails.vue -->

<template>
  <div v-if="movie">
    <h2>{
    
    {
    
     movie.title }}</h2>
    <p>{
    
    {
    
     movie.description }} </p>
  </div>
  <div v-else>
    <h2>Movie Not Found</h2>
  </div>
</template>

<script>
import {
    
     defineComponent } from 'vue'
import {
    
     useRoute } from 'vue-router'
import {
    
     useStore } from 'pinia'

export default defineComponent({
    
    
  name: 'MovieDetails',

  setup() {
    
    
    const route = useRoute()
    const store = useStore('movie')
    const movieId = route.params.id
    const movie = store.getMovies().find((movie) => movie.id === movieId)

    return {
    
    
      movie,
    }
  },
})
</script>

The above code shows how to share and manage state using store, getter and action in Pinia. Among them, movieStore defines a store containing two states of isShow and movies, and an action for modifying isShow and obtaining the movie list. In the Menu.vue component, we use the useStore hook to get the isShow state from the store, and control the display and hiding of the bottom menu bar button according to its value. In the MovieList.vue component, we use the useStore hook to get the movies state from the store, and the fetchMovies() action to fetch the movie list from the network. In the MovieDetails.vue component, we use the useRoute hook to get the routing parameter id of the current page, use the useStore hook to get the movies status from the store, and get the detailed information of the current movie according to the movieId and getMovies() getter.

Note that in the setup() hook, we use the useStore hook to get state and perform operations from the store. Since the useStore hook returns a reactive proxy, we don't need to manually update the state reactively. Also, we can decouple the components from the store, making them easier to test and reuse.

In the combined API, the implementation code is slightly different. Here we only take the MovieList.vue page as an example. Other pages are written in a similar way and will not be displayed. The code of the MovieList.vue page is as follows:

<!-- components/MovieList.vue -->

<template>
  <ul>
    <li v-for="movie in movies" :key="movie.id">
      <router-link :to="`/movies/${
      
      movie.id}`">{
    
    {
    
     movie.title }}</router-link>
    </li>
  </ul>
</template>

<script setup>
import {
    
     onMounted, computed } from 'vue'
import {
    
     useStore } from 'pinia'

const store = useStore('movie')

onMounted(() => {
  store.fetchMovies()
})

const movies = computed(() => store.getMovies())
</script>

OK, this is the end of the introduction about the use of Pinia for global state management in Vue3. If you like it, like it and follow it to favorites!

Guess you like

Origin blog.csdn.net/w137160164/article/details/131160122