Bài 15: Scoped CSS trong VueJS và các kĩ thuật liên quan

Chào mừng tất cả các bạn đã quay trở lại với series học VueJS với Laravel của mình, ở bài trước mình đã hướng dẫn các bạn cách xử lý các sự kiện khi người dùng tương tác bằng chuột hoặc bàn phím với VueJS, các bạn có thể xem lại ở đây. Ở bài này chúng ta sẽ tìm hiểu về một trợ thủ rất đắc lực trong quá trình phát triển các ứng dụng Vue nữa đó là thiết lập phạm vi cho CSS trong Vue.

Vấn đề

Trong quá trình phát triển ứng dụng, theo thời gian càng ngày code càng phình to theo thời gian, số lượng component tăng lên vùn vụt, các thiên tài trong group liên tục commit code lên branch, ngày mới đến công ty chào buổi sáng bằng command git pull ... xem lại project thì đã thêm một đống component, code mới. Ngại ngùng không muốn động vào mớ code ấy. Các bạn tiếp tục code phần của mình.

Dẫu vậy đời không như là mơ, đến một ngày các bạn tạo một component Vue mới, viết code ngon lành, dẫu vậy lúc load lại trang chạy thử thì dòng text màu xanh mà liên tục bị chuyển thành màu đỏ. Bực mình sửa mãi không được thế là đánh bài cùi, sửa lại phần css ở component đó cho toàn bộ là !important cho chắc 😃.

Vấn đề ở đây là sau này code phình to hơn lần nào bạn cũng phải set !important hay sao? Hay sẽ ra sao nếu một thiên tài khác ở trong team khi code lại set !important cho class giống như của bạn? Chính bởi những điều đó nên các bạn nên nghĩ tới scoped trong Vue.

Cách sử dụng

Ở bài này chúng ta tạo ra 2 file để làm ví dụ như sau:

Parent.vue

<template>
    <div>
        <div class="welcome-text">
            This is parent
        </div>
        <Child></Child>
    </div>
</template>

<script>
    import Child from './Child.vue'
    export default {
        components: {
            Child
        }
    }
</script>

<style lang="scss">
.welcome-text {
	color: red;
}
</style>

Child.vue

<template>
    <div class="welcome-text">
        This is child
    </div>
</template>

<script>
    export default {

    }
</script>

<style lang="scss">
.welcome-text {
    color: blue;
}
</style>

Sau đó các bạn khai báo component Parent trong file app.js rồi thêm component đó vào welcome.blade.php nhé, nhớ luôn chạy php artisan servenpm run watch nha.

Sau khi các bạn load trang lên có thể thấy kết quả như sau:

scoped_css_VueJS

Có thể thấy rằng 2 dòng text in ra 1 của Parent và 1 của Child đều có cùng màu xanh theo css ở trong Child, thử inspect HTML ta thấy rằng css class welcome-text đã ghi đè thuộc tính color thành blue

Để khắc phục điều này ta có thể làm như sau, sửa lại phần CSS ở cả 2 component như sau:

<style lang="scss" scoped>
//your CSS
</style>

Ở đây khi chúng ta thêm thuộc tính scoped vào thẻ style thì CSS sẽ CHỈ được áp dụng duy nhất cho component đó. Bằng cách này các component khác nhau có CSS trùng nhau sẽ không bị ảnh hưởng đến nhau.

Các lời khuyên khi viết CSS cho component

Sử dụng scoped cho tất cả các component

Mình khuyến khích các bạn thêm scoped vào tất cả các component trên ứng dụng VueJS của các bạn (thực ra đối với những component là layout hay component root các bạn không để scoped cũng chấp nhận được). Nhưng để an toàn nhất, tránh việc sau này có những ảnh hưởng xấu có thể xảy đến khi 5-10 người cùng code và thay đổi.

Viết CSS sử dụng pre-compiled SCSS

Khi viết CSS cho component Vue hỗ trợ chúng ta viết bằng SCSS, các bạn có thể tuỳ chỉnh bằng cách thay đổi tham số lang="scss" trong thẻ style.

Bằng cách viết này thay vì viết dài dòng như trước:

<style lang="css" scoped>
.welcome-text {
    color: red;
}

.welcome-text .text {
    color: blue;
}
</style>

Giờ đây code đã gọn hơn rất nhiều như sau:

<style lang="scss" scoped>
.welcome-text {
    color: red;
    .text {
        color: blue;
    }
}
</style>

Bên cạnh đó viết bằng SCSS giúp ta dễ quan sát hơn CSS được áp dụng lồng nhau như thế nào. code gọn hơn nhiều, lúc cần tìm một selector cũng sẽ dễ hơn.

Cách đặt tên class

Thường khi chúng ta sử dụng các framework UI nổi tiếng kiểu như Bootstrap, họ dùng những tên class cho button như btn, btn-success. Do đó để tránh việc bị tác động lên code HTML do bạn viết, thì mình khuyến khích các bạn nên đặt tên cho các class của riêng bạn bằng những cái tên độc nhất.

Ngoài scoped các bạn sử dụng CSS module để khi biên dịch mã HTML, tên các class sẽ được sinh độc nhất, tránh được tối đa việc bị trùng lặp(đồng thời làm khó các thanh niên chuyên đi xem trộm CSS 😄, Facebook, Manychat là 2 hệ thống lớn với rất nhiều mã HTML và mình thấy họ đã tận dụng cách này. Để sử dụng CSS module các bạn làm như sau:

<template>
    <div>
        <div :class="[$style.welcome_text]">
            This is parent
            <div :class="[$style.text]">
                hey
            </div>
        </div>
        <Child></Child>
    </div>
</template>

<script>
    import Child from './Child.vue'
    export default {
        components: {
            Child
        }
    }
</script>

<style lang="scss" scoped module>
.welcome_text {
    color: red;
    .text {
        color: blue;
    }
}
</style>

Chúng ta thêm option module vào thẻ style sau đó mỗi tên class các bạn muốn gọi thì ở trên phần template chúng ta bind class với biến là $style nhé.

Sau đó ta thử load lại trang và xem kết quả (mở inspect HTML nhé các bạn):

css_module_VueJS

Các bạn có thể thấy 2 class của component Parent có tên rất dị 😃, còn với component Child thì tên vẫn giữ nguyên do không dùng CSS module.

Kết luận

Qua bài này có 2 kĩ thuật mình muốn gửi tới các bạn đó là scoped CSSCSS module, đồng thời mình khuyến khích các bạn nên áp dụng các kĩ thuật ấy một cách thích hợp vào trong các ứng dụng của riêng các bạn, bên cạnh đó mình có đưa ra một số lưu ý cho các bạn trong quá trình viết CSS cho component.

Cám ơn các bạn đã theo dõi, ở bài sau mình sẽ hướng dẫn các bạn cách chúng ta gọi API từ backend Laravel nhé (oh finally, tên series là học ..... với Laravel mà đến tận bài này mới ra đc 1 bài 😄). Nếu có gì thắc mắc các bạn để lại dưới phần comment nhé ^^!