Tìm hiểu về toán tử mới optional chaining (?.) trong JavaScript và ứng dụng trong Vue.js

1270
11-11-2021
Tìm hiểu về toán tử mới optional chaining (?.) trong JavaScript và ứng dụng trong Vue.js

Như chúng ta đã biết, để hiển thị giá trị của một object trong template của Vue thì chúng ta thường sử dụng cú pháp nội suy (text interpolation) {{}} như sau:

<template>

  <p>{{ data.user.name }}</p>

</template>

Tuy nhiên, vấn đề đặt ra ở đây là ở đây là trong trường hợp giá trị chúng ta cần hiển thị lại nằm trong một nested object (object lồng nhau), khi một thuộc tính đứng trước nó không tồn tại sẽ dẫn đến lỗi, không hiển thị được template trên trình duyệt và ứng dụng sẽ bị crash ngay.

Trong ví dụ trên, chúng ta muốn hiển thị giá trị “name” của object data, nhưng nếu thuộc tính user đứng trước nó không tồn tại thì trình duyệt sẽ báo lỗi Error in render: "TypeError: Cannot read property 'name' of undefined".

Giải pháp thường hay được sử dụng để hiển thị nested object trong template mà không gây lỗi đó là sử dụng directive v-if của Vue để kiểm tra sự tồn tại của các thuộc tính trong nested object. Chẳng hạn trong ví dụ trên, chúng ta sẽ đặt v-if trên template để kiểm tra object data, nếu object data có thuộc tính user và user có thuộc tính name thì giá trị name mới được hiển thị trên trình duyệt:

<template v-if=”data.user && data.user.name”>

  <p>{{ data.user.name }}</p>

</template>

Chúng ta thấy rằng, giải pháp trên có thể tiện dụng trong trường hợp nested object chỉ có độ sâu khoảng một đến hai cấp. Trong trường hợp nested object được lồng nhau nhiều cấp thì chúng ta sẽ phải viết điều kiện v-if cho nhiều cấp, như trường hợp dưới đây:

<template v-if = ” data.obj1

      && data.obj1.obj2

      && data.obj1.obj2.obj3

      && data.obj1.obj2.obj3.obj4”

>

  <p>{{ data.obj1.obj2.obj3.obj4}}</p>

</template>

Như vậy có thể thấy code của chúng ta sẽ trở nên dài dòng, khó theo dõi và có thể lỗi khi chúng ta viết không đúng thứ tự các thuộc tính bên trong object. Để khắc phục được những bất cập ở trên, chúng ta có thể sử dụng một toán tử mới của javaScript là toán tử ?. (Optional Chaining)

optional chaining

Giới thiệu về toán tử optional chaining (ký hiệu ?.)

Được giới thiệu trong ES2020, toán tử ?. giúp giải quyết vấn đề truy cập đến các thuộc tính trong object ngay cả khi object hoặc thuộc tính bên trong nó không tồn tại. Thông thường, có 3 kiểu cú pháp để sử dụng toán tử ?. như sau:

- Sử dụng để truy cập thuộc tính của object:

Ví dụ: let abc = data?.obj.abc

- Sử dụng để truy cập thuộc tính của mảng:

Ví dụ: let abc = data?.[obj].abc

- Sử dụng để gọi hàm của một object ngay cả khi không chắc chắn hàm đó có tồn tại hay không:

Ví dụ: data.method?.()

Áp dụng toán tử ?. vào trường hợp hiển thị nested object trong template của Vue, chúng ta sẽ có thể viết ngắn gọn và an toàn như sau:

<template>

  <p>{{ data?.user?.name }}</p>

</template>

Trình duyệt sẽ không còn báo lỗi Error in render: "TypeError: Cannot read property 'name' of undefined" nữa. Tuy nhiên, trong trường hợp mà thuộc tính data hoặc user không tồn tại, chúng ta thấy giá trị undefined sẽ được hiển thị ra trên trình duyệt, như vậy thì sẽ không tốt cho trải nghiệm của người dùng. Để tránh vấn đề này, chúng ta có thể set giá trị rỗng “” hoặc một giá trị mặc định nào đó sẽ thay thế undefined bằng cách thêm giá trị đó vào sau 2 dấu ?? ngay phía sau cùng của nested object như sau:

- Set giá trị rỗng “”

<template>

  <p>{{ data?.user?.name ??”” }}</p>

</template>

- Set giá trị mặc định “abc”

<template>

  <p>{{ data?.user?.name ??”abc” }}</p>

</template>

Khi sử dụng một giá trị mặc định trong toán tử ?. chúng ta thấy thẻ p vẫn được render và hiển thị ra trong template của Vue. Có một cách hay hơn đó là sử dụng kết hợp toán toán ?. và directive v-text của Vue, trong ví dụ ở trên, thẻ p sẽ chỉ được render và hiển thị trong template của Vue khi giá trị của thuộc tính name không phải là undefined hoặc được set giá trị mặc định khác rỗng. Có thể thấy v-text là một directive rất hay của Vue nhưng lại bị rất nhiều người bỏ qua.

Ví dụ trên của chúng ta sẽ trở thành.

<template>

  <p v-text="data?.user?.name"></p>

</template>

Kiểm tra trình duyệt, chúng ta thấy trong trường hợp object data hoặc thuộc tính user không tồn tại thì trình duyệt không báo lỗi, thẻ p sẽ chỉ render trong trường hợp thuộc tính name có giá trị cụ thể. Code trở nên ngắn gọn. dễ theo dõi và an toàn. Như vậy rõ ràng, chúng ta thấy sử dụng toán tử ?. đi kèm với directive v-text của Vue để hiển thị giá trị của một nested object chính là một sự kết hợp quá hoàn hảo.

optional-chaining

Do toán tử ?. là một toán tử khá mới, được giới thiệu trong ES2020 nên có thể một số phiên bản trình duyệt cũ từ trước năm 2019 không hỗ trợ toán tử này. Ngoài ra, hiện chỉ có phiên bản Vue 3 hỗ trợ toán tử này, do vậy, để có thể sử dụng được toán tử này trên phiên bản Vue 2 và an toàn trên các trình duyệt cũ, chúng ta cần cấu hình Vue 2 như sau:

- Trước hết, cài đặt thư viện vue-template-babel-compiler để biên dịch code sử dụng toán tử ?. Chạy lệnh để cài đặt yarn add -D vue-template-babel-compiler

- Chỉnh sửa lại file vue.config.js, sử dụng vue-template-babel-compiler để giúp webpack biên dịch

// file vue.config.js

module.exports = {

  chainWebpack: config => {

    config.module

      .rule('vue')

      .use('vue-loader')

      .tap(options => {

        options.compiler = require('vue-template-babel-compiler')

        return options

      })

  }

}

Hoặc có thể cấu hình tại file webpack.config.js

// webpack.config.js

module: {

  rules: [

    {

      test: /\.vue$/,

      loader: "vue-loader",

      options: {

        compiler: require("vue-template-babel-compiler"),

      },

    },

  ],

}

Với cấu hình như trên thì chúng ta đã có thể sử dụng được toán tử ?. trong Vue 2. Trong trường hợp cần viết unit test bằng Jest cho các hàm hoặc các Vue template mà có sử dụng toán tử ?. chúng ta cần cấu hình thêm jest.config để có thể biên dịch được trên môi trường test như sau.

//file jest.config.js

module.exports = {

  transform: {

    '.*\\.(vue)$': 'vue-jest',

  },

  globals: {

    'vue-jest': {

      templateCompiler: {

        compiler: require('vue-template-babel-compiler'),

        transformAssetUrls: true,

      },

    },

  },

}

Lưu ý: phiên bản của vue-jest phải lớn hơn hoặc bằng 4.0.0 và jest nhỏ hơn hoặc bằng 26.6.3.

Kết luận

Mặc dù có một chút lích kích khi config để có thể sử dụng được toán tử ?. trên Vue 2, nhưng chúng ta có thừa lý do để thực hiện vì nhiều lợi ích và ưu điểm vượt trội mà toán tử ?. mang lại cho chúng ta khi nó được sử dụng trong Vue như đã phân tích ở trên.

SHARE