Chào mừng các bạn đến với Cafedev! Trong bài viết này, chúng ta sẽ cùng nhau tìm hiểu về việc sử dụng Router Vuejs. Với sự phát triển không ngừng của công nghệ, việc sử dụng các framework front-end như Vuejs trở nên ngày càng phổ biến. Và để tạo ra các ứng dụng web linh hoạt và mạnh mẽ, việc sử dụng router là một phần không thể thiếu. Bắt đầu từ các khái niệm cơ bản đến các kỹ thuật nâng cao, chúng ta sẽ khám phá sâu hơn về Router Vuejs và cách nó có thể nâng cao trải nghiệm phát triển của bạn.


Pinia bắt đầu như một thử nghiệm để tái thiết kế cái gì một Store cho Vue có thể trông như với Composition API vào khoảng tháng 11 năm 2019. Kể từ đó, các nguyên tắc ban đầu vẫn giữ nguyên, nhưng Pinia hoạt động cho cả Vue 2 và Vue 3 và không yêu cầu bạn sử dụng Composition API. API là giống nhau cho cả hai ngoại trừ cài đặtSSR, và các tài liệu này được định về Vue 3 với ghi chú về Vue 2 khi cần thiết để có thể đọc được bởi người dùng Vue 2 và Vue 3!

1. Tại sao tôi nên sử dụng Pinia?

Pinia là một thư viện store cho Vue, nó cho phép bạn chia sẻ một trạng thái qua các thành phần/trang. Nếu bạn quen với Composition API, bạn có thể nghĩ rằng bạn đã có thể chia sẻ một trạng thái toàn cục với một đoạn mã đơn giản export const state = reactive({}). Điều này đúng đối với các ứng dụng trang đơn nhưng tạo ra những lỗ hổng bảo mật nếu nó được render phía máy chủ. Nhưng ngay cả trong các ứng dụng trang đơn nhỏ, bạn cũng có thể nhận được nhiều lợi ích từ việc sử dụng Pinia:

– Công cụ kiểm thử
– Các plugin: mở rộng tính năng của Pinia với các plugin
– Hỗ trợ TypeScript đúng đắn hoặc tự động hoàn thiện cho người dùng JS
– Hỗ trợ render phía máy chủ
– Hỗ trợ Devtools
– Một dòng thời gian để theo dõi các hành động, thay đổi
– Các store xuất hiện trong các thành phần mà chúng được sử dụng
– Du hành thời gian và dễ dàng gỡ lỗi hơn
– Hot module replacement
– Sửa đổi các store mà không cần tải lại trang của bạn
– Giữ bất kỳ trạng thái hiện tại nào trong quá trình phát triển
Nếu bạn vẫn còn nghi ngờ, hãy kiểm tra khóa học **chính thức** Mastering Pinia. Ở đầu, chúng tôi tìm hiểu cách xây dựng hàm defineStore() của chính mình và sau đó chúng tôi chuyển sang API chính thức của Pinia.

2. Ví dụ cơ bản

Đây là cách sử dụng Pinia trong thuật ngữ API (hãy chắc chắn kiểm tra Bắt đầu để biết hướng dẫn hoàn chỉnh). Bạn bắt đầu bằng cách tạo một store:

// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => {
    return { count: 0 }
  },
  // could also be defined as
  // state: () => ({ count: 0 })
  actions: {
    increment() {
      this.count++
    },
  },
})

Và sau đó bạn sử dụng nó trong một thành phần:

<script setup>
import { useCounterStore } from '@/stores/counter'

const counter = useCounterStore()

counter.count++
// with autocompletion ✨
counter.$patch({ count: counter.count + 1 })
// or using an action instead
counter.increment()
</script>

  <!-- Access the state directly from the store -->
  <div>Current Count: {{ counter.count }}</div>

Thử nghiệm trong Playground
Bạn thậm chí có thể sử dụng một hàm (tương tự như setup() của thành phần) để định nghĩa một Store cho các trường hợp sử dụng phức tạp hơn:

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  function increment() {
    count.value++
  }

  return { count, increment }
})

Thử nghiệm trong Playground
Nếu bạn vẫn chưa quen với setup() và Composition API, đừng lo lắng, Pinia cũng hỗ trợ một tập hợp tương tự các công cụ hỗ trợ như Vuex. Bạn định nghĩa các store cùng một cách nhưng sau đó sử dụng mapStores(), mapState() hoặc mapActions():

{22,24,28}
const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})

const useUserStore = defineStore('user', {
  // ...
})

export default defineComponent({
  computed: {
    // other computed properties
    // ...
    // gives access to this.counterStore and this.userStore
    ...mapStores(useCounterStore, useUserStore),
    // gives read access to this.count and this.double
    ...mapState(useCounterStore, ['count', 'double']),
  },
  methods: {
    // gives access to this.increment()
    ...mapActions(useCounterStore, ['increment']),
  },
})

Thử nghiệm trong Playground
Bạn sẽ tìm thấy thông tin chi tiết hơn về mỗi công cụ hỗ trợ trong các khái niệm cốt lõi.</em<>

3. Khóa học Chính thức

Khóa học chính thức cho Pinia là Mastering Pinia. Được viết bởi tác giả của Pinia, nó bao gồm mọi thứ từ cơ bản đến các chủ đề nâng cao như plugin, kiểm thử và render phía máy chủ. Đây là cách tốt nhất để bắt đầu với Pinia và làm chủ nó.

4. Tại sao Pinia

Pinia (phát âm /piːnjʌ/, giống như “peenya” trong tiếng Anh) là từ gần nhất với piña (dứa trong tiếng Tây Ban Nha) mà là một tên gói hợp lệ. Một quả dứa trong thực tế là một nhóm các bông hoa riêng lẻ kết hợp với nhau để tạo thành một loại trái cây đa dạng. Tương tự như các store, mỗi cái được sinh ra một cách riêng lẻ, nhưng chúng đều được kết nối vào cuối cùng. Đó cũng là một loại trái cây nhiệt đới ngon lành đặc trưng của Nam Mỹ.

5. Một ví dụ thực tế hơn

Đây là một ví dụ hoàn chỉnh hơn về API bạn sẽ sử dụng với Pinia với kiểu dữ liệu ngay cả trong JavaScript. Đối với một số người, điều này có thể là đủ để bắt đầu mà không cần đọc thêm, nhưng chúng tôi vẫn khuyến khích kiểm tra phần còn lại của tài liệu hoặc thậm chí bỏ qua ví dụ này và quay lại sau khi đã đọc về tất cả các Khái niệm Cốt lõi.

import { defineStore } from 'pinia'

export const useTodos = defineStore('todos', {
  state: () => ({
    /** @type {{ text: string, id: number, isFinished: boolean }[]} */
    todos: [],
    /** @type {'all' | 'finished' | 'unfinished'} */
    filter: 'all',
    // type will be automatically inferred to number
    nextId: 0,
  }),
  getters: {
    finishedTodos(state) {
      // autocompletion! ✨
      return state.todos.filter((todo) => todo.isFinished)
    },
    unfinishedTodos(state) {
      return state.todos.filter((todo) => !todo.isFinished)
    },
    /**
     * @returns {{ text: string, id: number, isFinished: boolean }[]}
     */
    filteredTodos(state) {
      if (this.filter === 'finished') {
        // call other getters with autocompletion ✨
        return this.finishedTodos
      } else if (this.filter === 'unfinished') {
        return this.unfinishedTodos
      }
      return this.todos
    },
  },
  actions: {
    // any amount of arguments, return a promise or not
    addTodo(text) {
      // you can directly mutate the state
      this.todos.push({ text, id: this.nextId++, isFinished: false })
    },
  },
})

Thử nghiệm trong Playground

6. So sánh với Vuex

Pinia bắt đầu như là một sự khám phá về điều gì có thể là phiên bản tiếp theo của Vuex, tích hợp nhiều ý tưởng từ các cuộc thảo luận của nhóm cốt lõi cho Vuex 5. Cuối cùng, chúng tôi nhận ra rằng Pinia đã thực hiện hầu hết những gì chúng tôi muốn trong Vuex 5, và quyết định làm cho nó trở thành lời khuyên mới thay thế.
So với Vuex, Pinia cung cấp một API đơn giản hơn với ít nghi lễ hơn, cung cấp các API theo kiểu Composition-API, và quan trọng nhất, hỗ trợ suy luận kiểu dữ liệu vững chắc khi sử dụng với TypeScript.

Các RFCs

Ban đầu, Pinia không trải qua bất kỳ quy trình RFC nào. Tôi đã thử nghiệm các ý tưởng dựa trên kinh nghiệm của mình trong việc phát triển ứng dụng, đọc mã của người khác, làm việc cho các khách hàng sử dụng Pinia, và trả lời câu hỏi trên Discord. Điều này cho phép tôi cung cấp một giải pháp hoạt động và được điều chỉnh cho nhiều trường hợp và kích thước ứng dụng khác nhau. Tôi thường xuyên công bố và làm cho thư viện phát triển trong khi vẫn giữ nguyên API cốt lõi của nó.
Bây giờ Pinia đã trở thành giải pháp quản lý trạng thái mặc định, nó phải tuân thủ cùng một quy trình RFC như các thư viện cốt lõi khác trong hệ sinh thái Vue và API của nó đã đạt đến một trạng thái ổn định.

So sánh với Vuex 3.x/4.x

Vuex 3.x là Vuex cho Vue 2 trong khi Vuex 4.x là cho Vue 3
API của Pinia rất khác biệt so với Vuex ≤4, cụ thể là:
mutations không còn tồn tại. Chúng thường được coi là rất cồng kềnh. Ban đầu chúng mang lại tích hợp devtools nhưng điều đó không còn là vấn đề nữa.
– Không cần phải tạo các bọc phức tạp tùy chỉnh để hỗ trợ TypeScript, mọi thứ đều có kiểu dữ liệu và API được thiết kế một cách để tận dụng suy luận kiểu TS càng nhiều càng tốt.
– Không còn chuỗi kỳ diệu để tiêm, nhập các hàm, gọi chúng, tận hưởng tính năng tự động hoàn thiện!
– Không cần phải động thêm các store nữa, chúng đều là động mặc định và bạn sẽ không thậm chí nhận ra. Lưu ý rằng bạn vẫn có thể sử dụng một cách thủ công để đăng ký một store bất kỳ khi nào bạn muốn nhưng vì nó là tự động nên bạn không cần phải lo lắng về điều đó.
– Không còn cấu trúc modules lồng nhau nữa. Bạn vẫn có thể lồng ghép các store một cách ngầm định bằng cách nhập và sử dụng một store bên trong một store khác nhưng Pinia cung cấp một cấu trúc phẳng theo thiết kế trong khi vẫn cho phép cách thức hỗ trợ sự phối hợp chéo giữa các store. Bạn thậm chí có thể có các phụ thuộc vòng tròn của các store.
– Không có namespaced modules. Với kiến trúc phẳng của các store, “đặt tên không gian” cho các store là tính cách thiết kế của chúng và bạn có thể nói rằng tất cả các store đều có không gian tên.
Để biết hướng dẫn chi tiết hơn về cách chuyển đổi một dự án Vuex ≤4 hiện có để sử dụng Pinia, xem Hướng dẫn di chuyển(migration) từ Vuex.

Hy vọng qua bài viết này, bạn đã có cái nhìn tổng quan về cách sử dụng Pinia Vuejs và hiểu rõ hơn về tầm quan trọng của nó trong phát triển ứng dụng web. Nếu bạn quan tâm đến các bài viết và hướng dẫn khác về công nghệ, đừng ngần ngại truy cập vào trang web của chúng tôi tại Cafedev để cập nhật những thông tin mới nhất. Cảm ơn bạn đã đọc bài viết và chúc bạn thành công trên hành trình phát triển phần mềm!

Tham khảo thêm: MIỄN PHÍ 100% | Series tự học Vuejs từ cơ bản tới nâng cao

Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của CafeDev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!