1. 程式人生 > >淺探webpack優化

淺探webpack優化

由於前端的快速發展,相關工具的發展速度也是相當迅猛,各大框架例如vue,react都有自己優秀的腳手架工具來幫助我們快速啟動一個新專案,也正式因為這個原因,我們對於腳手架中最關鍵的一環webpack相關的優化知之甚少,腳手架基本上已經為我們做好了相關的開發準備,但是當我們想要做一些定製化的優化操作時,對webpack的優化也需要有一定的瞭解,否則無從下手,接下來就讓我們進入webpack的優化世界

構建速度提升

loader提升

loader是webpack中最重要的特性,由於webpack自身只支援JavaScript,因此需要一系列的loader來處理那些非JavaScript模組,因此在我們用webpack建專案的時候一定會使用一系列的loader,例如:vue-loader、sass-loader、babel-loader等等,就以babel-loader為例,來看具體配置:


module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader?cacheDirectory=true',
      options: {
        presets: ['@babel/preset-env'],
        plugins: ['@babel/transform-runtime']
      }
    }]
  }
  • 對於loader來說最常用的就是exclude屬性,用來避免不必要的轉譯,上面通過exclude來避免對node_modules中js中進行轉譯來提升構建速度,但是這樣帶來的提升效果有限。
  • cacheDirectory是對babel-loader的轉譯結果進行快取,之後的webpack進行構建時,都會去嘗試讀取快取來避免高耗能的babel重新轉譯過程,cacheDirectory可以指定一個快取目錄或者指定為true,為true時將使用預設的快取目錄node_modules/.cache/babel-loader。
  • babel對一些公共方法使用了非常小的輔助程式碼,預設會注入到每一個需要的檔案,這樣就造成重複引入,這時候就需要像上面那樣引入transform-runtime來告訴babel引入runtime來代替注入

第三方庫優化

externals

externals提高構建速度的方法就是在構建時不會將指定的依賴包打包到bundle中,而是在執行時再從外部獲取依賴,具體是怎麼用的呢?來看個例子:


externals : {
  vue : "Vue",
  vueRouter : "VueRouter",
  vueResource : "VueResource",
  vuex : "Vuex"
},
<script type="text/javascript" src="https//xxxx/vue.famliy.1.1.0.min.js"></script>

上面的例子的將vue全家桶都配置在externals中,然後將壓縮包合成一個js檔案放在cdn上面,這樣就不會在構建時將檔案打包到bundle中,提升打包速度,同時cdn又可以做快取,提高訪問速度,美滋滋

DllPlugin

DllPlugin是用來幹什麼的呢?DllPlugin會將第三方包到一個單獨檔案,並且生成一個對映的json檔案,打包的生成的檔案就是一個依賴庫,這個依賴不會隨著你的業務程式碼改變而被重新打包,只有當它自身依賴的包發生變化時才會需要重新打包依賴庫,接下來來看具體配置吧:


module.exports = {
  entry: {
    vendor: ['vue', 'vue-router', 'vue-resource', 'vuex']
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].js',
    library: '[name]_hash',
  },
  plugins: [
    new webpack.DllPlugin({
      name: '[name]_[hash]',
      path: path.join(__dirname, 'dist', '[name]-manifest.json'),
      context: __dirname
    })
  ]
}

首先我們需要一個如上面例子那樣的dll配置檔案,然後編譯這個配置檔案,生成一個vendor.js和一個對映檔案vendor-manifest.json,然後再在我們的webpack配置檔案中對進行配置:


plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require('./dist/vendor-manifest.json')
    })
  ]

這樣就完成配置了,是不是很簡單呢?趕緊動手試試吧

happypack

happypack這是個什麼呢?我們都知道webpack是個單執行緒處理任務的,當又多個任務需要處理的時候,需要排隊,那happypack就是用多執行緒來處理任務,通過併發處理來提高任務處理速度,那麼這個需要怎麼配置呢?來看具體例子:


const happypack = require('happypack')
// 建立併發池
const threadPool = happypack.ThreadPool({size: os.cpus().length})
module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'happypack/loader?id=happyBabel' // id對應happypack外掛id
    }]
  },
plugins: [
    new happypack({
      id: 'happyBabel',
      threadPool: threadPool,
      loaders: ['babel-loader?cacheDirectory']
    })
  ],

減小構建體積

webpack-bundle-analyzer

這個相信大家都很熟悉,就是一個視覺化工具,用來檢視各個包的大小以及相互之間的依賴關係,配置方法也很簡單,就和外掛的配置一樣,來看具體例子:


const bundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
plugins: [
    new bundleAnalyzerPlugin()
  ],

tree shaking

tree shaking指的是什麼呢?通常指的是JavaScript上下文中未引用的程式碼,怎麼理解呢?比如你引用了lodash包,裡面有許多和JavaScript相關的便利方法,但你實際只用了其中的一兩個,此時打包時如果把所有的方法都打進去了,是不是很浪費呢?tree shaking的概念就是去除多餘程式碼。來看一個簡單的例子:


import {plus} from './count'

console.log(plus(1, 2))

function plus(x, y) {
  return x + y
}
function minus(x, y) {
  return x - y
}
export {
  plus,
  minus
}

const path = require('path')

module.exports = {
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].js',
  },
  mode: 'development'
}

如上例所示,在入口檔案中我們引入count.js中plus方法,我們期望的當然是隻會引入plus方法,而不是都引入,但往往不隨人願,來看結果:

在這裡插入圖片描述

你會發現編譯後的程式碼中,整個count.js都被編譯進去了,這時候你就需要tree shaking了,接下來看做tree shaking的具體方法

UglifyJsPlugin

這個外掛大家一定都用過,使用UglifyJsPlugin就可以在構建的過程中對冗餘的程式碼進行刪除,在webpack4中只需要將上面mode的值改為production,就會啟用UglifyJsPlugin,是不是很簡單,或許你想知道webpack4中怎麼自己配置UglifyJsPlugin,那就來看具體配置吧:


const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
optimization: {
    minimizer: [
      new UglifyJsPlugin({
        parallel: true,
        cache: true,
        uglifyOptions: {
          compress: {
            drop_console: true,
            reduce_vars: true
          },
          output: {
            comments: false,
            beautify: false
          }
        }
      })
    ]
  }

是的在webpack4中的UglifyJsPlugin是配置在optimization中的minimizer中的,配置是不很簡單呢?趕緊動手嘗試吧

按需載入(import)

這裡的import是指webpack中的動態載入,它的語法和ES6中的動態載入語法一摸一樣,這是官方推薦的按需載入的方式,還是上面tree shaking的例子,我們只想引入plus方法,我們來看具體怎麼使用:


import('./count.js').then((count) => {
  console.log(count.plus(1, 2))
})

我們只需要將入口檔案改成上面的形式,其他的都不要變就可以實現按需引入,是不是很簡單呢?在vue中路由的按需載入也可以這麼用,來看一個簡單的例子:


function view (name) {
  return new Promise((resolve, reject) => {
    import('../views/' + name + '.vue')
      .then((res) => {
        resolve(res)
      }).catch(e => {
        reject('網路異常,請稍後再試')
      })
  }).catch(err => {
    throw new Error('err,元件載入失敗')
  })
}

傳入一個名字,動態引入對應目錄的下的檢視檔案,這只是一個簡單的例子,具體的使用形式還是依據具體的場景

總結

這篇文章簡單的從構建速度和程式碼體積兩個方面簡單的介紹了webpack優化相關的方法,希望大家都能自己動手去寫一寫,畢竟只有實踐出真知,更何況是程式設計。
這篇文章如果有錯誤或不嚴謹的地方,歡迎批評指正,如果喜歡,歡迎點贊收藏

原文地址:https://segmentfault.com/a/1190000017218108