vue3 仓库 Pinia使用

922 阅读2分钟

vue2 vuex仓库使用

Vuex用来实现vue组件全局状态(全局状态也就是数据)管理的一种机制,vuex插件的设计思想就是中央事件总线。

vuex仓库环境集成

安装:
npm i vuex --save

导入:
import Vuex from "vuex"

Vue.use(Vuex)

1.在main.js中把仓库挂载到vm对象

//main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

把仓库挂载到vm对象:挂载以后,所有的组件就可以直接从store中获取全局数据

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

2.创建仓库

//在仓库的入口文件index.js中引入vuex插件
import Vue from 'vue'
import Vuex from 'vuex'
const store=new Vuex.Store({
    state: {
  //用于保存所有组件的公共数据的对象
  },
  getters: {
  //getter理解为store的计算属性
  //getters的返回值会被缓存起来,且只有当它依赖的数据发生了改变才会被重新计算。
  },
  mutations: {
  // 用来修改state和getters里面的数据
  },
  actions: {
  // vuex中用于发起异步请求
  },
  modules: {
  a: moduleA,
  b: moduleB
  // 拆分模块
  //每个模块拥有自己的 state、mutation、action、getter可以实现模块化开发
  }
}
})
export default store

3.vuex模块化设计

//分块设计: 可以是一份单独的js文件,然后在引入到仓库的入口文件中
//也可以直接在仓库的入口文件中直接注册
const moduleA = {
  namespaced: true,//局部命名空间(让state的中变量与其他模块中的同名变量不冲突)	
  state: { msg:1 },
  mutations: { change(state,n){state.msg=n} },
  actions: { change(context,n){context.commit("change",n)} },
  getters: { x(state){return state.msg} }
}
export default moduleA

4.组件使用

//在store的index.js中引入模块
import Module1 from './Module1.js'
const store=new Vuex.store({
    state:{msg:"我就是所有共享的数据",
    arr: [
      { id: 1, text: '...', birth: "1997-02-03" },
      { id: 2, text: '...', birth:  "2019-10-03" }
    ]
    },
    getters: {
     bigMans(state) {
         return state.arr.filter((man)=>{
    	  let manY=new Date(man.birth).getFullYear()
    	  let nowY=new Date().getFullYear()
    	  return (nowY-manY)>=18
    	  })
    }
  },
  mutations: {
	//默认传第一个参数传state
	increment (state,obj) {
	  // 变更状态
	  state.count=obj.n
	}
  },
  actions: {
    //默认第一个参数传一个跟store一样的对象
    increment(context, obj) {
      setTimeout(() => {
        // 提交载荷
        context.commit('change', obj)
      },100)
    }
  },
  modules: { 
  // 注入模块 
  Module1 }
})
//Module1.js
export default{
//局部命名空间(让state的中变量与其他模块中的同名变量不冲突)
    namespaced: true,
    state: {
        msg: '我是Module1的数据',
      },
      getters: {
        getmsg(state) {
          return state.msg+"666666"
        }
      },
      mutations: {
        changecount(state, value) {
          //默认传第一个参数传state
          // 变更状态
          state.msg = value.n;
        },
        change(state,value){
          // 变更状态
          state.msg=value.n;
        }
      },
      actions: {
        //默认第一个参数传一个跟store一样的对象
        increment(context, obj) {
          setTimeout(() => {
            // 提交载荷
            context.commit('change', obj)
          },100)
        }
      }
}
1.在组件中获取state的数据
this.$store.state.msg
2.在组件中使用getters
this.$store.getters.bigMans
3.在组件中提交载荷修改全局状态(仓库中的数据)
3.1直接触发并传值(提交载荷)
this.$store.commit('increment',{n:100})
3.2以一个对象的形式传入
传入对象时相当于把整个对象作为了第二个参数传入mutations
this.$store.commit({
  type: 'increment',
  n:100
})
4.组件中actions提交的是mutations而不是直接变更状态
4.1直接分发
this.$store.dispatch('increment',{n:100})
4.2以对象形式分发
this.$store.dispatch({
  type: 'increment',
  n:100
})
5.组件中的使用注入的模块
this.$store.state.Module1.msg // -> Module1的状态
this.$store.commit("Module1/change",100)-> Module1 的mutations
this.$store.getters["Module1/getmsg"]-> Module1的getters
this.$store.dispatch("Module1/change",100)-> Module1 的actions

新的仓库技术Pinia使用

新的数据仓库的优点:

  • 更符合组合式API的语法习惯
  • 没有mutaions只有actions,它可以同步也可以异步修改仓库状态
  • 没有专门的模块化设计,但是它的设计本身就是模块的思想,因为每一个仓库都是单独创建的,然后main.js集成了Pinia
  • 服务器端渲染支持
  • 可以在devTool中可以追踪仓库数据
  • 上手快使用比vuex方便

环境集成

1.安装

npm install pinia

2.在main.js中安装pinia

//main.js文件
import { createPinia } from 'pinia'
let app=createApp(App)
app.use(createPinia())

3.在src目录中创建store目录,store目录中创建单独的js文件就是单独创建每一个仓库并导出

//  store/counter.js文件
import { defineStore } from 'pinia'

export const useStore = defineStore('storeId', {
  // 这里易错语法:如果箭头函数直接指向返回值不写return需要把返回的对象用小括号括起来
  //否则对象会认为还是代码块不会是返回一个对象
  state: () => {
    return {
      msg: 'hello'
    }
  }
})
这个name也称为id,是必要的,Pinia 使用它来将 store 连接到 devtools。

官网是export单独导出也可以写成export default默认导出。官方的解释为大家一起约定仓库用use打头的单词固定统一仓库的名字不易混乱,也可以自己命名符合规则的任意单词。

4.在组件API中引入

如果采用单独导出,则引入的变量名必须和导出时的变量名一致,且必须用对象的大括号将变量名包起来
import { useStore } from './stores/counter.js'
如果是默认导出,则引入的变量名可以自定义任意单词也不需要用大括号将变量名包起来
import usePerson from "../store/index.js"

组合API中使用

1.获取store中的数据

<template>
  <div class="box1"></div>
  <p>{{name}}</p>
  <p>{{title}}</p>
</template>

<script setup>
import { ref, reactive } from "vue"
import {usePerson} from './store/person.js'
let store=usePerson();
//获取store中的数据
let name=store.info.name;
let title=store.info.title;

</script>

<style scoped>

</style>

2.组件中修改数据(修改状态)

修改仓库状态方法1:

//修改仓库状态方法1:
<script setup>
	import useStore from "../store/index.js"
	let store=useStore()
    let fm=()=>{store.msg="666修改成功"}
    //store是一个响应性对象 因此可以修改并刷新
	let {msg}=store
	let fn=()=>{ msg="6666修改失败"} 
        //解构之后修改是不行的,因为解构后msg为非响应式变量 
    
    //解决方案:利用toRefs把响应式对象的属性解构为响应性的变量
    import {toRefs} from "vue"
    import useStore from "../store/index.js"
	let store=useStore()
	let {msg}=toRefs(store)
	let fn=()=>{msg.value="6666修改成功啦"} 
        //转化后是ref对象操作数据时一定不要忘了.value
</script>

修改仓库状态方法2:(重置仓库回到仓库初始值)

//修改仓库状态方法2:
store.$reset()//将仓库状态重置回到其初始值

image.png

2.gif

修改仓库状态方法3:调用 $patch 方法,方法使用部分“state”对象同时应用多个更改,$patch 方法也接受一个函数来批量修改集合内部分对象的情况

store.$patch((state)=>{
    state.msg="666修改成功";
    state.count= state.count+100,
})
store.$patch({
  msg:"666修改成功",
  count:store.count+100,
})
//修改时,如果是原来仓库中就有的数据就会更新状态
//原来仓库中没有的数据也不会添加取数据会得到undefined,取对象没有的成员就是undefined

image.png

问:$patch整体修改和一起单条修改的区别?

let fm=()=>{
store.msg="666修改成功";
store.count=store.count+100
}
store.$patch((state)=>{
    state.msg="666修改成功";
    state.count= state.count+100,
})

答:$patch批量修改时状态是整体刷新一次,而一起单条修改实际上还是一条一条的修改,每修改一次就刷新仓库状态一次。

3.组件中替换state

可以通过将其 $state 属性设置为新对象来替换 Store 的整个状态

store.$state = { counter: 666, name: 'Paimon' }

4.组件中订阅修改

//可以通过 store 的 $subscribe() 方法查看状态及其变化,通过patch修改状态时就会触发一次
store.$subscribe((mutation, state) => { 
  // 每当它发生变化时,将整个状态持久化到本地存储
  localStorage.setItem('test', JSON.stringify(state))
})

5.Getter

Getter完全等同于 Store 状态的计算属性。getters的返回值会被缓存起来,且只有当它依赖的数据发生了改变才会被重新计算。它们可以用 defineStore() 中的 getters 属性定义。

import { defineStore } from 'pinia'
export const usePerson = defineStore('person', {
    state:()=>{
        return {
            info:{name:'gloria',title:'启示录'}
        }
    },
    getters:{
    //箭头函数接收“状态”作为第一个参数
        change:(state)=>{
            return state.info.name+'天空没有极限';
        },
    //不是箭头函数内部可以用this代表state
        sketch(){
            console.log(this,666666);
            return this.info.title+'专辑大卖';
        }
    }
  })
组件中直接使用
<p>{{store.change}}</p>
<p>{{store.sketch}}</p>

image.png

6.Actions

Actions 相当于组件中的 methods。在pinia中没有提供mutaion,Actions可以异步也可以同步的统一修改状态

export const useStore = defineStore('main', {
  state: () => ({
    counter: 0,
  }),
  actions: {
    increment() {
      this.counter++
    },
    randomizeCounter() {
        //异步函数
        axios("/test").then(res=>{
             this.counter = res.data.length
        })  
    }
  },
})

//组件中的使用:
  setup() {
    const store = useStore()
    store.increment()
    store.randomizeCounter()
  }

7.模块化

一个文件一个小仓库,仓库之间可以相互访问数据,不同组件也可以访问任意小仓库数据。要在一个store中使用另一个 store ,可以直接在操作内部使用它,和组件使用仓库方式一样。

import { useAuthStore } from './auth-store'

export const useSettingsStore = defineStore('settings', {
  state: () => ({
    // ...
  }),
  actions: {
    async fetchUserPreferences(preferences) {
    //使用useAuthStore仓库的数据
      const auth = useAuthStore()
      if (auth.isAuthenticated) {
        this.preferences = await fetchPreferences()
      } else {
        throw new Error('User must be authenticated')
      }
    },
  },
})