文章出處

前面的話

  webpack是當下最熱門的前端資源模塊化管理和打包工具。它可以將許多松散的模塊按照依賴和規則打包成符合生產環境部署的前端資源。當webpack處理應用程序時,它會遞歸地構建一個依賴關系圖表(dependency graph),其中包含應用程序需要的每個模塊,然后將所有這些模塊打包成少量的bundle(通常只有一個),由瀏覽器加載。它是高度可配置的,在開始前需要先理解四個核心概念:入口(entry)、輸出(output)、加載器(loader)、插件(plugins)。本文將詳細介紹webpack的這四個基礎概念

 

入口

  webpack將創建所有應用程序的依賴關系圖表。圖表的起點被稱之為入口起點(entry point)。入口起點告訴 webpack 從哪里開始,并遵循著依賴關系圖表知道要打包什么。可以將應用程序的入口起點認為是根上下文或 app 第一個啟動文件

  類比于requirejs中的入口文件main.js,最終使用r.js打包時,都打包在main.js里

  在webpack中,使用webpack配置對象中的entry屬性來定義入口,包括以下多種方式

【單個入口(簡寫)語法】

  用法:entry: string|Array<string>

  [注意]在設置entry屬性時,如果是當前頁面,一定要在屬性值前面設置為'./',否則無法識別

//webpack.config.js
var config = {
  entry: './path/to/my/entry/file.js'
};

  entry屬性的單個入口語法,是下面的簡寫:

//webpack.config.js
var config = {
  entry: {
    main: './path/to/my/entry/file.js'
  }
};

  向entry傳入一個數組時,將創建“多個主入口(multi-main entry)”

entry:['./entry1','./entry2']

【對象語法】

  用法:entry: {[entryChunkName: string]: string|Array<string>}

//webpack.config.js
var config = {
  entry: {
    app: './src/app.js',
    vendors: './src/vendors.js'
  }
};

  對象語法會比較繁瑣。然而,這是應用程序中定義入口的最可擴展的方式

  從上面的代碼可知,webpack從app.js和vendors.js開始創建依賴圖表。這些圖表是彼此完全分離、互相獨立的。這種方式比較常見于,只有一個入口起點(不包括vendor)的單頁應用程序(single page application)中

 

出口

  將所有的資源(assets)歸攏在一起后,還需要告訴webpack在哪里打包應用程序。webpack的output屬性描述了如何處理歸攏在一起的代碼(bundled code)

//webpack.config.js
var path = require('path');
module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};

  在上面的代碼中,我們通過output.filename和output.path屬性,來告訴webpack bundle的名稱,以及我們想要生成(emit)到哪里

  [注意]即使可以存在多個入口起點,但只指定一個輸出配置,如下所示output的filename必須為[name]或其類似,不能為確定的名稱,否則會提示Conflict: Multiple assets emit to the same filename bundle.js,翻譯過來是多入口不能指定出口文件中同樣的filename名稱

  entry: {
    'main': './entry.js',
    'hello':'./hello.js'
  },
  output: {
    path: __dirname,//出口路徑
    filename: '[name].js'//出口名稱
  }

 

【用法(Usage)】

  在webpack中配置output屬性,需要將它的值設置為一個對象,并包含filename和path屬性這兩個必選項

  filename:編譯文件的文件名,首選推薦:main.js||bundle.js||index.js

  path:對應一個絕對路徑,此路徑是希望一次性打包的目錄

//webpack.config.js
var config = {
  output: {
    filename: 'bundle.js',
    path: '/home/proj/public/assets'
  }
};

【選項(Options)】

output.chunkFilename

  非入口的chunk(non-entry chunk)的文件名,路徑相對于output.path目錄

[id] 被chunk的id替換
[name] 被chunk的name替換(或者,在chunk沒有name時使用id替換)
[hash] 被compilation生命周期的hash替換
[chunkhash] 被chunk的hash替換

output.crossOriginLoading

  此選項可以啟用跨域加載(cross-origin loading)chunk,可選的值有:

false - 禁用跨域加載(默認值)
"anonymous" - 啟用跨域加載。當使用 anonymous 時,發送不帶憑據(credential)的請求。
"use-credentials" - 啟用跨域加載。發送帶憑據(credential)的請求

output.devtoolLineToLine

  所有指定模塊啟用行到行映射(line-to-line mapped)模式。行到行映射模式使用一個簡單的SourceMap,即生成資源(generated source)的每一行都映射到原始資源(original source)的同一行。這是一個可做性能優化之處。當需要更好的性能,并且只要確保輸入行(input line)和生成行(generated line)匹配時,才會考慮啟用

true 在所有模塊啟用(不推薦)
{test, include, exclude} 對象,對特定文件啟用(類似于 module.loaders)
默認值:
false

output.filename

  指定硬盤每個輸出文件的名稱。在這里不能指定為絕對路徑。output.path選項規定了文件被寫入硬盤的位置。filename僅用于命名每個文件

//單個入口
{
  entry: './src/app.js',
  output: {
    filename: 'bundle.js',
    path: __dirname + '/build'
  }
}
//多個入口
{
  entry: {
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    filename: '[name].js',//被 chunk 的 name 替換
    path: __dirname + '/build'
  }
}

output.hotUpdateChunkFilename

  熱更新chunk(Hot Update Chunk)的文件名。它們在output.path目錄中

[id] 被chunk的id替換
[hash] 被compilation生命周期的hash替換。(最后一個hash存儲在記錄中)
默認值:"[id].[hash].hot-update.js"

output.hotUpdateFunction

  webpack中用于異步加載(async load)熱更新(hot update)chunk的JSONP函數

默認值:"webpackHotUpdate"

output.hotUpdateMainFilename

  熱更新主文件(hot update main file)的文件名

[hash] 被compilation生命周期的hash替換。(最后一個hash存儲在記錄中)
默認值:"[hash].hot-update.json"

output.jsonpFunction

  webpack中用于異步加載(async loading)chunk的JSONP函數

  較短的函數可能會減少文件大小。當單頁有多個webpack實例時,請使用不同的標識符(identifier)

默認值:"webpackJsonp"

output.library

  如果設置此選項,會將bundle導出為library。output.library是library的名稱。

  如果正在編寫library,并且需要將其發布為單獨的文件,請使用此選項

output.libraryTarget

  library的導出格式

"var" - 導出為一個變量:var Library = xxx(默認)
"this" - 導出為 this 的一個屬性:this["Library"] = xxx
"commonjs" - 導出為 exports 的一個屬性:exports["Library"] = xxx
"commonjs2" - 通過 module.exports:module.exports = xxx 導出
"amd" - 導出為 AMD(可選命名 - 通過 library 選項設置名稱)
"umd" - 導出為 AMD,CommonJS2 或者導出為 root 的屬性

  如果output.library未設置,但是output.libraryTarget被設置為var以外的值,則「所導出對象」的每個屬性都被復制到「對應的被導出對象」上(除了amd,commonjs2和umd)

output.publicPath

  一般地,publicPath用于設置上線地址,在開發過程中,該值不需要設置

output: {
    filename:'main.js'
    path: "/home/proj/public/assets",
    publicPath: "http://cdn.com"
}

  以上面代碼為例,最終main.js的線上地址是'http://cdn.com/home/proj/public/assets/main.js'

output.path

  導出目錄為絕對路徑(必選項)

//config.js
output: {
    path: "/home/proj/public/assets",
    publicPath: "/assets/"
}
//index.html
<head>
  <link href="/assets/spinner.gif"/>
</head>
//config.js
output: {
    path: "/home/proj/cdn/assets/[hash]",
    publicPath: "http://cdn.example.com/assets/[hash]/"
}

  [注意]在編譯時不知道最終輸出文件的 publicPath 的情況下,publicPath 可以留空,并且在入口起點文件運行時動態設置。如果你在編譯時不知道 publicPath,你可以先忽略它,并且在入口起點設置 __webpack_public_path__

 __webpack_public_path__ = myRuntimePublicPath

output.sourceMapFilename

  JavaScript 文件的 SourceMap 的文件名

[file] 被 JavaScript 文件的文件名替換
默認值:"[file].map"

 

加載器

  webpack的目標是,讓webpack聚焦于項目中的所有資源(asset),而瀏覽器不需要關注考慮這些。webpack把每個文件(.css,.html,.scss,.jpg,etc.)都作為模塊處理。然而webpack只理解JavaScript。webpack loader會將這些文件轉換為模塊,而轉換后的文件會被添加到依賴圖表中

  loader可以使你在require()或"加載"模塊時預處理文件。因此,loader類似于其他構建工具中“任務(task)”,并提供了處理前端構建步驟的強大方法

  webpack的配置要能識別出(identify)應該被對應的loader進行轉換(transform)的那些文件。由于進行過文件轉換,所以能夠將被轉換的文件添加到依賴圖表(并且最終添加到bundle中)(use屬性)

var path = require('path');
var config = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      {test: /\.(js|jsx)$/, use: 'babel-loader'}
    ]
  }
};
module.exports = config;

  以上配置中,對一個單獨的module對象定義了rules屬性,里面包含兩個必須屬性:test和use。相當于告訴webpack compiler,碰到「在require()/import語句中被解析為'.js'或'.jsx'的路徑」時,在把它們添加并打包之前,要先使用babel-loader去轉換”

  [注意]在webpack配置中定義loader時,要定義在module.rules中,而不是rules。在定義錯誤時webpack會給出嚴重的警告

【示例】

  例如,使用loader加載CSS文件,或將TypeScript轉為JavaScript。首先,安裝對應的loader:

npm install --save-dev css-loader
npm install --save-dev ts-loader

  其次,配置webpack.config.js,對每個.css文件使用css-loader,然后類似地,對每個.ts文件使用ts-loader:

//webpack.config.js
module.exports = {
  module: {
    rules: [
      {test: /\.css$/, use: 'css-loader'},
      {test: /\.ts$/, use: 'ts-loader'}
    ]
  }
};

  [注意]根據配置選項,下面的規范定義了同等的loader用法:

{test: /\.css$/, loader: 'css-loader'}
// 等同于
{test: /\.css$/, use: 'css-loader'}
// 等同于
{test: /\.css$/, use: {
  loader: 'css-loader',
  options: {}
}}

【配置】

  在應用程序中,有三種使用 loader 的方式:1、通過配置;2、在 require 語句中顯示使用;3、通過 CLI

通過配置

  module.rules允許在webpack配置中指定幾個loader。 這是展示loader的一種簡明的方式,并且有助于使代碼變得簡潔。而且對每個相應的loader有一個完整的概述

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        { loader: 'style-loader'},
        {
          loader: 'css-loader',
          options: {
            modules: true
          }
        }
      ]
    }
  ]
}

通過require

  可以在require語句(或define,require.ensure,等語句)中指定loader。使用!將資源中的loader分開。分開的每個部分都相對于當前目錄解析

require('style-loader!css-loader?modules!./styles.css');

  通過前置所有規則及使用!,可以對應覆蓋到配置中的任意loader

  選項可以傳遞查詢參數,就像在web中那樣(?key=value&foo=bar)。也可以使用JSON對象(?{"key":"value","foo":"bar"})

通過CLI

webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'

  這會對 .jade 文件使用 jade-loader,對 .css 文件使用 style-loader 和 css-loader

【特性】

  loader 支持鏈式傳遞。能夠對資源使用流水線(pipeline)。loader 鏈式地按照先后順序進行編譯。loader 鏈中的第一個 loader 返回值給下一個 loader。在最后一個 loader,返回 webpack 所預期的 JavaScript

  loader 可以是同步或異步函數。loader 運行在 Node.js 中,并且能夠執行任何可能的操作

  loader 接收查詢參數。用于 loader 間傳遞配置。loader 也能夠使用 options 對象進行配置

  除了使用 package.json 常見的 main 屬性,還可以將普通的 npm 模塊導出為 loader,做法是在 package.json 里定義一個 loader 字段

  插件(plugin)可以為 loader 帶來更多特性。loader 能夠產生額外的任意文件。

  loader 通過(loader)預處理函數,為 JavaScript 生態系統提供了更多有力功能。用戶現在可以更加靈活的引入細粒度邏輯,例如壓縮(compression)、打包(package)、語言翻譯(language translation)和其他更多

【解析】

  loader 遵循標準的模塊解析。多數情況下,loader 將從模塊路徑(通常將模塊路徑認為是 npm install, node_modules)解析。

  loader 模塊需要導出為一個函數,并且使用 Node.js 兼容的 JavaScript 編寫。在通常情況下,可以使用 npm 來管理 loader,也可以將 loader 模塊作為應用程序中的文件。按照約定,loader 通常被命名為 xxx-loader(例如 json-loader)

 

插件

  插件是wepback的支柱功能。在使用webpack配置時,webpack自身也構建于同樣的插件系統上。插件目的在于解決loader無法實現的其他事情。由于loader僅在每個文件的基礎上執行轉換,而插件(plugins)最常用于(但不限于)在打包模塊的“compilation”和“chunk”生命周期執行操作和自定義功能。webpack的插件系統極其強大和可定制化。

  想要使用一個插件,只需要require()它,然后把它添加到plugins數組中。多數插件可以通過選項(option)自定義。也可以在一個配置文件中因為不同目的而多次使用同一個插件,需要使用new創建實例來調用它

【剖析】

  webpack插件是一個具有apply屬性的JavaScript對象。apply屬性會被webpack compiler調用,并且compiler對象可在整個compilation生命周期訪問

//ConsoleLogOnBuildWebpackPlugin.js
function ConsoleLogOnBuildWebpackPlugin() {

};
ConsoleLogOnBuildWebpackPlugin.prototype.apply = function(compiler) {
  compiler.plugin('run', function(compiler, callback) {
    console.log("webpack 構建過程開始!!!");

    callback();
  });
};

【用法】

  由于plugin可以攜帶參數/選項,必須在wepback配置中,向plugins屬性傳入new實例

var HtmlWebpackPlugin = require('html-webpack-plugin'); //通過 npm 安裝
var webpack = require('webpack'); //訪問內置的插件
var path = require('path');
var config = {
  entry: './path/to/my/entry/file.js',
  output: {
    filename: 'my-first-webpack.bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    loaders: [
      {
        test: /\.(js|jsx)$/,
        loader: 'babel-loader'
      }
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};
module.exports = config;

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()