Vue3——pinia部分(小满版本)(一)https://developer.aliyun.com/article/1470384
第四章 — 结构store - 源码解析
在pinia是不允许直接解构,是会失去响应式的
const Test = useTestStore() //pinia解构不具有响应式 const { current, name } = Test//这是解构 console.log(current, name);
差异对比
修改 Test current 解构完之后的数据不会变
而源数据是会变的
<template> <div>origin value"这是不解构的"{{Test.current}}</div> <div> pinia:{{ current }}--{{ name }}//这是解构的,从Test中解构出来,直接使用,不在前面加Test change : <button @click="change">change</button> </div> </template> <script setup lang='ts'> import { useTestStore } from './store' const Test = useTestStore() const change = () => { Test.current++ } const { current, name } = Test console.log(current, name); </script> <style> </style>
解决方案可以使用 storeToRefs
import { storeToRefs } from 'pinia' const Test = useTestStore() const { current, name } = storeToRefs(Test)//在解构之前先包一层
其原理跟 toRefs 一样的给里面的数据包裹一层 toref
源码 通过 toRaw 使 store 变回原始数据防止重复代理(这个在前面Vue3基础部分的源码部分有讲过为什么会发生重复代理的,可以翻一翻
Vue3基础笔记(小满版本)
)
循环 store 通过 isRef isReactive 判断 如果是响应式对象直接拷贝一份给 refs 对象 将其原始对象包裹 toRef 使其变为响应式对象
源码解析 - storeToRefs
//源码 function storeToRefs(store) { // See https://github.com/vuejs/pinia/issues/852 // It's easier to just use toRefs() even if it includes more stuff if (isVue2) { // @ts-expect-error: toRefs include methods and others console.log("store变化之前",store) return toRefs(store); //- 刚开始就先判断一个是不是对象,是的话将其里面的值放到数组中初始化一下(ps:真的很严谨) //然后每个属性去做一下toRef,然后把这个内容做一个返回。也判断了一下是不是Proxy对象。如果是Proxy对象就走下面的那个判断了 console.log("store变化之后",store) } else { store = toRaw(store);//将store转化为原始对象,toRaw把reactive套上的Proxy外壳给脱掉了 const refs = {}; for (const key in store) {//for循环做了一个toRef的一个操作 const value = store[key];//拷贝(复制)了一层store if (isRef(value) || isReactive(value)) { // @ts-expect-error: the key is state or getter refs[key] = // --- toRef(store, key);//然后进行包裹 } } return refs; } }
相较于小满录制视频的3月份来说,制作笔记的时间是同年11月份,此时pinia已经进行了优化,源码部分对Vue2进行了一个兼容,我根据自己的理解重新写上了注释
第五章 — Actions,getters
Actions(支持同步异步)
1.同步
同步 直接调用即可
//商店store下的index.ts文件 import { defineStore } from 'pinia' import { Names } from './store-naspace' export const useTestStore = defineStore(Names.TEST, { state: () => ({ counter: 0, }), actions: { increment() { this.counter++ }, randomizeCounter() { this.counter = Math.round(100 * Math.random())//同步写法,直接使用即可 }, }, })
<template> <div> <button @click="Add">+</button> <div> {{Test.counter}} </div> </div> </template> <script setup lang='ts'> import {useTestStore} from './store' const Test = useTestStore() const Add = () => { Test.randomizeCounter()//直接就是进行一个调用,这里的Test是对从store中解构出来的userTestStore的一个调用 } </script> <style> </style>
2.异步
异步 可以结合 async await 修饰
Promise 的构造函数接收一个参数,是函数,并且传入两个参数:resolve
,reject
,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。其实这里用 “成功” 和 “失败” 来描述并不准确,按照标准来讲,resolve 是将 Promise 的状态置为 fullfiled,reject 是将 Promise 的状态置为 rejected。不过在我们开始阶段可以先这么理解
import { defineStore } from 'pinia' import { Names } from './store-naspace' type Result = { name: string isChu: boolean } const Login = (): Promise<Result> => { return new Promise((resolve) => { setTimeout(() => { resolve({ name: '小满', isChu: true }) }, 3000) }) } export const useTestStore = defineStore(Names.TEST, { state: () => ({ user: <Result>{},//定义泛型 name: "123" }), actions: { async getLoginInfo() {//异步操作 const result = await Login()//调用了上面的Login异步操作 this.user = result; this.user = setName("大飞机")// }, setName(name:string){ this.name = name; } }, })
template
<template> <div> <button @click="Add">test</button> <div> {{Test.user}} </div> </div> </template> <script setup lang='ts'> import {useTestStore} from './store' const Test = useTestStore() const Add = () => { Test.getLoginInfo() } </script> <style> </style>
3.多个 action 互相调用 getLoginInfo setName
state: () => ({ user: <Result>{}, name: "default" }), actions: { async getLoginInfo() { const result = await Login() this.user = result; this.setName(result.name) }, setName (name:string) { this.name = name; } },
getters
在使用 this 访问时,需要定义返回类型(在 TypeScript 中),这是因为 TypeScript 中的一个已知限制 这不会影响使用箭头函数定义的 getter,也不会影响不使用 this 的 getter
1.箭头函数
- 使用箭头函数不能使用 this this 指向已经改变指向 undefined 修改值请用 state
主要作用类似于 computed 数据修饰并且有缓存 - Getter 完全等同于 Store 状态的计算值,在 defineStore () 中的 getters 属性中定义
当它接收 “状态” 作为第一个参数时,鼓励箭头函数的使用
getters:{ newPrice:(state)=> `$${state.user.price}` },
2.普通函数
- 普通函数形式可以使用 this
- getter 只会依赖状态,可能会使用到其他 getter,因此可以在定义常规函数时通过 this 访问到整个 store 的实例
getters:{ newCurrent ():number { return ++this.current } },
3.getters 互相调用
与计算属性一样,可以组合多个 getter。通过 this 访问任何其他 getter
getters:{ newCurrent ():number | string { return ++this.current + this.newName }, newName ():string { return `$-${this.name}` } },
第六章 —(API)
1.$reset
重置store到他的初始状态
state: () => ({ user: <Result>{}, name: "default", current:1 }),
Vue 例如我把值改变到了 10
const change = () => { Test.current++ }
视频写法
const reset = ()=>{ Test.$reset() }//直接进行初始化,调用reset即可
调用 $reset ();
将会把 state 所有值 重置回 原始状态
2. 订阅 state 的改变
类似于 Vuex 的 abscribe 只要有 state 的变化就会走这个函数
Test.$subscribe((args,state)=>{//subscribe是订阅的意思 console.log(args,state);//返回两个值如下 })
返回值
第二个参数
如果你的组件卸载之后还想继续调用请设置第二个参数
第二个参数是一个对象 {detached: true},在组件销毁后依然监听状态的改变
Test.$subscribe((args,state)=>{ console.log(args,state); },{ detached:true })
3. 订阅 Actions 的调用
只要有 actions 被调用就会走这个函数
第一个参数是工厂函数(就是返回的是一个对象的)
Test.$onAction((args)=>{ console.log(args); args.after(()=>{ console.log('after'); }) }) //args能够捕获到外面写入的数据内容 //App.vue文件(就是你使用pinia的文件,不一定是App.vue) const change = ()=>{ Test.setUser('123')//被args捕获到 }
第七章 — pinia插件
pinia 和 vuex 都有一个通病 页面刷新状态会丢失
我们可以写一个 pinia 插件缓存他的值
const __piniaKey = '__PINIAKEY__' //定义兜底变量 type Options = { key?:string } //定义入参类型 //将数据存在本地 const setStorage = (key: string, value: any): void => {//设置的存储函数 localStorage.setItem(key, JSON.stringify(value)) } //存缓存中读取 const getStorage = (key: string) => { return (localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key) as string) : {})//判断有没有key,没有就返回空对象 } //利用函数柯丽华接受用户入参 const piniaPlugin = (options: Options) => { //将函数返回给pinia 让pinia 调用 注入 context return (context: PiniaPluginContext) => { const { store } = context; const data = getStorage(`${options?.key ?? __piniaKey}-${store.$id}`)//将数据取出来 store.$subscribe(() => { setStorage(`${options?.key ?? __piniaKey}-${store.$id}`, toRaw(store.$state));// }) //返回值覆盖pinia 原始值 return { ...data } } } //初始化pinia const pinia = createPinia() //注册pinia 插件 pinia.use(piniaPlugin({ key: "pinia" }))