webpack優化入門詳解

如下測試中,均在webpack版本3.5以上,若有不對的地方請指出,謝謝。javascript

新特性優化

Scope Hoisting-做用域提高,將模塊都放到一個閉包函數中,經過減小閉包函數數量從而加快JS的執行速度。 基本配置以下:css

plugins: [
    new webpack.optimize.ModuleConcatenationPlugin()
]    
複製代碼

配置後,自定義的兩個模塊函數被放在一個必包函數體中,打包結果以下:html

配置前,自定義的兩個模塊函數分別在不一樣的必包函數體中,經過行數就能看出來,打包結果以下:vue

配置項優化

externals配置優化

設置externals配置項分離不須要打包的庫文件,而後在模版文件中使用script引入便可,配置代碼片斷以下:java

externals: {
  'jquery': 'jquery'
},
複製代碼

alias配置優化

  • 能夠選擇對應的須要打包文件的大小規格
  • 經過定義全局路徑,減小webpack編譯過程當中的搜索硬盤時間

基本配置代碼片斷以下:node

resolve: {
  extensions: ['.js', '.json'],
  alias: {
    'jquery': 'jquery/dist/jquery.slim.min.js',
    '@': resolve('src'),
  }
},
複製代碼

插件使用優化

內鏈css減小請求

webpack插件配置片斷代碼以下:react

plugins: [
  new StyleExtHtmlWebpackPlugin({
    minify: true
  })
]
複製代碼

最終打包能夠將css文件直接之內鏈的形式插入網頁中,進而能夠減小網頁請求。jquery

preload插件使用

webpack插件配置片斷代碼以下:webpack

plugins: [
  new PreloadWebpackPlugin({
    rel: 'preload',
    as: 'script',
    include: 'all'
  })
]
複製代碼

最終打包生成的頁面在head上會生成preload的link標籤,代碼片斷以下:git

<link rel="preload" as="script" href="app.8898b6c9e3b39f7a1c9d.js">
<link rel="preload" as="script" href="common.6ecc97ec2b5dceebbd5e800322c2a3c0.css">
<link rel="preload" as="script" href="vendor.dcd374ee43fd57d2365b.js">
<link rel="preload" as="script" href="manifest.f3e58576762e216d8867.js">
複製代碼

更多配置參考這裏詳細更多

緩存優化與分析

css打包優化

contenthash

使用webpack打包css用的是extract-text-webpack-plugin插件,因爲hash是webpack的module identifier計算的,不變內容的狀況下每次打包也會產生不一樣的hash,所以選用chunkhash,它是根據文件內容計算的,所以比較符合實際使用,基本配置片斷代碼以下:

module: {
  rules: [{
    test: /\.(less|scss|css)$/,
    use: ExtractTextPlugin.extract({
      fallback: "style-loader",
      use: [
        {
          loader:"css-loader",
          options:{
            minimize: true //css壓縮
          }
        }, 
        {
          loader:"less-loader",
          options:{
            minimize: true //css壓縮
          }
        }, 
        {
          loader:"sass-loader",
          options:{
            minimize: true //css壓縮
          }
        }]
    })
  }]
},
plugins: [
  new ExtractTextPlugin({
    filename: 'common.[chunkhash].css',
    allChunks: true
  }),
]
複製代碼

單純改變css文件

// a.less
.ac {
 .bc {
   font-size: 12px;
 }
 .cc {
   font-weight: 700;
   border: 1px solid #ccc;
 }
}
複製代碼

module文件中的a.js引入了a.less文件

// module/a.js
require('../style/a.less');
複製代碼

屢次打包發現hash值未改變,不但依賴的js文件hash未改變,就連css文件的hash也未改變,最終使用webpack打包以下:

這顯然不是想要的結果,但願改變css文件內容,js文件的hash不會改變,只有相應的css文件的hash值改變,所以再ExtractTextPlugin插件中應該使用contenthash,這也是官方特別註明的,直到再次看到文檔才發現。

設置contenthash以後,再次打包發現只有css文件hash變了,而且改變js代碼依然不影響css文件的打包,最終打包結果以下:

js打包優化

HashedModuleIdsPlugin

webpack插件基本配置片斷代碼以下:

entry: {
  app: './src/app.js',
  vendor: ['lodash']
},
plugins: [
    new CleanWebpackPlugin(['cdist']),
    new HtmlWebpackPlugin({
      template: './src/index.template.html', //html模板路徑
      filename: 'wq.html', //生成的html存放路徑,相對於path
      favicon: './src/favicon.ico', //favicon路徑,經過webpack引入同時能夠生成hash值
      inject: 'body', //js插入的位置,true/'head'/'body'/false
      // chunks: ['app', 'vendor'],
      //hash: true ,//爲靜態資源生成hash值
      minify: { //壓縮HTML文件
        removeComments: true, //移除HTML中的註釋
        collapseWhitespace: false //刪除空白符與換行符
      }
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor'
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest'
    }),
    new ExtractTextPlugin({
      filename: 'common.[contenthash].css',
      allChunks: true
    }),
    new webpack.optimize.UglifyJsPlugin({
      beautify: false,
      comments: false,
      compress: {
        warnings: false,
        drop_console: true,
        collapse_vars: true,
        reduce_vars: true,
      }
    })
  ]
複製代碼

項目入口文件爲app.js

import _ from 'lodash';
import a from './module/a';
// import b from './module/b';
require("babel-polyfill")
require('./style/lib.css')

function fn () {
  // let aa = _.clone({key:4})
  // aa.key = 2;
  return aa
}

fn()
複製代碼

第一次打包生成的文件以下:

修改入口文件app.js代碼以下:

import _ from 'lodash';
import a from './module/a';
import b from './module/b';
require("babel-polyfill")
require('./style/lib.css')

function fn () {
  let aa = _.clone({key:4})
  aa.key = 2;
  return aa
}

fn()
複製代碼

再次使用webpack打包,最後顯示以下:

從打包結果來看,不但入口文件app.js哈希值變了,第三方庫vendor.js的哈希值也變了,這固然不是我想要的,由於我更本沒有改變第三方庫文件。爲了解決這個問題能夠引入HashedModuleIdsPlugin這個配置項,這樣就能夠解決以上遇到的問題。

將插件配置作以下修改,代碼片斷以下:

plugins: [
    new webpack.HashedModuleIdsPlugin(),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor'
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest'
    }),
  ]
複製代碼

在通過以上兩個步驟,再次使用webpack打包,結果以下:

webpack3中tree-shaking分析

tree-shaking實際上是webpack2中就有的功能特性,可是會因模塊函數定義的形式,會有失效的機率,所以,我想用webpack3來測試下,看看是否官網有優化這部分特性。

tree-shaking必須有UglifyJsPlugin這個配置項才能生效,不然統一不生效。

常規定義方法

module/a.js代碼以下:

require('../style/a.less');
function a() {
  return 'aaaaaa'
}
export {a};
複製代碼

module/b.js代碼以下:

require('../style/b.scss');
function b() {
  return 'bbbbbbb'
}
export {b};
複製代碼

入口文件代碼以下:

// app.js
import _ from 'lodash';
import { a } from './module/a';
import {b} from './module/b';
require("babel-polyfill")
require('./style/lib.css')
function wq() {
  return a()
}
wq()
複製代碼

最後使用webpack打包,在壓縮的文件中發現bbbbbbb被刪除了,所以能夠得出一個結論,webpack會動態判斷引入的包是否被使用從而再次精簡打包文件大小。

原型定義方法

此次我改寫a.js和b.js的形式,再其原型鏈上定義方法,基本代碼以下:

// module/a.js
require('../style/a.less');
function a() {
  return 'aaaaaa'
}
a.prototype.fn = () => {
  return 'aaaaaa'
}
export {a};
複製代碼
// module/b.js
require('../style/b.scss');
function b() {
  return 'bbbbbbb'
}
b.prototype.fn = () => {
  return 'bbbbbbb'
}
export {b};
複製代碼

入口文件代碼以下:

// app.js
import _ from 'lodash';
import { a } from './module/a';
import {b} from './module/b';
require("babel-polyfill")
require('./style/lib.css')

function wq() {
  return a.prototype.fn()
}

wq()
複製代碼

最後使用webpack打包,在壓縮的文件中發現bbbbbbb沒有被刪除,只是調用函數的方法不同,致使tree-shaking並無生效。

class類編寫模式

我再次使用es6中類的概念改寫a、b的定義方法,基本代碼以下:

// module/a.js
require('../style/a.less');
class a {
  fn() {
    return 'aaaaaaa'
  }
}
export {a};
複製代碼
// module/b.js
require('../style/b.scss');
class b {
  fn() {
    return 'bbbbbb'
  }
}
export {b};
複製代碼

最後使用webpack打包,在壓縮的文件中發現bbbbbbb依然沒有被刪除,tree-shaking並無生效。

let定義形式

最後我使用最簡單的定義形式改寫,基本代碼以下:

// module/a.js
require('../style/a.less');
let a = 'aaaaaaa'

export {a};
複製代碼
// module/b.js
require('../style/b.scss');
let b = 'bbbbbbb'

export {b};
複製代碼

最後使用webpack打包,在壓縮的文件中發現bbbbbbb被刪除,tree-shaking生效。

分析

原型定義寫法不會被刪除很好理解,就是這些原型方法,由於原型方法可能會將要被調用,是一個未知狀況,若是直接刪除,那麼一旦其餘模塊會動態調用原型方法,那就會形成代碼報錯,這樣是不合理的。

class類的寫法彷佛不會產生反作用,類中的函數彷佛也不是未來可能被調用,再來看最後webpack打包生成的代碼,片斷以下:

/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck__ = __webpack_require__("Zrlr");
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck__);
var b = function () {
  function b() {
    __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default()(this, b);
  }

  __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_createClass___default()(b, [{
    key: 'fn',
    value: function fn() {
      return 'bbbbbb';
    }
  }]);

  return b;
}();
複製代碼

首先b是一個自運行函數,而且最終還調用了__webpack_require__("Zrlr")這個模塊,所以就相似prototype,會產生一點有反作用的函數,因此不能直接刪除。

最後貼上完整的webpack配置項,這裏並無加上vue/react的相關配置,如須要能夠本身安裝相應插件,經過配合babel-loader進行編譯打包,.babelrc配置入門詳解

const webpack = require('webpack'); //to access built-in plugins
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const StyleExtHtmlWebpackPlugin = require('style-ext-html-webpack-plugin')
const PreloadWebpackPlugin = require('preload-webpack-plugin')


const config = {
  entry: {
    app: './src/app.js',
    vendor: ['lodash']
  },
  output: {
    path: path.resolve(__dirname, 'cdist'),
    filename: '[name].[chunkhash].js'
  },
  externals: {
    'jquery': 'jquery'
  },
  // resolve: {
  // extensions: ['.js', '.json'],
  // alias: {
  // 'jquery': 'jquery/dist/jquery.slim.min.js',
  // '@': resolve('src'),
  // }
  // },
  module: {
    rules: [{
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.(less|scss|css)$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: [
            {
              loader:"css-loader",
              options:{
                minimize: true //css壓縮
              }
            }, 
            {
              loader:"less-loader",
              options:{
                minimize: true //css壓縮
              }
            }, 
            {
              loader:"sass-loader",
              options:{
                minimize: true //css壓縮
              }
            }]
        })
      }
    ]
  },
  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin(),
    new CleanWebpackPlugin(['cdist']),
    new HtmlWebpackPlugin({
      template: './src/index.template.html', //html模板路徑
      filename: 'wq.html', //生成的html存放路徑,相對於path
      favicon: './src/favicon.ico', //favicon路徑,經過webpack引入同時能夠生成hash值
      inject: 'body', //js插入的位置,true/'head'/'body'/false
      // chunks: ['app', 'vendor'],
      //hash: true ,//爲靜態資源生成hash值
      minify: { //壓縮HTML文件
        removeComments: true, //移除HTML中的註釋
        collapseWhitespace: false //刪除空白符與換行符
      }
    }),
    new PreloadWebpackPlugin({
      rel: 'preload',
      as: 'script',
      include: 'all'
    }),
    // 解決第三方打包文件hash值不變,最大化緩存
    new webpack.HashedModuleIdsPlugin(),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor'
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest'
    }),
    new ExtractTextPlugin({
      filename: 'common.[contenthash].css',
      allChunks: true
    }),
    new webpack.optimize.UglifyJsPlugin({
      beautify: false,
      comments: false,
      compress: {
        warnings: false,
        drop_console: true,
        collapse_vars: true,
        reduce_vars: true,
      }
    }),
    new StyleExtHtmlWebpackPlugin({
      minify: true
    })
  ]
};

module.exports = config;
複製代碼
相關文章
相關標籤/搜索