[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1AStktRL-1646506914963)(pinia笔记.assets/image-20220306012334725.png)]

Pinia官网: https://pinia.vuejs.org/

1. 创建项目

1.1 脚手架搭建

npm init vite@latest

后面操作见图示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QOFMgNIk-1646506914964)(pinia笔记.assets/image-20220305232517213.png)]

1.2 安装pinia

该笔记, pinia基于版本2.0.11

npm i pinia@2.0.11 或者 yarn add pinia@2.0.11



2. Pinia - Store

2.1 创建Store

在入口文件main.ts中:

import { createApp } from "vue";
import App from "./App.vue";
// 引入pinia
import { createPinia } from "pinia";

// 创建 Pinia 实例
const pinia = createPinia();

const app = createApp(App);

// 挂载到 Vue 根实例
app.use(pinia);

app.mount("#app");

2.2 Store详讲

在src下, 新建文件夹store, 再新建index.ts

在index.ts中:

/**
 * 一般在容器中做这4件事
 *    1. 定义容器并导出
 *    2. 使用容器中的state
 *    3. 修改容器中的state
 *    4. 使用容器中的action
 */
import { defineStore } from "pinia";

/**
 * 1. 定义容器并导出
 * 参数一: 容器ID, 唯一, 将来 Pinia 会把所有的容器挂载到根容器
 * 参数二: 选项对象
 * 返回值: 函数, 调用的时候要空参调用, 返回容器实例
 */
export const mainStore = defineStore('main', {
    /**
     * 类似组件的 data, 用于存储全局的的状态
     * 注意:
     *    1.必须是函数, 为了在服务端渲染的时候避免交叉请求导致的数据交叉污染
     *    2.必须是箭头函数, 为了更好的 TS 类型推导
     */
    state: () => {
        return {
            count: 100,
          	foo: 'bar',
          	age: 18
        }
    },
    /**
     * 类似组件的 computed, 用来封装计算属性, 具有缓存特性
     */
    getters: {

    },
    /**
     * 类似组件的 methods, 封装业务逻辑, 修改state
     * 注意: 里面的函数不能定义成箭头函数(函数体中会用到this)
     */
    actions: {

    }
})

2.3 使用store中的状态

2.3.1 简单访问store状态

components/HelloWorld.vue中的代码清空, 加入以下代码

<template>
  <div v-text="mainStoreI.count"></div>
</template>

<script lang="ts" setup>
  import {mainStore} from '../store'

  const mainStoreI = mainStore()
  console.log(mainStoreI.count);
</script>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6WYMImef-1646506914964)(pinia笔记.assets/image-20220306001245122.png)]


2.3.2 访问及简单修改store(标准操作)

但是上面的访问未免过于麻烦, 使用 count、foo 前面还要加 mainStoreI , 如果直接采用解构的方式, 会导致数据不是响应式的, 具体解决方式如下:

import { storeToRefs } from ‘pinia’;

<template>
  <div v-text="mainStoreI.count"></div>
  <hr>
  <p>count: {{ count }}</p>
  <p>foo: {{ foo }}</p>
  <p>age: {{ age }}</p>
  <button @click="handleClick">修改count数据</button>
</template>

<script lang="ts" setup>
  import {mainStore} from '../store'
  import { storeToRefs } from 'pinia';

  const mainStoreI = mainStore()
  const {count, foo, age} = storeToRefs(mainStoreI)
  
  /** 修改store中的数据 */
  const handleClick = () => {
    // 修改单个数据,方式一
    // count.value++

    // 修改单个数据,方式二
    mainStoreI.count++
  }

</script>

2.4 store - $patch 批量简单修改

  • 多个数据修改,建议使用 $patch 批量更新,

  • 不单纯是写法优化,还有性能的优化

  /** 修改store中的数据 */
  const handleClick = () => {
    // 哪些数据项需要修改就写数据项
    mainStoreI.$patch({
      count: ++mainStoreI.count,
      foo: 'hello',
    })
  }

2.5 $patch怎么更新数组?

方式一: 利用ES6展开运算符

const handleClick = () => {
  mainStoreI.$patch({
    // 更新数组
    arr: [...mainStoreI.arr, 4]
  })
}

方式二: $patch可以接收一个函数(推荐)

const handleClick = () => {
  // 给$patch传入一个函数,函数体中修改数据项
  // 形参state即为容器
  mainStoreI.$patch((state) = {
    state.count++;
    state.foo = 'hello';
    state.arr.push(4)
  })
}

2.6 总结: 修改状态数据的四种方式



3. Pinia - actions

  • 修改 Store 状态时, 如果逻辑较多, 可以借助 actions
  • actions 中也可以使用 $patch, 见下面代码

注意:

  • actions 中的函数, 不能定义成箭头函数
  • 因为箭头函数中没有this, 在运行时, 会向外部的作用域找

src\store\index.ts

/**
 * 类似组件的 methods, 封装业务逻辑, 修改state
 */
actions: {
    changeState(num: number): void {
        // this访问当前容器的实例
        this.count += num
        this.foo='你好啊'
        this.arr.push(555)
      
        // 这里也可以使用$patch
        // this.$patch({})
        // this.$patch(state => {})
    }
}

业务代码<script>中:

/** 修改store中的数据 */
const handleClick = () => {    
	// 当逻辑较多时,可以封装到actions中处理  
	mainStoreI.changeState(10)
}


4. Pinia - getters

  • 具有缓存特性

  • getters的定义有两种

    1. 接受一个形参, 代表容器状态实例(数据)

      count10 (state) {  state.count + 10}
      
    2. 不使用形参, 函数体中使用this, 必须手动指定返回值类型

      原因: 无法推导this的类型, 会导致编译报错, 需手动指定返回值类型

      count10 (): number {  
      	this.count + 10
      }
      
    3. (不推荐) 既使用state, 也使用this, 这个时候可以不手动指定返回值类型

      这种写法怪怪的, 写了形参不用

      count10 (state){  
      	this.count + 10
      }
      

定义:

export const mainStore = defineStore('main', {    
	state: () => {        
		return {            
			count: 100,            
			foo: 'bar',            
			age: 18,            
			arr:[99, 55]        
	}    
},    
 /**     
  * 类似组件的 computed, 用来封装计算属性, 具有缓存特性     
  */    	
  getters: {       
  	// 每次使用的 count10 都是在 count 的基础上加10        
  	count10 (state) {            
  		console.log('count10 调用了');            
  		return state.count+10        
  	}    
  }
})

如图, count10 访问了3次, 但是由于缓存, 实际 getters 只运行了一次:



5. Store相互调用

如果存在相互调用的情况, 和在业务组件中使用一致

  • 在A中导入B
  • 实例化
  • 调用B中的方法等…
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐