下一代vue状态管理工具 pinia,快来看看这个可爱的菠萝吧

简介: 下一代vue状态管理工具 pinia,快来看看这个可爱的菠萝吧

尝试过vuex @4.x的同学都知道,对ts的支持不是很好,写起来不是很方便。不了解的同学请看这篇文章


pinia官网


pinia简单介绍


Pinia是新一代的状态管理器,由 Vue.js团队中成员Phan An所开发的,因此也被认为是下一代的 Vuex,即 Vuex5.x。并被加入官方账户下


Pinia 有如下特点:


  • 支持options api和composition api。


  • 完整的 typescript 的支持。


  • 去除 mutations,只有 state,getters,actions。


  • actions 支持同步和异步。


  • 没有模块嵌套,扁平式的模块组织方式,极大的简化了代码书写过程。


  • 自动代码分割。


  • 支持vue devtools。


安装


npm install pinia
    // yarn add pinia


在vue中注册该库。


import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')


核心概念


pinia从使用角度和vuex几乎一样。通过defineStore来创建一个容器对象。


state


就是以前vuex中的state。类似于组件的data,保存该模块下的数据。


import { defineStore } from 'pinia'
    const useUserStore = defineStore('user', {
      state: () => {
        return {
          name "zh",
          age: 20,
          friends: []
        }
      }
    })


如何获取和修改该state中的数据呢?下面就来介绍一下。


获取state属性


直接导入该模块


<ul>
      <li>姓名: {{ userStore.name }}</li>
      <li>年龄: {{ userStore.age }}</li>
      <li>朋友: {{ userStore.friends }}</li>
    </ul>
   import useUserStore from '../store/userStore'
    const userStore = useUserStore()


看到上面取值这么麻烦,那我来解构看看吧。


<ul>
      <li>姓名: {{ name }}</li>
      <li>年龄: {{ age }}</li>
      <li>朋友: {{ friends }}</li>
    </ul>
    // 不能直接解构,结构后数据将不再是响应式。
    const { name, age, friends } = userStore


貌似上面也能展示,没啥问题,那么我们来尝试改变一下数据,看看界面的变化。


<h1>响应式state数据</h1>
    <ul>
      <li>姓名: {{ userStore.name }}</li>
      <li>年龄: {{ userStore.age }}</li>
      <li>朋友: {{ userStore.friends }}</li>
    </ul>
    <h1>非响应式state数据,通过解构</h1>
    <ul>
      <li>姓名: {{ name }}</li>
      <li>年龄: {{ age }}</li>
      <li>朋友: {{ friends }}</li>
    </ul>
    <button @click="handleUserState">修改数据:handleUserState</button>
    const handleUserState = () => {
      userStore.$patch((state) => {
        state.name = 'llm'
        state.age = 30
        state.friends.push('llm', 'zh')
      })
    }


网络异常,图片无法展示
|


从上面的gif可以看出,基本数据类型是不会做到响应式的,但是引用数据类型可以。(这一问题,通过不同方式修改state,会有不同的效果。因为state返回数据的引用问题。直接修改后,返回的仍旧是原来的对象, 通过$patch和action派发返回的是一个对于原来对象的拷贝。)尽管如此,如果我们想要解构state,那么我们需要使用pinia提供的storeToRefs来为我们服务。


<h1>响应式state数据,通过解构</h1>
    <ul>
      <li>姓名: {{ reactiveName }}</li>
      <li>年龄: {{ reactiveAge }}</li>
      <li>朋友: {{ reactiveFriends }}</li>
    </ul>
    // 将其变为响应式
    const {
      name: reactiveName,
      age: reactiveAge,
      friends: reactiveFriends,
    } = storeToRefs(userStore)


网络异常,图片无法展示
|


修改state属性


  • 直接修改


userStore.name = 'llm'


  • 通过$patch进行批量修改。


  • 可以直接传入一个对象。导入模块获取state值进行修改。


  • 也可以传入一个函数。该函数接收state作为参数。


// 这种方式只有基本数据类型不能响应式
 userStore.$patch((state) => {
    state.name = 'llm'
    state.age = 30
    state.friends.push('llm', 'zh')
  })
  // 这种方式任何数据都不能作为响应式
    userStore.$patch({
        name: 'llm',
        age: 30,
        friends: [...userStore.friends, 'zh', 'llm'],
    })


  • 通过action派发。


// useUserStore.js
    import { defineStore } from 'pinia'
    const useUserStore = defineStore('user', {
      state: () => {
        return {
          name: 'zh',
          age: 20,
          friends: []
        }
      },
      actions: {
        changeState() {
          this.name = 'llm'
          this.age = 30
          this.friends.push('zj', 'llm')
        }
      }
    })


// vue
  userStore.changeState()


这些方式,我依旧是推荐使用action派发,从而做到统一管理。解构state对象时,一定要使用storeToRefsapi。


action


上面已经提到了,就是对state做更新的。和vuex中action一样。但是用法有所改变。 如果管理数据,基本上都是用过action派发函数,利于管理。 下面来看看吧。


基本使用


获取state中的数据直接通过this即可。


// useUserStore.js
    import { defineStore } from 'pinia'
    const useUserStore = defineStore('user', {
      state: () => {
        return {
          name: 'zh',
          age: 20,
          friends: []
        }
      },
      actions: {
        changeState() {
          this.name = 'llm'
          this.age = 30
          this.friends.push('zj', 'llm')
        }
      }
    })


组件中使用直接通过该模块调用action函数即可。


userStore.changeState()


处理异步数据


// 更新异步数据
    getSong() {
      axios({
        url: 'http://123.207.32.32:9001/song/detail?ids=1441758494'
      }).then((res) => {
        console.log('res', res.data.songs[0])
      })
    }


和其他store模块中的action函数使用


其实就是导入其他模块的store,然后直接调用对应的action即可。


import { useAuthStore } from './auth-store'
    export const useSettingsStore = defineStore('settings', {
      state: () => ({
        preferences: null,
        // ...
      }),
      actions: {
        async fetchUserPreferences() {
          const auth = useAuthStore()
          if (auth.isAuthenticated) {
            this.preferences = await fetchPreferences()
          } else {
            throw new Error('User must be authenticated')
          }
        },
      },
    })


getter


用法同vuex中的getter。类似于计算属性,具有缓存功能。利于性能优化。


基本使用


  • getter函数函数中可以使用其他getter。 直接通过this获取即可。


import { defineStore } from 'pinia'
const useUserStore = defineStore('user', {
  // arrow function recommended for full type inference
  state: () => {
    return {
      name: 'zh',
      age: 20,
      friends: [],
      song: {},
      discount: 0.6,
      books: [
        { name: '深入Vuejs', price: 200, count: 3 },
        { name: '深入Webpack', price: 240, count: 5 },
        { name: '深入React', price: 130, count: 1 },
        { name: '深入Node', price: 220, count: 2 }
      ]
    }
  },
  getters: {
    currentDiscount(state) {
      return state.discount * 0.9
    },
    totalPrice(state) {
      let _totalPrice = 0
      for (const book of state.books) {
        _totalPrice += book.count * book.price
      }
      return _totalPrice * this.currentDiscount
    }
  }
})


  • 如果想向getter函数传递参数,我们可以让getter返回一个函数,在外部调用,并传入参数即可。


getters: {
    currentDiscount(state) {
      return state.discount * 0.9
    },
    totalPrice(state) {
      let _totalPrice = 0
      for (const book of state.books) {
        _totalPrice += book.count * book.price
      }
      return _totalPrice * this.currentDiscount
    },
    totalPriceCountGreaterN(state) {
      return function (n) {
        let totalPrice = 0
        for (const book of state.books) {
          if (book.count > n) {
            totalPrice += book.count * book.price
          }
        }
        return totalPrice * this.currentDiscount
      }
    }
  }


// vue
<h1>getters展示</h1>
<div>{{ userStore.totalPrice }}</div>
<div>{{ userStore.totalPriceCountGreaterN(2) }}</div>


网络异常,图片无法展示
|


使用其他store模块的getter


import { useOtherStore } from './other-store'
    export const useStore = defineStore('main', {
      state: () => ({
        // ...
      }),
      getters: {
        otherGetter(state) {
          const otherStore = useOtherStore()
          return state.localData + otherStore.data
        },
      },
    })


注意事项


我们定义getter函数时,可以接受state参数。


  • 如果未接受,并且使用了ts,那么我们需要指定返回值类型。


  • 如果接受了,可以不指定返回值类型。


相关文章
|
2天前
|
JavaScript
vue的生命周期
vue的生命周期
11 3
|
1天前
|
缓存 JavaScript
什么是vue的计算属性?为什么使用?怎么使用?举例说明
什么是vue的计算属性?为什么使用?怎么使用?举例说明
|
2天前
|
缓存 JavaScript 前端开发
Vue基础
Vue基础
13 2
|
2天前
|
JavaScript 前端开发 API
什么是vue
什么是vue
11 4
5分钟上手Vue+ts+vite+pinia
5分钟上手Vue+ts+vite+pinia
667 0
|
2天前
|
JavaScript 前端开发
vue的生命周期
vue的生命周期
12 2
|
2天前
|
JavaScript API 开发者
Vue中双向数据绑定是如何实现的?底层原理简介
Vue中双向数据绑定是如何实现的?底层原理简介
9 4
|
2天前
|
JavaScript 安全 前端开发
vue怎么处理跨域,原理?
vue怎么处理跨域,原理?
14 2
|
2天前
|
资源调度 JavaScript
vue的跳转传参
vue的跳转传参
9 0
|
2天前
|
缓存 JavaScript 开发者
什么是vue的计算属性
什么是vue的计算属性
7 0