Vue router lazy loading - Xử lý lỗi khi không có internet

5/5/2022 vuevue-router

#Vấn đề

Trong quá trình làm trang note này (ver cũ, hiện tại thì mình đã chuyển sang dùng Nextjs), mình gặp lỗi không load được view khi mất kết nối mạng, kể cả sau đó có mạng lại vẫn không thể load được view. Nhưng điều quan trọng là không có bất kỳ một sự kiện gì xảy ra để user biết là có gì đó không đúng, chỉ thấy click vào link mà cứ trơ ra pepe_cry.

Nguyên nhân chính của lỗi này là do browser cache lại module url đã được gọi import, nên mặc dù Vue router cũng có gọi import lại khi tiếp tục click nhưng không thể import do browser đã cache kết quả request lỗi trước đó.

#Tìm cách giải quyết

#Cách 1

Mình tham khảo thì react-router-dom có hành vi là vẫn update url trên address bar, nhưng hiển thị màn hình trắng. Mặc dù cách này vẫn chưa ổn, tuy nhiên ít nhất user còn biết là có điều gì đó sai sai, thấy màn hình trắng thì hành vi quen thuộc sẽ là refresh lại trang (lúc này sẽ biết bị mất kết nối mạng). Trơ ra như Vue thì sẽ không biết làm gì. Từ cách làm này của React, mình suy nghĩ về hành vi của MPA là khi click link thì chuyển hướng sang link đó, nếu không có internet thì browser báo lỗi. Mình áp dụng hành vi này vào Vue router bằng cách khi có lỗi import view sẽ redirect sang link tương ứng, nếu internet vẫn mất thì browser sẽ báo lỗi cho user, ngược lại user vẫn sẽ đến được trang mong muốn.

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/test',
      component() {
        return import('./views/Test.vue').catch(() => {
          window.location.href = '/test';
        });
      }
    }
  ]
});

Kết quả:

#Cách 2

Cách trên tạm chấp nhận được, tuy nhiên lại làm mất hết state của app. Vì thế mình nghĩ đến giải pháp thứ 2, khi có lỗi import view, nếu không có internet sẽ alert lên cho user biết, ngược lại thì thêm param version là ngày hiện tại để tránh việc cache của browser:

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/test',
      component() {
        return import('./views/Test.vue').catch(() => {
          if (!navigator.onLine || xmlHttpRequestFailed) {
            alert('Please check your internet connection!');
          } else {
            return import('./views/Test.vue?v=' + new Date().toISOString());
          }
        });
      }
    }
  ]
});

xmlHttpRequestFailed: việc check navigator.onLine là chưa đủ, vì nó chỉ kiểm tra việc có kết nối đến LAN hay router không thôi (navigator.onLine). Vì thế nếu navigator.onLine === true cần kiểm tra thêm bằng cách request đến server của bạn xem có được không.

Kết quả:

#Kết

Mình khá hài lòng với cách giải quyết thứ 2, tuy nhiên vẫn chưa triệt để. Trường hợp mà trong module view A có import một module B mà trước đó module B cũng đã bị lỗi import do không có internet, thì sau khi A được load, A import B vẫn bị lỗi.

Để xử lý case này về mặt ý tưởng mình nghĩ là cần custom method import, xử lý khi có mạng sẽ thêm param version để tránh cache của browser. Tuy nhiên đẹp nhất vẫn là browser xử lý case này cho ta, là khi có mạng sẽ import lại những module bị lỗi thay vì lấy trong cache.