Tổ chức store trong dự án lớn với Vue.js

Tạo bởi Hoàng Vũ, chỉnh sửa cuối lúc 13 tháng 3, 2025

Trong các ứng dụng Vue.js lớn, việc tổ chức store hợp lý giúp dễ dàng mở rộng, bảo trì và tối ưu hiệu suất.

Mục tiêu bài học:

  • Hiểu cách chia nhỏ store thành nhiều module để quản lý tốt hơn.
  • Cấu trúc thư mục tối ưu khi làm việc với Vuex / Pinia.
  • Sử dụng mapState, mapGetters, mapMutations, mapActions để truy cập store nhanh chóng trong Component.
  • Thực hành: Xây dựng hệ thống quản lý giỏ hàng với Vuex / Pinia.

Tổ chức store

1. Chia nhỏ store thành nhiều module

Tại sao cần chia nhỏ store?

  • Giúp dễ quản lý khi dự án mở rộng.
  • Giảm tính phụ thuộc giữa các phần trong store.
  • Tăng hiệu suất bằng cách chỉ tải những module cần thiết.

Chia store thành nhiều module trong Vuex

// store/modules/products.js
export default {
  namespaced: true,
  state: {
    products: []
  },
  mutations: {
    setProducts(state, products) {
      state.products = products;
    }
  },
  actions: {
    async fetchProducts({ commit }) {
      const response = await fetch("https://api.example.com/products");
      const data = await response.json();
      commit("setProducts", data);
    }
  },
  getters: {
    productCount: (state) => state.products.length
  }
};
// store/modules/cart.js
export default {
  namespaced: true,
  state: {
    cart: []
  },
  mutations: {
    addToCart(state, product) {
      state.cart.push(product);
    }
  },
  getters: {
    cartTotal: (state) => state.cart.length
  }
};
// store/index.js (import các module vào store chính)
import Vue from "vue";
import Vuex from "vuex";
import products from "./modules/products";
import cart from "./modules/cart";

Vue.use(Vuex);

export default new Vuex.Store({
  modules: {
    products,
    cart
  }
});

Chia store thành nhiều module trong Pinia

// store/products.js
import { defineStore } from "pinia";

export const useProductStore = defineStore("product", {
  state: () => ({
    products: []
  }),
  actions: {
    async fetchProducts() {
      const response = await fetch("https://api.example.com/products");
      this.products = await response.json();
    }
  },
  getters: {
    productCount: (state) => state.products.length
  }
});
// store/cart.js
import { defineStore } from "pinia";

export const useCartStore = defineStore("cart", {
  state: () => ({
    cart: []
  }),
  actions: {
    addToCart(product) {
      this.cart.push(product);
    }
  },
  getters: {
    cartTotal: (state) => state.cart.length
  }
});

Sử dụng store trong Component:

<template>
  <div>
    <h2>Tổng số sản phẩm: {{ productCount }}</h2>
    <button @click="fetchProducts">Tải sản phẩm</button>
  </div>
</template>

<script>
import { useProductStore } from "../store/products";
import { storeToRefs } from "pinia";

export default {
  setup() {
    const store = useProductStore();
    return { fetchProducts: store.fetchProducts, ...storeToRefs(store) };
  }
};
</script>

2. Cấu trúc thư mục tối ưu khi làm việc với Vuex / Pinia

Cấu trúc thư mục hợp lý giúp dễ dàng mở rộng

src/
│── store/
│   ├── index.js       # Store chính (Vuex)
│   ├── products.js    # Store sản phẩm
│   ├── cart.js        # Store giỏ hàng
│── components/
│   ├── ProductList.vue
│   ├── Cart.vue
│── views/
│   ├── Home.vue
│   ├── Checkout.vue

Với Pinia, không cần index.js, mỗi store là một file độc lập.

3. Sử dụng mapState, mapGetters, mapMutations, mapActions

Các hàm giúp dễ dàng truy cập store trong Component Vue

Hàm Dùng để Vuex Pinia
mapState Lấy state từ store Không (Dùng storeToRefs)
mapGetters Lấy getters từ store Không (Dùng storeToRefs)
mapMutations Gọi mutations Không (Dùng actions trực tiếp)
mapActions Gọi actions

Ví dụ sử dụng trong Vuex

<template>
  <div>
    <h2>Tổng số sản phẩm: {{ productCount }}</h2>
    <button @click="fetchProducts">Tải sản phẩm</button>
  </div>
</template>

<script>
import { mapState, mapGetters, mapActions } from "vuex";

export default {
  computed: {
    ...mapState("products", ["products"]),
    ...mapGetters("products", ["productCount"])
  },
  methods: {
    ...mapActions("products", ["fetchProducts"])
  }
};
</script>

Ví dụ sử dụng trong Pinia

<template>
  <div>
    <h2>Tổng số sản phẩm: {{ productCount }}</h2>
    <button @click="fetchProducts">Tải sản phẩm</button>
  </div>
</template>

<script>
import { useProductStore } from "../store/products";
import { storeToRefs } from "pinia";

export default {
  setup() {
    const store = useProductStore();
    return { fetchProducts: store.fetchProducts, ...storeToRefs(store) };
  }
};
</script>

Thực hành: Xây dựng hệ thống quản lý giỏ hàng với Vuex / Pinia

Mô tả bài thực hành

  • Tạo store cart để quản lý giỏ hàng.
  • Thêm sản phẩm vào giỏ hàng bằng action.
  • Hiển thị số lượng sản phẩm trong giỏ hàng.

Xây dựng store giỏ hàng (Vuex)

export default {
  namespaced: true,
  state: {
    cart: []
  },
  mutations: {
    addToCart(state, product) {
      state.cart.push(product);
    }
  },
  getters: {
    cartTotal: (state) => state.cart.length
  }
};

Xây dựng store giỏ hàng (Pinia)

export const useCartStore = defineStore("cart", {
  state: () => ({
    cart: []
  }),
  actions: {
    addToCart(product) {
      this.cart.push(product);
    }
  },
  getters: {
    cartTotal: (state) => state.cart.length
  }
});

Sử dụng store trong Component Vue

<template>
  <div>
    <h2>Giỏ hàng: {{ cartTotal }} sản phẩm</h2>
    <button @click="addToCart({ id: 1, name: 'Laptop' })">Thêm Laptop</button>
  </div>
</template>

<script>
import { useCartStore } from "../store/cart";
import { storeToRefs } from "pinia";

export default {
  setup() {
    const store = useCartStore();
    return { addToCart: store.addToCart, ...storeToRefs(store) };
  }
};
</script>

Kết luận

Tóm tắt kiến thức quan trọng:

  • Chia store thành nhiều module để quản lý tốt hơn.
  • Cấu trúc thư mục hợp lý giúp dễ mở rộng.
  • mapState, mapGetters, mapMutations, mapActions giúp code gọn hơn.
  • Thực hành: Xây dựng hệ thống quản lý giỏ hàng.
Website Logo

Với hơn 10 năm kinh nghiệm lập trình web và từng làm việc với nhiều framework, ngôn ngữ như PHP, JavaScript, React, jQuery, CSS, HTML, CakePHP, Laravel..., tôi hy vọng những kiến thức được chia sẻ tại đây sẽ hữu ích và thiết thực cho các bạn.

Bình luận

Website Logo

Chào, tôi là Vũ. Đây là blog hướng dẫn lập trình của tôi.

Liên hệ công việc qua email dưới đây.

lhvuctu@gmail.com

Chúng Tôi Trên

Bạn đang muốn học về lập trình website?

Bạn cần nâng cao kiến thức chuyên nghiệp hơn để nâng cao cơ hội nghề nghiệp? Liên hệ