Vue Router

[Vue-router] Lazy loading routes

Chào mừng mọi người quay trở lại với blog của mình. Đây là bài đầu tiên sau một thời gian tạm dừng viết blog thì hôm nay lại ngứa chân tay quay trở lại chia sẻ với mọi người những gì mình học được.

Ở bài này chúng ta sẽ cùng tìm hiểu Lazy loading routes khi dùng Vue-router nhé. Nghe thấy từ lazy chắc chúng ta cũng có thể nhớ lại được đây là 1 kĩ thuật áp dụng rất nhiều trong phát triển ứng dụng web để tiết kiệm tài nguyên, xem đến đâu tải nội dung đến đó. Chúng ta cùng tìm hiểu xem lazy load route có gì hay nhé.

Story

Trong quá trình phát triển ứng dụng, khi mà web của mình lớn lên, nhiều trang hơn, mỗi trang lại có nhiều xử lý hơn thì việc làm thế nào để tốc độ tải trang tối ưu nhất là điều mình quan tâm hơn cả. Mình cũng có đọc được về lazy load route từ lâu rồi nhưng nó chỉ là 1 section bé bé ở trang chủ vue-router, và cũng không quan tâm lắm, vì nghĩ cùng lắm load nhanh hơn đc 1-2 giây, đến giờ thì mới thấy dù chỉ là 1-2 giây đó cũng ảnh hưởng rất nhiều đến trải nghiệm của người dùng (chả thế mà thegioididong tốn cả đống tiền để làm trang web tải với tốc độ chỉ cỡ vài ba trăm ms 💯). Chúng ta cùng bắt tay vào tìm hiểu nhé 😃.

VueJS hỗ trợ chúng ta cách load component không đồng bộ, các bạn có thể xem ở đây, component chỉ cần load khi nào nó được dùng đến. Hay nói cách khác, 1 component có thể được khai báo bằng cách trả về một Promise như sau:

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // Pass the component definition to the resolve callback
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

//hoặc

const Foo = () => Promise.resolve({ /* component definition */ })

Bình thường khi muốn import 1 component ta hay làm như sau:

import('/Foo.vue')

Ở hàm import bên trên cũng trả về cho chúng ta là 1 Promise. Từ 2 cách trên chúng ta có thể khai báo 1 async component (componen được load không đồng bộ) như sau:

const Foo = () => import('./Foo.vue')

viết đầy đủ sẽ như sau:

const Foo = () => import('./Foo.vue')
const router = new VueRouter({
  routes: [
    { path: '/foo', component: Foo }
  ]
})

Cũng không khác bình thường lắm nhỉ 😉. Khi ta khai báo như trên, webpack sẽ tự chia ra cho mỗi route mà sử dụng 1 hoặc 1 số async component một file .js riêng biệt và sẽ chỉ cần load các component đó khi chúng ta đi vào route. Khác với cách bình thường ta hay dùng là tất cả trong 1 file app.js 😃 (cũng tiết kiệm đáng kể, và giảm tải load cho client)

Code snippets

Ở ví dụ này mình sẽ dùng Vue trong project Laravel nhé, các bạn nào dùng Vue-cli thì cũng làm tương tự luôn nhé.

Các bạn tạo mới 1 project, chạy npm install, sau đó chạy npm install --save vue-router

Đầu tiên ta tạo component App.vue ở thực mục `resources/js/components/ như sau:

<template>
    <div>
        <div>
            <ul>
                <li>
                    <router-link to="/">Go to Foo</router-link>
                </li>
                <li>
                    <router-link to="/bar">Go to Bar</router-link>
                </li>
                <li>
                    <router-link to="/baz">Go to Baz</router-link>
                </li>
            </ul>
        </div>
        <div>
            <router-view></router-view>
        </div>
    </div>
</template>

<script>
    export default {

    }
</script>

<style>

</style>

Ở trên các bạn có thể thấy ta có 3 router link đi tới routes. Tiếp theo, vẫn ở thư mục hiện tại ta có 3 components tương ứng với 3 routes đó:

Foo.vue

<template>
    <div>
        This is Foo
    </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

Bar.vue

<template>
    <div>
        Welcome to the Bar
    </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

Baz.vue

<template>
    <div>
        Hello, My name is Baz
    </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

Bước quan trọng tiếp theo là khai báo vue-router với 3 component trên được khai báo là async component như sau: router.js (cùng thư mục với file app.js)

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
export default new VueRouter({
  routes: [
    {
        path: '/',
        name: '',
        component: () => import('./components/Foo')
    },
    {
        path: '/bar',
        name: '',
        component: () => import('./components/Bar')
    },
    {
        path: '/baz',
        name: '',
        component: () => import('./components/Baz')
    }
  ]
})

Các bạn có thể thấy các viết cũng không khác bình thường là mấy đúng không 😃

Khai báo router và component "tổng" App.vue ở file app.js như sau: app.js

//Your code
import router from './router.js'

Vue.use(router)

Vue.component('app', require('./components/App.vue').default);
const app = new Vue({
    el: '#app',
    router
});

Cuối cùng là sửa lại file welcome.blade.php nhé:

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">
        <title>Laravel</title>
        <style>
            html, body {
                height: 100vh;
            }
            #app {
                display: flex;
                justify-content: center;
                height: 100%;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <app></app>
        </div>
        <script src="/js/app.js"></script>
    </body>
</html>

Ở đây nếu các bạn chạy npm run dev hoặc npm run watch sẽ bị báo lỗi Support for the experimental syntax 'dynamicImpo rt' isn't currently enabled bởi vì Babel không thể parse được cú pháp chúng ta viết ở trong file router.js. Ta chỉ cần cài thêm gói sau:

npm install --save @babel/plugin-syntax-dynamic-import

sau đó tại thư mục gốc, tạo file .babelrc với nội dung như sau:

{
    "plugins": ["@babel/plugin-syntax-dynamic-import"]
}

Cuối cùng cấu trúc thư mục của ta trông sẽ như sau: Folder_structure Bây giờ các bạn chạy:

npm run watch

Sau khi compile thành công ta mở folder public sẽ thấy xuất hiện các file 0.js, 1.js....như sau (các số 0,1,2 là việc webpack quyết định chứ không theo thứ tự các route ta định nghĩa từ trên xuống dưới đâu nhé ae 😃 ):

router_file

Đó chính là các file tương ứng với component của 1 route mà ta định nghĩa ở file router.js. Các bạn có thể mở lên và thấy nội dung của từng component rất rõ ở trong đó nhé. 😃.

Ổn rồi đó, khởi chạy project php artisan server (với Laravel) hoặc npm start (với vue-cli) và mở trình duyệt, ta sẽ thấy như sau:

sau đó mở Chrome devtool để thấy hiện như sau nhé:

Các bạn có thể thấy khởi đầu khi mở app lên ta truy cập vào route / nên ta chỉ cần load lên file 2.js tương ứng với nội dung của Foo sau đó ta truy cập vào Bar thì file 0.js được load và tương ứng với Baz.

Ta thấy các file có tên là 0.js, 1.js trông không được đẹp lắm, và ta cũng muốn đưa nó vào trong thư mục js (vì nó là file js mà 😄). Khi đó ta sửa lại file router.js một chút như sau: router.js

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
export default new VueRouter({
  routes: [
    {
        path: '/',
        name: '',
        component: () => import(/* webpackChunkName: "js/routes/foo" */ './components/Foo')
    },
    {
        path: '/bar',
        name: '',
        component: () => import(/* webpackChunkName: "js/routes/bar" */ './components/Bar')
    },
    {
        path: '/baz',
        name: '',
        component: () => import(/* webpackChunkName: "js/routes/baz" */ './components/Baz')
    }
  ]
})

Lưu lại file và chạy lại npm (nếu bạn vẫn chạy npm run watch thì không cần 😄 ). Mở folder public/js ta sẽ thấy như sau:

Folder_structure

Vậy là ta đã có các file route được format tên tuổi rõ ràng và đặt ở trong thư mục js 😄

The end

Sau nhiều project cả làm với team hoặc freelance cá nhân, thì mình thấy khả năng project scale sẽ rất nhanh, project sẽ có nhiều routes, và mỗi routes thì các luồng xử lý loằng ngoằng. Và nếu để chung tất cả trong app.js thì client sẽ phải tải về toàn bộ file app.js ngay từ lần truy cập đầu tiên mà thực chất tại mỗi thời điểm họ chỉ ở 1 route.

Bằng các Lazy loading routes chúng ta sẽ tiết kiệm được kha khá tài nguyên cho user, tăng tốc độ tải trang, cải thiện đáng kể UX. Hi vọng rằng qua bài này các bạn biết thêm được một kĩ thuật khi làm việc với Vue. Hẹn gặp lại các bạn ở các bài sau. Nếu có gì thắc mắc cứ comment bên dưới để mình được biết nhé 😉

Registration Login
Sign in with social account
or
Lost your Password?
Registration Login
Sign in with social account
or
A password will be send on your post
Registration Login
Registration