首页 前端知识 pinia学习笔记

pinia学习笔记

2024-05-24 08:05:36 前端知识 前端哥 514 311 我要收藏

1、创建项目(vite+vue+ts)

pnpm create vite

2、安装pinia

yarn add pinia

3、使用

1、引入注册main.ts

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

2、初始化仓库Store(存储是使用定义的defineStore(),并且它需要一个唯一的名称,作为第一个参数传递)

新建store/main.ts

import { defineStore } from  'pinia'

export const mainStore = defineStore('main',{
    state:()=>{
        return {
            count:100,
            price:250
        }
    },
    getters:{
        doubleCount():number{
            return this.count*2
        },
        doublePrice():number{
            return this.price*2
        }
    },
    actions:{
        changeStoreData(){
            this.count+=1
            this.price+=1
        },
    }
})

3、页面使用

 

 

<script setup lang="ts">
import { ref } from 'vue'
import { storeToRefs } from 'pinia';
import { mainStore } from '../store/main';
const store = mainStore()
let { count,doubleCount,price,doublePrice } = storeToRefs(store)
defineProps<{ msg: string }>()
const changeData = ()=>{
  //1、直接修改count
  // count.value++
  // price.value++
  //2、$patch批量修改State的值
  // store.$patch({
  //   count:500,
  //   price:1000
  // })
  //3、$patch批量修改函数形式
  // store.$patch((state)=>{
  //   state.count++
  //   state.price++
  // })
  //4、通过原始对象修改整个实例
  //$state您可以通过将store的属性设置为新对象来替换store的整个状态
  //缺点就是必须修改整个对象的所有属性
  // store.$state={
  //   count:0,
  //   price:1
  // }
  //5、通过actions修改
   store.changeStoreData()
  
}

</script>
<template>
  <h1>{{ msg }}</h1>
  <h1>数量</h1>
  <h2>pinia-count数据{{count}}</h2>
  <h3>pinia- getters-2倍数量{{doubleCount}}</h3>
  <h1>价格</h1>
   <h2>pinia-price数据{{price}}</h2>
  <h3>pinia- getters-2倍价格{{doublePrice}}</h3>
  <el-button size='large' type="primary" @click="changeData">改变数据</el-button>
</template>

<style scoped>

</style>

4、actions(异步及调用其他actions方法) 

新建store/use.ts模块

import { defineStore } from  'pinia'
import { ElLoading } from 'element-plus'
type Res = {
    nickname:string,
    age:number
}
const Login = ()=>{
    return new Promise<Res>((resolve,reject)=>{
        const loading = ElLoading.service({
            lock: true,
            text: 'Loading',
            background: 'rgba(0, 0, 0, 0.7)',
          })
        setTimeout(() => {
            resolve({
                nickname:'张三',
                age:22
            })
            loading.close()
        }, 5000);
    })
}
export const userStore = defineStore('user',{
    state:()=>{
        return {
           nickname:'',
           age:0
        }
    },
    getters:{
        
    },
    actions:{
        async getUserInfo(){
            const res = await Login();
            this.nickname=res.nickname;
            //this.age=res.age;
            //调用其他actions
            this.setAge(res.age)
        },
        setAge(age:number){
            this.age=age
        }
    }
})
<template>
 <h1>用户组件</h1>
 <h2>昵称{{nickname}}--年龄{{age}}</h2>
 <el-button type="primary" size="large" @click="getUserInfo">获取用户信息</el-button>
 
</template>
<script setup lang='ts'>
import { ref,reactive} from  'vue'
import { userStore } from '../store/user';
import { storeToRefs } from 'pinia';
const user = userStore()
let {nickname,age} = storeToRefs(user)
const getUserInfo = ()=>{
    user.getUserInfo()
}
</script>
<style lang='scss' scoped>
</style>

5、getters (主要作用类似于computed 数据修饰并且有缓存)

getters 可以互相调用 普通函数形式可以使用this 使用箭头函数不能使用this this指向已经改变指向undefined 修改值请用state

  getters:{
        doubleCount():number{
            //return this.count*2
            return this.doublePrice*2
        },
        doublePrice:(state)=>state.price*2
    },

6、 常用api

1、$reset(重置store到他的初始状态)

  <el-button size='large' type="primary" @click="resetData">重置数据</el-button>
const resetData =()=>{
  main.$reset()
}

 2、订阅state改变(类似于Vuex 的abscribe  只要有state 的变化就会走这个函数)

main.$subscribe((args,state)=>{
  console.log(args,state,'数据改变')
})

第二个参数

如果你的组件卸载之后还想继续调用请设置第二个参数

main.$subscribe((args,state)=>{
  console.log(args,state,'数据改变')
},{
  detached:true
})

3、订阅Actions的调用(只要有actions被调用就会走这个函数)

main.$onAction((args)=>{
   console.log(args,'action调用'); 
})

7、 pinia插件 --数据持久化

1、安装

cnpm i pinia-plugin-persist --save

2、新建store/index.ts

import { createPinia } from 'pinia'
//pinia 持久化插件
import piniaPluginPersist from 'pinia-plugin-persist'
const store = createPinia()
store.use(piniaPluginPersist)
export default store

3、修改main.ts

import { createApp } from 'vue'
import App from './App.vue'

import 'element-plus/dist/index.css'
import store from './store/index';
import piniaPluginPersist from 'pinia-plugin-persist';
store.use(piniaPluginPersist)
const app=createApp(App)
app.use(store).mount('#app')

4、保存nickname,修改store/user.ts

import { defineStore } from  'pinia'
import { ElLoading } from 'element-plus'
type Res = {
    nickname:string,
    age:number
}
const Login = ()=>{
    return new Promise<Res>((resolve,reject)=>{
        const loading = ElLoading.service({
            lock: true,
            text: 'Loading',
            background: 'rgba(0, 0, 0, 0.7)',
          })
        setTimeout(() => {
            resolve({
                nickname:'张三',
                age:22
            })
            loading.close()
        }, 5000);
    })
}
export const userStore = defineStore('user',{
    state:()=>{
        return {
           nickname:'',
           age:0
        }
    },
    getters:{
        
    },
    actions:{
        async getUserInfo(){
            const res = await Login();
            this.nickname=res.nickname;
            //this.age=res.age;
            //调用其他actions
            this.setAge(res.age)
        },
        setAge(age:number){
            this.age=age
        }
    },
    persist:{
        enabled:true,
        strategies:[
            {
                storage:localStorage,paths:['nickname']
            }
        ]
    }
})

 4、购物车案例

 

1、新建src/api/shop.ts

/**
 * src/api/shop.ts
 * Mocking client-server processing
 */
 export interface IProduct {
    id: number
    title: string
    price: number
    inventory: number // 库存
  }
  
  const _products: IProduct[] = [
    {id: 1, title: 'iPad 4 Mini', price: 500.01, inventory: 2},
    {id: 2, title: 'H&M T-Shirt White', price: 10.99, inventory: 10},
    {id: 3, title: 'Charli XCX -Sucker CD', price: 19.99, inventory: 5}
  ]
  
  export const getProducts = async () => {
    await wait(1000)
    return _products
  }
  
  export const buyProducts = async () => {
    await wait(1000)
    return Math.random() > 0.5
  }
  
  /**
   * 封装了Promise版本的定时器
   * @param delay 延迟时间
   * @returns Promise
   */
  async function wait(delay:number) {
    return new Promise(resolve => setTimeout(resolve, delay))
  }

2、新建components/ProductList.vue

<template>
  <ul>
    <li class="item" v-for="item in productsStore.all">
      <div class="title">商品名称:{{item.title}}</div>--
      <div class="price">商品价格:{{item.price}}</div>
      <div class="price">商品库存:{{item.inventory}}</div>
      <el-button type="primary" :disabled="!item.inventory" @click="cartStore.addProductToCart(item)">添加到购物车</el-button>
    </li>
  </ul>
</template>

<script lang="ts" setup>
import { useCartStore } from '../store/cart';
import { useProdunctsStore } from '../store/products'

const productsStore = useProdunctsStore()
const cartStore = useCartStore()

productsStore.loadAllProducts() // 加载所有数据
</script>
<style lang="scss" scoped>
.item{
    display: flex;
    align-items: center;
    justify-content: center;
    .title{
        margin: 0 15px;
    }
    .price{
        margin: 0 15px;
    }
}
</style>

3、新建components/Cart.vue

<template>
  <div class="cart">
    <h2>我的购物车</h2>
   
    <ul v-if="cartProducts&&cartProducts.length>0">
      <li v-for="item in cartProducts">商品名称:=:{{ item.title }} - 商品价格:{{ item.price }} × 商品数量:{{ item.num }}</li>
    </ul>
     <p v-else>
      <i>请添加一些商品到购物车</i>
    </p>
    <p>商品总价: {{ totalPrice }}</p>
    
  </div>
</template>

<script lang="ts" setup>
import { useCartStore } from '../store/cart'
import { storeToRefs } from 'pinia';
const cartStore = useCartStore()
let { cartProducts,totalPrice } = storeToRefs(cartStore)
</script>

4、组件在App.vue中引用

<script setup lang="ts">
import ProductList from './components/ProductList.vue';
import Cart from './components/Cart.vue';
</script>

<template>
  <ProductList />
  <hr>
  <Cart />
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
ul li{
  list-style: none;
  line-height: 50px;
}
</style>

5、新建store/products.ts

import { defineStore } from "pinia"
import { getProducts, IProduct } from "../api/shop"

export const useProdunctsStore = defineStore('products', {
  state: () => {
    return {
      all: [] as IProduct[] // 所有商品列表(学习类型断言的使用)
    }
  },

  getters: {}, 

  actions: {
    //actions既支持异步操作
    async loadAllProducts() {
      const ret = await getProducts()
      this.all = ret
    },
    // 也支持同步操作
    decrementProduct(product: IProduct) {
      const ret = this.all.find(item => item.id === product.id)
      if (ret) {
        ret.inventory--
      }
    }
  }
})

6、新建store/cart.ts

import { defineStore } from "pinia";
import { buyProducts, IProduct } from "../api/shop";
import { useProdunctsStore } from "./products";

/**
 * {id, title, price, quantity}
 */
type CartProduct = { // 合并
  num: number
} & Omit<IProduct, 'inventory'> // Omit是过滤

export const useCartStore = defineStore('cart', {
  state: () => {
    return {
      cartProducts: [] as CartProduct[],
      checkoutStatus: null as null | string
    }
  },

  getters: {
    // 总价
    totalPrice(state) {
      return state.cartProducts.reduce((total, item) => {
        return total + item.price * item.num
      }, 0)
    }
  },

  actions: {
    /**
     * 往购物车添加商品
     * 这是一个相对复杂的逻辑,与容器中的数据强相关,所以肯定要定义在actions里面!
     * @param product 需要添加的商品
     */
    addProductToCart(product: IProduct) {
      // 先看商品有没有库存
      if (product.inventory <= 0) {
        return
      }
      // 检查购物车中是否已有该商品
      
      const cartItem = this.cartProducts.find(item => item.id === product.id)
      // 如果有则让商品数量+1
      if (cartItem) {
        cartItem.num++
      } else {
        // 如果没有则添加到购物车列表中
        this.cartProducts.push({
          id: product.id,
          title: product.title,
          price: product.price,
          num: 1
        })
      }

      // 跟新商品库存(应该由商品容器暴露API)
      const productsStore = useProdunctsStore()
      productsStore.decrementProduct(product)
      // 跨容器通信!!!!!竟然如此简单!!!
    },
    /**
     * 结算
     */
    async checkOut() {
      const ret = await buyProducts() 
      this.checkoutStatus = ret ? '成功' : '失败'
      if (ret) {
        this.cartProducts = [] // 清空购物车
      }
    }
  }
})

转载请注明出处或者链接地址:https://www.qianduange.cn//article/9318.html
标签
评论
发布的文章

用JS生成本周日期代码

2024-04-18 17:04:15

js 递归函数

2024-05-31 10:05:46

jQuery是什么?如何使用?

2024-03-12 01:03:24

js延迟加载的六种方式

2024-05-30 10:05:51

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!