vite 動態 import 引入打包報錯解決方案


關注公眾號: 微信搜索 前端工具人 ; 收貨更多的干貨

原文鏈接: 自己掘金文章 https://juejin.cn/post/6951557699079569422/

一、介紹

之前一直是使用 webpack 構建項目, 但是那種隨着項目越來越大運行、打包、熱更新緩慢帶來的無奈。。。

新項目開發果斷使用了vite2.0, 和 webpack 對比起來, 其他的優點不是很直觀,但在運行、打包、熱更新方面簡直倍數差距, 只能說我覺得 vite2.0 很舒服。

當然 vite 作為熱門構建共建優點還是很多的,詳情可以去官網看看 vite 官網地址

二、問題

// 問題代碼 () => import( /* @vite-ignore */ `${fileSrc}`)
routerList.push({
  path: `/${routerPath}`,
  name: `${routerName}`,
  component: () => import( /* @vite-ignore */ `${fileSrc}`)
})

之前使用 webpack 構建項目一直使用動態導入 require.context API 自動化注冊組件及路由;

轉移到 vite 之后,開發習慣當然不能變;隨即使用的是 import.meta.globEager 完成動態導入;

本地開發過程中很舒服沒問題,打包后部署到服務器報錯找不到動態導入的文件;裂開~~~

經過這幾天陸陸續續的嘗試最終解決,總結了以下幾種方案

三、需求

主要項目結構

├── components                    // 公共組件存放目錄
└── views                         // 路由視圖存放目錄
    └── Home                      // Home 頁面
        └── components            // Home 頁面封裝組件存放文件
            └── HomeHeader.vue    // Home 頁面頭部組件
        └── index.vue             // Home 主入口
        └── types                 // Home頁面專屬類型
    └── Login                     // Login 頁面
        └── components            // Login 頁面封裝組件存放文件
            └── LoginHeader.vue   // Login 頁面頭部組件
        └── index.vue             // Login 主入口
        └── types                 // Login頁面專屬類型
    ....
    ....

文件內部

export default defineComponent({
  name: 'home',
  isRouter: true,
  isComponents: false,
  setup() {
    ...
  }
})

組件內部通過定義

  • name: 路由組件名稱
  • isRouter: 是否自動為路由;
  • isComponents: 是否自動注冊為公共組件

四、 解決 (推薦方案二)

vite 動態導入有兩種方式

  • import.meta.glob: 通過動態導入默認懶加載,通過遍歷加 then 方法可拿到對應的模塊文件詳情信息

  • import.meta.globEager: 直接引入所有的模塊, 即靜態 import; 我的就是使用該方案打包部署報錯

以下方案有需要自行取舍

4.1 方案一

使用 import.meta.glob

缺點:

  • 不使用 then 方法拿不到模塊信息,無法進行判斷是否需要自動注冊組件或路由;
  • 使用了 then 方法成異步的了, 路由渲染的時候文件還沒獲取成功注冊不到

但是你可以用單獨文件夾來區分,我認為限制性太大不夠優雅;

// global.ts
export const vueRouters = function (): Array<RouteRecordRaw> {
  let routerList: Array<RouteRecordRaw> = [];
  const modules = import.meta.glob('../views/**/*.vue')
  Object.keys(modules).forEach(key => {
    const nameMatch = key.match(/^\.\.\/views\/(.+)\.vue/)
    if(!nameMatch) return
    const indexMatch = nameMatch[1].match(/(.*)\/Index$/i)
    let name = indexMatch ? indexMatch[1] : nameMatch[1];
    // 首字母轉小寫 letterToLowerCase 首字母轉大寫 letterToUpperCase
    routerList.push({
      path: `/${letterToLowerCase(name)}`,
      name: `${letterToUpperCase(name)}`,
      component: modules[key]
    });
  })
  return routerList
};

使用 router.ts

import { vueRouters } from '../services/global'
const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'Login',
    component: () => import('@/views/Login/index.vue')
  },
  ...vueRouters()
]

4.2 方案二 “推薦”

使用 import.meta.globimport.meta.globEager

  • import.meta.glob: 因為 import.meta.glob 獲取到的文件就是懶加載的,避免了使用 import 語句, 所以打包后不會報錯不存在動態引入了
  • import.meta.globEager:不使用 then 也能獲取到文件全局上下文進行有需要的判斷
// global.ts
function getModules() {
  const components = import.meta.glob('../views/**/*.vue')
  return components
}
function getComponents() {
  const components = import.meta.globEager('../views/**/*.vue')
  return components
}
// 自動注冊組件
export const asyncComponent = function (app: App<Element>): void {
  const modules = getModules();
  const components = getComponents();
  Object.keys(modules).forEach((key: string) => {
    const viewSrc = components[key];
    const file = viewSrc.default;
    if (!file.isComponents) return
    const AsyncComponent = defineAsyncComponent(modules[key])
    app.component(letterToUpperCase(file.name), AsyncComponent)
  });
  // console.log(app._component.components)
};

// 自動注冊路由
export const vueRouters = function (): Array<RouteRecordRaw> {
  let routerList: Array<RouteRecordRaw> = [];
  const modules = getModules();
  const components = getComponents();
  Object.keys(modules).forEach(key => {
     const viewSrc = components[key];
     const file = viewSrc.default;
     if (!file.isRouter) return
     // 首字母轉小寫 letterToLowerCase 首字母轉大寫 letterToUpperCase
     routerList.push({
       path: `/${letterToLowerCase(file.name)}`,
       name: `${letterToUpperCase(file.name)}`,
       component: modules[key]
     });
  })
  return routerList
}  
 

使用 router.ts (路由注冊)

import { vueRouters } from '../services/global'
const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'Login',
    component: () => import('@/views/Login/index.vue')
  },
  ...vueRouters()
]

使用 main.ts (組件注冊)

import { asyncComponent } from './services/global';
export const app = createApp(App)
asyncComponent(app)

4.3 方案三

使用 import.meta.globthen 方法, 加上路由內置 addRoute() 方法注冊
缺點:

  • 由於文件懶加載獲取, 頁面加載有明顯的的卡頓
  • 不適用於注冊組件
  • addRoute() 方法適用於根據后台接口返回的路由權限注冊鑒權路由
// global.ts
export const vueRouters = function (router: Router): void {
  let routerList: Array<RouteRecordRaw> = [];
  const modules = import.meta.glob('../views/**/*.vue')
   for (const path in modules) {
    modules[path]().then((mod) => {
      const file = mod.default;
      if (!file.isRouter) return
      // 首字母轉小寫 letterToLowerCase 首字母轉大寫 letterToUpperCase
      router.addRoute({
        path: `/${letterToLowerCase(file.name)}`,
        name: `${letterToUpperCase(file.name)}`,
        component: file
      })
    })
  }
};

使用 router.ts

import { vueRouters } from '../services/global'
const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'Login',
    component: () => import('@/views/Login/index.vue')
  }
]
const router = createRouter({
  history: createWebHashHistory(),
  routes
})
vueRouters(router)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM