一文速覽Webpack

Webpack是一個打包模塊化JavaScript的工具,在Webpack裏一切文件皆模塊,經過Loader轉換文件,經過Plugin注入鉤子,最後輸出由多個模塊組合成的文件。Webpack專一於構建模塊化項目,稱爲模塊打包機。css

1. 核心概念

entry

入口,Webpack執行構建的第一步將從entry開始,可抽象成輸入;html

module.exports = {
  entry: './main.js'
};
複製代碼

output

輸出結果,在Webpack通過一系列處理並獲得最終想要的代碼後輸出結果;vue

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist');
  }
};
複製代碼

loader

模塊轉換器,用於將模塊的原內容按照需求轉換成新內容;node

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist');
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader?minimize'],
      }
    ]
  }
};
複製代碼

plugins

擴展插件,在Webpack構建流程中的特定時機注入擴展邏輯,來改變構建結果或咱們想作的事情;react

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist');
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        loaders: ExtractTextPlugin.extract({
          use: ['css-loader']
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      fliename: `[name]_[contenthash:8].css`,
    })
  ]
};
複製代碼

module

模塊,在Webpack裏一切皆模塊,一個模塊對應一個文件。Webpack會從配置的entry開始遞歸找出全部依賴的模塊;jquery

chunk

代碼塊,一個chunk由多個模塊組合而成,用於代碼合併與分割;webpack

總結: Webpack在啓動後會從Entry裏配置的Module開始,遞歸解析Entry依賴的全部Module。每找到一個Module,就會根據配置的Loader去找出對應的轉換規則,對Module進行轉換後,再解析出當前Module依賴的Module。這些模塊會以Entry爲單位進行分組,一個Entry及其全部依賴的Module被分到一個組也就是一個Chunk。最後Webpack會將全部Chunk轉換成文件輸出,在整個流程中,Webpack會在恰當的時機執行Plugin裏定義的邏輯。git

2. 基本配置

1. Entry

  • context

Webpack在尋找相對路徑的文件時會以context爲根目錄,context默認爲當前所在的工做目錄,注意context必須是一個絕對路徑的字符串github

module.exports = {
  context: path.resolve(__dirname, 'app'),
};
複製代碼
  • Entry類型
類型 例子 含義
String './main.js' 入口模塊的文件路徑,能夠是相對路徑
Array ['./main.js', './app.js'] 入口模塊的文件路徑,能夠是相對路徑
Object {a: './main.js', b: './app.js'} 配置多個入口,每一個入口生成一個chunk
  • chunk的名稱

chunk的名稱和entry的配置有關,若是entry是一個String或Array,只會生成一個chunk;若是entry是一個Object,就會生成多個chunk,名稱爲Object中的key。web

  • 配置動態Entry

若是不肯定有多少個頁面入口,能夠設置爲一個函數動態的返回entry配置;

// 同步函數
entry: () => {
  return {
    a: './page1/index',
    b: './page2/index'
  }
}
// 異步函數
entry: () => {
  return new Promise( (resolve) => {
    resolve({
      a: './page1/index',
      b: './page2/index'
    })
  })
}
複製代碼

2. Output

  • filename

filename是配置輸出的文件名稱,爲string類型,若是隻有一個輸出文件能夠寫死爲bundle.js,可是有多個輸出時能夠藉助模板變量[name].js,經常使用的變量包括:

變量名稱 含義
id Chunk的惟一標識,默認從0開始
name Chunk的名稱
hash Chunk的惟一標識的Hash值,[hash:8]表明取8位Hash值,默認是20位
chunkhash Chunk內容的Hash值,取值同上
  • chunkFilename

用於指定在運行過程當中生成的Chunk在輸出時的文件名稱,常見場景爲使用CommonChunkPlugin;支持和filename一致的內置變量;

  • path

配置輸出文件的本地目錄,爲string類型的絕對路徑,支持字符串模板,內置變量只有一個Hash;

module.exports = {
  output: {
    path: path.resolve(__dirname, 'dist_[hash]')
  }
};
複製代碼
  • publicPath

配置發佈到線上資源的URL前綴,爲string類型的相對路徑,默認爲'',也支持字符串模板,內置變量只有一個Hash;

module.exports = {
  output: {
    filename: '[name]_[chunkhash:8].js',
    publicPath: 'https://XX.cdn.com/static/' 
  }
};
複製代碼
  • crossOriginLoading

Webpcak輸出的部分代碼塊可能須要異步加載,是經過JSONP方式實現的,因此能夠在<script>標籤中設置crossorigin屬性,經常使用來獲取異步加載的腳本執行時的詳細錯誤信息。

crossorigin屬性值 含義
anonymous(默認) 在加載此腳本資源時不會帶上用戶的cookie
use-credentials 在加載此腳本資源時帶上用戶的cookie
  • LibraryTarget和library

當用Webpack去構建一個能夠被其餘模塊導入使用的庫時,須要用到LibraryTarget和library,它們一般搭配在一塊兒使用,推薦使用Rollup來構建基礎庫;

LibraryTarget配置以何種方式導出庫,經常使用的有var/this/commonjs/window/global等;

Library配置導出庫的名稱,配合LibraryTarget一塊兒使用;
複製代碼
  • LibraryExport

配置爲要導出的模塊中哪些子模塊須要被導出,它只有在output.libraryTarget被設置爲commonjs或者commonjs2時使用纔有意義;

// 某模塊源代碼
export const a = 1;
export default b = 2;

// 若是在output.libraryExport設置爲a;那麼將構建輸出的代碼和使用方法將變成如下內容:
// Webpack輸出的代碼
module.export = lib_code['a'];
// 使用庫的方法
require('library-name') === 1;
複製代碼

3. Module

  • 配置Loader

rules配置模塊的讀取和解析規則,一般用來配置Loader。類型爲數組,數組裏的每一項都描述瞭如何處理部分文件,每一項大體有以下三種方式來完成;

  1. 條件匹配: 經過test、include、exclude三個配置項來選中Loader要處理的文件;
  2. 應用規則: 對選中的文件經過use配置項來應用Loader,也能夠是一個,也能夠是按照從後往前的順序一組Loader,同時能夠分別向Loader傳入參數;
  3. 重要順序: 一組Loader的默認順序是從右往左執行的,經過enforce選項能夠將其中一個Loader放在最前(pre)或最後(post);
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [{
          loader: 'babel-loader',
          options: {
            cacheDirectory: true
          },
          enforce: 'post'
        }],
        include: path.resolve(__dirname, 'src'),
      },
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
        exclude: path.resolve(__dirname, 'node_modules')
      }
    ]
  }
};
複製代碼
  • noParse

該可選配置項可讓Webpack忽略對部分沒采用模塊化的文件的遞歸解析和處理,從而提升構建性能,類型爲RegExp、[RegExp]、Function中的一種,注意被忽略的文件裏不能包含import、require、define等模塊化語句,否則會致使在瀏覽器中沒法執行該模塊化語句;

// 正則形式
noParse: /jquery|chartjs/
// 函數形式
noParse: (content) => {
  return /jquery|chartjs/.test(content);
}
複製代碼
  • parser

該配置項能夠精確到語法層面,讓Webpack只解析對應的模塊化文件;

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['babel-loader'],
        include: path.resolve(__dirname, 'src'),
        parser: {
          amd: false,
          commonjs: true,
          requirejs: false,
          harmony: true
        }
      }
    ]
  }
};
複製代碼

4. Resolve

  • alias

該配置項經過別名來將原導入路徑映射成一個新的導入路徑;

module.exports = {
  resolve: {
    alias: {
      @components: './src/common/components',
      @assets: './src/common/assets'
    }
  }
};
複製代碼
  • mainFields

有一些第三方模塊會針對不一樣的環境提供幾份代碼,例如分別提供ES5和ES6兩份代碼,Webpack會根據mainFields的配置去決定優先採用哪份代碼,若是想優先採用ES6的代碼,則能夠這樣配置:

mainFields: ['jsnext:main', 'browser', 'main'],
複製代碼
  • extensions

在導入語句沒帶文件後綴時,Webpack會自動帶上後綴嘗試訪問文件是否存在,默認是:

extensions: ['.js', '.json'],
複製代碼

若是咱們想讓Webpack優先使用typescript文件,則能夠這樣配置:

extensions: ['.ts', '.js', '.json']
複製代碼
  • modules

配置Webpack去哪些目錄下尋找第三方模塊,有時候咱們項目中的大量模塊會放在common下面,則能夠這樣去配置:

modules: ['./src/common', 'node_modules']
複製代碼
  • descriptionFiles

該配置項是描述第三方模塊的文件名稱,也就是package.json文件,默認以下:

descriptionFiles: ['package.json']
複製代碼
  • enforceExtension

若是resolve.enforceExtension爲true,則全部導入語句都必須帶後綴,如import './main.js';

  • enforceModuleExtension

和resolve.enforceExtension做用相似,專門針對node_modules下的模塊生效,由於第三方模塊大部分是不帶後綴的,若是resolve.enforceExtension設置爲true了,須要把resolve.enforceModuleExtension設爲false來兼容第三方模塊;

5. Plugin

Plugin的配置很簡單,plugins接收一個數組,數組中的每一項都是一個Plugin的實例,Plugin的參數經過構造函數傳入;

module.exports = {
  plugins: [
    new CommonChunkPlugin({
      name: 'common',
      chunks: ['a', 'b']
    })
  ]
};
複製代碼

6. DevServer

  • hot

開啓模塊熱替換功能,在不刷新整個頁面的狀況下,經過用新模塊替換老模塊來作到實時預覽;

  • inline

用於配置是否將這個代理客戶端自動注入將運行在頁面中的chunk裏,默認自動注入;

-- 若是開啓inline,則devserver會在構建變化後的代碼時經過代理客戶端控制網頁刷新;

-- 若是關閉inline,則devserver會經過iframe的方式去運行要開發的網頁,在構建完變化後的代碼時,會經過刷新iframe來實現實時預覽,這時須要去http://localhost:8080/webpack-dev-server/ 實時預覽本身的網頁;

  • historyApiFallback

用於方便的開發使用了HTML5的History API的單頁應用,老是返回同一個html文件,瀏覽器會從URL裏解析出當前頁面的狀態,從而顯示對應的界面;

  • contentBase

用來配置devserver HTTP服務器的文件根目錄,在默認狀況下爲當前的執行目錄,一般是項目的根目錄,因此在通常狀況下沒必要設置;

  • headers

能夠在http響應中注入一些http響應頭,使用以下:

module.exports = {
  devServer: {
    headers: {
      'x-cookie': 12345
    }
  }
};
複製代碼
  • host

用於配置devServer服務監聽的地址,若是想讓局域網的其餘設備訪問本身的本地服務,則能夠在啓動devserver時帶上參數--host 0.0.0.0;

  • port

用於配置devServer服務監聽的端口,默認使用8080端口,若是80端口被其餘程序佔用則依次+1類推;

  • allowedHosts

配置一個白名單列表,只有http請求的host在該列表中才會正常返回;

  • disableHostCheck

配置是否關閉用於DNS從新綁定的http請求的host檢查,devserver默認只接受本地的請求,關閉後能夠接收來自任意host的請求;

  • https

devsever默認使用http服務,在某些狀況下須要使用https服務時,能夠開啓此配置,此時devsever會自動爲咱們在本地生成一份https證書,這時須要重啓服務,如HTTP2和Service Worker就必須運行在https上;

  • clientLogLevel

配置客戶端的日誌等級,會影響咱們在瀏覽器控制檯裏看到的內容,默認爲info級別,即輸出因此類型(none/error/warning/info)的日誌;

  • compress

配置是否啓用Gzip壓縮,爲Boolean類型,默認爲false;

  • open

用於在devserver啓動且第一次構建完成時,自動用系統默認瀏覽器打開咱們開發的頁面,還提供了openPage配置項來打開指定URL的頁面;

7. 其它

  • Target

target配置可讓Webpack構建除針對不一樣運行環境的代碼,常見的有:

target值 含義
web 針對瀏覽器(默認),全部代碼都集中在一個文件裏
node 針對nodejs,使用require語句加載chunk代碼
async-node 針對nodejs,異步加載chunk代碼
webworker 針對webworker
electron-mian 針對Electron主線程
electron-render 針對Electron渲染線程
  • Devtool

配置Webpack如何生成Source Map,默認值是false即不生成,若想構建出的代碼生成Source Map方便調試,則能夠這樣配置:

module.exports = {
  devtool: 'source-map'
};
複製代碼
  • Watch和WatchOptions

Webpack支持監聽文件更新,在文件發生變化時從新編譯,監聽模式默認是關閉的,如想打開則配置爲:

module.exports = {
  watch: true
};
複製代碼

在使用devserver時,監聽模式默認開啓;除此以外,還能夠靈活的控制監聽模式;

module.exports = {
  watch: true,
  watchOptions: {
    // 不監聽的文件或文件夾,支持正則匹配
    ignored: /node_modules/,
    // 監聽到變化後會等300ms再去執行,防抖
    // 默認是300ms
    aggregateTimeout: 300,
    // 判斷文件是否發生變化經過不停地詢問系統指定文件有沒有變化
    // 默認每秒詢問1000次
    poll: 1000
  }
};
複製代碼
  • Externals

用於告訴Webpack要構建的代碼中使用了哪些不用被打包的模塊,也就是說這些模版是外部環境提供的,Webpack在打包時能夠忽略它們;

module.exports = {
  externals: {
    jquery: 'jQuery'
  }
};
複製代碼
  • ResloveLoader

用來告訴Webapck如何去尋找Loader,該配置項經常使用來加載本地的Loader,默認配置以下:

module.exports = {
  resolveLoader: {
    modules: ['node_modules'],
    extensions: ['js', 'json'],
    mainFields: ['loader', 'main']
  }
};
複製代碼

3. 性能優化

優化開發體驗:

1. 縮小文件搜索範圍

2. 使用DllPlugin

3. 使用HappyPack

4. 使用ParallelUglifyPlugin

5. 使用自動刷新

6. 開啓模塊熱替換

優化輸出質量:

1. 壓縮代碼

2. CDN加速

3. 抽取公共代碼

4. 使用Tree Shaking

5. 使用Code Spliting

6. 開啓Scope Hoisting

7. 輸出分析

4. 經常使用Loader

文件

Loader名稱 功能描述
raw-loader 加載文件原始內容(utf-8)
val-loader 將代碼做爲模塊執行,並將 exports 轉爲 JS 代碼
url-loader 像 file loader 同樣工做,但若是文件小於限制,能夠返回 data URL
file-loader 將文件發送到輸出文件夾,並返回(相對)URL

JSON

Loader名稱 功能描述
json-loader 加載 JSON文件(默認包含)
json5-loader 加載和轉譯 JSON 5文件

轉換編譯(Transpiling)

Loader名稱 功能描述
script-loader 在全局上下文中執行一次 JavaScript 文件(如在 script 標籤),不須要解析
babel-loader 加載 ES2015+ 代碼,而後使用 Babel 轉譯爲 ES5
buble-loader 使用 Bublé 加載 ES2015+ 代碼,而且將代碼轉譯爲 ES5
traceur-loader 加載 ES2015+ 代碼,而後使用 Traceur轉譯爲 ES5
ts-loaderawesome-typescript-loader 像 JavaScript 同樣加載 TypeScript2.0+
coffee-loader 像 JavaScript 同樣加載 CoffeeScript

模板(Templating)

Loader名稱 功能描述
html-loader 導出 HTML 爲字符串,須要引用靜態資源
pug-loader 加載 Pug 模板並返回一個函數
jade-loader 加載 Jade 模板並返回一個函數
markdown-loader 將 Markdown 轉譯爲 HTML
react-markdown-loader 使用 markdown-parse parser(解析器) 將 Markdown 編譯爲 React 組件
posthtml-loader 使用 PostHTML 加載並轉換 HTML 文件
handlebars-loader 將 Handlebars 轉移爲 HTML
markup-inline-loader 將內聯的 SVG/MathML 文件轉換爲 HTML。在應用於圖標字體,或將 CSS 動畫應用於 SVG 時很是有用。

樣式

Loader名稱 功能描述
style-loader 將模塊的導出做爲樣式添加到 DOM 中
css-loader 解析 CSS 文件後,使用 import 加載,而且返回 CSS 代碼
less-loader 加載和轉譯 LESS 文件
sass-loader 加載和轉譯 SASS/SCSS 文件
postcss-loader 使用 PostCSS加載和轉譯 CSS/SSS 文件
stylus-loader 加載和轉譯 Stylus 文件

清理和測試(Linting && Testing)

Loader名稱 功能描述
mocha-loader 使用 mocha 測試(瀏覽器/NodeJS)
eslint-loader PreLoader,使用 ESLint 清理代碼
jshint-loader PreLoader,使用 JSHint 清理代碼
coverjs-loader PreLoader,使用 CoverJS 肯定測試覆蓋率

框架(Frameworks)

Loader名稱 功能描述
vue-loader 加載和轉譯 Vue 組件
polymer-loader 使用選擇預處理器(preprocessor)處理,而且 require() 相似一等模塊(first-class)的 Web 組件
angular2-template-loader 加載和轉譯 Angular 組件

5. 經常使用Plugin

Plugin名稱 功能描述
AggressiveSplittingPlugin 將原來的 chunk 分紅更小的 chunk
BabelMinifyWebpackPlugin 使用 babel-minify進行壓縮
BannerPlugin 在每一個生成的 chunk 頂部添加 banner
CommonsChunkPlugin 提取 chunks 之間共享的通用模塊
CompressionWebpackPlugin 預先準備的資源壓縮版本,使用 Content-Encoding 提供訪問服務
ContextReplacementPlugin 重寫 require 表達式的推斷上下文
CopyWebpackPlugin 將單個文件或整個目錄複製到構建目錄
DefinePlugin 容許在編譯時(compile time)配置的全局常量
DllPlugin 爲了極大減小構建時間,進行分離打包
EnvironmentPlugin DefinePluginprocess.env 鍵的簡寫方式。
ExtractTextWebpackPlugin 從 bundle 中提取文本(CSS)到單獨的文件
HotModuleReplacementPlugin 啓用模塊熱替換(Enable Hot Module Replacement - HMR)
HtmlWebpackPlugin 簡單建立 HTML 文件,用於服務器訪問
I18nWebpackPlugin 爲 bundle 增長國際化支持
IgnorePlugin 從 bundle 中排除某些模塊
LimitChunkCountPlugin 設置 chunk 的最小/最大限制,以微調和控制 chunk
LoaderOptionsPlugin 用於從 webpack 1 遷移到 webpack 2
MinChunkSizePlugin 確保 chunk 大小超過指定限制
NoEmitOnErrorsPlugin 在輸出階段時,遇到編譯錯誤跳過
NormalModuleReplacementPlugin 替換與正則表達式匹配的資源
NpmInstallWebpackPlugin 在開發時自動安裝缺乏的依賴
ProvidePlugin 沒必要經過 import/require 使用模塊
SourceMapDevToolPlugin 對 source map 進行更細粒度的控制
EvalSourceMapDevToolPlugin 對 eval source map 進行更細粒度的控制
UglifyjsWebpackPlugin 能夠控制項目中 UglifyJS 的版本
ZopfliWebpackPlugin 經過 node-zopfli 將資源預先壓縮的版本

更多第三方LoaderPlugin,查看 awesome-webpack 列表。

本文整理於【深刻淺出Webpack】,若有錯誤,敬請雅正😄

更多精彩內容歡迎關注個人公衆號【天道酬勤Lewis】

相關文章
相關標籤/搜索