Webpack4.0進階

1. Tree Shaking

1.1 JS Tree Shaking

1.1.1 本地代碼Tree Shaking

  1. 一個簡單的打包示例

(1) 打包入口代碼
src/index.js

import { add } from './math'
add(1,5)

src/math.js

export const add = (a, b) => {
  console.log(a + b)
}

export const minus = (a, b) => {
  console.log(a - b)
}

(2) 打包輸出
npm run bundle

//...
/*! exports provided: add, minus */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "minus", function() { return minus; });
const add = (a, b) => {
  console.log(a + b);
};
const minus = (a, b) => {
  console.log(a - b);
};
//...

(3) 問題分析
src/index.js僅引入了add方法汉矿,但是卻打包了add方法和minus方法崎坊。

  1. Tree Shaking
    tree shaking 是一個術(shù)語,通常用于描述移除 JavaScript 上下文中的未引用代碼(dead-code)洲拇。

webpack 2.0及之后版本支持Tree Shaking奈揍。
webpack 3.X版本開啟Tree Shaking方式與 webpack 4.X不同。
Tree Shaking只支持ES Module模塊引入方式赋续。不支持commonjs模塊引入方式男翰。

  1. development模式開啟Tree Shaking

(1) 編輯打包配置文件
webpack.dev.config.js

optimization: {
   usedExports: true
}

(2) 將文件標(biāo)記為side-effect-free(無副作用)
編輯package.json

"sideEffects": ["*.css"]

side-effect-free數(shù)組中標(biāo)記的文件即使沒有通過ES Module,也會被打包輸出纽乱。如果沒有文件設(shè)置為side-effect-free蛾绎,則sideEffects值設(shè)置為false

(3) 打包輸出

/*! exports provided: add, minus */
/*! exports used: add */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return add; });
/* unused harmony export minus */
const add = (a, b) => {
  console.log(a + b);
};
const minus = (a, b) => {
  console.log(a - b);
};

/***/
  1. production模式開啟Tree Shaking
    生產(chǎn)模式自動開啟Tree Shaking鸦列,無需設(shè)置optimization租冠。

Tree Shaking開啟的關(guān)鍵在于JavaScript代碼壓縮。在webpack3.X版本中薯嗤,通過UglifyJsPlugin插件進行JavaScript代碼壓縮顽爹。在webpack4.X版本中,mode: production生產(chǎn)模式默認進行JavaScript代碼壓縮骆姐。

  1. 結(jié)論
    你可以將應(yīng)用程序想象成一棵樹镜粤。綠色表示實際用到的 source code(源碼) 和library(庫),是樹上綠色的樹葉玻褪∪饪剩灰色表示未引用代碼,是秋天樹上枯萎的樹葉归园。為了除去死去的樹葉黄虱,你必須搖動這棵樹,使它們落下庸诱。

在以import { add } from './math'的方式引入模塊時捻浦,Tree Shaking能夠?qū)?code>'./math'中未被引入的模塊過濾掉。

1.1.2 Lodash Tree Shaking

  1. 編輯打包入口文件
    src/index.js
import { join } from 'lodash';
console.log(_.join(['1','2', '3'], '-'))
  1. 打包輸出
     Asset       Size  Chunks             Chunk Names
index.html  199 bytes          [emitted]  
   main.js   70.3 KiB       0  [emitted]  app

只用到了lodash中的join方法桥爽,main.js包大小為0.3 KiB朱灿。很明顯。Tree Shaking并沒有生效钠四。

  1. 安裝依賴
    npm i babel-plugin-lodash -D
  2. 編輯打包配置文件
    webapck.dev.config.js
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            [ "@babel/preset-env", {"useBuiltIns": "usage", "corejs": 2}]
          ],
          plugins: [
            "lodash" //對lodash進行Tree Shaking
          ]
        }
      }
  1. 打包輸出
     Asset       Size  Chunks             Chunk Names
index.html  199 bytes          [emitted]  
   main.js   1.08 KiB       0  [emitted]  app

經(jīng)過Tree Shaking后盗扒,main.js包大小為1.08 KiB跪楞。

使用babel-plugin-lodash插件后,即使使用import lodash from 'lodash'方式引入lodash侣灶,Tree Shaking仍然生效甸祭。

1.2 CSS Tree Shaking

  1. 安裝依賴
    npm i -D purifycss-webpack purify-css glob-all
  2. 編輯打包配置文件
    webpack.dev.config.js
const PurifyCSS = require('purifycss-webpack');
const glob = require('glob-all');
module.exports = {
//...
  plugins: [
    new PurifyCSS({
      paths: glob.sync([
        path.join(__dirname, './src/*.js')
      ])
    })
  ]
}
  1. 打包輸出
    生成的css文件不包含./src/*.js中使用不到的樣式。

purify-csscss modules不可以同時使用褥影。

2. webpack-merge

  1. 安裝依賴
    npm i webpack-merge -D
  2. 打包配置文件
    (1) build/webpack.base.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')

module.exports = {
  entry: {
    app: path.resolve(__dirname, '../src/index.js')
  },
  output: {
    publicPath: '',
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, '../dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              localIdentName: '[path][name]__[local]--[hash:base64:5]'
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: [ require('autoprefixer')]
            }
          }
        ]
      },
      {
        test: /\.scss$/,
        use: [
          "style-loader",
          "css-loader",
          "sass-loader",
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: [ require('autoprefixer')]
            }
          }
        ]
      },
      {
        test: /\.html$/,
        use: [
          {
            loader: "html-loader",
            options: {
              attrs: [':src', ':data-src']
            }
          }
        ]
      },
      {
        test: /\.(eot|ttf|svg|woff)$/,
        use: {
          loader: "file-loader",
          options: {
            name: '[name]-[hash:5].[ext]',
            outputPath: 'font/'
          }
        }
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: {
          loader:'url-loader',
          options: {
            name: '[name]-[hash:5].[ext]',
            outputPath: 'images/',
            limit: 4096
          }
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../src/index.html')
    }),
    new CleanWebpackPlugin()
  ]
}

(2) build/webpack.dev.config.js

const webpack = require('webpack');
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.config')

const devConfig = {
  mode: "development",
  devtool: 'cheap-module-eval-source-map',
  optimization: {
    usedExports: true
  },
  devServer: {
    open: true, //瀏覽器自動打開
    port: 9000,
    contentBase: './dist',
    hot: true,
    //hotOnly: true
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
}

module.exports = merge(baseConfig, devConfig)

(3) build/webpack.prod.config.js

const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.config')

const prodConfig = {
  mode: "production",
  devtool: 'cheap-module-source-map',
}

module.exports = merge(baseConfig, prodConfig)

webpack-merge可以對module.rules進行合并池户,但無法對單個rule中的loader進行合并。

  1. 創(chuàng)建打包命令
    package.json
  "scripts": {
    "build": "webpack --config ./build/webpack.prod.config.js",
    "dev": "webpack --config ./build/webpack.dev.config.js",
    "start": "webpack-dev-server --config ./build/webpack.dev.config.js",
  }

3. js代碼分割(Code Splitting)

3.1 單獨文件打包輸出的缺點

  1. 安裝lodash
    npm i --save lodash
  2. 編輯src/index.js
import _ from 'lodash'
console.log(_.join(['1','2', '3'], '-'))
  1. 打包分析
        Asset       Size  Chunks             Chunk Names
app.bundle.js   1.38 MiB     app  [emitted]  app
   index.html  221 bytes          [emitted]  
Entrypoint app = app.bundle.js

lodash和業(yè)務(wù)代碼打包到一個文件app.bundle.js凡怎。頁面加載js耗時時間久校焦。頁面代碼更新時,app.bundle.js全量更新统倒。

3.2 多入口實現(xiàn)分包

  1. 編輯打包配置文件
  entry: {
    lodash: path.resolve(__dirname, '../src/lodash.js'),
    app: path.resolve(__dirname, '../src/index.js')
  }
  1. 編輯src/lodash.js
import lodash from 'lodash'
window.lodash = lodash
  1. 編輯src/index.js
console.log(window.lodash.join(['1','2', '3'], '-'))
  1. 打包分析
           Asset       Size  Chunks             Chunk Names
   app.bundle.js   29.1 KiB     app  [emitted]  app
      index.html  284 bytes          [emitted]  
lodash.bundle.js   1.38 MiB  lodash  [emitted]  lodash
Entrypoint lodash = lodash.bundle.js
Entrypoint app = app.bundle.js

entry為多入口時寨典,入口文件順序即是html模板引入對應(yīng)輸出文件的順序。不同入口文件之間沒有依賴關(guān)系房匆。

3.3 SplitChunksPlugin配置

3.3.1 同步代碼分割

  1. 通過SplitChunksPlugin實現(xiàn)同步代碼分割招盲。

webpack 4+支持SplitChunksPlugin畜份。

  1. 編輯打包配置文件
    webpack.base.config.js
  optimization: {
    splitChunks: {
      chunks: "all"
    }
  }
  1. 編輯src/index.js
import _ from 'lodash'
console.log(_.join(['1','2', '3'], '-'))
  1. 打包分析
    npm run dev
Built at: 04/12/2019 9:37:25 AM
                Asset       Size       Chunks             Chunk Names
        app.bundle.js   32.4 KiB          app  [emitted]  app
           index.html  289 bytes               [emitted]  
vendors~app.bundle.js   1.35 MiB  vendors~app  [emitted]  vendors~app
Entrypoint app = vendors~app.bundle.js app.bundle.js

lodash打包輸出代碼被分割到vendors~app.bundle.js文件中柏蘑。

chunk表示打包輸出模塊重抖,打包輸出幾個文件姨夹,chunks就有幾個米诉。
同步代碼分割可以通過瀏覽器緩存功能提升第二次頁面加載速度威酒。

  1. 指定代碼分割打包輸出文件名

(1) 編輯打包配置文件

  output: {
    //...
    chunkFilename: '[name].chunk.js',
    //...
  }

html頁面中直接引入的資源文件(js煌恢、css)命名以 filename為規(guī)則骗卜。間接引用的資源文件命名以chunkFilename為規(guī)則

(2) 打包分析
npm run dev

Built at: 04/12/2019 9:39:07 AM
               Asset       Size       Chunks             Chunk Names
       app.bundle.js   32.4 KiB          app  [emitted]  app
          index.html  288 bytes               [emitted]  
vendors~app.chunk.js   1.35 MiB  vendors~app  [emitted]  vendors~app
Entrypoint app = vendors~app.chunk.js app.bundle.js

3.3.2 異步代碼分割

  1. 使用@babel/plugin-syntax-dynamic-import實現(xiàn)代碼分割
  2. 安裝依賴
    npm i @babel/plugin-syntax-dynamic-import -D
  3. 編輯babel配置
"plugins": [
    "@babel/plugin-syntax-dynamic-import"
]
  1. 編輯src/index.js
import('lodash').then(({default : _}) => {
  console.log(_.join(['1','2', '3'], '-'))
})
  1. 打包分析
    npm run dev
Built at: 04/12/2019 9:29:30 AM
        Asset       Size  Chunks             Chunk Names
  0.bundle.js   1.35 MiB       0  [emitted]  
app.bundle.js   33.8 KiB     app  [emitted]  app
   index.html  221 bytes          [emitted]  

webpack會自動對通過import()方法異步加載的模塊進行代碼分割宠页。
異步代碼分割既可以通過瀏覽器緩存功能提升第二次頁面加載速度,又可以通過懶加載的方式提升首次頁面加載速度寇仓。

  1. 指定代碼分割打包輸出文件名
import(/* webpackChunkName: "lodash" */'lodash').then(({default : _}) => {
//...

import()方法代碼分割的底層還是通過SplitChunksPlugin實現(xiàn)的举户,splitChunks配置參數(shù)同樣會影響import()方法代碼分割情況。

3.3.3 SplitChunksPlugin配置參數(shù)

  1. optimization.splitChunks默認配置項
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

webpack4.X版本才支持SplitChunksPlugin遍烦。在webpack3.X中俭嘁,使用CommonsChunkPlugin進行代碼分割。

  1. optimization.splitChunks配置項說明
  optimization: {
    splitChunks: {
      chunks: 'all',
      //initial只對同步代碼分割服猪,async(默認)只對異步代碼分割供填、all所有代碼都做代碼分割
      minSize: 30000,
      //大于30000Bit的模塊才做代碼分割
      maxSize: 0,
      //當(dāng)模塊大于maxSize時,會對模塊做二次代碼分割罢猪。當(dāng)設(shè)置為0時近她,不做二次分割。
      minChunks: 1,
      //當(dāng)打包輸出chunks文件引用該模塊的次數(shù)達到一定數(shù)目時才做代碼分割膳帕。
      maxAsyncRequests: 5,
      //異步加載的js文件最大數(shù)目為邊界條件進行代碼分割
      maxInitialRequests: 3,
      //以初始加載的js文件最大數(shù)目為邊界條件進行代碼分割
      automaticNameDelimiter: '~',
      //代碼分割生成文件連接符
      name: true,
      //代碼分割生成文件自動生成文件名
      cacheGroups: {
        //代碼分割緩存組粘捎,被分割代碼文件通過緩存組輸出為一個文件。
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          //模塊路徑正則表達式
          priority: -10,
          //緩存組優(yōu)先級,一個模塊優(yōu)先打包輸出到優(yōu)先級高的緩存組中攒磨。
          name: 'vendor'
          //代碼分割打包輸出文件名
        },
        lodash: {
          test: /[\\/]lodash[\\/]/,
          priority: -5,
        },
        jquery: {
          test: /[\\/]jquery[\\/]/,
          priority: -5,
        },
        default: {
          //默認緩存組泳桦,一般設(shè)置priority優(yōu)先級數(shù)值最小
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
          //代碼分割的模塊A引用其他模塊B,B已經(jīng)被打包輸出娩缰,則不再重新打包進入A
          name: 'common',
          chunks: 'all'
        }
      }
    }
  }

optimization.splitChunks.chunks可設(shè)置灸撰,默認值為async,表示默認只對動態(tài)import()做代碼分割漆羔。splitChunks.cacheGroups.{cacheGroup}.chunks同樣可以設(shè)置梧奢,默認值為all,表示cacheGroups分組代碼分割優(yōu)先級高于import()

3.4 懶加載(Lazy Loading)

  1. Lazy Loading文檔演痒。
  2. import()實現(xiàn)懶加載
const lazyConsole = async () => {
  const {default : _} = await import(/* webpackChunkName: "lodash" */'lodash');
  console.log(_.join(['1','2', '3'], '-'))
};

document.addEventListener('click', lazyConsole)

import()動態(tài)加載不僅可以實現(xiàn)代碼分割亲轨,還可以實現(xiàn)懶加載。
lodash模塊生成的vendors~lodash.bundle.js文件在點擊頁面時才加載鸟顺。

只有配置chunkFilename之后惦蚊,webpackChunkName才生效。
如果多個 import()的魔法注釋webpackChunkName指定同一個名字讯嫂,則這多個import()模塊會打包成一個bundle蹦锋。
如果外部也引入了import()方法中引入的模塊,則該模塊不會分割單獨打包欧芽。

3.5 預(yù)取/預(yù)加載模塊(prefetch/preload module)

3.5.1 查看頁面代碼利用率

  1. chrome瀏覽器打開網(wǎng)頁
  2. 打開調(diào)試面板
  3. commond + shift + p - show coverage - instrument coverage
    image.png
  4. 刷新網(wǎng)頁


    代碼利用率
  5. 分析結(jié)果
    紅色表示加載并運行代碼莉掂,綠色表示只加載未運行代碼。
    可以看到該頁面加載的每一個文件的利用率以及綜合利用率千扔。
    點擊右側(cè)橫條憎妙,可以看到具體文件代碼利用情況。


    image.png

3.5.2 提高代碼利用率

  1. 通過import()異步模塊懶加載的方式可以提高首屏代碼利用率曲楚。
  2. 未使用懶加載
    src/index.js
document.addEventListener('click',  () => {
  const element = document.createElement('div');
  element.innerHTML = 'Dell Li';
  document.body.appendChild(element)
});

代碼利用率為:77%

image.png

  1. 通過懶加載
    src/index.js
document.addEventListener('click',  () => {
  import('./click').then(({default: click}) => {
    click && click()
  })
});

src/click.js

const handleClick = () => {
  const element = document.createElement('div');
  element.innerHTML = 'Dell Li';
  document.body.appendChild(element)
};

export default handleClick

代碼利用率為:81.5%

image.png

異步模塊懶加載雖然可以減少首屏代碼量厘唾,縮短網(wǎng)頁首次加載時間,但等待用戶交互后才請求對應(yīng)js文件龙誊,會影響用戶體驗抚垃。可以通過prefetch/preload方式解決該問題趟大。

3.5.3 prefetch/preload module

  1. webpack v4.6.0+ 添加了預(yù)取和預(yù)加載(prefetch/preload module)的支持鹤树。
  2. 使用prefetch
    src/index.js
document.addEventListener('click',  () => {
  import(/* webpackPrefetch: true */ './click').then(({default: click}) => {
    click && click()
  })
});

這會生成 <link rel="prefetch" href="1.bundle.js"> 并追加到頁面頭部,指示著瀏覽器在閑置時間預(yù)取1.bundle.js文件护昧。

  1. prefetch / preload指令對比
  • preload chunk會在父chunk加載時魂迄,以并行方式開始加載。prefetch chunk 會在父 chunk 加載結(jié)束后開始加載惋耙。
  • preload chunk 具有中等優(yōu)先級捣炬,并立即下載熊昌。prefetch chunk 在瀏覽器閑置時下載。
  • preload chunk 會在父 chunk 中立即請求湿酸,用于當(dāng)下時刻婿屹。prefetch chunk 會用于未來的某個時刻。
  • 瀏覽器支持程度不同推溃。

4. CSS文件的代碼分割

4.1 現(xiàn)有CSS打包分析

  1. 打包配置
    webpack.base.config.js
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              localIdentName: '[path][name]__[local]--[hash:base64:5]'
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: [ require('autoprefixer')]
            }
          }
        ]
      },
      {
        test: /\.scss$/,
        use: [
          "style-loader",
          "css-loader",
          "sass-loader",
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: [ require('autoprefixer')]
            }
          }
        ]
      }
    ]
  }
  1. 入口文件

src/index.js

import './style.css'

src/style.css

body {
  background: yellow;
}
  1. 打包輸出
    npm run build
Built at: 04/12/2019 3:53:13 PM
            Asset       Size  Chunks             Chunk Names
    app.bundle.js   6.79 KiB       0  [emitted]  app
app.bundle.js.map   3.04 KiB       0  [emitted]  app
       index.html  221 bytes          [emitted]  
Entrypoint app = app.bundle.js app.bundle.js.map
  1. 存在的問題
    沒有打包輸出css文件昂利,css代碼被打包到js中。

4.2 MiniCssExtractPlugin

  1. MiniCssExtractPlugin文檔介紹
    該插件將CSS分割到文件中铁坎。對每個js文件中的css代碼創(chuàng)建一個css文件蜂奸。支持css按需加載和sourcemap
    MiniCssExtractPlugin不支持HMR(模塊熱更新)硬萍,建議在生產(chǎn)環(huán)境中使用扩所。

webpack4版本中,我們之前首選使用的extract-text-webpack-plugin完成了其歷史使命朴乖。推薦使用mini-css-extract-plugin祖屏。

  1. 安裝MiniCssExtractPlugin
    npm install --save-dev mini-css-extract-plugin
  2. webpack.base.config.js中對cssscss文件的loader處理移動到 webpack.dev.config.js中。
  3. 修改打包配置文件
    webpack.pro.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
//...
module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              modules: true,
              localIdentName: '[path][name]__[local]--[hash:base64:5]'
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: [ require('autoprefixer')]
            }
          }
        ]
      },
      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          "sass-loader",
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: [ require('autoprefixer')]
            }
          }
        ]
      },
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({})
  ]

與之前的配置相比做了兩點修改买羞,一個是引入new MiniCssExtractPlugin({})插件袁勺,一個是MiniCssExtractPlugin.loader替換style-loader
由于webpack-merge可以對module.rules進行合并畜普,但無法對單個rule中的loader進行合并期丰。所以在webpack.pro.config.js里寫了完整的處理csssass文件的rule。也可以在webpack.base.config.js通過環(huán)境變量的邏輯進行判斷添加MiniCssExtractPlugin.loader或者style-loader吃挑。

  1. 打包輸出
    npm run build
Built at: 04/12/2019 4:16:56 PM
            Asset        Size  Chunks             Chunk Names
    app.bundle.js  1010 bytes       0  [emitted]  app
app.bundle.js.map    3.04 KiB       0  [emitted]  app
          app.css    66 bytes       0  [emitted]  app
      app.css.map   170 bytes       0  [emitted]  app
       index.html   259 bytes          [emitted]  
Entrypoint app = app.css app.bundle.js app.css.map app.bundle.js.map

打包輸出了css文件咐汞。

如果沒有打包輸出css文件。原因可能是production自動開啟Tree Shaking儒鹿,需要將css文件標(biāo)記為side-effect-free(無副作用)。
"sideEffects": ["*.css"]

  1. css文件命名規(guī)則
plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].chunk.css"
    })
]
  1. css文件壓縮

(1) 安裝依賴
npm install --save-dev optimize-css-assets-webpack-plugin
(2) 編輯配置文件
webpack.prod.config.js

var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

const prodConfig = {
  //...
  optimization: {
    minimizer: [
      new OptimizeCssAssetsPlugin({})
    ]
  }
  //...
}

(3) 打包輸出

Built at: 04/12/2019 4:50:40 PM
            Asset       Size  Chunks             Chunk Names
    app.bundle.js    4.1 KiB       0  [emitted]  app
app.bundle.js.map   3.66 KiB       0  [emitted]  app
          app.css   56 bytes       0  [emitted]  app
       index.html  259 bytes          [emitted]  
Entrypoint app = app.css app.bundle.js app.bundle.js.map
  1. 可通過cacheGroups實現(xiàn)將所有js文件中的css打包到一個css文件中(Extracting all CSS in a single file)和將一個入口文件對應(yīng)的所有css打包到一個css文件中(Extracting CSS based on entry)几晤。

5. 打包分析(bundle analysis)

5.1 打包分析工具介紹

  1. 如果我們以分離代碼作為開始约炎,那么就應(yīng)該以檢查模塊的輸出結(jié)果作為結(jié)束,對其進行分析是很有用處的蟹瘾。
  2. 官方提供分析工具 是一個好的初始選擇圾浅。下面是一些可選擇的社區(qū)支持工具:
    (1) webpack-chartwebpack stats 可交互餅圖。
    (2) webpack-visualizer:可視化并分析你的bundle憾朴,檢查哪些模塊占用空間狸捕,哪些可能是重復(fù)使用的。
    (3) webpack-bundle-analyzer:一個pluginCLI工具众雷,它將bundle內(nèi)容展示為便捷的灸拍、交互式做祝、可縮放的樹狀圖形式。
    (4) webpack bundle optimize helper:此工具會分析你的bundle鸡岗,并為你提供可操作的改進措施建議混槐,以減少bundle體積大小。

5.2 官方分析工具

  1. analyse文檔
  2. 編輯打包命令
    package.json
  "scripts": {
    "dev": "webpack  --profile --json > stats.json --config ./build/webpack.dev.config.js"
  }
  1. 打包輸出
    npm run dev
    生成stats.json文件轩性,該文件中包含打包信息声登。
  2. 使用analyse分析打包結(jié)果
    stats.json文件上傳到analyse分析地址,即可看到打包細節(jié)信息揣苏。

5.3 webpack-bundle-analyzer

  1. 安裝依賴
    npm install --save-dev webpack-bundle-analyzer
  2. 編輯打包配置文件
    webpack.base.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}
  1. 打包結(jié)果分析
    image.png

    如果打包生成的不同Asset引入了相同js文件悯嗓,則說明該js文件被重復(fù)打包進兩個不同的資源,需要修改配置將該js文件進行分割卸察。

6. Shimming

shimming文檔

6.1 shimming 全局變量

  1. 一些第三方的庫(library)可能會引用一些全局依賴(例如jQuery 中的 $)脯厨。這些“不符合規(guī)范的模塊”就是 shimming 發(fā)揮作用的地方。
  2. 安裝jquery
    npm i jquery lodash --save
  3. 修改打包配置文件
    webpack.base.config.js
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
      join: ['lodash', 'join']
    })
  ]

當(dāng)有模塊使用$時蛾派,會自動import $ from 'jquery'

  1. 可直接使用$
    src/index.js
const dom = $('div')
dom.html(join(['hello', 'world'], ' '))
$('body').append(dom)

shimmingalias對比:alias的作用是創(chuàng)建 importrequire 的別名俄认,來確保模塊引入變得更簡單。shimming的作用是解決一些第三方的庫(library)可能會引用的一些全局依賴洪乍。即:alias使模塊引入更簡單眯杏,不用寫復(fù)雜路徑;shimming使模塊不用引入壳澳,使用全局變量的方式岂贩。

6.2 imports-loader

  1. 打印現(xiàn)在模塊中this指向
    src/index.js
console.log(this === window); //false
  1. 安裝依賴
    npm i imports-loader -D
  2. 編輯打包配置文件
    webpack.base.config.js
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {loader: "babel-loader"},
          {loader: "imports-loader?this=>window"}
        ]
      }
    ]
  }
  1. 打印現(xiàn)在模塊中this指向
    src/index.js
console.log(this === window); //true

項目中配置imports-loader?this=>window可能導(dǎo)致打包錯誤,'import' and 'export' may only appear at the top level (4:0)巷波。

7. 環(huán)境變量

  1. 修改打包配置文件

webpack.dev.config.js

const devConfig = {
  //...
}
module.exports = devConfig

webpack.prod.config.js

const prodConfig = {
  //...
}
module.exports = prodConfig

webpack.base.config.js

const merge = require('webpack-merge')
const devConfig = require('./webpack.dev.config')
const prodConfig = require('./webpack.prod.config')

const baseConfig = {
  //...
}
module.exports = (env) => {
  if(env && env.production) {
    return merge(baseConfig, prodConfig)
  } else {
    return merge(baseConfig, devConfig)
  }
}
  1. 修改打包命令
    package.json
  "scripts": {
    "build": "webpack --env.production --config ./build/webpack.base.config.js",
    "dev": "webpack --config ./build/webpack.base.config.js",
    "start": "webpack-dev-server --config ./build/webpack.base.config.js"
  }

這里的--env.production與打包配置文件中的env && env.production對應(yīng)萎津。
如果使用--env.production=abc,則打包配置文件中需要使用env && env.production==='abc'的寫法抹镊。
如果使用--env production锉屈,則打包配置文件中需要使用env === 'production'的寫法。

8. TypeScript

8.1 引入TypeScript

  1. 安裝依賴
    ? webpack-operate npm i ts-loader typescript -D
  2. 項目根目錄創(chuàng)建TypeScript配置文件
    tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs", //模塊引入機制
    "target": "es5", //轉(zhuǎn)化為es5
    "sourceMap": true, //支持sourceMap
    "allowJs": true //支持js引入
  },
  "exclude": [
    "node_modules"
  ]
}
  1. 創(chuàng)建入口文件
    src/index.ts
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message
    }
    greet() {
        return 'Hello' + this.greeting;
    }
}

let greeter = new Greeter('world')
alert(greeter.greet())
  1. 編輯打包配置文件
    webpack.config.base.js
  entry: {
    app: path.resolve(__dirname, '../src/index.ts'),
  }
  //...
  module: {
    rules: [
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use:  "ts-loader"
      }
     ]
     //...
  }
  1. 打包輸出
    npm run bundle

8.2 對庫代碼進行編譯檢查

  1. 查詢TypeScript支持編譯檢查的庫垮耳。
  2. 對庫代碼進行編譯檢查——以lodash為例

(1) 安裝依賴
? webpack-operate npm i @types/lodash --save-dev
(2) 修改ts文件
src/index.ts

import * as _ from 'lodash'

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message
    }
    greet() {
        //return _.join(123) //傳參不是數(shù)組颈渊,標(biāo)紅報錯
        return _.join([ 'Hello', this.greeting], ' ');
    }
}

let greeter = new Greeter('world')
alert(greeter.greet())

9. Eslint

9.1 使用eslint

  1. 安裝依賴
    ? webpack-operate npm i eslint -D
  2. 初始化eslint配置文件
    npx eslint --init
    自動生成.eslintrc.js文件。
?  webpack-operate npx eslint --init
? How would you like to use ESLint? To check syntax and find problems
? What type of modules does your project use? JavaScript modules (import/export)
? Which framework does your project use? React
? Where does your code run? (Press <space> to select, <a> to toggle all, <i> to invert selection)Browser
? What format do you want your config file to be in? JavaScript
The config that you've selected requires the following dependencies:

eslint-plugin-react@latest
? Would you like to install them now with npm? Yes
  1. 使用airbnb規(guī)則
    (1) 安裝依賴
    ? webpack-operate npm install eslint-config-airbnb eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y babel-eslint -D
    (2) 修改.eslintrc.js配置文件
  "extends": "airbnb",
  "parser": "babel-eslint"
  1. 檢查代碼
    (1) 命令行檢查方式
    npx eslint XXX(文件夾名字)
    (2) 編輯器檢查方式
    image.png
  2. 使某些規(guī)則失效
    編輯.eslintrc.js規(guī)則文件
  "rules": {
    "no-unused-vars": 0
  }

以上是在項目中使用eslint终佛,與Webpack無關(guān)俊嗽。

9.2 Webpack中配置eslint

  1. eslint-loader 文檔
  2. 安裝依賴
    ? webpack-operate npm i eslint-loader -D
  3. 編輯打包配置文件

webpack.base.config.js

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {loader: "babel-loader"},
          {loader: "eslint-loader"}
        ]
      }
    ]
  }

webpack.dev.config.js

  devServer: {
    overlay: true
  }

eslint-loader的作用是打包時先使用eslint檢查規(guī)則,應(yīng)該放在babel-loader之后铃彰。overlay的作用是使用webpack-dev-server打包時绍豁,將報錯信息顯示在頁面上。

  1. 對不符合規(guī)范的代碼進行簡單修復(fù)
   {
     loader: "eslint-loader",
     options: {
       fix: true
     }
   }

使用eslint-loader會在打包前對代碼進行檢查牙捉,降低打包效率竹揍。在實際項目開發(fā)中敬飒,一般使用eslintgit結(jié)合,在代碼提交到git倉庫時對代碼進行檢查鬼佣。

10. PWA

  1. 安裝依賴
    ? webpack-operate npm i workbox-webpack-plugin -D
  2. 編輯生產(chǎn)環(huán)境打包配置文件
    webpack.prod.config.js
const WorkBoxPlugin = require('workbox-webpack-plugin')

var prodConfig = {
  //...
  plugins: [
    new WorkBoxPlugin.GenerateSW({
      clientsClaim: true,
      skipWaiting: true
    })
  ]
  //...
}

生產(chǎn)環(huán)境才需要使用PWA驶拱。

  1. 編輯入口文件
    src/index.js
//業(yè)務(wù)代碼
import('lodash').then(({default : _}) => {
  console.log(_.join(['1','2', '3'], '-'))
})

//使用serviceWorker
if('serviceWorker' in navigator) { //如果瀏覽器支持serviceWorker
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
      .then(res => {
        console.log('serviceWorker registed')
      })
      .catch(err => {
        console.log('serviceWorker registe err')
      })
  })
}

service-worker.js文件在打包時生成。

  1. 打包輸出
    npm run build
                                                Asset       Size  Chunks             Chunk Names
                      2.6c02624b28028221db11.chunk.js    529 KiB       2  [emitted]  
                  2.6c02624b28028221db11.chunk.js.map    630 KiB       2  [emitted]  
                    app.051fb24e16eb3c7493d6.chunk.js  812 bytes       0  [emitted]  app
                app.051fb24e16eb3c7493d6.chunk.js.map  783 bytes       0  [emitted]  app
                                           index.html  326 bytes          [emitted]  
precache-manifest.a8a4feb9efc884fe5d31eed9b7b76ac0.js  445 bytes          [emitted]  
               runtime.a366ecc84e6df04acf79.bundle.js   8.81 KiB       1  [emitted]  runtime
           runtime.a366ecc84e6df04acf79.bundle.js.map    8.8 KiB       1  [emitted]  runtime
                                    service-worker.js  927 bytes          [emitted]  
Entrypoint app = runtime.a366ecc84e6df04acf79.bundle.js runtime.a366ecc84e6df04acf79.bundle.js.map app.051fb24e16eb3c7493d6.chunk.js app.051fb24e16eb3c7493d6.chunk.js.map
  1. 本地開啟一個服務(wù)
?  webpack-operate cd dist
?  dist http-server
Starting up http-server, serving ./
Available on:
  http://127.0.0.1:8080
  http://192.168.1.3:8080
  http://192.168.57.1:8080
Hit CTRL-C to stop the server
  1. 測試PWA
    打開http://127.0.0.1:8080晶衷,可以看到html頁面蓝纲。
    關(guān)閉服務(wù),刷新瀏覽器晌纫,仍然可以正常訪問頁面税迷。

11. 編寫并發(fā)布一個npm包

  1. 創(chuàng)建文件夾nmw-lodash
  2. 將項目初始化為一個npm
    ? nmw-lodash npm init -y
  3. 安裝依賴
    ? nmw-lodash npm i webpack webpack-cli --save
    ? nmw-lodash npm i lodash --save
  4. 編寫代碼

src/index.js

import * as math from './math'
import * as string from './string'

export default {
  math,
  string
}

src/math.js

export function add(a, b) {
  return a + b;
}

src/string.js

import _ from 'lodash'

export function join(a, b) {
  return _.join([a, b], ' ')
}
  1. 創(chuàng)建并編輯打包配置文件
    webpack.config.js
const path = require('path')

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'nmw-lodash.js',
    library: "nmwLodash", //以script標(biāo)簽引入時,支持nmwLodash全局變量
    libraryTarget: "umd" //支持umd規(guī)范引入
    //libraryTarget: "global" nmwLodash掛載在global上锹漱。
  },
  externals: [
    "lodash" //不打包lodash
  ]
}

文檔:output.libraryExport; output.library; externals

  1. 創(chuàng)建打包命令
    package.json
  "scripts": {
    "build": "webpack"
  }
  1. 修改npm包入口文件
    package.json
  "main": "./dist/nmw-lodash.js",
  1. 打包輸出
    npm run build
  2. npm官網(wǎng)注冊賬號
  3. 添加賬號密碼
    ? nmw-lodash npm adduser
  4. 發(fā)布項目
    npm publish

12. 打包性能優(yōu)化

12.1 優(yōu)化配置

  1. 跟上技術(shù)的迭代
    Node箭养、NpmYarn
  2. 在盡可能少的模塊上應(yīng)用Loader
    例如:使用excludeinclude哥牍。
{
  test: /\.js$/,
  exclude: /node_modules/,
  use: [{loader: "babel-loader"}]
}

exclude表示進行loader編譯的路徑毕泌。
include表示進行loader編譯的路徑。

  1. Plugin盡可能精簡并確毙崂保可靠
    例如:只在生產(chǎn)環(huán)境使用MiniCssExtractPluginCSS進行分割撼泛。
var MiniCssExtractPlugin = require("mini-css-extract-plugin");
//...
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].chunk.css"
    })
  ]
  1. resolve參數(shù)合理配置 (文檔)
    (1) resolve.alias:創(chuàng)建 importrequire 的別名,來確保模塊引入變得更簡單澡谭。
    例如:import Utility from 'Utilities';
    (2) resolve.extensions:自動解析確定的擴展愿题。能夠使用戶在引入模塊時不帶擴展。
    例如:import File from '../path/to/file';
    (3) resolve.mainFiles
    解析目錄時要使用的文件名蛙奖。
    例如:resolve配置如下
  resolve: {
    extensions: ['.js', '.jsx'],
    mainFiles: ['index'],  //默認配置
    alias: {
      child: path.resolve(__dirname, '../src/components/child')
    }
  }

模塊引入方式如下:

import Child from 'child';

resolve配置不宜過于復(fù)雜潘酗,否則會使模塊查找時間增加,降低webpack打包速度雁仲。

  1. 控制包文件大小
    (1) 使用Tree Shaking
    (2) Code Splitting代碼分割
  2. thread-loader仔夺、parallel-webpackhappypack多進程打包
  3. 合理使用SourceMap
  4. 結(jié)合state分析打包結(jié)果(bundle analysis)
  5. 開發(fā)環(huán)境內(nèi)存編譯(webpack-dev-server)
  6. 開發(fā)環(huán)境無用插件剔除

12.2 DIIPlugin

12.2.1 使用DIIPlugin

  1. 測試打包速度
    npm run build 打包耗時約950ms
  2. 第三方模塊沒有必要頻繁重新打包攒砖∏糇疲可以將第三方模塊打包輸出,webpack進行項目打包時祭衩,直接使用已經(jīng)被打包的第三方模塊,不再重新打包阅签。
  3. 創(chuàng)建并編輯打包配置文件
    webpack.dll.config.js
const path = require('path');
const webpack = require('webpack')

module.exports = {
  mode: 'production',
  entry: {
    vendors: ['react', 'react-dom', 'lodash'],
  },
  output: {
    filename: "[name].dll.js",
    path: path.resolve(__dirname, '../dll'),
    library: "[name]" //以vendors全局變量的方式暴露
  },
  plugins: [
    new webpack.DllPlugin({ //生成vendors.manifest.json映射文件
      name: '[name]',
      path: path.resolve(__dirname, '../dll/[name].manifest.json')
    })
  ]
}
  1. 創(chuàng)建打包命令
    package.json
  "scripts": {
    //...
    "build:dll": "webpack --config ./build/webpack.dll.config.js",
    //...
  }
  1. 打包生成vendors
    npm run build:dll
    生成vendors.dll.js以及vendors.manifest.json映射文件掐暮。
  2. 安裝依賴
    npm i add-asset-html-webpack-plugin -D
  3. 編輯打包配置文件
    webpack.base.config.js
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
//...
  plugins: [
    //...
    new AddAssetHtmlPlugin({
      //將vendors.dll.js插入html模板中
      filepath: path.resolve(__dirname, '../dll/vendors.dll.js') 
    }),
      //打包代碼時,第三方模塊如果在vendors.manifest.json有映射政钟,則直接在vendors全局變量中取路克。
    new webpack.DllReferencePlugin({ 
      manifest: path.resolve(__dirname, '../dll/vendors.manifest.json')
    })
  ],

AddAssetHtmlPlugin插件必須放在HtmlWebpackPlugin后面樟结。

  1. 測試打包速度
    npm run build 打包耗時約670ms
  2. 總結(jié)
    生成vendors包及映射 - 將vendors包插入html模板 - 以vendors全局變量暴露 - 使用vendors

12.2.2 多個DIIPlugin

  1. 編輯dll包打包配置文件
    webpack.dll.config.js
  //...
  entry: {
    vendors: ['lodash'],
    react: ['react', 'react-dom']
  }
  //...
  1. 編輯打包配置文件
    webpack.base.config.js
    動態(tài)生成plugins數(shù)組。
const fs = require('fs')

const plugins =[
  new HtmlWebpackPlugin({
    template: path.resolve(__dirname, '../src/index.html')
  }),
  new CleanWebpackPlugin()
  //...
]

const files = fs.readdirSync(path.resolve(__dirname, '../dll'));
//根據(jù) dll 目錄中生成的文件精算,添加對應(yīng)插件瓢宦。
files.forEach(file => {
  if(/.*\.dll\.js/.test(file)) {
      //XXX.dll.js插入html模板中
      plugins.push(new AddAssetHtmlPlugin({ 
        filepath: path.resolve(__dirname, '../dll', file)
      }))
  }
  if(/.*\.manifest\.json/.test(file)) { 
    //根據(jù)XXX.manifest.json映射,直接在XXX全局變量中獲取第三方模塊灰羽。
    plugins.push(new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, '../dll', file)
    }))
  }
})

13. 多頁面打包配置

13.1 介紹

  1. 多頁面應(yīng)用
    ① 生成多個html文件驮履。
    ② 各個html文件引入對應(yīng)的jsbundle
  2. 多頁面應(yīng)用的實現(xiàn)方式
    (1) 多配置
    對多個webpack配置分別打包廉嚼,生成多個html頁面玫镐。
    (2) 單配置
    對一個webpack配置進行打包,生成多個html頁面怠噪。

html-webpack-plugin文檔

13.2 多配置

  1. 技術(shù)基礎(chǔ)
    (1) webpack打包可以接收一個配置數(shù)組恐似。
    (2) parallel-webpack提高打包速度。

直接使用webpack也可以接收一個配置數(shù)組傍念,但串行打包過程速度比較慢矫夷。parallel-webpack可以并行打包,提高打包速度憋槐。

  1. 特點
    (1) 優(yōu)點
    可以使用parallel-webpack提高打包速度双藕。
    配置之間更加獨立、靈活秦陋。
    (2) 缺點
    不能多頁面之間共享代碼蔓彩。
  2. 創(chuàng)建編輯模板文件
    src/index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>

如果打包配置文件添加了html-loader,會正常解析html文件作為模版驳概,就會直接把 <%= htmlWebpackPlugin.options.title %>解析成字符串赤嚼。

  1. 創(chuàng)建編輯入口文件

src/index.js

console.log('this is index.js');

src/list.js

console.log('this list.js');
  1. 編輯打包配置文件
    webpack.pro.config.js
const baseConfig = require('./webpack.base.config');
//...
const prodConfig = {
//...
}
const buildConfig = merge(baseConfig, prodConfig);

const generatePage = function (
  { entry = '',
    title = '',
    name = '',
    chunks = [],
    template = path.resolve(__dirname, '../src/index.html')
  } = {}) {
  return {
    entry,
    plugins: [
      new HtmlWebpackPlugin({
        chunks,
        template,
        title,
        filename: name + '.html'
      })
    ]
  }
};

const indexConfig = generatePage({
  entry: {
    app: path.resolve(__dirname, '../src/index.js')
  },
  title: 'page index',
  name: 'index',
  chunks: ['runtime','vendors','app']
});

const listConfig = generatePage({
  entry: {
    list: path.resolve(__dirname, '../src/list.js')
  },
  title: 'page list',
  name: 'list',
  chunks: ['runtime','vendors','list']
});

const pagesConfig = [indexConfig, listConfig];
module.exports = pagesConfig.map(pageConfig => merge(pageConfig, buildConfig));

多配置在同一個文件中,生成一個配置數(shù)組顺又。
????這里的chunks: ['runtime','vendors','app'/'list']可以省略更卒,因為是多配置,默認會插入所有chunks稚照。如果是13.3中的單配置蹂空,入口有多個,那么就必須指定插入的chunks果录。

  1. 打包
    npm run build
Hash: 10dc11f107c648d35db3659186349535b844a395
Version: webpack 4.30.0
Child
    Hash: 10dc11f107c648d35db3
    Time: 876ms
    Built at: 06/01/2019 4:38:36 PM
            Asset       Size  Chunks             Chunk Names
    app.bundle.js  963 bytes       0  [emitted]  app
       index.html  190 bytes          [emitted]  
Child
    Hash: 659186349535b844a395
    Time: 850ms
    Built at: 06/01/2019 4:38:36 PM
             Asset       Size  Chunks             Chunk Names
    list.bundle.js  962 bytes       0  [emitted]  list
         list.html  191 bytes          [emitted]  

多配置打包不可以使用clean-webpack-plugin上枕,否則后一個打包會清除前一個打包結(jié)果。

  1. 使用parallel-webpack打包
    (1) 安裝
    npm i parallel-webpack -D
    (2) 打包
    ./node_modules/parallel-webpack/bin/run.js --config=build/webpack.prod.config.js

13.3 單配置

  1. 特點
    (1) 優(yōu)點
    可以共享各個entry之間的公用代碼弱恒。
    (2) 缺點
    打包比較慢辨萍。
    輸出的內(nèi)容比較復(fù)雜。
    配置不夠獨立返弹,相互耦合锈玉。例如:無法實現(xiàn)對不同入口設(shè)置不同的splitChunks代碼分割規(guī)則爪飘、無法實現(xiàn)對不同入口設(shè)置不同的動態(tài)路由(splitChunks會將公共代碼提出來,提前加載)拉背。

單配置時师崎,webpack打包配置對不同入口的所有chunks都生效。只要有一個入口的同步代碼依賴樹中含有某一個模塊椅棺,該模塊就不會被動態(tài)路由異步加載犁罩。

  1. 編輯打包配置文件
    webpack.pro.config.js
//...
const buildConfig = merge(baseConfig, prodConfig);
//...
const pagesConfig = [indexConfig, listConfig];
module.exports = merge(pagesConfig.concat(buildConfig));

webpack-merge可以接收多個參數(shù)merge(object1, object2, object3, ...),也可以接收一個數(shù)組merge([object1, object2, object3, ...])土陪。

  1. 打包
    npm run build
Version: webpack 4.30.0
Time: 668ms
Built at: 06/01/2019 4:54:57 PM
         Asset       Size  Chunks             Chunk Names
 app.bundle.js  963 bytes       0  [emitted]  app
    index.html  190 bytes          [emitted]  
list.bundle.js  963 bytes       1  [emitted]  list
     list.html  191 bytes          [emitted]  
Entrypoint app = app.bundle.js
Entrypoint list = list.bundle.js
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昼汗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鬼雀,更是在濱河造成了極大的恐慌顷窒,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件源哩,死亡現(xiàn)場離奇詭異鞋吉,居然都是意外死亡,警方通過查閱死者的電腦和手機励烦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門谓着,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坛掠,你說我怎么就攤上這事赊锚。” “怎么了屉栓?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵舷蒲,是天一觀的道長。 經(jīng)常有香客問我友多,道長牲平,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任域滥,我火速辦了婚禮纵柿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘启绰。我一直安慰自己昂儒,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布委可。 她就那樣靜靜地躺著荆忍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上刹枉,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天,我揣著相機與錄音屈呕,去河邊找鬼微宝。 笑死,一個胖子當(dāng)著我的面吹牛虎眨,可吹牛的內(nèi)容都是我干的蟋软。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼嗽桩,長吁一口氣:“原來是場噩夢啊……” “哼岳守!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起碌冶,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤湿痢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后扑庞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體譬重,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年罐氨,在試婚紗的時候發(fā)現(xiàn)自己被綠了臀规。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡栅隐,死狀恐怖塔嬉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情租悄,我是刑警寧澤谨究,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站恰矩,受9級特大地震影響记盒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜外傅,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一纪吮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧萎胰,春花似錦碾盟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春熙尉,著一層夾襖步出監(jiān)牢的瞬間联逻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工检痰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留包归,地道東北人。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓铅歼,卻偏偏與公主長得像公壤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子椎椰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,509評論 2 348