18/10/2018, 23:33

Những lỗi cần tránh khi làm việc với Vuejs

Include template compiler Vấn đề đầu tiên của chúng ta là một vấn đề khá cơ bản. Việc đầu tiên cần làm để sử dụng Vue.js là import nó. Nếu bạn làm theo hướng dẫn chính thức và sử dụng một inline template cho component của bạn, thì bạn sẽ nhận được một trang trắng. import Vue from 'vue'; var ...

Include template compiler

Vấn đề đầu tiên của chúng ta là một vấn đề khá cơ bản. Việc đầu tiên cần làm để sử dụng Vue.js là import nó. Nếu bạn làm theo hướng dẫn chính thức và sử dụng một inline template cho component của bạn, thì bạn sẽ nhận được một trang trắng.

import Vue from 'vue';

var vm = new Vue({
  el: '#vm',
  template: '<div>Hello World</div>',
});

Lưu ý rằng vấn đề này không xảy ra khi bạn định nghĩa template với render function hoặc SFC (Single File Component).

Trên thực tế, có rất nhiều Vue builds. Trình build mặc định được export bởi package NPM đó là runtime-only build. Và nó không mang trình biên dịch template.

Đối với một số thông tin cơ bản, thì template compiler hoạt động giống như JSX của React. Nó thay thế template string bằng các function calls để tạo một Virtual DOM node.

// #1: import full build in JavaScript file
import Vue from 'vue/dist/vue.js';

// OR #2: make an alias in webpack configuration
config.resolve: {
  alias: { vue: 'vue/dist/vue.js' }
}

// OR #3: use render function directly
var vm = new Vue({
  el: '#vm',
  render: function(createElement) {
    return createElement('div', 'Hello world');
  }
});

Với các SFC, vấn đề này sẽ không xảy ra. Cả vue-loader và vueify đều là các công cụ được sử dụng để handle các SFC. Chúng tạo ra các JavaScript components đơn giản bằng cách sử dụng hàm render để định nghĩa template. Để sử dụng các tring templates trong các components, hãy sử dụng một Vue.js build đầy đủ.

Tóm lại, để khắc phục sự cố này, hãy chỉ định bản build chính xác trong quá trình import hoặc tạo một alias dành cho Vue trong bundler config.

Bạn nên lưu ý rằng việc sử dụng các string template làm giảm performance ứng dụng, bởi vì trình biên dịch sẽ được thực thi khi chạy ứng dụng.

Có nhiều cách khác để định nghĩa một component template, vì vậy hãy xem bài viết này. Ngoài ra, tôi cũng recommend nên sử dụng hàm render trong Vue instance.

Keep property’s reactivity

Nếu bạn sử dụng React, bạn có thể biết reactivity của nó phụ thuộc vào việc gọi hàm setState để cập nhật giá trị của các thuộc tính.

Reactivity với Vue.js hơi khác một chút. Nó dựa trên việc ủy quyền các component properties. Các hàm Getter và setter sẽ bị ghi đè và sẽ thông báo update Virtual DOM.

var vm = new Vue({
  el: '#vm',
  template: `<div>{{ item.count }}<input type="button" value="Click" @click="updateCount"/></div>`,
  data: {
    item: {}
  },
  beforeMount () {
    this.$data.item.count = 0;
  },
  methods: {
    updateCount () {
      // JavaScript object is updated but
      // the component template is not rendered again
      this.$data.item.count++;
    }
  }
});

Trong đoạn code trên, Vue instance có một thuộc tính được gọi là item (được định nghĩa trong data). Thuộc tính này chứa một empty object.

Trong quá trình khởi tạo component, Vue tạo proxy dưới dạng các get và set function được gắn với thuộc tính item. Vì vậy, framework sẽ giám sát value thay đổi và thực hiện phản ứng cuối cùng.

Tuy nhiên, thuộc tính count không phản ứng, vì nó không được khai báo lúc khởi tạo.

Trên thực tế, việc proxifying chỉ xảy ra ở thời gian khởi tạo component và hàm lifecycle thebeforeMount sẽ kích hoạt sau đó.

Bên cạnh đó, item setter không được gọi trong việc định nghĩa count. Vì vậy, proxy sẽ không kích hoạt và thuộc tính count sẽ không có watch.

beforeMount () {
  // #1: Call parent setter
  // item setter is called so proxifying is propaged
  this.$data.item = {
    count: 0
  };
  
  // OR #2: explicitly ask for watching
  // item.count got its getter and setter proxyfied
  this.$set(this.$data.item, 'count', 0);
  
  // "Short-hand" for:
  Vue.set(this.$data.item, 'count', 0);
}

Nếu bạn giữ item.count trong beforeMount, và gọi Vue.set thì sau đó nó sẽ không tạo watch.

Vấn đề tương tự cũng xảy ra với array khi sử dụng direct affection trên các index không xác định. Trong những trường hợp như vậy, bạn nên sử dụng các array proxifyed functions như push và slice.

Bạn cũng có thể đọc thêm bài viết này.

Be careful with SFC exports

Bạn có thể sử dụng Vue thường xuyên trong các file JavaScript, nhưng bạn cũng có thể sử dụng các file Component đơn. Nó giúp tập trung code JavaScript, HTML và CSS trong một file.

Với các SFC, file component là tập hợp code với đuôi .vue. Vẫn được viết bằng JavaScript, và export ra component.

Có nhiều cách để export một variable/component. Thông thường, chúng ta sử dụng direct, named, hoặc exports mặc định. Named export sẽ ngăn người dùng đổi tên component. Nó cũng sẽ đủ điều kiện với tree-shaking.

/* File: user.vue */
<template>
  <div>{{ user.name }}</div>
</template>

<script>
  const User = {
    data: () => ({
      user: {
        name: 'John Doe'
      }
    })
  };
  export User; // Not work
  export default User; // Works
</script>

/* File: app.js */
import {User} from 'user.vue'; // Not work
import User from 'user.vue'; // Works (".vue" is required)

Sử dụng named exports không tương thích với cacs SFC, hãy lưu ý về điều này! Tóm lại, việc export một biến được đặt tên theo mặc định có thể là một ý tưởng hay. Bằng cách này, bạn sẽ nhận được message debug rõ ràng hơn.

Don’t mix SFC components

Việc đặt code, template và style cùng với nhau là một ý tưởng hay. Nhưng bên cạnh đó, tùy thuộc vào các ràng buộc và ý kiến của bạn, bạn có thể muốn giữ sự [tách biệt] (https://en.wikipedia.org/wiki/Separation_of_concerns) giữa code html, javascript và css.

Như được mô tả trong Vue documentation, nó tương thích với SFC.

Sau đó, một ý tưởng xuất hiện. Sử dụng cùng một code JavaScript và include nó trong các template khác nhau. Bạn có thể cho đó là một bad practice, nhưng nó giữ mọi thứ được đơn giản.

Ví dụ, một component có thể có cả read-only và read-write mode và nó hoạt động giống nhau.

* File: user.js */
const User = {
  data: () => ({
    user: {
      name: 'John Doe'
    }
  })
};
export default User;

/* File: user-read-only.vue */
<template><div>{{ user.name }}</div></template>
<script src="./user.js"></script>

/* File: user-read-write.vue */
<template><input v-model="user.name"/></template>
<script src="./user.js"></script>

Trong đoạn code này, cả các template read-only và read-write đều sử dụng cùng một file user.js.

Và khi bạn import cả hai component, bạn sẽ thấy rằng nó hoạt động không như mong đợi.

// The last import wins
import UserReadWrite from './user-read-write.vue';
import UserReadOnly from './user-read-only.vue';

Vue.component('UserReadOnly', UserReadOnly);
Vue.component('UserReadWrite', UserReadWrite);

// Renders two times a UserReadOnly
var vm = new Vue({
  el: '#vm',
  template: `<div><UserReadOnly/><UserReadWrite/></div>`
});

Component được định nghĩa trong user.js chỉ có thể được sử dụng trong một SFC. Nếu không, SFC được imported cuối cùng sử dụng nó sẽ override lên cái trước đó.

SFCs allow splitting code in separate files. But you can’t import thoses files in multiple Vue components.

Để làm cho nó đơn giản, thì không sử dụng lại JavaScript component code trong multiple SFC component. Tính năng separate code là một cú pháp tiện dụng, không phải là một design pattern.

Trên đây là một số lỗi cần tránh khi làm việc với Vuejs. Hy vọng nó sẽ giúp các bạn tránh được trong quá trình làm việc với Vuejs.

Mình mới làm quen với Vuejs trong 1 tháng nay nên bản dịch ở trên có thể sẽ còn sai sót, rất mong nhận được sự ghóp ý cũng như bổ sung để bản dịch được pefect hơn.

References

https://medium.freecodecamp.org/common-mistakes-to-avoid-while-working-with-vue-js-10e0b130925b

0