為什麼我寫了路由懶載入但程式碼卻沒有分割?
事情的起因是這樣的,最近有相當一部分的精力都在做專案的效能優化上,之前有一個專案出現了一個老大難的問題糾結很久了,一直沒時間去看,正好一併解決一下。這個問題很簡單:我用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);
- 【演算法篇】刷了兩道大廠面試題,含淚 ”重學陣列“
- 2022 開源軟體安全狀況報告:超 41% 的企業對開源安全沒有足夠的信心
- JavaScript中async和await的使用以及佇列問題
- Flex & Bison 開始
- Obsidian基礎教程
- 分享自己平時使用的socket多客戶端通訊的程式碼技術點和軟體使用
- iNeuOS工業網際網路作業系統,增加2154個檢視建模(WEB組態)行業向量圖元、大屏背景及相關圖元
- 多臺雲伺服器的 Kubernetes 叢集搭建
- Elasticsearch學習系列四(聚合搜尋)
- 關於swiper外掛在vue2的使用
- 使用 Abp.Zero 搭建第三方登入模組(一):原理篇
- LVGL庫入門教程 - 顏色和影象
- Node.js精進(4)——事件觸發器
- 物聯網?快來看 Arduino 上雲啦
- SpringBoot JWT Redis 開源知識社群系統
- CVPR2022 | 可精簡域適應
- Spring框架系列(3) - 深入淺出Spring核心之控制反轉(IOC)
- 面試突擊59:一個表中可以有多個自增列嗎?
- CVPR2022 | 弱監督多標籤分類中的損失問題
- JDBC、ORM、JPA、Spring Data JPA,傻傻分不清楚?一文帶你釐清箇中曲直,給你個選擇SpringDataJPA的...