為什麼我寫了路由懶載入但程式碼卻沒有分割?
- 2022 年 5 月 23 日
- 筆記
事情的起因是這樣的,最近有相當一部分的精力都在做項目的性能優化上,之前有一個項目出現了一個老大難的問題糾結很久了,一直沒時間去看,正好一併解決一下。這個問題很簡單:我用vue-cli創建的項目,按照vue的路由懶載入寫法,打包後卻發現程式碼並沒有分割,全部都打包到app.js中了,導致app.js體積過大,且沒有路由的按需載入了。
找出問題的原因
我開始思考問題原因可能是以下幾點造成的:
- 路由懶載入寫法不對;
- vue-cli版本問題;
- vue-cli的配置問題。
但是這三個可能得原因很快排除了,因為有一個項目上面三個都一樣,程式碼分割正常,那隻能是程式碼問題了。但是那麼多文件總不能全部review一遍吧,毫無頭緒之下只能採用樸素但實用二分法的方式定位問題文件了。一番體力活下來終於讓我找到了兩個罪魁禍首,通過觀察這兩個文件發現都用了同一種的文件引用方式,類似程式碼如下:
let form = null;
let cpnName = this.template.name;
this.$options.components[cpnName] = require('@/' + this.template.path).default;
form = <cpnName />
return (
<div>{form}</div>
)
組件通過拼接入參的路徑來動態引入組件,其實看到這裡我心裡大概就知道什麼原因了,因為是動態路徑,webpack打包時是靜態解析依賴,根本無法確認文件的具體地址,所以導致程式碼全部都打到app.js中。為了證明我的想法,我到webpack的github issue中也找到了跟我類似的場景:
這個老哥是想根據傳入的圖片名稱來動態引入圖片,但是打包時候發現其他目錄的圖片也都被打包進來了,webpack的維護者也回答了說,這就是require的工作機制,它不知道你會用哪個資源,它就把它們全部都打包了。
驗證問題
為了驗證這個問題,我創建了一個項目,來複現一下問題:
動態引入的組件程式碼如下:
// src/components/common/DynamicRequireCpn.vue
<script>
export default {
name: 'DynamicRequireCpn',
props: {
template: Object
},
render () {
let form = null;
let cpnName = this.template.name;
this.$options.components[cpnName] = require('@/' + this.template.path).default;
form = <cpnName />
return (
<div>{form}</div>
)
}
}
</script>
路由程式碼如下:
// src/router/index.js
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
}
];
打包結果如下:
發現程式碼還是都打包到一起了,about組件並沒有分割出來。而且我還在app.js中發現了沒有引用的程式碼。也就是說這種情況下,webpack把src目錄下所有的文件都打包了。
解決問題的方案
按照上面的實驗和require的工作原理,我想通過縮小require的查到範圍是不是能解決問題呢?
<script>
export default {
name: 'DynamicRequireCpn',
props: {
template: Object
},
render () {
let form = null;
let cpnName = this.template.name;
this.$options.components[cpnName] = require('@/components/common/' + this.template.path).default;
form = <cpnName />
return (
<div>{form}</div>
)
}
}
</script>
這下我把require的動態路徑精確到@/components/common/
,重新打包看看:
Bingo!看到了about組件對應的分割文件,而且搜索app.js文件,也沒有發現未引用的程式碼了,問題解決了!
總結
在使用webpack時,應該盡量減少資源的動態路徑引入,如果必須這樣引入的話,那也要盡量傳入更短的文件路徑,或者將要動態引入的文件放到一個目錄下面,防止webpack找到非目標目錄下面。
GOOD
require(‘@/components/common/’ + this.template.path);
BAD
require(‘@’ + this.template.path);