10 kinh nghiệm khi làm việc với các dự án lớn viết bằng Vue.js

Sử dụng slot để component dễ hiểu hơn và dễ tùy biến hơn

Bài viết hướng dẫn chi tiết khái niệm và cách dùng slot bạn đọc lại ở đây. Với việc sử dụng slot bạn sẽ có những component với khả năng xào đi nấu lại dễ hơn.

Một ví dụ để thấy lợi ích của slot trong thực tế.

Khi mới nhận thiết kế từ bên design, popup modal vô cùng đơn giản, chỉ gồm title, description và dâm ba cái button bên dưới. Thoạt nhìn chúng ta chỉ cần 3 cái prop và một sự kiện bắn ra khi click button để thay đổi tùy theo tình huống sử dụng.

Nhưng sau một thời gian, bên design họ vẽ vời thêm một mớ mới, nhúng form, chèn hẳn một component khác vào trong đó, vâng vâng. Prop không đáp ứng nổi độ khùng của mấy bạn design. Và cách mà chúng ta refactor lại cái modal bằng slot

<template>
  <div class="c-base-popup">
    <div v-if="$slot.header" class="c-base-popup__header">
      <slot name="header">
    </div>
    <div v-if="$slot.subheader" class="c-base-popup__subheader">
      <slot name="subheader">
    </div>
    <div class="c-base-popup__body">
      <h1>{{ title }}</h1>
      <p v-if="desc">{{ desc }}</p>
    </div>
    <div v-if="$slot.actions" class="c-base-popup__actions">
      <slot name="actions">
    </div>
    <div v-if="$slot.footer" class="c-base-popup__footer">
      <slot name="footer">
    </div>
  </div>
</template>

<script>
export default {
  props: {
    description: {
      type: String,
      default: null
    },
    title: {
      type: String,
      required: true
    }
  }
}
</script>

Tổ chức Vuex Store

Một lập trình viên bắt đầu mò mẫm cách xài Vuex vì họ gặp 2 vấn đề sau

  1. Cần truy cập vào dữ liệu từ một component khác, nó nằm xa ơi là xa so với component hiện tại.
  2. Sau khi component bị xóa khỏi DOM, bạn vẫn muốn dữ liệu đó không bị xóa

Khi bắt đầu tạo Vuex Store, chúng ta bắt đầu học được khái niệm module và cách sử dụng trong ứng dụng.

Không một quy chuẩn nào để tổ chức module, đồng nghĩa là mỗi người mỗi ý, đa phần các lập trình viên sẽ tổ chức theo dạng tính năng

  • Auth
  • Blog
  • Inbox
  • Setting

Hợp lý mà, một cách tiếp cận khác cũng hợp lý luôn là tổ chức theo dữ liệu trả về từ API

  • User
  • Team
  • Message
  • Widget
  • Article

Vì là quan điểm nên không thể nói đúng sai, nhưng chúng ta phải thống nhất một cách tổ chức Vuex Store mà mọi người đều đồng ý là hợp lý. Người mới vào team cũng dễ follow hơn.

Xem Kinh nghiệm tổ chức Vuex cho ứng dụng lớn

# Sử dụng flat state trong Vue Store

Sử dụng action để lấy và gửi dữ liệu

Hầu hết các network request được đưa vào Vuex action. Bạn có thắc mắc, tại sao lại thế? 🤨

Đơn giản là vì hầu hết các dữ liệu lấy về sẽ được đưa vào trong store. Mặc khác, xét trên khía cạnh đóng gói và tái sử dụng thì đây là cách mang lại sự dễ chịu khi sử dụng nhất.

Sử dụng mapState, mapGetters, mapMutationsmapActions

Không có lý do gì phải có nhiều giá trị computed hoặc phương thức chỉ vì bạn cần truy cập vào state/getter hoặc gọi actions/mutations bên trong component. Sử dụng các hàm được cung cấp sẵn của Vuex mapState, mapGetters, mapMutationsmapActions

import { mapState, mapGetters, mapActions, mapMutations } from "vuex";

export default {
  computed: {
    // truy cập properties trong my_module
    ...mapState("my_module", ["property"]),
    // truy cập getters
    ...mapGetters("my_module", ["property"]),
    // truy cập các properties khác
    ...mapState("my_module", {
      property: (state) => state.object.nested.property,
    }),
  },

  methods: {
    // truy cập actions
    ...mapActions("my_module", ["myAction"]),
    // truy cập mutations
    ...mapMutations("my_module", ["myMutation"]),
  },
};

Xem tài liệu của Vuex

Sử dụng API Factories

Tạo một hàm this.$api để có thể gọi ở bất kỳ đâu khi cần tạo network request. Trong thư mục gốc, thêm một thư mục tên api chứa tất cả các phương thức liên quan

api
├── auth.js
├── notifications.js
└── teams.js

Xem cách đăng ký một phương thức global

Sử dụng $config để truy cập biến môi trường

Trong bộ source chúng ta sẽ luôn cần những biến chưa config trên môi trường khác nhau

config
├── development.json
└── production.json

Nếu có thể truy cập những biến này thông qua this.$config có phải tiện lợi lắm không

import Vue from "vue";

import development from "@/config/development.json";
import production from "@/config/production.json";

if (process.env.NODE_ENV === "production") {
  Vue.prototype.$config = Object.freeze(production);
} else {
  Vue.prototype.$config = Object.freeze(development);
}

Tuân theo một nguyên tắc chung khi viết commit

Nếu bạn nào có contribute cho các dự án trên Github, sẽ thấy lợi ích của việc có một chuẩn chung khi viết diễn giải cho commit. Có thể lấy cách viết của Angular tham khảo

git commit -am "<type>(<scope>): <subject>"

# Một vài ví dụ
git commit -am "docs(changelog): update changelog to beta.5"
git commit -am "fix(release): need to depend on latest rxjs and zone.js"

Xem Bộ guide để viết code sạch dành riêng cho Vue

Khi lên production, fix luôn các package version đang xài

Không phải package nào cũng được đặt version theo quy tắc đã chuẩn hóa. Để tránh nửa đêm bị gọi dậy vì một trong các package đã cài bộ source bỗng dưng không tương thích, production không còn chạy như trên local.

Tội đồ là cái prefix ^ này. Xóa hết nó khi lên production

{
  "name": "my project",

  "version": "1.0.0",

  "private": true,

  "dependencies": {
    "axios": "0.19.0",
    "imagemin-mozjpeg": "8.0.0",
    "imagemin-pngquant": "8.0.0",
    "imagemin-svgo": "7.0.0",
    "nuxt": "2.8.1"
  },

  "devDependencies": {
    "autoprefixer": "9.6.1",
    "babel-eslint": "10.0.2",
    "eslint": "6.1.0",
    "eslint-friendly-formatter": "4.0.1",
    "eslint-loader": "2.2.1",
    "eslint-plugin-vue": "5.2.3"
  }
}

Xem Nguyên tắc đặt version

Sử dụng Virtual Scroller khi hiển thị nhiều dữ liệu

Khi cần hiển thị một số lượng lớn các hàng dữ liệu trên mộ trang, việc loop qua toàn bộ dữ liệu và render sẽ bị chậm. Dùng vue-virtual-scroller

npm install vue-virtual-scroller

Nó sẽ chỉ render các dữ liệu có thể vừa vặn trong viewport, phần dữ liệu chưa hiển thị trên viewport sẽ được lazy render khi cuộn tới, tăng tốc độ đáng kể

<template>
  <RecycleScroller
    class="scroller"
    :items="list"
    :item-size="32"
    key-field="id"
    v-slot="{ item }"
  >
    <div class="user">
      {{ item.name }}
    </div>
  </RecycleScroller>
</template>

Kiểm tra dung lượng package

Bộ source lớn thường đồng nghĩa sử dụng nhiều package lụm được trên mạng, nếu không để ý, việc cài đặt bừa bãi các package này dễ dẫn đến việc dung lượng tăng vọt

Visual Studio Code có công cụ để kiểm tra dung lượng import hoặc chạy công cụ Webpack Bundle Analyzer

Bài viết sử dụng tham khảo

https://www.telerik.com/blogs/10-good-practices-building-maintaining-large-vuejs-projects