Cách viết route gọn hơn trong Vue

VueJS Aug 11, 2020

Pre-conditions

  • Có kiến thức về VueJS
  • Có kiến thức về VueRouter

Nêu vấn đề

  Thông thường các dự án dùng VueJS như front end framework đều sử dụng VueRouter để quản lý các routes. Một cách thông thường các bạn thường khởi tạo VueRouter từ app.js hoặc main.js như sau

import router from './router';

Vue.router = router;

export default new Vue({
  el: '#app',
  components: {
    App,
  },
  template: '<App/>',
  ...,
  router,
});

trong đó thư mục router sẽ chứa file index.js là nơi giúp bạn thực hiện config router như sau

import Vue from 'vue';

import VueRouter from 'vue-router';
import routes from './routes';

export default new VueRouter({
  linkActiveClass: 'open active',
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition;
    } else {
      return {x: 0, y: 0};
    }
  },
  history: true,
  mode: 'history',
  routes,
});

Kiểu config này không có gì sai và ta thấy là toàn bộ routes của chúng ta sẽ được lưu trong cùng thư mục trong file routes.js và đây là nơi "Sự khốn nạn bắt đầu".

  • Thông thường it khi bạn phát triển các dự án nhỏ với ít routes mà thường là vài chục routes cho web
  • Bên cạnh sự tồn tại của route web thì còn 1 bộ không nhỏ các route api
  • Kết quả là : Khi anh bạn làm cùng chỉ sửa 1 chút trong routes và gây ra conflict thì bạn sẽ è cổ đi resolve conflict và check lại mớ routes hổ đốn này.

Ý tưởng và giải pháp

Ý tưởng của tôi ở đây là tìm cách :

  • Chia nhỏ file routes ra thành nhiều file js để dễ quản lý
  • Tiến hành load các route này thông qua code

Giai pháp :

  • File routes sẽ được chia thành nhiều file bé hơn

như các bạn thấy ở đây tôi chia ra file api, profile, timesheets ... cho mục đích của mình

  • Mỗi file js sẽ đảm bảo theo cấu trúc quy định bởi vue router. Ví dụ :
const MyProfile = () => import('@/pages/profile/MyProfile')
export default [
    {
        path: 'profile',
        name: 'MyProfile',
        meta: {
            auth: true
        },
        component: MyProfile
    },
];
export default [
    {
        path: '/api/v1',
        name: 'api',
        meta: {
            auth: true
        },
        children: [
            {
                name: 'api.profiles',
                path: 'profiles',
                children: [
                    {
                        name: 'api.profiles.me',
                        path: 'me',
                    },
                ]
            }
        ]
    }
]
  • Giờ việc còn lại là tìm cách load các file routes này thông qua file index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

const TheContainer = () => import('@/containers/TheContainer')

const Login = () => import('@/pages/Login')
// Views
const Dashboard = () => import('@/pages/Dashboard')

// Error Pages
const Page404 = () => import('@/pages/errors/Page404')

Vue.use(VueRouter)

let routes =  [
    {
        path: '/',
        component: TheContainer,
        name: 'Home',
        meta: {
            auth: true
        },
        redirect: 'dashboard',
        children: [
            {
                path: 'dashboard',
                name: 'Dashboard',
                meta: {
                    auth: true
                },
                component: Dashboard
            },
        ]
    },
    {
        path: '/login',
        name: 'Login',
        meta: {
            auth: false
        },
        component: Login
    },
    {
        path: "*",
        meta: {
            auth: undefined
        },
        component: Page404
    }

];

export default new VueRouter({
    mode: 'history', // https://router.vuejs.org/api/#mode
    linkActiveClass: 'active',
    scrollBehavior: () => ({y: 0}),
    routes: configRoutes()
})

function configRoutes() {
    const requireRoutes = require.context('.', true, /\.js$/)
    requireRoutes.keys().forEach(fileName => {
        if (fileName === './index.js') return;
        if (fileName === './api.js'){
            routes = routes.concat(requireRoutes(fileName).default)
        }else{
            let child = routes[0].children;
            child = child.concat(requireRoutes(fileName).default)
            routes[0].children = child;
        }
    });
    return routes;
}
  • Các bạn chú ý một số điểm

   + Tôi bỏ qua file index.js cùng thư mục vì nó ko chứa route tôi muốn load

   + Route API hơi khác các route khác ở chỗ tôi muốn nó nằm ở gốc nên sẽ concat nó trực tiếp với route default

    + Ở đây tôi dùng CoreUI nên các route web thường ko nằm ở gốc mà là con của container, chính vì vậy tôi phải xử lý đặc biệt một chút là dùng route container là route default rồi khi load tôi sẽ add nó vào routes[0] chính thuộc về containers. Cách làm này ko được đẹp mắt nhưng tạm thời hoạt động tốt

    +   Việc tìm kiếm file js được thực hiện thông qua `require.context`

Kết luận

  • Với cách làm như này bạn sẽ dễ dàng thao tác và định nghĩa routes hơn
  • Các file routes được chia nhỏ sẽ dễ maintain hơn
  • It conflict và đỡ mệt mỏi hơn khi làm việc chung trong nhóm

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.