+11

Composition API trong VueJS (P2)

1. Mở đầu


Ở bài viết trước, mình đã giới thiệu cho các bạn về cách sử dụng Composition API trong VueJS 3x cũng như nhưng ưu điểm của nó so với mixin trước kia. Nếu bạn chưa đọc thì có thể xem lại tại đây. Còn ở phần này ta sẽ cùng nhau tìm hiểu chi tiết hơn về các tính năng mà Composition API cung cấp cho chúng ta. Nào chúng ta cùng bắt đầu.

2. Composition API


a. Lifecycle

Đối với các bạn đã làm việc với VueJS thì chắc hẳn chúng ta không còn lạ lẫm gì nữa đối với các Lifecycle hook mà VueJS cung cấp:

Trước kia khi dùng mixin ta hoàn toàn có thể sử dụng các lifecycle hook này trong đó luôn, còn với Composition API, mọi thứ có một chút thay đối là các lifecycle hook dùng trong đó sẽ thường bắn đầu bằng từ khóa on, cụ thể:

Normal lifcycle hook Lifecycle hook in Composition API 2
beforeCreate ---
created ---
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted

Đầu tiên bạn có thể thấy ngay so với các lifcycle hook gốc thì bền Composition API đang không hỗ trợ hai hàm là beforeCreate()created() vì hàm setup() của Composition API thực chất sẽ được gọi ngay sau beforeCreate() còn created() được gọi ngay sau setup():

Khi ta sử dụng các lifecycle hook trong một hàm setup() thì ta sẽ cần import nó và sử dụng như sau:

import { onBeforeMount, onMounted } from "vue";

export default {
    setup() {
        onBeforeMount(() => {
            console.log('Before mounte');
        });
        onMounted(() => {
            console.log('Mounted');
        });
    }
}

Ngoài các lifecycle hook chính mà mình kể đến ở trên ra thì Composition API cũng hỗ trợ các hook còn lại là:

Normal lifcycle hook Lifecycle hook in Composition API 2
activated onActivated
deactivated onDeactivated
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggerd

b. Computed property

Chắc hẳn các bạn còn nhớ đến tính năng computed property mà VueJS cung cấp cho chúng ta, về cơ bản thì nó sẽ là một giá trị được tạo ra dựa trên một số dữ liệu khác trong component của chúng ta, ví dụ:

<template>
    <div id="app">
        <p>Sum of numbers: {{ total }}</p>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                numbers: [5, 8, 3]
            }
        },
        computed: {
            total() {
                return this.numbers.reduce((sum, val) => sum + val);
            }
        }
    }
</script>

Mỗi khi giá trị numbrs trong phần data của component của chúng ta thay đổi thì giá trị của numberTotal sẽ được tính toán lại và cụ thể ở đây nó sẽ là tổng của các số có trong numbers. Ví dụ trên là cú pháp cơ bản cho việc dùng computed property trong VueJS, còn đối với Composition API thì ta sẽ có cú pháp như sau:

<template>
    <div id="app">
        <p>Sum of numbers: {{ numberTotal }}</p>
    </div>
</template>

<script>
    import { ref, computed } from 'vue';

    export default {
        const numbers = ref([5, 8, 3]);
        
        const total = computed(() => {
            return numbers.value.reduce((sum, val) => sum + val);
        })

        return { total }
    }
</script>

Đầu tiên bạn sẽ cần import hàm computed từ vue, tiếp sau đó để tạo ra một computed property như trước kia thì ta sẽ sử dụng hàm computed() với cú pháp như bạn thấy ở trên. Như bạn thấy đây cũng chỉ là một function và trả về một giá trị cho biến total của chúng ta đem đi sử dụng. Tất nhiên nếu nội dung của numbers thay đổi thì total sẽ lập tức được thay đổi ngay sau đó tương tự như computed property mà bạn đã biết trước đây.

Lưu ý: Ở đây bên trong hàm computed() để truy cập đến dữ liệu trong numbers thì ta vẫn phải dùng numbers.value như ở bài trước mìn hđã nhắc đến.

c. watch và watchEffect

Ngay từ các phiên bản từ trước đó, VueJS cung cấp cho chúng ta một tính năng rất hữu ích đó là watchers. Tính năng này cho phép bạn tạo ra một chức năng theo dõi sự thay đổi props hoặc state và thực hiện các tác vụ cần thiết với các thay đổi đó. Đơn giản nhất đó là trường hợp bạn làm tính năng filter, khi nội dung query string trên url của bạn thay đổi thì tất nhiên bạn sẽ cần gọi API để phản hội là thao tác đó của người dùng:

export default {
    watch: {
        '$route.query': function(value) {
            // Call API
        }
    }
}

Còn đối với Composition API thì sẽ cung cấp cho bạn hai hàm mới đó là watchwatchEffect. Ta xét ví dụ sau:

<template>
    <div>
        Search: <input v-model="keyword" />
        Result: <p>{{ result }}</p>
    </div>
</template>

<script>
    import { ref } from "vue";
    import { returnSomeValue } from "@/api/function";
    
    export default {
        setup() {
            const keyword = ref("")
            const result = ref("')

            return { keyword, result }
        }
    }
</script>

Bất cứ khi nào nội dung ô input bị thay đổi, ta mong muốn sẽ tự động gọi hàm returnSomeValue() với input là giá trị của thẻ input lúc đấy và sẽ trả về dữ liệu là một string nào đó cho biến result của chúng ta. Để làm được điều này, ta có thể dùng hàm watchEffect như sau:

<script>
    import { ref, watchEffect } from "vue";
    import { returnSomeValue } from "@/api/function";
    
    export default {
        setup() {
            const keyword = ref("")
            const result = ref("')

            watchEffect(() => {
                result.value = returnSomeValue(keyword.value);
            });

            return { keyword, result }
        }
    }
</script>

Hàm watchEffect nói trên sẽ được tự động chạy nếu có bất cứ ref object nào được sử dụng bên trong nó thay đổi, ở đây nếu keyword bị thay đổi khi chúng ta nhập nội dung và ô input thì nó sẽ đồng thời gọi lại nội dung bên trong watchEffect mà chúng ta đã định nghĩa. Tuy nhiên nếu bạn muốn định nghĩa rõ ràng theo biến cụ thể nào đó thay đổi thì mới chạy lại hàm thì bạn có thể dùng watch:

<script>
    import { watch } from "vue";
    
    export default {
        setup() {
        ...
            watch(valueToWatch, () => {
                ...
            });
            ...
        }
    }
</script>

Với cách viết như trên thì dù bên trong nội dung watch() của bạn có bao nhiêu ref object đi nữa thì hàm đó cũng sẽ không bị chạy lại chỉ trừ khi giá trị của valueToWatch thay đổi. Ngoài ra bạn cũng có thể truy cập vào giá trị cũ và giá trị mới của biến valueToWatch với cú pháp:

watch(valueToWatch, (newValue, oldeValue) => {
    ...
});

Thậm chí, bạn cũng có thể tracking nhiều giá trị một lúc và sử dụng giả trị cũ, mới của nhiều giá trị tracking đó luôn:

watch([valueToWatch1, valueToWatch2], ([newValue1, oldeValue1], [newValue2, oldeValue2]) => {
    ...
});

Có một điểm nữa bạn cần biết đó là nếu bạn sử dụng watchEffect thì nội dung bên trong hàm của bạn sẽ được chạy ngay làn đầu tiên component của bạn được load lên, tuy nhiên mắc định thì watch sẽ không chạy ngay lần đầu mà nó chỉ chạy khi có thay đổi. Nếu bạn muốn hàm watch của bạn cũng chạy ngay lần đầu khi component được load thì bạn có thể thêm vào option như sau:

watch(valueToWatch, (newValue, oldeValue) => {
    ...
}, { immediate: true });

3. Kết bài


Vậy là phần 2 và cũng là phần cuối của bài viết giới thiệu về Composition API đến đây là kết thúc, cám ơn các bạn đã ủng hộ. Và như mọi khi nếu có bất cứ thắc mắc gì thì bạn có thể comment ngay ở bên dưới và cũng đừng quên để lại một upvote và một share để ủng hộ cho bài viết của mình nhé. Hẹn gặp lại các bạn ở những bài viết sau.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.