初識webpack——webpack四個基礎概念

前面的話

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

 

入口

  webpack將建立全部應用程序的依賴關係圖表。圖表的起點被稱之爲入口起點(entry point)。入口起點告訴 webpack 從哪裏開始,並遵循着依賴關係圖表知道要打包什麼。能夠將應用程序的入口起點認爲是根上下文或 app 第一個啓動文件html

  類比於requirejs中的入口文件main.js,最終使用r.js打包時,都打包在main.js裏前端

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

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

  用法:entry: string|Array<string>web

  [注意]在設置entry屬性時,若是是當前頁面,必定要在屬性值前面設置爲'./',不然沒法識別npm

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

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

//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 + 'dist',//出口路徑
    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屬性)

  常見的加載器loader包括如下三類:

  一、編譯相關:babel-loader、ts-loader

  二、樣式相關:style-loader、css-loader、less-loader、postcss-loader

  三、文件相關:file-loader、url-loader

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 的方式:一、經過配置;二、在 require 語句中顯示使用;三、經過 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)

 

插件

  插件plugin是wepback的支柱功能。在使用webpack配置時,webpack自身也構建於一樣的插件系統上。插件目的在於解決loader沒法實現的其餘事情。因爲loader僅在每一個文件的基礎上執行轉換,而插件(plugins)最經常使用於(但不限於)在打包模塊的「compilation」和「chunk」生命週期執行操做和自定義功能,包括打包優化壓縮及配置編譯時的變量等功能。webpack的插件系統極其強大和可定製化

  經常使用的plugin插件包括如下兩類:

  一、優化相關:commonsChunkPlugin、UglifyjsWbpackPlugin

  二、功能相關:ExtractTextWebpackPlugin、HtmlWebpackPlugin、HotModuleReplacementPlugin、CopyWebpackPlugin

  想要使用一個插件,只須要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;

   最後,介紹兩個webpack中常見的名詞:chunk和bundle。chunk是指代碼塊,而bundle是指打包後的代碼

相關文章
相關標籤/搜索