webpack的四個核心概念介紹

前言

webpack 是一個當下最流行的前端資源的模塊打包器。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成少許的bundle - 一般只有一個,由瀏覽器加載。css

webpack.png

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

入口(entry)

webpack 建立應用程序全部依賴的關係圖。圖的起點被稱之爲入口起點(entry point)。入口起點告訴 webpack 從哪裏開始,並根據依賴關係圖肯定須要打包的內容。能夠將應用程序的入口起點認爲是根上下文或 APP第一個啓動文件。前端

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

簡單語法

用法:entry: string | Arrary<string>webpack

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

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

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

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

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

對象語法

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

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

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

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

對象語法,更常見的應該是多入口應用程序-多頁應用(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-loader和 css-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中比較重要的四個概念,在平時開發過程當中會常常遇到。裏面還能夠有不少的詳細配置,須要咱們在項目開發的過程當中慢慢了解。

相關文章
相關標籤/搜索