Stories

Detail Return Return

【包真】我的第一次webpack優化,首屏渲染從9s到1s - Stories Detail

大家好,我是貓小白,本文基於vue2,全文閲讀大約需要3分鐘。
談到webpack優化大部分人可能都看膩了,無非就那幾招嘛,我之前也是看過許多類似的文章,但都沒有自己真正上手過,下面是我用公司的項目真實操練下來的,首屏加載速度提升很大(刷刷的),希望能幫到你。

微信圖片_20220306173511.jpg

廢話不多説,先看看對比成果!

類型 優化前 優化後
js文件大小 24MB 3MB
主頁首屏顯示 9s 1s
這簡直太誇張了,提升了8倍?可以想象以前是多慢,要等半天啊。蠻王這個真男人都開大2次了~

可以看到,優化前後首屏加載速度有質的提升,之前一直想優化我們項目的首屏加載時間,有緩存還好,沒有緩存屏幕白屏都要等待7、8s。特別是有的客户第一次打開這個系統,那7、8秒猶如過了一個世紀,非常尷尬。那麼我做了那些常規操作呢?

1.生產環境關閉productionSourceMapcss sourceMap

眾所周知,SourceMap就是當頁面出現某些錯誤,能夠定位到具體的某一行代碼,SourceMap就是幫你建立這個映射關係的,方便代碼調試。在生產環境中我們完全沒必要開啓這個功能(誰在生產環境調試代碼?不會是你吧

如下配置:

const isProduction = process.env.NODE_ENV === 'production' // 判斷是否是生產環境
module.exports = {
    productionSourceMap: !isProduction, //關閉生產環境下的SourceMap映射文件
    css: {
        sourceMap: !isProduction, // css sourceMap 配置
        loaderOptions: {
            ...其它代碼
        }
    },
     ...其它代碼
}

此時再npm run build 打包,就會發現速度快了很多,體積瞬間只有幾兆了!

2.分析大文件,找出內鬼

安裝 npm install webpack-bundle-analyzer -D 插件,打包後會生產一個本地服務,清楚的展示打包文件的包含關係和大小。

vue.config.js 配置:


const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports = {
    ...其它
    configureWebpack: [
        plugins: [
            new BundleAnalyzerPlugin() // 分析打包大小使用默認配置
        ]
    },
  ...其它
}

自動彈出一個服務,清晰的展示打包後js的文件大小:

優化前分析.png

通過圖中可以發現:

  1. element-ui和ant-design佔了近1/4的大小:1.53MB
  2. exceljs也是個大東西有:1.3MB
  3. echarts.js文件也接近1MB
  4. moment.js也有700KB

打包後js文件一共就5MB,這五個哥們就佔了4M左右。不分析好還,一分析嚇得夠嗆~

不要虛!找到刺了,一個一個來拔掉就好了。相信我拔掉的過程是很爽的。

一個一個解決,拔刺

1.把必須要用的第三方js通過cdn的方式引用

分析發現,elementui、echarts是必須使用的,打包又耗時且頁面加載也較慢得很。可以通過cdn直接引入,方便且速度快。

1.element-ui是我們項目用的主要框架,所以這個肯定是少不了,但是項目裏面ant-design為什麼會存在呢,原來是發現有個頁面使用了antd的進度條組件,因為elementui的進度條不太好看。但是沒想到這樣把整個antd都導進來了。

方案:

  1. 捨棄antd組件,自己去找一個類似的vue插件或者乾脆自己實現一個。(這個方法短時間無法完成,且不想去動以前代碼,暫不考慮)
  2. 使用antd部分加載。只加載想要的進度條組件,可以減少文件體積(這個方法簡單粗暴,就是犧牲一些文件大小)。

我們使用方案2,根據antd官方的文檔配置部分組件的引入。

安裝 npm install babel-plugin-import -D

1 main.js導入需要的組件 Step

import { Steps } from 'ant-design-vue';
Vue.component(Steps.name, Steps);
Vue.component(Steps.Step.name, Steps.Step);

2 babel.config.js 加上配置:

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  //以下是按需加載的配置++++
  plugins: [
    [
      "import",
      {
        libraryName: "ant-design-vue",
        libraryDirectory: "es",
        style: true
      }
    ]
  ]
}

此時再分析,antd已經小了很多。

2.使用cdn加載第三方js。

我們項目裏面第三方js很多,有些打包下來會很大,而且加載速度較慢。我們把這些js分離出來,通過cdn的方式在html中的script標籤中直接使用,一方面減少打包體積,一方面提高了加載速度。

這裏推薦一個免費的cdn: BootCDN。也可以使用自己購買的付費cdn服務,我們到網站搜索自己項目需要的js。例如:vue

bootcdn.png

注意,一定要選擇自己項目對應的版本,否則會出現各種奇怪的問題

我的項目使用的是 "vue": "^2.6.12", (package.json)

vue.png

第一步:配置vue.config.js,讓webpack不打包這些js,而是通過script標籤加入。

const isProduction = process.env.NODE_ENV === 'production' // 判斷是否是生產環境
//正式環境不打包公共js
let externals = {}
//儲存cdn的文件
let cdn = {
    css: [
        'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.0/theme-chalk/index.min.css' // element-ui css 樣式表
    ],
    js: []
}
//正式環境才需要
if (isProduction) {
    externals = { //排除打包的js
        vue: 'Vue',
        'element-ui': 'ELEMENT',
        echarts: 'echarts',
    }
    cdn.js = [
        'https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js', // vuejs
        'https://cdn.bootcdn.net/ajax/libs/element-ui/2.6.0/index.js', // element-ui js
        'https://cdn.bootcdn.net/ajax/libs/element-ui/2.6.0/locale/zh-CN.min.js',
        'https://cdn.bootcdn.net/ajax/libs/echarts/5.1.2/echarts.min.js',
    ]
}
module.exports = {
//...其它配置
configureWebpack: {
        //常用的公共js 排除掉,不打包 而是在index添加cdn,
        externals, 
        //...其它配置
    },
chainWebpack: config => {
        //...其它配置  
        // 注入cdn變量 (打包時會執行)
        config.plugin('html').tap(args => {
            args[0].cdn = cdn // 配置cdn給插件
            return args
        })
    }
//...其它配置     
}

第二步:html模板中加入定義好的cdn變量使用的代碼

<!DOCTYPE html>
<html lang="">

<head>
<meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>web</title>
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- 引入樣式 -->
    <% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
       <link rel="stylesheet" href="<%=css%>" >
    <% } %>

    <!-- 引入JS -->
    <% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
       <script src="<%=js%>"></script>
    <% } %>
</head>
<body style="font-size:14px">
    <div id="app"></div>
</body>
</html>

可以發現cdn.js中,我把vue、echarts、element-ui這三個大頭加入了。在externals對象中左側是npm包的名稱,右側是在代碼中暴露的全局變量。注意element-ui對應的是 ELEMENT

沒有ant-design-vue是因為我們上面使用了部分加載的方式,如果使用cdn這種方式是加載全部的代碼,有點浪費。

沒有使用exclejs,是因為exceljs在我的業務代碼中不是直接引用的,而是一個叫table2excel間接依賴的。所以就算我通過上面的方法排除掉它,在打包的時候還是會通過table2excel的依賴找到它並打包。

那這種不可避免的情況,該如何優化,讓加載速度不受影響呢?

答案是通過懶加載的方式:

1.script標籤中註釋掉 import Table2Excel from "table2excel.js";

2.下載的方法中:
download(){
    //使用import().then()方式
    import("table2excel.js").then((Table2Excel) => {
        new Table2Excel.default("#table").export('filename') //多了一層default 
    })
}

這樣在進入系統時,不會加載Table2Excelexceljs,當需要時才會去加載,第一次會慢一點,後面就不需要加載了,會變快。

3 moment.js的優化

我們發現monentjs在項目中有使用來對時間格式化,但是使用頻率並不高,完全可以自己實現一個format方法,或者使用只有6kbday.js.

但這裏我們暫不替換,把moment變得瘦小一些即可,刪除掉除中文以外的語言包

第一步:vue.config.js

...其它配置
 chainWebpack: config => {
     config.plugin('ignore')
        //忽略/moment/locale下的所有文件
     .use(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/))
 }
...其它配置

第二步:main.js

import moment from 'moment'
//手動引入所需要的語言包
import 'moment/locale/zh-cn';
// 指定使用的語言
moment.locale('zh-cn');

這次我們看看moment打包後多大:

mometn.png

只有174kb了。不過,有一説一還是day.js香~

做完上面這些動作我們的js文件總大小:3.04MB ,其中包含 1.3MB的懶加載js,剩下的1.7MB左右的js基本上不會對頁面造成很大的卡頓。

優化後打包大小.png

還有進步空間?

1.通過 compression-webpack-plugin 插件把代碼壓縮為gzip。但是!需要服務器支持

webpackvue.config.js配置如下:

//打包壓縮靜態文件插件
const CompressionPlugin = require("compression-webpack-plugin")

//...其它配置
module.exports = {
    //...其它配置
    chainWebpack: config => {
        //生產環境開啓js\css壓縮
        if (isProduction) {
            config.plugin('compressionPlugin').use(new CompressionPlugin({
                test: /\.(js)$/, // 匹配文件名
                threshold: 10240, // 對超過10k的數據壓縮
                minRatio: 0.8,
                deleteOriginalAssets: true // 刪除源文件
            }))
        }
    }
    //...其它配置
}

打包大小由3MB860KB,感覺起飛了~

gzip.png

服務器端配置這裏就不詳細説明了可以谷百: nginx開啓靜態壓縮 找到答案。

最後貼上優化前後的無緩存下的首屏加載時間對比(chrome瀏覽器),絕對包真:

優化前項目網站首屏加載數據:9.17s

優化前頁面加載時間.png

優化後項目網站首屏加載數據:1.24s

優化後頁面加載時間.png

微信圖片_20220306173458.jpg

這些都是在工作之餘,自己抽時間去查閲各位大佬的帖子,雖然都是些耍欄了的技術,但是真的要在自己項目中實施還是需要一些時間和精力,大多數都是為了完成功能快速迭代而忽略掉了做程序原本的目的,就是要讓用户有一個良好的使用體驗。

肯請各位大佬,不要忘了給我點贊評論收藏

往期精彩:

1.什麼是迭代器(iterator)?Generator和它有什麼關聯

2.微信小程序UI組件、圖表、自定義bar這些坑都幫你踩了

Add a new Comments

Some HTML is okay.