關注公眾號: 微信搜索 前端工具人
; 收貨更多的干貨
原文鏈接: 自己掘金文章 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.glob
和 import.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.glob
的 then
方法, 加上路由內置 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)