目录
什么是Pinia?
Vue3中的Pinia
创建项目
数据准备和引入Pinia
使用Pinia
采用action修改数据
总结
什么是Pinia?
Pinia是Vue3的专属的状态管理工具,什么是状态呢?其实我们可以把状态理解为数据,或者一个业务逻辑实体,都可以为状态。Pinia是Vue3的状态管理工具,这是Vuex的替代工具,不过它比Vuex简单直接很多,省去了很多语法。Vue3的一个重要思想就是数据为尊,加之Vue3是在之前的Vue的基础上的优化升级,所以简洁明了了很多 Pinia的引入,成功的解决了当组件繁多,不同组件之间有很多数据嵌套,包含的情况的时候,我们写代码繁重,逻辑不清晰的问题 #几大特点
-
提供了更加简单的API,去掉了mutation
-
提供更加贴近组合式API的新语法,并且和Vue3的其他语法统一
-
去掉了modules的概念,每个store都是一个独立的模块
-
对于TypeScript更加友好,类型推断更加可靠
-
对于插件的支持更加好,我们可以根据项目的特定需求来自定义状态管理 #代码演示
Vue3中的Pinia
创建项目
首先我们创建一个Vue3的项目,来专门演练一下Pinia。首先就是按照Vue3官方网站的步骤,创建项目,然后在选项工具这一步的时候,选择Pinia工具的时候,选上Yes,语言你可以用JavaScript,也可以是TypeScript都行。
npm create vue@latest
✔ Project name: … <your-project-name>
✔ Add TypeScript? … No / Yes ✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit testing? … No / Yes
✔ Add an End-to-End Testing Solution? … No / Cypress / Playwright
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes Scaffolding project in ./<your-project-name>... Done
数据准备和引入Pinia
下面我们来到代码部分,我用的是Webstorm,其他编辑器也是一样,文件目录都是这样,我们可以看到src文件目录下的stores文件,里面有个js文件movie.js,这个就是我们引入Pinia的地方。由于我们在创建项目的时候,选择了引入Pinia,那么在stores文件下,就会有一个counter.js文件,这里我重命名为了movie.js,下面的代码是这个js文件原本的样子。
下面的代码分别是movie.js文件和main.js文件,我们都按照官方的写法去一步一步地引入,写代码按照官方规范。
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
//我们还想完成什么样的功能,在这里写功能代码,然后在其他组件引入就可以直接使用了
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
import { createApp } from 'vue'
//第一步:引入pinia
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
//第二步:创建pinia
const app = createApp(App)
//第三步:安装pinia
app.use(createPinia())
app.use(router)
app.mount('#app')
下面我们就可以在组件中使用Pinia工具了,当然,把其他所有的.vue的组件和相应的引入全都删掉,我们就只看Pinia所要完成的功能,我们在components文件夹下新建一个.vue组件,叫Movies.vue,我们在这个组件内编写功能。
<script setup name="Movies">
import {reactive} from "vue";
//数据
let movieList = reactive([
{id:1,title:'火星救援'},
{id:2,title:'搏击俱乐部'},
{id:3,title:'流浪地球2'}
])
//要完成的功能
async function getMovie(){
//发送请求,连续解构赋值,然后再重命名
}
</script>
<template>
<div class="movie">
<button @click="getMovie">获取一部电影</button>
<ul>
<li v-for="movie in movieList" :key="movie.id">{{movie.title}}</li>
</ul>
</div>
</template>
<style scoped>
.movie {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
我们在Movies.vue组件中,写一些基本的功能,三个对象形式的数据,用reactive包裹,采用v-for遍历这个对象数组,并且在App.vue中引入Movies这个组件。
<script setup>
import Movies from "@/components/Movies.vue";
</script>
<template>
<div>
<h2>
我是App.vue
</h2>
</div>
<Movies>
</Movies>
</template>
<style scoped>
</style>
启动项目,不管你是用npm,yarn还是pnpm,都是一样的,我们打开浏览器就能看到这样的效果。
接下来,我们引入axios,发送一个请求,这里我就直接用一个现成的网站了,https://api.uomg.com/api/rand.qinghua?format=json,这是尚硅谷的一个服务器网站。
在Movies.vue组件中写下这样的代码,读者可以先不必纠结这一部分,我们的重点不是这里,这里都是一些铺垫工作,总之这里的async部分是一个功能。
<script setup name="Movies">
import {reactive} from "vue";
import axios from "axios";
import {nanoid} from 'nanoid'
//数据
let movieList = reactive([
{id:1,title:'火星救援'},
{id:2,title:'搏击俱乐部'},
{id:3,title:'流浪地球2'}
])
//要完成的功能
async function getMovie(){
//发送请求,连续解构赋值,然后再重命名
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
//把请求回来的字符串,处理成一个对象
let obj = {id:nanoid(),title}
//再放到数组中
movieList.unshift(obj)
}
</script>
<template>
<div class="movie">
<button @click="getMovie">获取一部电影</button>
<ul>
<li v-for="movie in movieList" :key="movie.id">{{movie.title}}</li>
</ul>
</div>
</template>
<style scoped>
.movie {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
使用Pinia
我们会看到Vue开发者工具中就有了Pinia,stores文件夹就是Pinia的一个具体体现,我们就把store下的movie.js想象成一个仓库,我们在这里完成功能代码,来处理刚才的Movies.vue。stores文件夹下的js代码,里面就存有,Pinia要管理的状态,可以是数据,也可也是方法。
import { defineStore } from 'pinia'
//我们还想完成什么样的功能,在这里写功能代码,然后在其他组件引入就可以直接使用了
export const useMovieStore = defineStore('movie',{
//数据真正存储的地方
state(){
return {
movieList:[
{id:1,title:'火星救援'},
{id:2,title:'搏击俱乐部'},
{id:3,title:'流浪地球2'}
]
}
}
})
然后回到Movies.vue中,我们可以去掉之前写的数据了,即movieList,因为数据已经存放到stores下的movie.js下,使用Pinia管理了,可以先把getMovie()中的代码注释一下,我们只需要在这里import引入useMovieStore,再声明一个变量movieStore,就可以使用数据了,数据存储在movie.js,而在Movie.vue组件中使用。
<script setup name="Movies">
import {reactive} from "vue";
import axios from "axios";
import {nanoid} from 'nanoid'
import {useMovieStore} from "@/stores/counter.js";
//数据
// let movieList = reactive([
// {id:1,title:'火星救援'},
// {id:2,title:'搏击俱乐部'},
// {id:3,title:'流浪地球2'}
// ])
//使用pinia后,我们就这样使用数据了
const MovieStore = useMovieStore()
//要完成的功能
async function getMovie(){
//发送请求,连续解构赋值,然后再重命名
//let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
//把请求回来的字符串,处理成一个对象
//let obj = {id:nanoid(),title}
//再放到数组中
//movieList.unshift(obj)
}
</script>
<template>
<div class="movie">
<button @click="getMovie">获取一部电影</button>
<ul> <li v-for="movie in MovieStore.movieList" :key="movie.id">{{movie.title}}</li>
</ul>
</div>
</template>
<style scoped>
.movie {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
这是打开开发者工具的效果,这就是存储数据的过程,先引入Pinia,再在stores下的movie.js下,存入数据。
采用action修改数据
数据存放好了,如果我们要对数据做改动,或者围绕数据要写一些方法,我们要采用Action,Action相当于组件中的method,它们可以通过defineStore()中的actions属性来定义。回到movie.js文件中,把之前的函数体弄到这里来,之前在Movies.vue的函数中的相应部分我们就可以删除了,功能写在这里,这样movieList数据和其相关的方法都在这一份代码中,简洁高效,思路清晰。
import { defineStore } from 'pinia'
import axios from "axios";
import {nanoid} from "nanoid";
//我们还想完成什么样的功能,在这里写功能代码,然后在其他组件引入就可以直接使用了
export const useMovieStore = defineStore('movie',{
//要完成什么样的逻辑,我们就在actions里面写
actions:{
async getMovies(){
//发送请求,连续解构赋值,然后再重命名
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
//把请求回来的字符串,处理成一个对象
let obj = {id:nanoid(),title}
//再放到数组中
this.movieList.unshift(obj)
}
},
//数据真正存储的地方
state(){
return {
movieList:[
{id:1,title:'火星救援'},
{id:2,title:'搏击俱乐部'},
{id:3,title:'流浪地球2'}
]
}
}
})
同时,如果我们想要在Vue组件中保持对Pinia存储中的响应式状态的直接引用,具体到这里,就是要让movieList具有响应式特性,就要使用storeToRefs来包裹movieList。
<script setup name="Movies">
import {useMovieStore} from "@/stores/movie.js";
import { storeToRefs } from "pinia";
//数据
// let movieList = reactive([
// {id:1,title:'火星救援'},
// {id:2,title:'搏击俱乐部'},
// {id:3,title:'流浪地球2'}
// ])
//使用pinia后,我们就这样使用数据了
const MovieStore = useMovieStore()
//我们要用storeToRefs包裹,storeToRefs只会关注store中的数据,不会对方法进行ref包裹
const { movieList } = storeToRefs(MovieStore)
function getMovie(){
MovieStore.getMovies()
}
</script>
<template>
<div class="movie">
<button @click="getMovie">获取一部电影</button>
<ul>
<li v-for="movie in movieList" :key="movie.id">{{movie.title}}</li>
</ul>
</div>
</template>
<style scoped>
.movie {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
总结
Pinia 是 Vue 3 的状态管理库,用于简化和集中管理应用的状态和逻辑。它提供了简洁的 API 和良好的 TypeScript 支持。在以上的代码中,Pinia 用于存储和管理电影列表数据,通过 defineStore
创建 store,使用 actions
处理异步获取电影数据的逻辑。通过 storeToRefs
,可以在 Vue 组件中保持对 Pinia store 中状态的响应式引用,确保当 store 中的状态更新时,组件能够实时反映这些变化。