113.vue中action负责异步请求

我们来看下面的代码

countstore.js

import { defineStore } from 'pinia'
import axios from "axios"

/**
 * 定义一个名为 count 的 Store
 * 使用 Option Store(选项式)写法
 */
const piniaCountStore = defineStore('count', {
  
  state(){
    return {
      count:10,
      data:[]
    }
  }
  
  ,
  getters:
  {
     getCount(state){
       return "当前count的值"+state.count
     },
     doubleCount(state){
      return this.getCount+state.count
    }
  },
  actions:{
    
    addCount(state){
      this.count +=10 
    },
    decrease(state){
      this.count-=5
    },
     async getData(url){
        axios.get(url).then(res =>{
          this.data=res.data.banner
        }).catch(error =>{
          console.log(error)
        })
    }     

  }
      
     
  }
)

export default piniaCountStore

app.vue中

<template>
  <h1>app vue</h1>
  <hello />
</template>

<script setup>
// 引入子组件
import hello from './components/hello.vue'
</script>

<style scoped>
h1 {
  color: #42b983;
}
</style>

hello.vue

<template>
  <p>hello.vue</p>
  <p>原始count: {{ store.count }}</p>

  <p>hello22.vue</p>
  <p>双倍count: {{ store.doubleCount }}</p>
  <p>{{ store.getCount }}</p>
  
  <!-- 操作按钮 -->
  <div class="btn-group">
    <button @click="store.count++">+1</button>
    <button @click="store.addCount">+10</button>
    <button @click="store.decrease">-5</button>
  </div>

  <h3>网络请求的结果如下所示</h3>
  <ul v-if="store.data.length">
    <li v-for="(item, index) in store.data" :key="index">
      <h3>{{ item.title }}</h3>
      <p>{{ item.content }}</p>
      <img :src="item.img" :alt="item.title" style="width: 200px; margin: 10px 0;">
    </li>
  </ul>
  <p v-else>加载中...</p>
</template>

<script setup>
import { onMounted } from 'vue'
import piniaCountStore from '../store/countstore'

const store = piniaCountStore()

onMounted(() => {
  // 使用你提供的接口地址
  store.getData('http://iwenwiki.com/api/blueberrypai/getIndexBanner.php')
})
</script>

<style scoped>
p {
  font-size: 18px;
}
.btn-group {
  margin: 15px 0;
}
button {
  margin-right: 10px;
  padding: 5px 15px;
}
li {
  border-bottom: 1px solid #eee;
  padding: 15px 0;
}
</style>

 程序运行的效果如下所示

image

 接下来对代码进行解析,效果如下所示

你的代码整体结构是正确的,但需要确保 Pinia Store 中有对应的 datastate 和 getDataaction。根据你提供的接口文档,我来帮你完善 Store 部分:

一、完整的 countstore.js(修正版)

import { defineStore } from 'pinia'
import axios from 'axios'

export const useCountStore = defineStore('count', {
  state: () => ({
    count: 10,
    data: [] // ✅ 必须添加这个 state 来存储接口数据
  }),

  getters: {
    getCount: (state) => "当前count的值" + state.count,
    doubleCount(state) {
      return this.getCount + state.count
    }
  },

  actions: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    },
    
    // ✅ 添加缺失的 action
    addCount() {
      this.count += 10
    },
    
    // ✅ 添加缺失的 action
    decrease() {
      this.count -= 5
    },
    
    // ✅ 添加获取数据的 action
    async getData(url) {
      try {
        const response = await axios.get(url)
        if (response.data.success) {
          this.data = response.data.banner // ✅ 将接口数据存入 state
        }
      } catch (error) {
        console.error('请求失败:', error)
        // 可以在这里添加错误处理逻辑
      }
    }
  }
})

二、你的组件代码分析(逐行点评)

✅ 做得很好的地方:

  1. 正确使用 onMounted:在组件挂载时调用接口

  2. 正确的模板语法v-for遍历 store.data

  3. 正确的事件绑定:按钮点击事件绑定到 store 的 actions

  4. 结构清晰:模板、脚本、样式分离明确

⚠️ 需要注意的小问题:

1. 重复的 count显示

<p>count: {{ store.count }}</p>
<p>count: {{ store.doubleCount }}</p>

建议改成更有意义的命名:

<p>原始count: {{ store.count }}</p>
<p>双倍count: {{ store.doubleCount }}</p>

2. 图片显示(可选增强)

接口返回的数据中有 img字段,可以添加图片显示:

<li v-for="(item,index) in store.data" :key="index">
  <h3>{{ item.title }}</h3>
  <p>{{ item.content }}</p>
  <img :src="item.img" alt="" style="width: 200px;">
</li>

三、完整的组件代码(优化版)

<template>
  <p>hello.vue</p>
  <p>原始count: {{ store.count }}</p>

  <p>hello22.vue</p>
  <p>双倍count: {{ store.doubleCount }}</p>
  <p>{{ store.getCount }}</p>
  
  <!-- 操作按钮 -->
  <div class="btn-group">
    <button @click="store.count++">+1</button>
    <button @click="store.addCount">+10</button>
    <button @click="store.decrease">-5</button>
  </div>

  <h3>网络请求的结果如下所示</h3>
  <ul v-if="store.data.length">
    <li v-for="(item, index) in store.data" :key="index">
      <h3>{{ item.title }}</h3>
      <p>{{ item.content }}</p>
      <img :src="item.img" :alt="item.title" style="width: 200px; margin: 10px 0;">
    </li>
  </ul>
  <p v-else>加载中...</p>
</template>

<script setup>
import { onMounted } from 'vue'
import piniaCountStore from '../store/countstore'

const store = piniaCountStore()

onMounted(() => {
  // 使用你提供的接口地址
  store.getData('http://iwenwiki.com/api/blueberrypai/getIndexBanner.php')
})
</script>

<style scoped>
p {
  font-size: 18px;
}
.btn-group {
  margin: 15px 0;
}
button {
  margin-right: 10px;
  padding: 5px 15px;
}
li {
  border-bottom: 1px solid #eee;
  padding: 15px 0;
}
</style>

四、关键知识点总结

1. 数据流闭环

组件 mounted → 调用 store.getData() → axios 请求 → 更新 store.data → 组件自动更新

2. 为什么组件能自动更新?

  • Pinia 的 state 是 响应式​ 的

  • store.data变化时,Vue 的响应式系统会自动重新渲染模板

3. 错误处理建议

可以在 store 中添加 loading 和 error 状态:

state: () => ({
  count: 10,
  data: [],
  loading: false,
  error: null
}),
actions: {
  async getData(url) {
    this.loading = true
    this.error = null
    try {
      const response = await axios.get(url)
      this.data = response.data.banner
    } catch (err) {
      this.error = err.message
    } finally {
      this.loading = false
    }
  }
}

五、运行效果预期

页面会显示:

  1. 当前 count 值和计算属性

  2. 三个操作按钮

  3. 从接口获取的 4 条 banner 数据(标题、内容、图片)

  4. 如果数据未加载完成,显示"加载中..."

现在你的代码已经完整了!​ 确保 countstore.js中有我上面添加的 data: []getDataaction,然后运行项目,你应该能看到接口返回的数据正确显示在页面上。👍

如果遇到跨域问题,记得在 vite.config.js中配置代理(参考我之前的回答)。需要我帮你配置代理吗?

在 Vue 3 项目中安装和使用 Axios,按以下步骤操作即可:


一、安装 Axios(核心步骤)

在项目根目录(有 package.json的目录)下,打开终端执行:

✅ 使用 npm(最常用)

npm install axios

✅ 使用 yarn

yarn add axios

✅ 使用 pnpm

pnpm add axios

安装完成后,你可以在 package.jsondependencies中看到:

"dependencies": {
  "axios": "^1.x.x",
  "vue": "^3.x.x"
}

二、在 Vue 组件中直接使用(入门)

安装后,在 .vue文件中直接引入使用:

<template>
  <div>
    <p>数据:{{ data }}</p>
    <button @click="fetchData">获取数据</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import axios from 'axios'

const data = ref('')

const fetchData = async () => {
  try {
    const res = await axios.get('https://api.example.com/data')
    data.value = res.data
  } catch (err) {
    console.error(err)
  }
}
</script>

三、在 Pinia 中使用 Axios(✅ 推荐做法)

结合你正在学的 Pinia,这是最标准的企业级写法

1️⃣ 在 Store 中封装接口请求

// src/store/countstore.js
import { defineStore } from 'pinia'
import axios from 'axios'

export const useCountStore = defineStore('count', {
  state: () => ({
    count: 0,
    list: []
  }),

  actions: {
    // 从接口获取数据
    async fetchList() {
      const res = await axios.get('https://api.example.com/list')
      this.list = res.data
    },

    // 提交数据
    async incrementAsync() {
      const res = await axios.post('https://api.example.com/add', {
        count: this.count + 1
      })
      this.count = res.data.count
    }
  }
})

2️⃣ 在组件中调用

<script setup>
import { useCountStore } from '../store/countstore'
const store = useCountStore()

store.fetchList()
</script>

四、全局配置 Axios(进阶)

创建 Axios 实例(推荐)

// src/utils/request.js
import axios from 'axios'

const request = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000
})

// 请求拦截器
request.interceptors.request.use(config => {
  config.headers.Authorization = 'Bearer token'
  return config
})

// 响应拦截器
request.interceptors.response.use(
  res => res.data,
  err => Promise.reject(err)
)

export default request

在 Store 中使用封装好的 request

import request from '../utils/request'

actions: {
  async fetchList() {
    this.list = await request.get('/list')
  }
}

五、常见问题(新手必看)

❓ 1. 安装后还是报错 axios is not defined

✅ 检查是否忘记 import axios from 'axios'

❓ 2. 跨域问题(最常见)

浏览器控制台报错:

CORS policy: No 'Access-Control-Allow-Origin'

✅ 解决方案:

  • 后端配置跨域

  • 或 Vue 开发环境配置代理(vite.config.js

// vite.config.js
export default {
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        rewrite: path => path.replace(/^\/api/, '')
      }
    }
  }
}

❓ 3. async / await 报错

✅ 确保在 actionsonMounted中使用


六、你当前学习阶段的最佳实践 ✅

结合你正在学的 Pinia,强烈建议这样用

 

层级

职责

组件

触发动作(@click

Pinia Actions

调用 Axios(业务逻辑)

Axios

纯数据请求

State

存储数据


七、一句话总结

npm install axios安装 → 在 Pinia 的 actions中调用 → 组件触发 action


八、下一步你可以学

  • ✅ Axios 拦截器(统一处理 Token / Loading)

  • ✅ Pinia + Axios 请求缓存

  • ✅ 请求失败重试机制

  • ✅ Vue 3 组合式函数封装 useRequest

如果你愿意,我可以:

  • 👉 帮你把现有 countstore改成带接口的版本

  • 👉 画一张「Vue → Pinia → Axios → 后端」的请求流程图

  • 👉 模拟一个 Mock 接口让你练手

你现在这一步,已经走到 “前后端打通”​ 的关键节点了 👍

 

posted on 2026-07-14 21:31  luzhouxiaoshuai  阅读(7)  评论(0)    收藏  举报

导航