webpack的四個核心概念介紹

前言

webpack 是一個當下最流行的前端資源的模塊打包器。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關系圖(dependency graph),其中包含應用程序需要的每個模塊,然后將所有這些模塊打包成少量的bundle - 通常只有一個,由瀏覽器加載。

webpack.png

它是高度可配置的,我們先理解四個核心概念:入口(entry)、輸出(output)、loader、插件(plugins)

入口(entry)

webpack 創建應用程序所有依賴的關系圖。圖的起點被稱之為入口起點(entry point)。入口起點告訴 webpack 從哪里開始,并根據依賴關系圖確定需要打包的內容。可以將應用程序的入口起點認為是根上下文或 APP第一個啟動文件。

簡單規則:每個 HTML 頁面都有一個入口起點。單頁應用(SPA):一個入口起點,多頁應用(MPA):多個入口起點。

簡單語法

用法:entry: string | Arrary<string>

//webpack.config.js
module.exports = {
  entry:  './src/index.js'
};

entry屬性的單個入口語法,在擴展配置的時候有失靈活性。它是下面的簡寫:

//webpack.config.js
module.exports = {
  entry: {
    main: './src/index.js'
  }
};

entry傳入一個數組時候,將創建"多個主入口(multi-main entry)"。在你想要多個依賴文件一起注入,并且將他們的依賴導向(graph)到一個"chunk"時候,傳入數組的方式就很有用。

//webpack.config.js
module.exports = {
  entry:  [
    './src/index.js',
    'babel-polyfill',
  ]
};

對象語法

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

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

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

這個配置告訴我們 webpack 從 app.jsvendors.js 開始創建依賴圖。這些依賴圖是彼此完全分離、互相獨立的。這種方式比較常見于,只有一個入口起點(不包括 vendorvendor一般都是動態加載的第三方模塊。動態加載的模塊不是入口起點。)的單頁應用程序(single page application)中。不過,為了支持提供更佳 vendor 分離能力的 DllPlugin。 官方現在不太建議將第三方模塊放到entry.vendors中。

對象語法,更常見的應該是多入口應用程序-多頁應用(MPA)。

//webpack.config.js
module.export = {
  entry: {
    home: "./home.js",
    about: "./about.js",
    contact: "./contact.js"
  }
};

此配置,告訴 webpack,我們 需要 3 個獨立分離的依賴關系圖.
在多頁應用中,每當頁面跳轉時,服務器將為你獲取一個新的 HTML 文檔。頁面重新加載新文檔,并且資源被重新下載。

根據經驗:每個 HTML 文檔只使用一個入口起點。

輸出(output)

將所有的資源(assets)歸攏在一起后,還需要告訴 webpack 在哪里打包應用程序。webpack 的 output 屬性描述了如何處理歸攏在一起的代碼(bundled code)。output選項可以控制webpack如何向硬盤寫入編譯文件。注意,即使可以存在多個entry起點,但只指定一個output配置。

簡單用法

在 webpack 中配置output 屬性的最低要求是,將它的值設置為一個對象,包括以下兩點:

  1. filename 用于輸出文件的文件名。
  2. 目標輸出目錄 path 的絕對路徑。
//webpack.config.js
module.exports = {
  entry: './src/index.js',
  output: {
    path: './home/proj/public/assets',
    filename: 'bundle.js'
  }
};

此配置將一個單獨的 bundle.js 文件輸出到 ./home/proj/public/assets 目錄中。

多個入口起點

如果配置創建了多個單獨的 "chunk"(例如,使用多個入口起點或使用像 CommonsChunkPlugin 這樣的插件),則應該使用占位符來確保每個文件具有唯一的名稱。

{
  entry: {
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    filename: '[name].js', //使用占位符
    path: __dirname + '/dist'
  }
}

// 寫入到硬盤:./dist/app.js, ./dist/search.js

loader

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

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

在更高層面,在webpack的配置中loader有兩個目標:

  1. 識別出應該被對應的loader進行轉換的那些文件。(test屬性)
  2. 轉換這些文件,從而使其能夠被添加到依賴圖中(并且最終添加到bundle中)。(use屬性)
//webpack.config.js
const path = require('path');

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

以上配置中,對一個單獨的 module 對象定義了 rules 屬性,里面包含兩個必須屬性:testuse。這相當于告訴 webpack 編譯器,當碰到「在 require()/import 語句中被解析為 .txt 的路徑」時,在對它打包之前,先使用 raw-loader 轉換一下。

在webpack配置中定義loader時,要定義在module.rules中,而不是rules。如果不這么做,webpack會給出嚴重警告

示例

例如,你可以使用loader告訴webpack加載css文件,或者將TypeScript轉為JavaScript。為此,首先安裝相對應的loader:

npm install -D css-loader
npm install -D ts-loader

然后指示webpack對每個.css使用css-loader, 以及對所有.ts文件使用ts-loader:

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

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

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

module.rules.loadermodule.rules.use: [ { loader } ] 的簡寫。

在應用程序中,有三種使用loader的方式:

  • 配置(推薦):在webpack.config.js文件中指定loader。
  • 內聯: 在每個imort語句中顯示指定loader。
  • CLI: 在shell命令中指定它們。

配置

module.rules 允許你在 webpack 配置中指定多個 loader。 這是展示loader的一種簡明方式,并且有助于使代碼變得簡潔。同時讓你對各個loader 有個全局概覽:

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

內聯

可以在import 語句或任何等效于 "import" 的方式中指定 loader。使用 ! 將資源中的 loader 分開。分開的每個部分都相對于當前目錄解析。

 import Styles from 'style-loader!css-loader?modules!./styles.css'

通過前置所有規則及使用!, 可以對應覆蓋到配置中的任意loader
選項可以傳遞查詢參數,例如 ?key=value&foo=bar,或者一個 JSON 對象,例如 ?{"key":"value","foo":"bar"}

盡可能使用 module.rules,因為這樣可以減少源碼中的代碼量,并且可以在出錯時,更快地調試和定位 loader 中的問題。

CLI

你也可以通過CLI使用loader:

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

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

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 生態系統提供了更多能力。

解析Loader

loader 遵循標準的模塊解析。多數情況下,loader 將從模塊路徑(通常將模塊路徑認為是npm install, node_modules)解析。
loader 模塊需要導出為一個函數,并且使用 Node.js 兼容的 JavaScript 編寫。通常使用 npm 來管理,也可以將自定義 loader 作為應用程序中的文件。按照約定,loader 通常被命名為 xxx-loader(例如 json-loader)。

插件-plugins

插件是 wepback 的支柱功能。webpack 自身也是構建于-你在 webpack 配置中用到的相同的插件系統之上!
插件目的在于解決 loader無法實現的其他事
由于loader僅在每個文件的基礎上執行轉換,而插件(plugins) 常用于(但不限于)在打包模塊的 “compilation” 和 “chunk” 生命周期執行操作和自定義功能。想要使用一個插件,你只需要 require() 它,然后把它添加到 plugins 數組中。
多數插件可以通過選項(option)自定義。你也可以在一個配置文件中因為不同目的而多次使用同一個插件,這時需要通過使用 new 來創建它的一個實例。

剖析

webpack 插件是一個具有 apply 屬性的 JavaScript 對象。apply
屬性會被 webpack compiler 調用,并且 compiler 對象可在整個編譯生命周期訪問。通過Function.prototype.apply方法,你可以把任意函數作為插件傳遞(this 將指向compiler)。我們可以在配置中使用這樣的方式來內聯自定義插件。

//ConsoleLogOnBuildWebpackPlugin.js
function ConsoleLogOnBuildWebpackPlugin() {

};

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

    callback();
  });
};

用法

由于插件可以攜帶參數/選項,你必須在webpack配置中,向plugins屬性中傳入new實例。
根據webpack的用法,可以有多種方式使用插件。但是,通過配置的方式使用是比較推薦的做法。

配置 (推薦)

//webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins
const path = require('path');

const config = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]0
};

module.exports = config;

Node API(不推薦)

即便使用 Node API,用戶也應該在配置中傳入 plugins 屬性。compiler.apply并不是推薦的使用方式。

// some-node-script.js
  const webpack = require('webpack'); //訪問 webpack 運行時(runtime)
  const configuration = require('./webpack.config.js');

  let compiler = webpack(configuration);
  compiler.apply(new webpack.ProgressPlugin());

  compiler.run(function(err, stats) {
    // ...
  });

總結

以上就是webpack中比較重要的四個概念,在平時開發過程中會經常遇到。里面還可以有很多的詳細配置,需要我們在項目開發的過程中慢慢了解。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容