Khám phá cách mới để cấu trúc các components trong ứng dụng Vue

1. Lời nói đầu?

Vue đã được một thời gian phát triển. Nó đã đạt được sự phổ biến rộng rãi trong cộng đồng nhà phát triển và đã tìm cách thu hút sự chú ý của nhiều Developers ứng dụng như react/angular. Phần lớn trong số này là nhờ các API components đơn giản và trực quan tồn tại trong Vue. Tuy nhiên, với Vue 3, các nhà sản xuất đã quyết định thay đổi (hoặc giới thiệu một giải pháp thay thế) cách tạo ra các components trong Vue. Đây là Composition API.

Khi được phát hành lần đầu, nó đã nhận được những phản ứng trái chiều từ cộng đồng, nhưng dần dần, hầu hết các Vue Developers đã nhận ra rằng nó dễ sử dụng và làm clean code hơn bản cũ. Hãy đào sâu vào nó ngay bây giờ và xem cách so sánh với API tùy chọn tiêu chuẩn từ các phiên bản Vue trước đó.

2. Ví dụ về một ứng dụng

Bạn có thể clone ứng dụng từ đây https://github.com/hungpnh/-vue-composition-api. Tôi chỉ đơn giản là tạo một trang đăng ký bình thường với tên user và password và nút gửi có khả năng thực hiện cuộc gọi API để tạo người dùng. Nó cũng cung cấp cho người dùng tùy chọn thay đổi ngôn ngữ giữa tiếng Anh và tiếng Việt. Tất cả các tính năng này được triển khai bằng options API.

Tất cả mọi thứ quan tâm xảy ra trong App.vue. Hãy cùng xem qua:

<template>
  <div id="app">
    <div class="form">
      <label for="username">{{dictionary.usernameLabel}}:</label>
      <input id="username" class="input" :value="username" @input="updateUsername" />
      <label for="password">{{dictionary.passwordLabel}}:</label>
      <input id="password" type="password" class="input"
      :value="password" @input="updatePassword" />
      <button class="submit" @click="submit">{{dictionary.signupLabel}}</button>
    </div>
    <div class="language-container">
      <label for="language">{{dictionary.languageLabel}}:  </label>
      <input type="radio" name="language" value="en" v-model="language"/>English
      <input type="radio" name="language" value="vn" v-model="language"/>Tiếng Việt
    </div>
  </div>
</template>

<script>
import axios from 'axios';
import en from './constants/en.json';
import vn from './constants/vn.json';

export default {
  name: 'app',
  data() {
    return {
      username: '',
      password: '',
      language: 'en',
    };
  },
  methods: {
    updateUsername(event) {
      this.username = event.target.value;
    },
    updatePassword(event) {
      this.password = event.target.value;
    },
    async submit() {
      // probably add some other businvns logic here :)
      await axios.post('https://jsonplaceholder.typicode.com/users', {
        username: this.username,
        password: this.password,
      });
      alert('User created');
    },
  },
  computed: {
    dictionary() {
      return this.language === 'vn' ? vn : en;
    },
  },
};
</script>

Chúng ta tạo hai trường trong dữ liệu tương ứng với hai trường đầu vào trong ứng dụng là username và password. Chúng ta có 2 methods cho việc cập nhật đó là updateUsername và updatePassword.

Nếu sau khi cập nhật sẽ gọi api và nếu hiện ra màn hình như bên dưới tức mọi thứ đều ok.

3. Vấn đề

Thoạt nhìn, mã này trông khá tốt. Đoạn code trên là một cái gì đó tất cả chúng ta đều quen thuộc.
Tuy nhiên, nếu bạn nhìn vào ứng dụng từ quan điểm "tính năng của ứng dụng", bạn sẽ nhận ra ứng dụng của chúng ta chỉ có một tính năng là "đăng ký". Mặc dù là một phần của một tính năng "đăng ký" đó, phần lớn các elements, hay các trường data các methods là những options khác nhau. Khi mà ứng dụng phình to ra và chúng ta thêm các tính năng trong "App" component, điều này có thể dẫn tới messy vì việc những tính năng khác nhau đó rải rác khắp các options trong component.

Những người tạo ra Vue đã nhận ra vấn đề này và đã đề xuất tạo ra composition api để giải quyết vấn đề này.

4. Sử dụng Composition API

Trong API mới, Vue cho phép chúng ta tạo ra chỉ một option, cụ thể là một setup method dùng để sets component của ta. Nó là một kiểu hooks cycle như created, mounted, ... trước đây.

Để hiểu rõ hơn về vấn đề này, hãy để rèactor lại ứng dụng hiện tại của sử dụng Composition API.
Trước tiên chúng ta cần cài đặt composition api và đăng ký nó trong ứng dụng:

yarn add @vue/composition-api

Sau đó đăng ký plugin này trong main.js:

import Vue from 'vue';
// Import the composition API plugin
import VueCompositionApi from '@vue/composition-api';
import App from './App.vue';

// Register the composition API
Vue.use(VueCompositionApi);

Vue.config.productionTip = false;

new Vue({
  render: h => h(App),
}).$mount('#app');

Chúng tôi sẽ bắt đầu refactor bằng cách duy trì khả năng reactive của ứng dụng vue đối với data attribute.
Trong Vue, chúng tôi có hai lựa chọn duy trì điều này; ref và reactive.

  • ref: Hiểu đơn giản là nó lấy giá trị bên trong một biến. Và giá trị này có thể thay đổi (reactive). Mỗi ref của một biến sẽ có một trường value chính là giá trị của biến này.
  • reactive: Tạo ra một object với giá trị reactive.
    Sau khi được cấu trúc lại, tệp App.vue sẽ trông như thế này:
<template>
  <div id="app">
    <div class="form">
      <label for="username">{{dictionary.usernameLabel}}:</label>
      <input id="username" class="input" :value="username" @input="updateUsername" />
      <label for="password">{{dictionary.passwordLabel}}:</label>
      <input id="password" type="password" class="input"
      :value="password" @input="updatePassword" />
      <button class="submit" @click="submit">{{dictionary.signupLabel}}</button>
    </div>
    <div class="language-container">
      <label for="language">{{dictionary.languageLabel}}:  </label>
      <input type="radio" name="language" value="en" v-model="language"/>English
      <input type="radio" name="language" value="vn" v-model="language"/>Tiếng Việt
    </div>
  </div>
</template>

<script>
import axios from 'axios';
import { ref, computed } from '@vue/composition-api';
import en from './constants/en.json';
import vn from './constants/vn.json';
export default {
  name: 'app',
  setup() {
    const username = ref('');
    const password = ref('');
    const language = ref('en');
    const updateUsername = (event) => {
      username.value = event.target.value;
    };
    const updatePassword = (event) => {
      password.value = event.target.value;
    };
    const submit = async () => {
      await axios.post('https://jsonplaceholder.typicode.com/users', {
        username,
        password,
      });
      alert('User created');
    };
    const dictionary = computed(() => (language.value === 'vn' ? vn : en));
    return {
      username,
      password,
      language,
      updateUsername,
      updatePassword,
      submit,
      dictionary,
    };
  },
};
</script>

Như bạn có thể thấy, tất cả code hiện chỉ nằm trong một option. Chúng tôi đã sử dụng ref để duy trì khả năng reactive của username, password và các language keys. Các methods giờ đã trở thành các functions. Ngoài ra ta đã sử dụng computed để đánh giá giá trị của từ điển dựa trên giá trị của ngôn ngữ.

Vài điều cần lưu ý ở đây:

  1. Bây giờ đoạn code trên hoàn toàn chỉ là functional programming.
  2. Bạn chỉ có thể truy cập vào những thứ mà bạn đã trả lại từ setup. Điều này làm cho mọi thứ dễ đọc hơn.
  3. Khi bạn sử dụng giá trị của refs, chỉ cần sử dụng tên là đủ, Nhưng trong setup method, chúng ta cần sử dụng ref.value.
  4. Tất cả các tính năng trước đây nằm rải rác trên các options khác nhau mà hiện tại đã được tập trung lại chỉ trong setup.

4. Kết luận

Tôi hy vọng rằng bài viết này đóng vai trò một phần nhỏ giới thiệu Vue 3 và khiến bạn hào hứng với Vue 3.

5. Nguồn

https://blog.bitsrc.io/making-sense-of-vue-3s-composition-api-775e37ae019e