重塑状态管理的艺术:Vue3中Pinia的魔法之旅内包含简易购物车案例

news2025/2/24 22:08:36

前言

Vue.js的世界里,每一次更新都是一次进化,Vue3携带着更强大的性能与灵活性翩然而至。而在这场技术盛宴中,Pinia以一种优雅而革命性的方式,重新定义了状态管理的体验。如果说VuexVue2时代的王者,那么Pinia无疑是Vue3新时代的璀璨明星,它不仅简化了状态管理的复杂度,还极大地提升了开发者的幸福感。接下来,让我们携手踏上这场探索Pinia魅力的魔法之旅。
如果想了解更全面的vue3,Vue3+TS+Vite+Pinia最全总结>>>

Pinia:Vue3的灵魂伴侣

Vue3引入了Composition API,为组件的状态和逻辑提供了更清晰、更模块化的组织方式。Pinia,作为Vue3的官方状态管理库,完美地融入了这一新体系,它不仅仅是Vuex的简单升级,而是在设计理念上的一次革新。

  • 直观简洁:告别繁复的actionsmutationsPinia提倡直接操作状态,让你的代码更加直观易懂。
  • TypeScript友好:原生支持TypeScript,为你的状态管理提供强类型检查,减少错误,提升开发效率。
  • 插件生态:尽管年轻,Pinia已经拥有丰富的插件生态,无论是持久化状态、调试,还是更复杂的业务需求,都能找到合适的解决方案。

pinia为什么取代了vuex官方的说法:

  1. 取消 mutations。因为在大部分开发者眼中,mutations 只支持 同步修改状态数据,而 actions 虽然支持 异步,却依然要在内部调用 mutations 去修改状态,无疑是非常繁琐和多余的。
  2. 所有的代码都是 TypeScript 编写的,并且所有接口都尽可能的利用了 TypeScript 的 类型推断,而不像 Vuex 一样需要自定义 TS 的包装器来实现对 TypeScript 的支持
  3. 不像 Vuex 一样需要在实例Vue原型上注入状态依赖,而是通过直接引入状态模块、调用 getter/actions 函数来完成状态的更新获取;并且因为自身对 TypeScript 的良好支持和类型推断,开发者可以享受很优秀的代码提示
  4. 不需要预先注册状态数据,默认情况下都是根据代码逻辑自动处理的;并且可以在使用中随时注册新的状态
  5. 没有 Vuexmodules 嵌套结构,所有状态都是扁平化管理的。也可以理解为 pinia 注册的状态都类似 vuexmodule,只是 pinia 不需要统一的入口来注册所有状态模块
  6. 虽然是扁平化的结构,但是依然支持 每个状态之间的互相引用和嵌套
  7. 不需要 namespace 命名空间,得利于扁平化结构,每个状态在注册时即使没有声明状态模块名称,pinia 也会默认对它进行处理
    总结一下就是:Pinia 在实现 Vuex 全局状态共享的功能情况下,改善了状态存储结构,优化了使用的方式,简化 API 设计与规范,并且基于 TypeScript 的类型推断,为开发者提供了良好的 TypeScript支持与代码提示。

pinia学习实践

一、安装Pinia

cd进入项目根目录,执行安装pinia

npm install pinia@2.1.4

二、全局中蹙额pinia

创建pinia实例并传递给应用,注册 pinia 实例到全局,在main.ts中创建pinia实例并传递给应用。

import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'`在这里插入代码片`
// 导入pinia实例方法
import { createPinia } from 'pinia'
// 创建pinia实例并挂载到应用上
const app = createApp(App).use(createPinia())
app.mount('#app')

三、 创建一个 Store,定义和修改Store状state

  • Store(如:Pinia)是一个保存状态和业务逻辑的实体,不与组件树绑定。它承载着全局状态(整个应用中访问的数据),每个引入它的组件都可以对其进行读写操作。
  • Store 中有三个类似组件的概念:state(data)getter(computed)action(methods)
  • 需要注意的是,并不是所有的数据都要放到 Store 中处理,像购物车、用户登录状态等这些需要快速处理跨组件之间的状态,使用 Store 更加便捷;而像控制元素是否可见这种组件内状态,则不需要Store 了。

通过 defineStore() 方法定义 Store(全局状态数据),其中参数和返回值说明:

  • 参数1:应用中 Store 的唯一标识字符串Id,不可重复。
  • 参数2:接受Option对象或 Setup 函数形式。
  • 返回值: defineStore() 的返回值进行任意命名,但最好使用 store 的名字,同时以 use 开头且以 Store 结 尾。
  • use开头是一个符合组合式函数风格的约定。(比如 useUserStore , useCartStore ,useProductStore )。

四、创建一个简单的购物车数据状态管理文件

/src/stores/cart.ts

import { defineStore } from "pinia";
interface Goods{
  id:number;
  name:string;
  num:number;
}
// 参数二,采用option对象形式
export const useCartStore = defineStore('cart',{
  // state用于定义一个返回初始状态的函数
  state:()=>{
    return {
      name:"wuyong",
      age:18,
      goodsList:[{id:1,name:'手机',num:1}] as Goods[]
    }
  }
})

五、结构state状态组件中使用

组件中获取和修改状态数据,解构出来的state状态,不是响应式的,使用 storeToRefs 方法将状态转成ref后,解构出来的状态才是响应式的,通过store实例操作状态是 响应式 的,通过 $patch 可同时修改多个状态属性。
如下表示:

<script setup lang="ts">
import { useCartStore } from './stores/cart';
import { storeToRefs } from 'pinia';
const cartStore = useCartStore();
// 通过store实例获取state状态
console.log("获取state状态", cartStore.$state, cartStore.name);
// 直接结构出来state状态,不是响应式的
//let { age } = cartStore;
// 使用storeToRefs方法将状态转成ref后,结构出来的状态是响应式的
const { age } = storeToRefs(cartStore);
console.log("age", age.value)
//修改state状态值
function handleAddAge() {
  // age ++; // 直接解构是`非响应式`的
  // age.value++; // 转成ref是`响应式`的
  // cartStore.age++;

  // 4. 通过 $patch 方法接受一个对象,可同时修改多个状态属性
  // cartStore.$patch({
  //   name: 'wuqi',
  //   age: cartStore.age+1,
  // });

  // $patch 方法接受一个带state参数的回调函数,state来操作状态
  cartStore.$patch((state) => {
    state.name = 'wuqi';
    state.age = 24;
    state.goodsList.push({ id: state.goodsList.length + 1, name: '电脑'+state.goodsList.length + 1, num: 2 });
  })
}
</script>
<template>
  <div>
    <p>用户名:{{ cartStore.name }}</p>
    <p>
      年龄:{{ age }}
      <button @click="handleAddAge">+1</button>
    </p>
    <ul>
      <li v-for="(item, index) in cartStore.goodsList" :key="index">
        商品ID: {{ item.id }},名称:{{ item.name }},数量:{{ item.num }}
      </li>
    </ul>
    <button @click="handleAddAge">年龄+1</button>
  </div>
</template>
<style scoped></style>

效果:
重塑状态管理的艺术:Vue3中Pinia的魔法之旅

六、重置状态:通过调用 store$reset() 方法,将 state 状态重置为初始值

<script setup lang="ts">
import { useCartStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';
const cartStore = useCartStore();
function handleResetState() {
	// 将 state 状态重置为初始值
	cartStore.$reset();
}
</script>
<template>
<div>
	<button @click="handleResetState">重置状态</button>
</div>
</template>
<style scoped>
</style>

效果:
重塑状态管理的艺术:Vue3中Pinia的魔法之旅

Getters派生属性

Getter 介绍

  • 有时候我们需要从 Store 中的 state 中派生出一些状态。例如:基于上面代码,增加一个userType 属性,当 age 值小于18,则 userType 值为 未成年人 ; 大于等 于18 , 则 userType 值为 成年人 。这时我们就需要用到 Getter 为我们解决。
  • Getter 相当于组件中的计算属性(computed)。可以通过 defineStore() 中的 getters 属性来定义它们。 推荐使用箭头函数,并且它将接收 state 作为第一个参数。
  • Getter中通过返回一个函数,该函数可以接受调用方传递任意参数。

总结:Getter 完全等同于 storestate 的计算值。可以通过 defineStore() 中的 getters 属性来定义它们。推荐使用箭头函数,并且它将接收 state 作为第一个参数

Getter 实操作

一、定义状态(state)、派生属性(getters),创建一个根据商品ID查找商品的getGoodsById,然后通过查找函数find从商品列表中匹配指定ID的商品并返回。

import { defineStore } from "pinia";
interface Goods{
  id:number;
  name:string;
  num:number;
}
// 参数二,采用option对象形式
export const useCartStore = defineStore('cart',{
  // state用于定义一个返回初始状态的函数
  state:()=>{
    return {
      name:"wuyong",
      age:18,
      goodsList:[{id:1,name:'手机',num:1}] as Goods[]
    }
  },
  //定义派生属性(等同于计算属性,根据state状态值得到新的一个状态值)
  getters:{
    //接受state作为第一个参数,使用建通函数声明:箭头函数方法不能使用this
    userType:(state)=>{
      return state.age<18?'未成年人':'成年人'
    },
    //使用普通函数声明,方法体可以使用this访问整个store实例
    getGoodsById(state){
      //通过this获取上面getter
      console.log("getter普通函数",this.userType);
      return (id:number)=>this.goodsList.find(item=>item.id===id)
    }
  }
})

二、在组件中获取Getter

<p>Getter 派生属性:{{ cartStore.userType }}</p>
<p>向 Getter 派生属性传递参数:{{ cartStore.getGoodsById(1) }}</p>

重塑状态管理的艺术:Vue3中Pinia的魔法之旅

actions属性

一、actions介绍
        actions属性扮演着关键角色,它们用于封装业务逻辑,执行异步操作,并且能够修改Store中的状态。与直接在state中修改不同,actions提供了更好的组织结构和可维护性,同时也便于跟踪状态变更的来源。下面是对actions属性的详细说明:

  • Action 是一个在Pinia Store中定义的方法,用于处理包含副作用或异步逻辑的操作。
  • 它们是Side Effect(副作用)的安全场所,比如调用API、修改状态(通过this.$patch或直接修改状态对象)、延时操作等。
  • Action 函数可以返回Promise,因此支持异步操作,这在处理网络请求时特别有用。
  • Vue的组件类似,Actions内部可以使用Composition API的函数,如ref、computed等。

二、如何定义Actions

export const useCartStore = defineStore('cart', {
  // ... state 和 getters

  actions: {
    // 添加商品到购物车的Action
    async addToCart(product: Goods) {
      // 模拟异步操作,如调用后端API
      await new Promise(resolve => setTimeout(resolve, 1000));

      // 使用this访问状态并修改
      this.goodsList.push({ ...product, num: 1 });
    },

    // 清空购物车的Action
    clearCart() {
      this.goodsList = []; // 直接修改状态数组
    }
  }
});

三、使用Actions
在组件内,通过从Store中解构出Action方法,可以直接在组件的方法或生命周期钩子中调用它们。

<script setup>
import { useCartStore } from './cart.store';

const cartStore = useCartStore();

async function onAddToCart(product) {
  await cartStore.addToCart(product);
}

function clearCart() {
  cartStore.clearCart();
}
</script>
Action购物车使用实例

将商品加入购物车 goodsList 状态中。

import { defineStore } from "pinia";
interface Goods{
  id:number;
  name:string;
  num:number;
}
// 参数二,采用option对象形式
export const useCartStore = defineStore('cart',{
  // state用于定义一个返回初始状态的函数
  state:()=>{
    return {
      name:"wuyong",
      age:15,
      goodsList:[{id:1,name:'手机',num:1}] as Goods[]
    }
  },
  //定义派生属性(等同于计算属性,根据state状态值得到新的一个状态值)
  getters:{
    //接受state作为第一个参数,使用建通函数声明:箭头函数方法不能使用this
    userType:(state)=>{
      return state.age<18?'未成年人':'成年人'
    },
    //使用普通函数声明,方法体可以使用this访问整个store实例
    getGoodsById(state){
      //通过this获取上面getter
      console.log("getter普通函数",this.userType);
      return (id:number)=>this.goodsList.find(item=>item.id===id)
    }
  },
  // 定义行为,类似method
  actions:{
    //新增商品
    addGoods(goods:Goods){
      if(goods.num){
        //将goods.nums的值除1转成数值
        goods.num = goods.num/1
        //查询购物车中是否存在此商品,存在则数量累加,不存在则追加商品到数组中
        const target = this.goodsList.find(item=>item.id === goods.id)
        if(target){
          target.num+=goods.num
        }else{
          this.goodsList.push(goods)
        }
      }
    }
  }
})

组件中使用

<script setup lang="ts">
import { useCartStore } from './stores/cart';
import { storeToRefs } from 'pinia';
import { ref } from 'vue';
//加入购物车逻辑
const goodsId = ref<number>(1)
const goodsName = ref<string>('')
const goodsNum = ref<number>(1)
function handleAddCart() {
  const goods = { id: goodsId.value, name: goodsName.value, num: goodsNum.value }
  //触发store中的action
  cartStore.addGoods(goods)
}
const cartStore = useCartStore();
// 通过store实例获取state状态
console.log("获取state状态", cartStore.$state, cartStore.name);
// 使用storeToRefs方法将状态转成ref后,结构出来的状态是响应式的
const { age } = storeToRefs(cartStore);
console.log("age", age.value)
//将state状态重置为初始值
function handleResetState() {
  cartStore.$reset();
}
//修改state状态值
function handleAddAge() {
  // $patch 方法接受一个带state参数的回调函数,state来操作状态
  cartStore.$patch((state) => {
    state.name = 'wuqi';
    state.age = ++state.age;
    state.goodsList.push({ id: state.goodsList.length + 1, name: '电脑' + state.goodsList.length + 1, num: 2 });
  })
  cartStore.$subscribe((mutation, state) => {
    console.log("mutation, state", mutation, state)
    // 获取组件标识id(和 cartStore.$id 一样)
    const { storeId } = mutation;
    console.log('storeId', storeId); // 'cart'
    // 每当状态发生变化时,将整个 state 持久化到本地存储。
    localStorage.setItem('cart', JSON.stringify(state))
  })
}
</script>

<template>
  <div>
    <p>用户名:{{ cartStore.name }}</p>
    <p>
      年龄:{{ age }}
    </p>
    <ul>
      <li v-for="(item, index) in cartStore.goodsList" :key="index">
        商品ID: {{ item.id }},名称:{{ item.name }},数量:{{ item.num }}
      </li>
    </ul>
    <input v-model="goodsId" placeholder="商品ID"> &nbsp;
    <input v-model="goodsName" placeholder="商品名称"> &nbsp;
    <input v-model="goodsNum" placeholder="商品数量"> &nbsp;
    <button @click="handleAddCart">加入购物车</button>
    <!-- <p>Getter 派生属性:{{ cartStore.userType }}</p>
    <p>向 Getter 派生属性传递参数:{{ cartStore.getGoodsById(1) }}</p> -->
  </div>
</template>

<style scoped></style>

最终效果:
重塑状态管理的艺术:Vue3中Pinia的魔法之旅内包含简易购物车案例

注意事项
Action内部应当尽量避免直接修改状态,推荐使用this.$patch或返回一个新的状态对象来更新状态,这样可以更好地利用Vue的响应式系统。
对于异步操作,务必确保正确处理异常,比如使用try-catch块来捕获错误并进行适当处理。

PiniaSetup函数形

一、PiniaSetup函数形式提供了一种更加灵活和现代的方式来组织和管理应用的状态,尤其适合那些喜欢Vue 3新特性的开发者。

二、使用场景

  • 当你偏好使用Vue 3的Composition API风格编码时。
  • 当你需要在Store内部使用其他Composition API功能,如watch、onMounted等。
  • 当你希望利用Vue的响应式系统来更精细地控制状态变化时。

三、示例代码解释

import {defineStore} from 'pinia'
import {ref,computed} from 'vue'
/**
* defineStore 参数2:采用 Setup 函数形式
* ref() 就是 state 属性
* computed() 就是 getters
* function() 就是 actions
*/
export const useCounterStore = defineStore('counter',()=>{
  // 定义store的state属性
  const count = ref(0)
  // 使用 computed 来定义派生状态(getters)
  const doubleCount = computed(()=>{
    count.value
  })
  function addCount(){
    count.value++
  }
  return {count,doubleCount,addCount}
})

组件中调用使用

<script setup lang="ts">
import {useCounterStore} from './stores/counter'
const counterStore = useCounterStore()
const { doubleCount} = cartStore;
</script>

<template>
  <h2>Setup Store</h2>
  <p>count:{{ doubleCount }}</p>
  <button @click="counterStore.addCount">+1</button>
</template>

<style scoped></style>

重塑状态管理的艺术:Vue3中Pinia的魔法之旅内包含简易购物车案例
完结,更多vue3学习总结>>>>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1801703.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Attendance Machine (KPI)

Attendance Machine &#xff08;KPI&#xff09; QQ机考勤机数据KPI

YOLOv5改进总目录 | backbone、Neck、head、损失函数,注意力机制上百种改进技巧

&#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏地址&#xff1a; YOLOv5改进入门——持续更新各种有效涨点方法 点击即可跳转 报错 解决Yolov5的RuntimeError: result type Float can…

spring-kafka-生产者服务搭建测试(SpringBoot整合Kafka)

文章目录 1、生产者服务搭建1.1、引入spring-kafka依赖1.2、application.yml配置----v1版1.3、使用Java代码创建主题分区副本1.4、发送消息 1、生产者服务搭建 1.1、引入spring-kafka依赖 <?xml version"1.0" encoding"UTF-8"?> <project xml…

关于使用XX源码,开始编译的时候报错:不允许使用返回函数的函数

在使用vlc源码&#xff0c;开始编译的时候遇到以下错误&#xff1a; 仔细一看&#xff0c;懵掉了&#xff0c;NND源码编译都有错&#xff1f;报错如下&#xff1a; 一顿查找后&#xff0c;得出解决问题的办法&#xff1a;把ssize_t改为int 在大多数情况下&#xff0c;你可以将…

【Go语言精进之路】构建高效Go程序:了解切片实现原理并高效使用

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 引言一、切片究竟是什么&#xff1f;1.1 基础的创建数组示例1.2 基础的创建切片示例1.3 切片与数组的关系 二、切片的高级特性&#xff1a;动态扩容2.1 使用 append 函数扩容2.2 容量管理与性能考量2.3 切片的截取与缩容 三…

数学建模 —— 层次分析法(2)

目录 一、层次分析法&#xff08;AHP&#xff09; 二、构造比较判断矩阵 2.1 两两比较法 三、单准则下的排序及一致检验 3.1 单准则下的排序 3.2 一致性检验 四、层次总排序 4.1 层次总排序的步骤 4.2 总排序一致性检验 一、层次分析法&#xff08;AHP&#xff09; 方…

RHEL - 订阅、注册系统和 Yum Repository(新版界面)

《OpenShift / RHEL / DevSecOps 汇总目录》 演示环境说明 本文需要有 redhat.com 账号以及包含 RHEL 的有效订阅。 演示环境使用了通过 minimal 方式安装的 RHEL 7.6 环境&#xff0c;RHEL 可以访问互联网。 红帽网站 access.redhat.com 针对新用户提供了新版界面&#xff0…

华为端云一体化开发 初始化云db表结构和表数据(实践2.0)(HarmonyOS学习第七课)

实例介绍&#xff1a;黑马鸿蒙刷题学习过程 1. 静态页面准备 借用黑马完成的页面&#xff0c;已经提供给大家一套写好的基本模板&#xff0c;大家直接将这套模板覆盖原有entry/src/main目录就可以 &#x1f4ce;main.ziphttps://www.yuque.com/attachments/yuque/0/2024/zip…

从零入手人工智能(2)——搭建开发环境

1.前言 作为一名单片机工程师&#xff0c;想要转型到人工智能开发领域的道路确实充满了挑战与未知。记得当我刚开始这段旅程时&#xff0c;心中充满了迷茫和困惑。面对全新的领域&#xff0c;我既不清楚如何入手&#xff0c;也不知道能用人工智能干什么。正是这些迷茫和困惑&a…

切莫被人工智能的洪流吞噬

切莫被人工智能的洪流吞噬 当今社会似乎正在被“人工智能”的概念淹没&#xff0c;各行各业也都期望能与其挂钩&#xff0c;彷佛这就是新世代下的灵药&#xff0c;有人把这一现象称为“旧互联网时代的新革命”。但是我们&#xff0c;这一现象的缔造者&#xff0c;又处于这洪流…

标准价与移动平均价简介

一、移动平均价 移动平均价优点&#xff1a; a.移动平均价格可反应”实时的”加权平均价格,特别是物料价格涨跌幅度大时物料的价格不会被差异扭曲。 b.因为是基于交易的实时加权平均计算价格,一般情况下,移动平均价不产生差异&#xff0c;价格相对真实。 c.如果所有的物料都使用…

calibre,一个超厉害的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个超厉害的 Python 库 - calibre。 Github地址&#xff1a;https://github.com/kovidgoyal/calibre 电子书籍已经成为现代阅读的重要形式&#xff0c;而管理和转换电子书籍格式的需求也随之增加…

[NOVATEK] NT96580行车记录仪功能学习笔记(持续更新~

一、u-Boot升级灯 运行u-Boot程序时LED灯闪烁,找到运行过程中一直在运行的函数在里面进行LED引脚电平的翻转 宏定义 Z:\SunFan\AHD580\pip\na51055_PIP\BSP\u-boot\include\configs\nvt-na51055-evb.h Z:\SunFan\AHD580\pip\na51055_PIP\BSP\u-boot\drivers\mtd\nvt_flash_…

weditor安装时提示This is an issue with the package mentioned above, not pip

报错如下&#xff1a; note: This error originates from a subprocess, and is likely not a problem with pip. error: metadata-generation-failed Encountered error while generating package metadata. ╰─> See above for output. note: This is an issue with …

【Java】解决Java报错:OutOfMemoryError

文章目录 引言1. 错误详解2. 常见的出错场景2.1 内存泄漏2.2 大数据结构2.3 JVM内存参数配置不当 3. 解决方案3.1 内存泄漏检测与修复3.2 优化数据结构3.3 调整JVM内存参数3.4 使用弱引用 4. 预防措施4.1 定期进行内存分析4.2 合理设计数据结构4.3 使用合适的JVM内存参数4.4 优…

C++11:列表初始化 初始化列表initializer_list decltype关键字

目录 前言 列表初始化 初始化列表initializer_list decltype关键字 左值和右值 move 前言 2003年C标准委员会曾经提交了一份技术勘误表&#xff08;简称TC1&#xff09;&#xff0c;使得C03这个名字取代了C98成为了C11前最新的C标准名称。不过由于C03主要是对C98标准中的…

stack overflow复现

当你在内存的栈中&#xff0c;存放了太多元素&#xff0c;就有可能在造成 stack overflow这个问题。 今天看看如何复现这个问题。 下图&#xff0c;是我写的程序&#xff0c;不断的创造1KB的栈&#xff0c;来看看执行了多少次&#xff0c;无限循环。 最后结果是7929kB时, 发…

06 Linux 设备驱动模型

1、Overview Linux-2.6 引入的新的设备管理机制 - kobject 降低设备多样性带来的 Linux 驱动开发的复杂度,以及设备热拔插处理、电源管理等将硬件设备归纳、分类,然后抽象出一套标准的数据结构和接口驱动的开发,就简化为对内核所规定的数据结构的填充和实现驱动模型是 Linu…

发生一件大事!

今天&#xff0c;又是一年高考&#xff0c;高考应该是很多人人生中的一件大事了&#xff01; 看到公众号推送&#xff0c;才知道原来高考都已经变成三天了&#xff0c;现在没有什么理综文综&#xff0c;大家可以自由组合科目&#xff0c;选择自己喜欢的。 我对高中生活印象还是…

Springboot 通过SSE 实现实时消息返回

网上搜了好多都是用 SseEmitter 实现的,自己搭的demo确实也可以了,但是我项目里有一个过滤器,死活配置都不行,终于用google搜了一下,第一篇帖子便解决了这个问题,代码和大佬链接如下: https://github.com/CodingChaozhang/spring_boot_practice_demo/blob/master/springboot_s…