webpack構建優化:bundle體積從3M到400k之路

在CQM平臺開發時,把demo網站給同事體驗,都紛紛反饋第一次打開頁面的時候需要等待很久,頁面一直在轉菊花。作爲一個爲韓國頭部廠商提供優質服務的網站,接到這種反饋,這不是啪啪打臉嗎。

代碼是在以前的老框架上寫的(必須堅定把鍋甩出去,手動捂臉)。喝杯咖啡鎮定下,找找什麼問題。趕緊打開chrome (disable cache):

哇,嚇了一跳,這打包出來的JS辣麼大:lib.js 2.3M(花了14s 才加載完)、app.js 1.2M(花了9s多加載完),難怪反饋頁面響應慢。

  • 1、祭出神器把Bundle分析利器拿出來:

  webpack-bundle-analyzer是一個基於webpack的插件,能夠用zoomable treemap可視化webpack輸出文件的大小(Visualize size of webpack output files with an interactive zoomable treemap)

不慌,喝口白開水,裝上webpack-bundle-analyzer看看裏面分別是啥。

npm install webpack-bundle-analyzer

     在webpack.app.config.js引入webpack-bundle-analyzer

constBundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
   output: {
       //省略
   },
   entry: { 
      //省略
   },
   plugins: [
      new BundleAnalyzerPlugin(),
   ],
};

很簡單吧,這樣我們就配好了webpack-bundle-analyzer

  • 2、優化app.js文件

項目的package.json文件如下:

{
  "private": true,
  "scripts": {
    "dev": "npm run development",
    "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    //開發環境生成 app.js及頁面js
    "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=webpack.app.config.js",
    "watch-poll": "npm run watch -- --watch-poll",
    "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "prod": "npm run production",
    //生成環境打包 app.js及頁面js
    "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=webpack.app.config.js",
    //單獨打包 lib.js
    "build-lib": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=webpack.lib.config.js",
    "build-online": "gulp pack --env production"
  },
  "devDependencies": {
   	//省略
  },
  "dependencies": {
   //
  }
}

運行下npm run watch,Webpack會自動打開瀏覽器,在127.0.0.1:8888上顯示如下:

ps: npm run watch 對應的配置文件webpack.app.config.js,打包的app.js及頁面js的各組成的大小(面積越大,表示比重越大)。可以看到,app.js裏面大頭分別是vue、vue-router、vue-i18n組件(好傢伙,這些組件都是在app.js裏面import進來的)。我們將他們踢出去:

a、在webpack.app.config.js裏面添加externals參數,這樣即使我們沒將這幾個組件打包到app.js中,我們依然能將其import進來並use:

module.exports = {
    externals : {
       'vue':'Vue',
       'vue-router':'VueRouter',
       'vue-i18n': 'VueI18n',
}};

b、接着在index.html中:

<scriptsrc="//cdn.bootcss.com/vue/2.3.3/vue.min.js"></script>
<script src="//cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
<scriptsrc="https://unpkg.com/[email protected]/dist/vue-i18n.js"></script>

爲什麼要這麼做呢?因爲vue和vue-router在cdn上都提供了min版的js,已經是壓縮精簡版的,而vue-i18n.js的官方網站也建議使用cdn方式引入,沒必要將都打包到app.js中。

對比優化前的app.js大小1.2M,以及下圖單獨引入vue.min.js、vue-i18n.js、vue-router.min.js的大小,分別爲78K、38K、24K。效果十分明顯

c、除了拆分依賴包,另一個重要的優化就是壓縮代碼,這裏使用的是uglifyjs-webpack-plugin,同樣在webpack.app.config.js的plugin裏面添加

constUglifyJsPlugin= require('uglifyjs-webpack-plugin');
module.exports = {
     plugins:[
         new UglifyJsPlugin({
             uglifyOptions: {
                 output: {
                    beautify: false,
                    comments: false, 
                 },
                compress: {
                   warnings: false,
                   drop_debugger: true,
                   drop_console: true,
                   pure_funcs: ['console.log', '(e = console).log' ]
               },
               sourceMap: false
          }
    })
]};

這樣之後效果如下,app.js從1.2M縮減到200kb,達到了可觀的效果

  • 3、優化lib.js文件

       導致我們頁面響應慢另一個大文件是 lib.js這裏介紹下,在我們工程裏,對常用的第三方UI組件、繪圖組件、編輯組件,項目裏將其統一打包到lib.js裏面,不用每次構建都重新打包lib.js,這樣可以加快構建速度),對應配置文件webp.lib.config.js。同樣用webpack-bundle-analyzer,如圖所示:裏面體積最大的分別是element-ui、vue2-editor、highchart、jquery

以前的webpack.lib.config.js文件如下:

module.exports = {
    output: {
        path: path.join(__dirname, './public/js'),
        filename: '[name].js',
        library: '[name]',
    },
    entry: {
        "lib": ['vue','jquery', 'element-ui', 'vue-highcharts', 'vue2-editor', 'lodash'],
    },
};

       對比上面的webpack.app.config.js,vue已經引進了,所以不必重複打包。jquery、element-ui可以通過引入cdn文件解決,vue2-editor目前項目中暫時沒用到,所以去掉唄。再添加上代碼壓縮插件,修改後的webpack.lib.config.js文件如下:

module.exports = {
    output: {
       path: path.join(__dirname, './public/js'),
       filename: '[name].js',
       library: '[name]',
    },
    entry: {
        "lib": ['vue-highcharts', 'lodash'],
    },
    newwebpack.optimize.UglifyJsPlugin({
       minimize: true
    }),
};

但是在app.js中還使用element-ui,所以在webpack.app.config.js中需要在external添加該項element-uijquery

module.exports = {
    externals : {
       'vue':'Vue',
       'vue-router':'VueRouter',
       'vue-i18n': 'VueI18n',
       'element-ui':'ElementUI',
       'jquery': 'jQuery.noConflict()'
}};

在index.html中添加jquery.all.js和element-ui,引入的js如下:

<script src="//cdn.bootcss.com/vue/2.3.3/vue.min.js"></script>
<script src="//cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
<script src="//unpkg.com/[email protected]"></script>
<script src="//unpkg.com/[email protected]/lib/umd/locale/en.js"></script>
<script>
    ELEMENT.locale(ELEMENT.lang.en)
</script>
<script src="https://unpkg.com/[email protected]/dist/vue-i18n.js"></script>
<script src="/js/lib.js?12124142"></script>
<script src="http://iwaibao.qq.com/js/jquery.all.js?v=" language="javascript" type="text/javascript"></script>
<script src="{{ mix('js/app.js') }}"></script>

這裏element-ui通過設置external然後引入cdn的js的時候有個坑,打包後老報錯:

索性通過npm uninstall element-ui,將這個組件刪除掉,直接通過cdn引入。執行npm run build-lib 生成,lib.js從2.3M減小到274kB

最終頁面的js文件如下:

     對比優化前,網站訪問流暢了不少(總共花了3s多加載完成)。雖然還不能做到如絲般柔滑,但羅馬不是一天建成的(畢竟不能一次優化的太完美,不然後面怎麼提升呢),比如打包速度提升(多線程打包)、頁面代碼分割與混淆等,後面咱們再慢慢優化

  • 最後

webpack基本已經成爲前端項目的標配構建工具了,個人感覺大而全,裏面有很多插件。比較贊同知乎網友對其的評價:

webpack充斥着大量名字類似 what-the-fuck-is-this-plugin 的插件,以及這個插件附帶的一千種配置和一萬種副作用,以至於每次出現打包的問題都會產生哲學三問:

    這個插件幹了什麼?

    我的配置有錯誤嗎?

    這個插件真的沒有bug嗎?

比如UglifyJsPlugin刪除生產環境裏console.log的選項drop_console死活不生效,最後只能通過vue-loader中的preLoader預加載選項,利用strip-loader將vue文件中的console去掉

let rules = [
 {
    test: /\.vue$/,
    loader: 'vue-loader',
    options: {
       preLoaders: Mix.inProduction ? {js: 'strip-loader?strip[]=console.log,strip[]=console.warn'}:{},
       loaders:  //省略
    }
]

最後的最後再加一條:這個插件版本對嗎?(手動哭臉)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章