自2022年2月7日前端Vue3成为默认版后,Vuex的状态管理地位受收到了Pinia 的挑战。我相信当你使用过Pinia之后,就会果断的抛弃Vuex的。因为Pinia确实太方便和简单了。
学习目标
一、Pinia 的优势和环境的安装
二、使用Pinia的方式创建一个Store
三、用简单方法改变状态数据和你需要注意的事情
四、Pinia改变状态数据的四种方式包括Actions的使用
五、Pinia种Getters的使用
六、Store的互相调用方法
七、Pinia在vue-devtools中的调试方法
一、Pinia的优势、环境安装
简介:Pinia是Vue生态里Vuex的代替者,一个全新Vue的状态管理库。在Vue3成为正式版以后,尤雨溪强势推荐的项目就是Pinia。那先来看看Pinia比Vuex好的地方,也就是Pinia的五大优势。
Pinia的优势
- 可以对Vue2和Vue3做到很好的支持,也就是老项目也可以使用Pinia。
- 抛弃了Mutations的操作,只有state、getters和actions.极大的简化了状态管理库的使用,让代码编写更加容易直观。
- 不需要嵌套模块,符合Vue3的Composition api ,让代码更加扁平化。
- 完整的TypeScript支持。Vue3版本的一大优势就是对TypeScript的支持,所以Pinia也做到了完整的支持。如果你对Vuex很熟悉的话,一定知道Vuex对TS的语法支持不是完整的(经常被吐槽)。
- 代码更加简洁,可以实现很好的代码自动分割。Vue2的时代,写代码需要来回翻滚屏幕屏幕找变量,非常的麻烦,Vue3的Composition api完美了解决这个问题。 可以实现代码自动分割,pinia也同样继承了这个优点。
简单理解:Pinia的优势就是,更加简洁的语法,完美支持Vue3的Composition api 和 对TypesCcript的完美支持。这些优势和尤雨溪的强烈推荐,个人觉得很快Pinia就会完全取代Vuex,成为最适合Vue3的状态管理库。
这里说一点哦,其实pinia的开发团队,就是Vuex的开发团队。(自己挑战自己?nice)
Vue3环境安装
明白了Pinia的优势后,下一步我们就需要安装开发环境了。Pinia是Vue的状态管理库,所以需要先安装Vue的项目环境。这里需要说一下Pinia同时支持Vue2和Vue3,但这里就用最新的Vite来创建一个Vue3项目。
第一种方式:
使用Vite就需要先初始化vite,一条命令搞定
npm init vite@latest
如果是第一次安装,会提示你安装对应的?packages
Need to install the following packages:
create-vite@latest
Ok to proceed? (y)
如果出现这句话,我们直接输入y ,回车后安装对应的?packages 。然后回让你输入名字,这里起名叫做?pinia-jc
输入完名字,会让你选择项目的框架。
? Select a framework: ? - Use arrow-keys. Return to submit.
> vanilla
vue
react
preact
lit
svelte
这里按鼠标的上下键进行选择。因为Pinia是专门为Vue项目开发的,所以这里我们只能选择vue。选择vue之后回车。
? Select a variant: ? - Use arrow-keys. Return to submit.
> vue
vue-ts
然后会让你选择是否使用?ts-vue 。如果你选择使用TypeScript,就选择第二项。
这里多说一点哦,如果你自己开发项目或者是新项目,还是建议使用TypeScript。用起来会减少很多错误,也适合团队的开发。对前端职业生涯很有好处。
如果你使用了Vue3,但不使用TypeScript,这就好比你到下一站去按摩,作为一个大老爷们,却找了一个男技师。虽然也舒服了,但是总感觉差点什么,心里不得劲。所以这里我们选择?vue-ts
这步选择完成后,Vite一瞬间就初始化好了项目。按照下面的顺序,只需要三步,就可以运行起来一个Vue3的项目
Done. Now run:
cd pinia-demo // 进入项目文件夹
npm install // 安装项目依赖
npm run dev // 运行项目
运行成功后,会提示可以通过下面的地址进行访问。复制地址,在浏览器中输入[http://localhost:3000/](http://localhost:3000/) , 就可以看到默认的项目页面了。
vite v2.7.13 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 503ms.
如果可以正常访问页面,说明Vue3的项目已经安装好了、
第二种方式:我们确定要用TS那全都用最新的,我这么里项目名称?pinia-jc
npm -v查看版本输入对应命令
# npm 6.x
npm init vite@latest pinia-jc --template vue
#npm 7+, 需要额外的双横线:
npm create vite@latest pinia-jc -- --template vue-ts
# yarn
yarn create vite pinia-jc --template vue
Pinia安装
安装好Vue3的开发环境后,就可以安装Pinia状态管理库了。安装的方法依然是使用?npm ?来安装
npm install pinia
# or with yarn
yarn add pinia
?二、使用Pinia的方式创建一个Store
在main.ts文件里引入Pinia
安装好Pinia后,需要作的第一件事就是在/src/main.ts 里引入pinia 。 这里我们直接使用import 引入。
import { createPinia } from 'pinia'
引入后,通过createPinia( ) 方法,得到pinia的实例和挂载到Vue根实例上。以下是?main.ts? 的全部代码。
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
// 创建pinia实例
const pinia = createPinia()
const app = createApp(App)
//挂载到 Vue 根实例上
app.use(pinia)
app.mount('#app')
接下来就可以在项目中进行编程了
创建store状态管理库
引入Pinia后,就可以创建状态管理库了,也就是常说的Store 。直接在/src 目录下,新建一个store 文件夹。有了文件夹之后,再创建一个index.ts 文件。
这个文件里的代码,我们一般只做三件事。
- 定义状态容器(仓库)
- 修改容器(仓库)中的 state
- 仓库中的 action 的使用
明确了这四件事以后,我们来编写代码。先来定义容器,这个写法是固定的,你甚至可以在VSCode中定义一个代码片段,以后用到的时候,直接可以生成这样的代码。
import { defineStore} from 'pinia'
export const mainStore = defineStore('main',{
state:()=>{
return {}
},
getters:{},
actions:{}
})
写完这段代码,你会感觉这个很像一个Vue的小组件,这也算是Pinia的一个优点。
defineStore() 方法的第一个参数:相当于为容器起一个名字。注意:这里的名字必须唯一,不能重复 。这个是官方特别说明的一个事情。defineStore() 方法的第二个参数:可以简单理解为一个配置对象,里边是对容器仓库的配置说明。当然这种说明是以对象的形式。- state 属性:用来存储全局的状态的,这里边定义的,就可以是为SPA里全局的状态了。
- getters属性:用来监视或者说是计算状态的变化的,有缓存的功能。
- actions属性:对state里数据变化的业务逻辑,需求不同,编写逻辑不同。说白了就是修改state全局状态数据的。
我们在Store里定义一个State,我们这里就写?JC! 。
state:()=>{
return {
JC: '前端-JC',
}
},
这时候这个JC?就是全局的状态数据,是每个页面和组件都可以通过Pinia方法读取到的。
Vue3组件读取Store数据
在\src\components 里,新建一个JC.vue 的组件。编写下面的代码
<template>
<h2>{{ store.JC }}</h2>
</template>
<script lang="ts" setup>
import { mainStore } from "../store/index";
const store = mainStore();
</script>
<style lang="scss" scoped></style>
先引入mainStore ,然后通过mainStore 得到store 实例,就可以在组件里调用state 里的state 定义的状态数据了。
写好这个组件后,到App.vue 里引入,就可以使用了。当然这里我们删除自动生成的一些无用代码。
<template>
<j-c />
<count-button />
</template>
<script setup lang="ts">
import JC from './components/JC.vue';
</script>
<style scoped>
</style>
写完这些后,在VSCode或者node中打开终端,然后输入yarn dev 或者npm run dev ?运行Vue服务,在浏览器中输入[http://localhost:3000/](http://localhost:3000/) ?查看结果。
三、Pinia改变状态数据和注意事项
我们已经会通过Pinia来创建Store状态仓库了,这里讲一下状态数据的修改和状态数据结构式你可能会踩到的一个小坑。
新建组件实现状态数据的改变
为了演示数据仓库的概念,新建一个组件。然后在一个组件里修改状态数据,看看另一个组件中的数据是否会改变。
注:这时候我们就是两个组件了,只是在一个页面中显示而已。
在\components\ 文件夹下新建一个文件CountButton.vue 。新建完成以后。
因为这里要作的是一个可以计数的组件,所以先到\store\index.ts 的state属性中,增加一个状态数据count : 0
state: () => {
return {
JC: '前端-JC',
count: 0,
}
},
有了这个状态数据后,再回到\components\CountButton.vue 文件里,增加button 和对应的业务逻辑(注意这里的业务逻辑就是修改状态数据,重点内容)
<template>
<div><button @click="handleClick">添加</button></div>
</template>
<script lang="ts" setup>
import { mainStore } from "../store/index";
const store = mainStore();
const handleClick = () => {
store.count++;
};
</script>
<style lang="scss" scoped></style>
我们先写了一个按钮,点击后直接执行handleClick 方法。你会发现,这种改变状态数据的方法是非常方便的,要比Vuex 简洁太多了。
写好后,我们把count 显示再JC.vue 组件里。
\src\components\JC.vue
<template>
? ? <h2>{{ store.JC }}</h2>
? ? <h2>{{ store.count }}</h2>
</template>
<script lang="ts" setup>
import { mainStore } from "../store/index";
const store = mainStore();
</script>
<style lang="scss" scoped></style>
然后把CountButton 加入到App.vue 页面中。
<template>
<j-c />
<count-button />
</template>
<script setup lang="ts">
import JC from './components/JC.vue';
import CountButton from './components/CountButton.vue';
</script>
<style scoped>
</style>
这步后,就可以到浏览器中查看一下最终的实现效果。如果一切正常,你可以看到我们点击按钮后,两个组件的数据通过Pinia的状态管理,已经可以实现联动了。
注意别踩解构的坑
我们可以把store 进行解构,然后直接template 中直接这样读出数据。
<template>
<h2>{{ store.JC }}</h2>
<h2>{{ store.count }}</h2>
<br />
<h2>{{ JC }}</h2>
<h2>{{ count }}</h2>
</template>
<script lang="ts" setup>
import { mainStore } from "../store/index";
const store = mainStore();
const { JC, count } = store
</script>
<style lang="scss" scoped></style>
这样看似简单,但通过解构的数据,只有一次作用,不是响应式数据(这就是我踩的坑了)。也就是说当你改变数据状态时,解构的状态数据不会发生变化。我们这时候再点击增加按钮,可以看到只有没结构的数据发生了变化。
于是我开始查找官方文档,显然Pinia团队也发现了这个问题,提供了storeToRefs( ) 方法。这个方法Pinia 中,所以我们先用import 引入。
import { storeToRefs } from "pinia";
有了storeToRefs( ) 方法后,就可以在解构的代码中,对store 使用方法了。其实这时候就是把解构出来的数据作了ref?响应式代理。所以数据拥有了响应式能力。
const { JC, count } = storeToRefs(store)
这时候再到浏览器中测试一下,就一切正常了
四、Pinia改变状态数据的四种方式包括Actions的使用
接着编写的CountButton.vue ?组件,我们再编写一个方法handleClickPatch( ) 这个方法。我们采用Pinia中的$patch 的方式编写。
\scr\components\CountButtton.vue
const handleClickPatch = () => {
store.$patch({
count: store.count + 10,
})
}
然后在<template> 里添加一个按钮,点击后执行这个方法。
<button @click="handleClickPatch">修改数据($patch)+10</button>
当然我在修改单条数据的时候,我喜欢这种直接修改的方式store.count++ ,因为足够简单。但是如果你同时修改多条数据,这里建议使用$patch 的方法。
比如现在我们点击按钮时,同时修改状态数据JC,就可以写成这种方式。
const handleClickPatch = () => {
store.$patch({
count: store.count + 10,
JC : store.JC === "HelloWord" ? "前端-JC" : "HelloWord"
})
}
那再说我在handleClick 里直接写两行代码,是不是也可以实现这样的效果。通过代码测试,是可以实现的。哪为什么还要用$patch 来做?
const handleClick = () => {
store.$patch(() => {
state.count++;
store.JC = store.JC === "HelloWord" ? "前端-JC" : "HelloWord"
});
};
因为Pinia 的官方网站,已经明确表示$patch 的方式是经过优化的,会加快修改速度,对程序的性能有很大的好处。所以如果你是多条数据同时更新状态数据,推荐使用$patch 方式更新。
<template>
<div>
<div><button @click="handleClick">修改数据(简单方法)</button></div>
<div><button @click="handleClickPatch">修改数据($patch)+10</button></div>
</div>
</template>
<script lang="ts" setup>
import { mainStore } from "../store/index";
const store = mainStore();
const handleClick = () => {
store.count++;
store.JC = store.JC === "HelloWord" ? "前端-JC" : "HelloWord"
};
const handleClickPatch = () => {
store.$patch({
count: store.count + 10,
JC : store.JC === "HelloWord" ? "前端-JC" : "HelloWord"
});
};
</script>
<style lang="scss" scoped></style>
$patch加函数的形式修改状态数据
上面的$patch 方法,我们的参数使用的是一个对象。还有一种方式是传递函数,这种方法适合复杂数据的修改,比如数组、对象的修改。
再编写一个方法handleClickMethod( ) ,然后传递一个箭头函数进去。
const handleClickMethod = () => {
store.$patch((state) => {
state.count++;
store.JC = store.JC === "HelloWord" ? "前端-JC" : "HelloWord"
});
};
?这时候的state就是store 仓库里的state ,所以我们可以直接在函数里改变任何状态数据的值。为了看到效果,我们再编写一个按钮,来执行这个方法。
<button @click="handleClickMethod">修改数据($patch+函数)</button>
写完后,可以到浏览器中看一下。这个方式我觉的和React非常像了。
在actions中写好逻辑再调用actions
如果有一个修改的过程非常复杂,可以先在store 里,定义好actions 中的函数,然后在组件里再调用函数。
我们先到\src\store\index.ts 文件里,在actions 的地方编写一个changeState( ) 方法,用来改变数据状态。代码如下:
? actions: {
? ? changeState() {
? ? ? this.count++
? ? ? this.JC = this.JC === "HelloWord" ? "前端-JC" : "HelloWord"
? ? },
}
有了这个changeState( ) 函数后,就可以在组件中调用这个函数来修改状态数据了。来到\src\components\CountButton.vue 文件。编写一个新的方法handleClickActions( ) 方法。然后就可以用store 调用changeState( ) 方法了。
const handleClickActions = () => {
store.changeState()
}
然后再加入一个按钮,调用这个方法就可以了。
<button @click="handleClickActions">修改数据(actions)</button>
注意:在用actions 的时候,不能使用箭头函数,因为箭头函数绑定是外部的this。这个兄弟们需要注意一下就可以了。
Pinia中改变状态数据的4种方式,这些方式各有利弊,根据实际开发情况进行选择就好。
五、Pinia种Getters的使用
Pinia中的Getter和Vue中的计算属性几乎一样,就是在获取State的值时作一些处理。比如我们有这样一个需求,就是在state 里有有一个状态数据是电话号码,我们想输出的时候把中间四位展示为**** .这时候用getters 就是非常不错的选择。
新增状态属性和编写Getters
先在\src\store\index.ts 文件的state 里增加一个phone 的状态数据。
state: () => {
return {
JC: '前端-JC',
count: 0,
phone: '17611622297'
}
},
然后再getters 里编写一个方法,这个方法就是隐藏手机号中间四位的,隐藏的方法就是使用正则表达式替换。代码如下
getters: {
phoneHidden(state) {
console.log('phoneHidden被带用了');
return state.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
}
},
然后到\src\components\JC.vue 中直接显示隐藏号码显示。
<template>
<h2>{{ store.JC }}</h2>
<h2>{{ store.count }}</h2>
<h2>{{ store.phoneHidden }}</h2>
<br>
<h2>{{ JC }}</h2>
<h2>{{ count }}</h2>
<h2>{{ phoneHidden }}</h2>
</template>
<script lang="ts" setup>
import { storeToRefs } from "pinia";
import { mainStore } from "../store/index";
const store = mainStore();
const { JC, count, phoneHidden } = storeToRefs(store)
</script>
<style lang="scss" scoped></style>
这时候打开浏览器,可以看到电话号码已经被隐藏了。
Getters的缓存特性
Getters 是有缓存特性的,现在我们的JC组件中调用了两次phoneHidden 吧,这时我们在index.ts 状态仓库里增加一个console.log('phoneHidden被调用了’) 。
然后回到浏览器中按F12 打开查看Console 面板,可以看到只显示了一次phoneHidden被调用了 ,也变相说明了getters 是有缓存的,虽然调用多次,但是值一样就不会被多次调用。
在\src\components\CountButton.vue 文件下,新编写一个方法handleClickChangePhone 。用来改变电话号码。
const handleClickChangePhone = () => {
store.phone = store.phone === "17611622298" ? "17611622297" : "17611622298"
}
有了函数后,再编写一个按钮,触发这个函数,电话号码就变化了。
<button @click="handleClickChangePhone">改变电话号码</button>
当电话号码改变时,Getters会自动工作,对应的phoneHidden 方法也会随着调用一次,清除以前的数据缓存。
关于this的使用
写完上面的小案例,相信你对Pinia的Getters的使用已经掌握了。这时候再回到\src\store\index.ts 文件里。我们看到actions 里是直接可以使用this 关键字操作的。
那我们思考一个问题,在getters 里可以用this进行操作吗?
答案时可以的,修改代码为下面的形式。
getters:{
phoneHidden() : String{
console.log('phoneHidden被调用了');
return this.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
}
},
因为我们使用的时TS,所以如果我们不传state , TypeScript是无法自动推到出来返回的数据类型的,所以这里我们要标明返回类型为?String 。就不会提示错误了。
Pinia中Getters的用法和Vue中的计算属性非常类似,但是拥有缓存属性。我们在编写Getters的时候,不仅可以传递state参数用来改变状态数据,还可以直接使用关键字this 来改变数据。
六、Store的互相调用方法
我们一直只使用了一个Store 仓库,在真实项目中我们往往是有多个Store 的。有多个Stroe 时,就会涉及Store内部的互相调用问题
在\src\store 下新建一个jc.ts 文件。然后下入下面的代码。
import { defineStore } from "pinia";
export const jcStore = defineStore('cj', {
state: () => {
return {
list: ['小美', '小丽', '胖丫']
}
},
})
这是一个非常简单的仓库,只有state (状态数据),需要注意的是ID 要是唯一的。有了这个仓库后,就可以回到index.ts 这个仓库中调用了。
先引入jc这个store .
import { jcStore } from './jc';
然后在actions 部分加一个getList( ) 方法。这部分就写的很简单了,只是用console.log( ) 打印到?控制台 ?上就可以了。
actions: {
changeState() {
this.count++
this.JC = this.JC === "HelloWord" ? "前端-JC" : "HelloWord"
},
getList() {
console.log(jcStore().list);
}
}
,这里给出\src\store\index.ts 的全部代码。
import { defineStore } from 'pinia';
import { jcStore } from './jc';
export const mainStore = defineStore('main', {
state: () => {
return {
JC: '前端-JC',
count: 0,
phone: '17611622297'
}
},
getters: {
phoneHidden(state) {
console.log('phone被带用了');
return state.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
}
},
actions: {
changeState() {
this.count++
this.JC = this.JC === "HelloWord" ? "前端-JC" : "HelloWord"
},
getList() {
console.log(jcStore().list);
}
}
})
这样就实现了两个store 中互相调用。为了看到效果,我们依然来到\src\components\CountButton.vue 这个文件里,写一个新的方法,就叫做getList( ) 。
const getList = () => {
store.getList()
}
有了getList( ) 方法后,在template 部分,写一个按钮进行触发。
<button @click="getList">展示List</button>
到浏览器中查看效果,按F12 打开控制台,点击按钮后,可以看到跨Store 的状态数据调用已经成功了。
好了,以上就是Pinia中Store的状态数据相互调用,内容不多,但工作中很有可能用得到
七、讲Pinia在vue-devtools中的调试方法
其实Pinia的调试也是在vue-devtools 当中进行调试的,如果你有科学上网,安装vue-devtools 非常简单,但是如果你还不会科学上网,安装可能就麻烦一点。但也是可以安装的,最后会附上vue-devtools 安装方法?
用vue-devtools对Pinia的调试
我这里默认已经安装好了vue-devtools 这个Chrome浏览器插件,我们打开VSCode,用yarn dev 打开服务,然后在浏览器预览。
http://localhost:3000
打开以前学习时编写的程序,这时候看到vue-devtools 这个插件已经亮起来了。我们直接按F12 进入调试模式,然后点击Vue 标签。
这时候在上半部分有一个面板,默认显示是Components ,点击后,也可以选择Pinia 。这时候点击Pinia 就可以看到store里边的state、getters... 等信息了。
也就是说vue-devtools 已经完全支持Pinia 的调试了。
支持可视化调试
并且vue-devtools 是支持可视化的调试,调试后会直接把结果显示在页面上。
vue-devtools 安装方法
方法一?
步骤一:调往链接地址?https://github.com/vuejs/vue-devtools
步骤二:解压链接地址中的包,到本地桌面
步骤三:win+r?命令cmd到虚拟环境,执行cd?当前目录名路径
步骤四:执行cnpm install?安装package.json中的依赖包
步骤五:执行npm run build命令会在? ?vue-devtools-mastershellschrome? 路径下生成一个src文件
步骤六:修改shells>chrome文件夹下的mainifest.json 中的persistant为true
步骤七:进入谷歌浏览器的扩展程序功能页面(直接输入 chrome://extensions),打开开发者模式,选加载已解压的扩展程序,然后把对应vue-devtools-mastershellschrome路径下的文件倒进去就哦了。
?实践一下,直接启动一个vue项目?npm run dev,F12就可以看到安装的调试工具了~~
????方法二
步骤一:通过谷歌应用商店安装 vue-devtools 扩展程序,一般这种方式需要翻墙才能访问谷歌应用商店
步骤二、把本地已有的扩展程序文件添加到谷歌浏览器的扩展程序里
|