【Vue】Vue2中的Vuex

目錄

  • Vuex
    • 介紹
    • Vuex 中的核心概念
  • 在vue2中使用Vuex
    • 安裝 Vuex
    • 創建一個 Vuex Store
    • 在 Vue 實例中使用 Vuex
    • 編寫 Vuex 的 state、mutations 和 actions
    • 在組件中使用 Vuex
  • Vuex的核心
    • State
      • 組件中獲取 Vuex 的狀態
      • mapState 輔助函數
      • 對象展開運算符
    • Getter
      • 基本使用
        • 示例
      • 通過屬性訪問
      • 通過方法訪問
      • mapGetters 輔助函數
    • Mutation
      • 定義mutation
        • mutations 中回調函數參數:
      • commit 提交 mutation
      • Mutation 必須是同步函數
      • mapMutations 輔助函數
    • Actions
      • Action 函數
      • dispatch 觸發 Action
      • action 內部執行異步操作
        • 購物車示例,涉及到調用異步 API 和分發多重 mutation
      • mapActions 輔助函數
      • 組合 Action
  • Modules
    • 基本使用
      • 示例:
    • 命名空間
      • 示例

Vuex

介紹

  • Vuex 是一個用于 Vue.js 應用程序的狀態管理模式。
  • 它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態的一致性。
  • Vuex 還集成了Vue的官方瀏覽器插件 vue-devtools,提供了一些強大的調試工具。
  • Vue 2 匹配的 Vuex 3 的文檔:文檔鏈接
    在這里插入圖片描述

Vuex 中的核心概念

  1. State(狀態):用于存儲應用程序的狀態,可以通過 this.$store.state 訪問。

  2. Getters(計算屬性):類似于 Vue 組件中的計算屬性,可以派生出一些狀態,可以通過 this.$store.getters 訪問。

  3. Mutations(突變):用于修改狀態,只能進行同步操作,可以通過 this.$store.commit 觸發。

  4. Actions(異步操作):用于處理異步操作,可以通過 this.$store.dispatch 觸發,并且可以調用多個突變。

  5. Modules(模塊化):可以將 store 分割成多個模塊,每個模塊擁有自己的 state、getters、mutations和actions。

使用 Vuex 可以幫助我們更好地組織和管理 Vue 應用的狀態,并且方便狀態的復用和共享。

在vue2中使用Vuex

安裝 Vuex

可以使用 npm 或者 yarn 進行安裝。

npm install vuex
//或者
npm install vuex@3.0.0 --save
//或者
yarn add vuex

創建一個 Vuex Store

在src/store目錄下創建一個名為 index.js 的文件,并在其中導入 Vue 和 Vuex,并創建一個新的 Vuex.Store 實例。

import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({state: {count: 1},mutations: {increment(state, value) {state.count += value}}
})
export default store;

在 Vue 實例中使用 Vuex

在 main.js 文件中導入剛才創建的 index.js 文件,并在 Vue 實例的配置對象中引入 store

import Vue from 'vue'
import App from './App.vue'
import store from './store'new Vue({store,render: h => h(App),
}).$mount('#app')

編寫 Vuex 的 state、mutations 和 actions

對于需要進行狀態管理的數據,可以在 store.js 文件中定義 state,并在 mutations 中編寫修改 state 的方法,在 actions 中編寫處理業務邏輯的方法。

export default new Vuex.Store({state: {count: 0,},mutations: {increment(state) {state.count++},},actions: {incrementAsync({ commit }) {setTimeout(() => {commit('increment')}, 1000)},},})

在組件中使用 Vuex

在需要使用 Vuex 中的狀態或觸發 Vuex 中 mutations/actions 的組件中,可以通過 this.$store.state 來獲取 state,通過 this.$store.commit 來觸發 mutations,通過 this.$store.dispatch 來觸發 actions。

 <template><div><p>Count: {{ count }}</p><button @click="increment">Increment</button></div></template><script>export default {computed: {count() {return this.$store.state.count},},methods: {increment() {this.$store.commit('increment')},},}</script>

通過上述步驟,就可以在 Vue 2 中使用 Vuex 進行狀態管理了。可以在組件中方便地共享和修改狀態,并且處理異步操作。

Vuex的核心

State

組件中獲取 Vuex 的狀態

  • 在計算屬性中返回某個狀態:

    // 創建一個 Counter 組件
    const Counter = {template: `<div>{{ count }}</div>`,computed: {count () {return store.state.count}}
    }
    
  • 在每個需要使用 state 的組件中需要頻繁地導入

  • 在根實例中注冊 store 選項,該 store 實例會注入到根組件下的所有子組件中

    const app = new Vue({el: '#app',store, // 把 store 對象提供給 “store” 選項,這可以把 store 的實例注入所有的子組件components: { Counter },template: `<div class="app"><counter></counter></div>`
    })
    
  • 子組件能通過 this.$store 訪問:

    const Counter = {template: `<div>{{ count }}</div>`,computed: {count () {return this.$store.state.count}}
    }
    

mapState 輔助函數

  • 用于組件需要獲取多個狀態的時候

    <template><div class="hello"><div><h3>組件中使用 store</h3>當前count:{{ $store.state.count }}</div><div><h3>組件中使用 mapState</h3><div>當前count:{{ count }}</div><div>當前countAlias:{{ countAlias }}</div><div>當前countPlusLocalState:{{ countPlusLocalState }}</div></div><div><button v-on:click="clickCount(0)">減1</button><button v-on:click="clickCount(1)">加1</button></div></div>
    </template><script>
    import { mapState } from "vuex";export default {name: "CountView",methods: {clickCount(val) {this.$store.commit("increment", val === 0 ? -1 : 1);},},data: () => ({localCount: 3,}),computed: {...mapState({// 箭頭函數可使代碼更簡練count: (state) => state.count,// 傳字符串參數 'count' 等同于 `state => state.count`countAlias: "count",// 使用常規函數,count + data中的localCountcountPlusLocalState(state) {return state.count + this.localCount;},}),},
    };
    </script>
    
  • 也可以給 mapState 傳一個字符串數組:

    computed: mapState([// 映射 this.count 為 store.state.count'count'
    ])
    

對象展開運算符

```js
computed: {localComputed () { /* ... */ },// 使用對象展開運算符將此對象混入到外部對象中...mapState({// ...})
}
```

Getter

基本使用

  • 就像計算屬性一樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變才會被重新計算。
示例
  • Getter 接受 2 個參數, state 作為其第一個參數,getter 作為第二個參數

    const store = new Vuex.Store({state: {todos: [{ id: 1, text: '...', done: true },{ id: 2, text: '...', done: false }]},getters: {doneTodos: (state, getters) => {return state.todos.filter(todo => todo.done)}}
    })
    

通過屬性訪問

  • Getter 會暴露為 store.getters 對象,可以以屬性的形式訪問這些值:

    store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
    

通過方法訪問

  • 也可以通過讓 getter 返回一個函數,來實現給 getter 傳參

    getters: {// ...getTodoById: (state) => (id) => {return state.todos.find(todo => todo.id === id)}
    }
    
  • 使用

    store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
    

mapGetters 輔助函數

  • mapGetters 輔助函數僅僅是將 store 中的 getter 映射到局部計算屬性:

    import { mapGetters } from 'vuex'export default {// ...computed: {// 使用對象展開運算符將 getter 混入 computed 對象中...mapGetters(['doneTodosCount','anotherGetter',// ...])}
    }
    
  • 如果你想將一個 getter 屬性另取一個名字,使用對象形式:

    ...mapGetters({// 把 `this.doneCount` 映射為 `this.$store.getters.doneTodosCount`doneCount: 'doneTodosCount'
    })
    

Mutation

定義mutation

更改狀態的唯一方法是提交 mutation

mutations 中回調函數參數:
  • state 為第一個參數,
  • payload(載荷)為第二個參數(可以為基本數據類型,也可以為對象)
const store = new Vuex.Store({state: {count: 1},mutations: {increment(state, payload) {state.count += payload}}
})

commit 提交 mutation

調用 store.commit 方法:

store.commit('increment', 2)

Mutation 必須是同步函數

一條重要的原則就是要記住 mutation 必須是同步函數

mutations: {someMutation (state) {api.callAsyncMethod(() => {state.count++})}
}
  • 原因是當 mutation 觸發的時候,回調函數還沒有被調用,devtools 不知道什么時候回調函數實際上被調用,這樣狀態的變化就變得不可追蹤
  • 解決方法:使用 Actions

mapMutations 輔助函數

使用 mapMutations 輔助函數將組件中的 methods 映射為 store.commit 調用:(需要先在根節點注入 store):

import { mapMutations } from 'vuex'export default {// ...methods: {...mapMutations(['increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')`// `mapMutations` 也支持載荷:'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)`]),...mapMutations({add: 'increment' // 將 `this.add()` 映射為 `this.$store.commit('increment')`})}
}

Actions

Action 類似于 mutation,不同在于:

  1. Action 提交的是 mutation,而不是直接變更狀態。
  2. Action 可以包含任意異步操作。

Action 函數

Action 函數參數

  • context 對象(與 store 實例具有相同方法和屬性,因此你可以調用 context.commit 提交一個 mutation)
  • payload 載荷(可以基本數據,也可以對象)

定義

const store = new Vuex.Store({state: {count: 0},mutations: {increment(state, payload) {state.count += payload;}},actions: {increment(context, payload) {context.commit('increment', payload)}}
})export default store;

dispatch 觸發 Action

Action 通過 store.dispatch 方法觸發:

store.dispatch('increment', 3)

action 內部執行異步操作

actions: {incrementAsync ({ commit }) {setTimeout(() => {commit('increment')}, 1000)}
}
購物車示例,涉及到調用異步 API 和分發多重 mutation
actions: {checkout ({ commit, state }, products) {// 把當前購物車的物品備份起來const savedCartItems = [...state.cart.added]// 發出結賬請求,然后樂觀地清空購物車commit(types.CHECKOUT_REQUEST)// 購物 API 接受一個成功回調和一個失敗回調shop.buyProducts(products,// 成功操作() => commit(types.CHECKOUT_SUCCESS),// 失敗操作() => commit(types.CHECKOUT_FAILURE, savedCartItems))}
}

mapActions 輔助函數

使用 mapActions 輔助函數將組件的 methods 映射為 store.dispatch 調用(需要先在根節點注入 store):

import { mapActions } from 'vuex'export default {// ...methods: {...mapActions(['increment', // 將 `this.increment()` 映射為 `this.$store.dispatch('increment')`// `mapActions` 也支持載荷:'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.dispatch('incrementBy', amount)`]),...mapActions({add: 'increment' // 將 `this.add()` 映射為 `this.$store.dispatch('increment')`})}
}

組合 Action

Action 通常是異步的,那么如何知道 action 什么時候結束呢?更重要的是,我們如何才能組合多個 action,以處理更加復雜的異步流程?
首先,你需要明白 store.dispatch 可以處理被觸發的 action 的處理函數返回的 Promise,并且 store.dispatch 仍舊返回 Promise:

actions: {actionA ({ commit }) {return new Promise((resolve, reject) => {setTimeout(() => {commit('someMutation')resolve()}, 1000)})}
}

現在這么寫

store.dispatch('actionA').then(() => {// ...
})

在另外一個 action 中也可以:

actions: {// ...actionB ({ dispatch, commit }) {return dispatch('actionA').then(() => {commit('someOtherMutation')})}
}

最后,如果我們利用 async / await (opens new window),我們可以如下組合 action:

// 假設 getData() 和 getOtherData() 返回的是 Promise
actions: {async actionA ({ commit }) {commit('gotData', await getData())},async actionB ({ dispatch, commit }) {await dispatch('actionA') // 等待 actionA 完成commit('gotOtherData', await getOtherData())}
}

Modules

基本使用

  • Vuex 允許我們將 store 分割成模塊(module)
  • 每個模塊擁有自己的 state、mutation、action、getter

示例:

store/modules/moduleA.js

const moduleA = {state: {countA: 1},getters: {},mutations: {},actions: {}
}export default moduleA;

store/modules/moduleB.js

const moduleB = {state: {countB: 2},getters: {sumWithRootCount(state, getters, rootState) {// 這里的 state 和 getters 對象是模塊的局部狀態,rootState 為根節點狀態console.log('B-state', state)console.log('B-getters', getters)console.log('B-rootState', rootState)return state.countB + rootState.count}},mutations: {increment(state, payload) {// 這里的 `state` 對象是模塊的局部狀態state.countB += payload;}},actions: {incrementIfOddOnRootSum({ state, commit, rootState }, payload) {console.log(payload)// 這里的 `state` 對象是模塊的局部狀態,rootState 為根節點狀態console.log(state)commit('increment', rootState.count + payload)}}
}export default moduleB;

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './modules/moduleA'
import moduleB from './modules/moduleB'Vue.use(Vuex)const store = new Vuex.Store({state: {count: 5},modules: {moduleA: moduleA,moduleB: moduleB,}
})export default store;

組件中使用

<template><div class="hello"><div><h3>組件中使用 store</h3><div>moduleA中的countA {{ countA }}</div><div>moduleB中的countB {{ countB }}</div></div><div><button v-on:click="clickCommit(1)">commit 加1</button><button v-on:click="clickDispatch(1)">dispatch 加1</button></div></div>
</template><script>export default {name: "CountView",methods: {clickCommit(val) {this.$store.commit("increment", val);},clickDispatch(val) {this.$store.dispatch("incrementIfOddOnRootSum", val);},},computed: {countA() {// moduleA 中的 countAreturn this.$store.state.moduleA.countA;},countB() {// moduleB 中的 countBreturn this.$store.state.moduleB.countB;},},
};
</script>

命名空間

  • 默認情況下,模塊內部的 action、mutation 和 getter 是注冊在全局命名空間的——這樣使得多個模塊能夠對同一 mutation 或 action 作出響應
  • 可以通過添加 namespaced: true 的方式使其成為帶命名空間的模塊。
  • 當模塊被注冊后,它的所有 getter、action 及 mutation 都會自動根據模塊注冊的路徑調整命名。

示例

store/modules/moduleA.js

const moduleA = {namespaced: true, // 設為命名空間state: {countA: 1},getters: {},mutations: {},actions: {}
}export default moduleA;

store/modules/moduleB.js

const moduleB = {namespaced: true, // 設為命名空間state: {countB: 2},getters: {sumWithRootCount(state, getters, rootState) {// 這里的 state 和 getters 對象是模塊的局部狀態,rootState 為根節點狀態console.log('B-state', state)console.log('B-getters', getters)console.log('B-rootState', rootState)return state.countB + rootState.count}},mutations: {increment(state, payload) {// 這里的 `state` 對象是模塊的局部狀態state.countB += payload;}},actions: {incrementIfOddOnRootSum({ state, commit, rootState }, payload) {console.log(payload)// 這里的 `state` 對象是模塊的局部狀態,rootState 為根節點狀態console.log(state)commit('increment', rootState.count + payload)}}
}export default moduleB;

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './modules/moduleA'
import moduleB from './modules/moduleB'Vue.use(Vuex)const store = new Vuex.Store({state: {count: 5},modules: {moduleA: moduleA,moduleB: moduleB,}
})export default store;

組件中使用

<template><div class="hello"><div><h3>組件中使用 store</h3><div>moduleA中的countA {{ countA }}</div><div>moduleB中的countB {{ countB }}</div></div><div><button v-on:click="increment(1)">commit 加1</button><button v-on:click="incrementIfOddOnRootSum(1)">dispatch 加1</button></div></div>
</template><script>
import { mapActions, mapMutations, mapState } from "vuex";export default {name: "CountView",methods: {// 將模塊的空間名稱字符串作為第一個參數傳遞給 mapMutations...mapMutations("moduleB", ["increment"]),// 將模塊的空間名稱字符串作為第一個參數傳遞給 mapActions...mapActions("moduleB", ["incrementIfOddOnRootSum"]),},computed: {// 將模塊的空間名稱字符串作為第一個參數傳遞給 mapState...mapState("moduleA", {countA: (state) => state.countA,}),...mapState("moduleB", {countB: (state) => state.countB,}),},
};
</script>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/18318.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/18318.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/18318.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

從多站點到多活,XEOS 對象數據容災能力再提升

近日&#xff0c; XSKY SDS V6.4 新版本發布&#xff0c;其中 XEOS V6.4 全新升級并完善了統一命名空間功能&#xff0c;更進一步增強和完善了異地容災方案&#xff0c;配合強一致代理讀&#xff0c;可以實現異地多活&#xff1b;同時大幅降低管理復雜度&#xff0c;有效降低容…

Nginx中的limit_req模塊和limit_conn模塊詳解

引言 在高流量場景下&#xff0c;良好的限流和連接控制策略至關重要&#xff0c;以防止服務器過載&#xff0c;確保服務穩定性和高可用性。Nginx 提供了 limit_req 和 limit_conn 模塊&#xff0c;用以實現請求頻率和并發連接數的限制。本文將詳細介紹這兩個模塊的生效階段和生…

TikTok電商帶貨特訓營,跟隨時代潮流,跨境掘金(8節課)

課程內容&#xff1a; 1-先導課 2-一、店鋪運營認知與思路 3-二、店鋪風控注意事項 4-三、美區Tiktok前期工作-1店鋪入駐模式 5-三、美區Tiktok前期工作-2指紋瀏覽器介紹 6-三、美區Tiktok前期工作-4綁定電話號碼 7-三、美區Tiktok前期工作-5添加倉庫地址 8-三、美區Ti…

GIS讀研與求職準備:植被定量遙感專業研0

本文介紹植被定量遙感專業研究生入學初期&#xff0c;為將來從事開發類工作所作求職準備的規劃路徑、方向選擇等方面的建議。 前面提到了&#xff0c;最近有很多師弟師妹詢問關于研究生方向選擇、求職準備等方面的問題。因為很多朋友的提問比較有共性&#xff0c;所以會在征得對…

【秒殺系統】從零開始打造簡易秒殺系統(一):防止超賣

【秒殺系統】從零開始打造簡易秒殺系統&#xff08;一&#xff09;&#xff1a;防止超賣 前言 大家好&#xff0c;好久不發文章了。&#xff08;快一個月了- -&#xff09;最近有很多學習的新知識想和大家分享&#xff0c;但無奈最近項目蠻忙的&#xff0c;很多文章寫了一半擱…

redis筆記1

1-nosql&#xff08;非關系型數據庫&#xff09; 定位緩存&#xff0c;提高數據讀寫速度&#xff0c;減輕對數據儲存與訪問壓力&#xff0c;不建議存敏感數據&#xff08;重要數據&#xff09;。 2-特征 &#xff08;1&#xff09;鍵值&#xff08;key-value&#xff09;型 &a…

【面試】Oracle JDK和Open JDK什么關系?

目錄 1. 起源與發展2. 代碼與許可3. 功能與組件4. 使用場景5. 版本更新與支持 1. 起源與發展 1.Oracle JDK是由Oracle公司基于Open JDK源代碼開發的商業版本。2.Open JDK是java語言的一個開源實現。 2. 代碼與許可 1.Oracle JDK包含了閉源組件&#xff0c;并根據二進制代碼許…

深入Java:JSON解析與操作的藝術

哈嘍&#xff0c;大家好&#xff0c;我是木頭左&#xff01; 一、初識JSON&#xff1a;數據格式的優雅舞者 在現代Web開發中&#xff0c;JSON&#xff08;JavaScript Object Notation&#xff09;以其輕量級和易于閱讀的特點成為了數據交換的首選格式。它基于JavaScript的一個…

用最通俗的話理解什么是協程

參考&#xff1a; 用最通俗的話理解什么是協程-CSDN博客

FreeRTOS_信號量_學習筆記

信號量的特性 消息隊列用于傳輸多個數據&#xff0c;但是有時候我們只需要傳遞狀態&#xff0c;這個狀態值需要用一個數值表示。套用隊列筆記中的流水線例子&#xff0c;可以理解為流水線上工件的數量。 信號&#xff1a;起通知作用 量&#xff1a;還可以用來表示資源的數量 當…

打印機手動雙面打印技巧

一、WORD和PDF &#xff08;1&#xff09;首先選擇要打印的頁面范圍&#xff0c;然后選擇僅奇數頁打印 &#xff08;2&#xff09;將打印完的紙張翻過來&#xff0c;白紙朝上&#xff0c;紙張的頭部先放入打印機 &#xff08;3&#xff09;選擇要打印的頁面范圍&#xff0c;然…

oracle.jdbc.OracleDatabaseException: ORA-00911: 無效字符

先吐槽一句&#xff0c;oracle 真坑啊&#xff01; 一個很正常的sql 語句一直報 ORA-00911: 無效字符 &#xff0c;拿到數據庫去執行一點問題沒有&#xff0c;一運行代碼就報錯&#xff0c;然后一個字符一個字符的對比&#xff0c;竟然是因為sql 結尾的一個 ";" 導致…

TP6開發文檔概述

TP6&#xff08;ThinkPHP 6&#xff09;是一個使用PHP語言開發的快速、兼容且簡單的面向對象開發框架。以下是一個簡化的TP6開發文檔概述&#xff0c;涵蓋了核心功能和一些常用方法&#xff1a; 一、環境準備 PHP安裝&#xff1a;確保已經安裝了與TP6兼容的PHP版本。 Composer…

PHPIPAM在建立數據庫階段報錯

如題&#xff0c;參考大佬的操作IP地址管理系統phpipam部署-CSDN博客搭建PHPIPAM&#xff0c;不過我沒有選擇1.4&#xff0c;而是直接搭建了1.6版本&#xff0c;一切順利&#xff0c;到了最后建立數據庫階段&#xff0c;輸入數據庫賬號和密碼后提示Cannot install sql SCHEMA f…

大模型部署_書生浦語大模型 _作業2

本節課可以讓同學們實踐 4 個主要內容&#xff0c;分別是&#xff1a; 1、部署 InternLM2-Chat-1.8B 模型進行智能對話 1.1安裝依賴庫&#xff1a; pip install huggingface-hub0.17.3 pip install transformers4.34 pip install psutil5.9.8 pip install accelerate0.24.1…

Elasticsearch之文本分析

文本分析基本概念 官網&#xff1a;Text analysis | Elasticsearch Guide [7.17] | Elastic 官網稱為文本分析&#xff0c;這是對文本進行一直分析處理的方式&#xff0c;基本處理邏輯是為按照預先制定的分詞規則&#xff0c;把原本的文檔進行分割成多個小顆粒度的詞項&#x…

Python pands使用引擎實現excel條件格式

截至我的知識更新日期&#xff08;2023年&#xff09;&#xff0c;Pandas 庫本身并不直接支持Excel條件格式。Pandas 是一個強大的Python數據分析庫&#xff0c;它主要用于數據分析和操作&#xff0c;而不是用于創建或編輯Excel文件的格式。 然而&#xff0c;你可以使用 openp…

如何給出好的“文言一心”指令?

一、文言一心是什么&#xff1f; 在現代技術背景下&#xff0c;“文言一心”還是百度公司創建的一款大語言模型。這款模型基于飛槳深度學習平臺和文心知識增強大模型&#xff0c;并擁有強大的中文語料庫&#xff0c;可以理解和生成富含文化內涵和哲理的文本內容。其核心技術架構…

社區醫院|基于SprinBoot+vue的社區醫院管理服務系統(源碼+數據庫+文檔)

社區醫院管理服務系統 目錄 基于SprinBootvue的社區醫院管理服務系統 一、前言 二、系統設計 三、系統功能設計 1系統功能模塊 2管理員功能模塊 3用戶功能模塊 4醫生功能模塊 四、數據庫設計 五、核心代碼 六、論文參考 七、最新計算機畢設選題推薦 八、源碼獲取…

看花眼,眼花繚亂的主食凍干到底應該怎么選?靠譜的主食凍干分享

隨著科學養貓知識的普及&#xff0c;主食凍干喂養越來越受到養貓人的青睞。主食凍干不僅符合貓咪的飲食天性&#xff0c;還能提供均衡的營養&#xff0c;有助于維護貓咪的口腔和消化系統健康。許多貓主人認識到了主食凍干喂養的諸多益處&#xff0c;計劃嘗試這種喂養方式&#…