npm create vite@latest my-vite-app --template vue-ts运行项目:
npm install npm run dev删除app.vue中的其它无用代码,最终页面如下:
yarn add pinia # 或者使用 npm npm install pinia安装完成后我们需要将pinia挂载到Vue应用中,也就是我们需要创建一个根存储传递给应用程序,简单来说就是创建一个存储数据的数据桶,放到应用程序中去。修改main.js,引入pinia提供的createPinia方法,创建根存储。
// main.ts // 堆代码 duidaima.com import { createApp } from "vue"; import App from "./App.vue"; import { createPinia } from "pinia"; const pinia = createPinia(); const app = createApp(App); app.use(pinia); app.mount("#app");4.2 创建store
/src/store/user.ts import { defineStore } from 'pinia' // 第一个参数是应用程序中 store 的唯一 id export const useUsersStore = defineStore('users', { // 其它配置项 })创建store很简单,调用pinia中的defineStore函数即可,该函数接收两个参数:
/src/App.vue <script setup lang="ts"> import { useUsersStore } from "../src/store/user"; const store = useUsersStore(); console.log(store); </script>使用store很简单,直接引入我们声明的useUsersStore 方法即可,我们可以先看一下执行该方法输出的是什么:
export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, });上段代码中我们给配置项添加了state属性,该属性就是用来存储数据的,我们往state中添加了3条数据。需要注意的是,state接收的是一个箭头函数返回的值,它不能直接接收一个对象。
读取state数据很简单,前面我们尝试过在App.vue中打印store,那么我们添加数据后再来看看打印结果,这个时候我们发现打印的结果里面多了几个属性,恰好就是我们添加的数据,修改App.vue,让这几个数据显示出来。
<template> <img alt="Vue logo" src="./assets/logo.png" /> <p>姓名:{{ name }}</p> <p>年龄:{{ age }}</p> <p>性别:{{ sex }}</p> </template> <script setup lang="ts"> import { ref } from "vue"; import { useUsersStore } from "../src/store/user"; const store = useUsersStore(); const name = ref<string>(store.name); const age = ref<number>(store.age); const sex = ref<string>(store.sex); </script>上段代码中我们直接通过store.age等方式获取到了store存储的值,但是大家有没有发现,这样比较繁琐,我们其实可以用解构的方式来获取值,使得代码更简洁一点。
import { useUsersStore } from "../src/store/user"; const store = useUsersStore(); const { name, age, sex } = store;上段代码实现的效果与一个一个获取的效果一样,不过代码简洁了很多。
<template> <h1>我是child组件</h1> <p>姓名:{{ name }}</p> <p>年龄:{{ age }}</p> <p>性别:{{ sex }}</p> </template> <script setup lang="ts"> import { useUsersStore } from "../src/store/user"; const store = useUsersStore(); const { name, age, sex } = store; </script>child组件和app.vue组件几乎一样,就是很简单的使用了store中的数据。这样我们就实现了多个组件同时使用store中的数据。
<template> <img alt="Vue logo" src="./assets/logo.png" /> <p>姓名:{{ name }}</p> <p>年龄:{{ age }}</p> <p>性别:{{ sex }}</p> <button @click="changeName">更改姓名</button> </template> <script setup lang="ts"> import child from './child.vue'; import { useUsersStore } from "../src/store/user"; const store = useUsersStore(); const { name, age, sex } = store; const changeName = () => { store.name = "张三"; console.log(store); }; </script>上段代码新增了changeName 方法,改变了store中name的值,我们点击按钮,我们可以看到store中的name确实被修改了,但是页面上似乎没有变化,这说明我们的使用的name不是响应式的。很多小伙伴可能会说那可以用监听函数啊,监听store变化,刷新页面...
import { storeToRefs } from 'pinia'; const store = useUsersStore(); const { name, age, sex } = storeToRefs(store);我们两个组件中获取state数据的方式都改为上段代码的形式,利用pinia的storeToRefs函数,将sstate中的数据变为了响应式的。除此之外,我们也给child.vue也加上更改state数据的方法。
<template> <h1>我是child组件</h1> <p>姓名:{{ name }}</p> <p>年龄:{{ age }}</p> <p>性别:{{ sex }}</p> <button @click="changeName">更改姓名</button> </template> <script setup lang="ts"> import { useUsersStore } from "../src/store/user"; import { storeToRefs } from 'pinia'; const store = useUsersStore(); const { name, age, sex } = storeToRefs(store); const changeName = () => { store.name = "小猪课堂"; }; </script>这个时候我们再来尝试分别点击两个组件的按钮,当我们store中数据发生变化时,页面也更新了!
<button @click="reset">重置store</button> // 重置store const reset = () => { store.$reset(); };当我们点击重置按钮时,store中的数据会变为初始状态,页面也会更新。
<button @click="patchStore">批量修改数据</button> // 批量修改数据 const patchStore = () => { store.$patch({ name: "张三", age: 100, sex: "女", }); };有经验的小伙伴可能发现了,我们采用这种批量更改的方式似乎代价有一点大,假如我们state中有些字段无需更改,但是按照上段代码的写法,我们必须要将state中的所有字段例举出了。为了解决该问题,pinia提供的$patch方法还可以接收一个回调函数,它的用法有点像我们的数组循环回调函数了。
store.$patch((state) => { state.items.push({ name: 'shoes', quantity: 1 }) state.hasChanged = true })上段代码中我们即批量更改了state的数据,又没有将所有的state字段列举出来。
store.$state = { counter: 666, name: '堆代码' }上段代码会将我们提前声明的state替换为新的对象,可能这种场景用得比较少,这里我就不展开说明了。
export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, getters: { getAddAge: (state) => { return state.age + 100; }, }, });上段代码中我们在配置项参数中添加了getter属性,该属性对象中定义了一个getAddAge方法,该方法会默认接收一个state参数,也就是state对象,然后该方法返回的是一个新的数据。
<template> <p>新年龄:{{ store.getAddAge }}</p> <button @click="patchStore">批量修改数据</button> </template> <script setup lang="ts"> import { useUsersStore } from "../src/store/user"; const store = useUsersStore(); // 批量修改数据 const patchStore = () => { store.$patch({ name: "张三", age: 100, sex: "女", }); }; </script>上段代码中我们直接在标签上使用了store.gettAddAge方法,这样可以保证响应式,其实我们state中的name等属性也可以以此种方式直接在标签上使用,也可以保持响应式。当我们点击批量修改数据按钮时,页面上的新年龄字段也会跟着变化。
export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂",// 堆代码 duidaima.com age: 25, sex: "男", }; }, getters: { getAddAge: (state) => { return state.age + 100; }, getNameAndAge(): string { return this.name + this.getAddAge; // 调用其它getter }, }, });上段代码中我们又定义了一个名为getNameAndAge的getter函数,在函数内部直接使用了this来获取state数据以及调用其它getter函数。细心的小伙伴可能会发现我们这里没有使用箭头函数的形式,这是因为我们在函数内部使用了this,箭头函数的this指向问题相信大家都知道吧!所以这里我们没有采用箭头函数的形式。
<p>调用其它getter:{{ store.getNameAndAge }}</p>4.6.4 getter传参
export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, getters: { getAddAge: (state) => { return (num: number) => state.age + num; }, getNameAndAge(): string { return this.name + this.getAddAge; // 调用其它getter }, }, });上段代码中我们getter函数getAddAge接收了一个参数num,这种写法其实有点闭包的概念在里面了,相当于我们整体返回了一个新的函数,并且将state传入了新的函数。
<p>新年龄:{{ store.getAddAge(1100) }}</p>4.7 actions属性
export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, getters: { getAddAge: (state) => { return (num: number) => state.age + num; }, getNameAndAge(): string { return this.name + this.getAddAge; // 调用其它getter }, }, actions: { saveName(name: string) { this.name = name; }, }, });上段代码中我们定义了一个非常简单的actions方法,在实际场景中,该方法可以是任何逻辑,比如发送请求、存储token等等。大家把actions方法当作一个普通的方法即可,特殊之处在于该方法内部的this指向的是当前store。
const saveName = () => { store.saveName("我是小猪"); };我们点击按钮,直接调用store中的actions方法即可。
import { createApp } from "vue"; import App from "./App.vue"; import { createPinia } from "pinia"; const pinia = createPinia(); const app = createApp(App); app.use(pinia); app.mount("#app"); user.ts代码: import { defineStore } from "pinia"; // 第一个参数是应用程序中 store 的唯一 id export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, getters: { getAddAge: (state) => { return (num: number) => state.age + num; }, getNameAndAge(): string { return this.name + this.getAddAge; // 调用其它getter }, }, actions: { saveName(name: string) { this.name = name; }, }, });App.vue代码:
<template> <img alt="Vue logo" src="./assets/logo.png" /> <p>姓名:{{ name }}</p> <p>年龄:{{ age }}</p> <p>性别:{{ sex }}</p> <p>新年龄:{{ store.getAddAge(1100) }}</p> <p>调用其它getter:{{ store.getNameAndAge }}</p> <button @click="changeName">更改姓名</button> <button @click="reset">重置store</button> <button @click="patchStore">批量修改数据</button> <button @click="saveName">调用aciton</button> <!-- 堆代码 duidaima.com --> <!-- 子组件 --> <child></child> </template> <script setup lang="ts"> import child from "./child.vue"; import { useUsersStore } from "../src/store/user"; import { storeToRefs } from "pinia"; const store = useUsersStore(); const { name, age, sex } = storeToRefs(store); const changeName = () => { store.name = "张三"; console.log(store); }; // 重置store const reset = () => { store.$reset(); }; // 批量修改数据 const patchStore = () => { store.$patch({ name: "张三", age: 100, sex: "女", }); }; // 调用actions方法 const saveName = () => { store.saveName("我是小猪"); }; </script>child.vue代码:
<template> <h1>我是child组件</h1> <p>姓名:{{ name }}</p> <p>年龄:{{ age }}</p> <p>性别:{{ sex }}</p> <button @click="changeName">更改姓名</button> </template> <script setup lang="ts"> import { useUsersStore } from "../src/store/user"; import { storeToRefs } from 'pinia'; const store = useUsersStore(); const { name, age, sex } = storeToRefs(store); const changeName = () => { store.name = "堆代码"; }; </script>