webpack 從入門到放棄(一)

什麼是 webpack,爲何要使用 webpack

什麼是 webpack

官網給出的概念是:javascript

本質上,webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 bundle。 根據官網最直觀最出名的那個圖咱們能夠知道,webpack 能夠打包/腳本/圖片/樣式/表 從圖中咱們能夠看出左邊有依賴關係的模塊(MODULES WITH DEPENDENCIES)經過 webpack 打包成了各類靜態資源(STATIC ASSETS)css

爲何要使用 webpack

經過上面的概念,你是否是已經大概知道了 webpack 是幹什麼的,那麼問題來了,爲何要使用 webpack 呢? 這就說來話長了,那就長話短說,emmmmhtml

爲何使用 webpack,這應該是和前端的發展有關係的,我認爲,webpack 是前端發展到必定階段的必然產物(貌似是一句廢話)。 由於計算機網絡的飛速發展,致使 web 前端也在迅猛發展。最初的實踐方案已經不能知足咱們的需求,因而,愈來愈多的新技術新思想新框架孕育而生,好比:前端

前端模塊化

隨着前端項目的複雜度愈來愈高,相互以前的依賴愈來愈多,以及爲了更好的複用代碼,前端也須要用模塊化的思想來組織代碼。java

首先咱們要明白模塊化解決了前端的哪些痛點:node

  • 命名衝突
  • 文件依賴(js 加載順序)
  • 代碼複用

咱們這裏說的模塊和 Java 的 package 的概念是相似的。邏輯上相關的代碼放在一個包中,每個包都是相互獨立的,不用擔憂命名衝突的問題,若是其餘人想要用這部分功能,直接 import 導入包就好react

因此前端代碼模塊化的實現,會幫咱們解決命名衝突和代碼複用的問題,那麼文件依賴要怎麼處理呢?這就用到了咱們的 webpack,稍後再作介紹。webpack

因此有了模塊,咱們就能夠方便的複用他人的代碼,那麼問題來了,無規矩不成方圓,咱們在使用他人代碼的時候確定是要遵循某種規範,因此就出現了 CommonJS、AMD 和 終極模塊化方案 —— ES6 模塊,這些都是前端模塊化的規範。web

咱們來簡單瞭解一下:正則表達式

CommonJS

node.js 採用的就是 CommonJS 規範,使用 require 的方法同步加載依賴,一個文件就是一個模塊,導入導出格式以下:

// 導入
const moduleA = require('./moduleA');

// 導出
module.exports = moduleA.someFunc;
複製代碼

缺點是加載的模塊是同步的,只有加載完才能執行後面的操做。由於 node.js 的模塊文件通常存在於本地硬盤,因此通常不會出現這個問題,可是在瀏覽器環境該規範就不那麼適用了。

AMD

緣由如上,由於 CommonJS 不適用於瀏覽器環境,因此出現了 AMD 規範。該規範異步加載依賴,能夠再聲明的時候指定須要加載的依賴,而且須要當作參數傳入,對於依賴的模塊提早執行,依賴前置。

寫法以下:

define("module", ["dep1", "dep2"], function(d1, d2) {
  return someExportedValue;
});
require(["module", "../file"], function(module, file) { /* ... */ });
複製代碼

ES6 模塊化

ES6 直接在語言層面上實現了模塊化。咱們如今用的最多的就是 ES6 模塊化的實踐方式。

寫法以下:

// 導入
import { readFile } from 'fs';
import React from 'react';
// 導出
export function hello() {};
export default {
  // ...
};
複製代碼

樣式模塊化

如今愈來愈多人也開始使用模塊化的思想寫樣式。好比如今大部分的 CSS 預編譯器都支持 @import 的寫法。將一些公用樣式放在一個文件中,在其餘文件中導入。


三大框架的出現,使咱們不須要像傳統的 JQ 同樣操做 DOM,將精力集中在對數據的處理上。

以及 ES6/7/8 和 TS 的使用越來普及,無疑使咱們的開發效率提升了不少。

可是出現的問題是:這些新興的技術並非在全部的瀏覽器上都適用,都須要將源代碼轉化爲能夠直接在瀏覽器上運行的代碼

因此,webpack 就解決了這個問題。

Gulp/Grunt、Rollup 和 webpack 的比較

Gulp/Grunt

其實,Gulp/Gunt 和 webpack 應該是沒有可比性的,可是他們均可以稱爲前端自動化構建工具(讓咱們再也不作機械重複的事情,解放咱們的雙手)

可是 Gulp/Gunt 和 webpack 確實乾的不是一件事

Gulp 本質是 task runner,Webpack 是 module bundler

我認爲 Gulp 正如他的定義同樣:基於流的自動化構建工具,定義每個任務,而後自動將一個個任務執行。

而 webpack 是模塊化地組織,模塊化地依賴,而後模塊化地打包。相對來上,場景侷限在前端模塊化打包上。

推薦知乎 寸志 大佬的回答:gulp 有哪些功能是 webpack 不能替代的?

Rollup

Rollup 是一個和 Webpack 很相似但專一於 ES6 的模塊打包工具。 Rollup 的亮點在於能針對 ES6 源碼進行 Tree Shaking 以去除那些已被定義但沒被使用的代碼,以及 Scope Hoisting 以減少輸出文件大小提高運行性能。 然而 Rollup 的這些亮點隨後就被 Webpack 模仿和實現。 因爲 Rollup 的使用和 Webpack 差很少,這裏就不詳細介紹如何使用了,而是詳細說明它們的差異:

Rollup 是在 Webpack 流行後出現的替代品; Rollup 生態鏈還不完善,體驗不如 Webpack; Rollup 功能不如 Webpack 完善,但其配置和使用更加簡單; Rollup 不支持 Code Spliting,但好處是打包出來的代碼中沒有 Webpack 那段模塊的加載、執行和緩存的代碼。 Rollup 在用於打包 JavaScript 庫時比 Webpack 更加有優點,由於其打包出來的代碼更小更快。 但功能不夠完善,不少場景都找不到現成的解決方案。

安裝與使用

建立 package.json 文件

能夠手動的建立,也可使用命令自動建立

npm init
複製代碼

安裝

webpack 能夠直接使用 npm 安裝,能夠安裝到全局,也能夠安裝到項目

//全局安裝
npm install -g webpack
//安裝到你的項目目錄
npm install --save-dev webpack
複製代碼

使用

推薦你們閱讀這篇文章: 入門 Webpack,看這篇就夠了

我就是跟着這篇文章作的

新建文件

首先新建一個文件夾,而後在終端打開該文件夾並執行來初始化你的項目,在初始過程當中,會有一些問題幫助你建立 package.json 文件。

npm init
複製代碼

初始化以後咱們還要建立幾個文件來存放咱們的項目文件。

建立一個 app 文件夾來存放咱們打包以前的源文件

建立一個 public 文件夾來存放一個入口文件 index.html 和經過 webpack 打包以後瀏覽器可直接運行的 js 文件

好比咱們在 app 文件夾下建立一個 Greeter.js 文件,裏面有一個方法能夠再頁面顯示文字信息 Hi there and greetings!

module.exports = function() {
  var greet = document.createElement('div');
  greet.textContent = "Hi there and greetings!";
  return greet;
};
複製代碼

而後咱們建立一個 main.js 來引入 Greeter.js 這個文件,

瀏覽器的入口 index.html 文件內容以下(其中 bundle.js 是打包以後的文件):

<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Webpack Sample Project</title>
  </head>
  <body>
    <div id='root'>
    </div>
    <script src="bundle.js"></script>
  </body>
</html>
複製代碼

此時的文件路徑以下

.
├── app
│   ├── Greeter.js
│   └── main.js
├── package.json
├── public
│   ├── bundle.js
│   └── index.html
複製代碼

安裝 webpack

初始化項目以後,要安裝 webpack

npm install --save-dev webpack
複製代碼

在安裝完 webpack 以後,能夠再 package.json 文件中看到增長了 webpack 的依賴。

配置 webpack

安裝完 webpack 以後,咱們就要配置 webpack 了,首先建立配置文件 webpack.config.js 文件內容以下:

module.exports = {
  entry:  __dirname + "/app/main.js",//已屢次說起的惟一入口文件
  output: {
    path: __dirname + "/public",//打包後的文件存放的地方
    filename: "bundle.js"//打包後輸出文件的文件名
  }
}
複製代碼

而後咱們在該項目的終端輸入

webpack
複製代碼

就能夠看到以下信息:

Hash: 4e6a6b5eb88a83b29e02
Version: webpack 4.12.0
Time: 551ms
Built at: 2018-06-24 14:53:39
    Asset      Size  Chunks             Chunk Names
bundle.js  6.82 KiB       0  [emitted]  main
[3] ./node_modules/css-loader!./app/main.css 190 bytes {0} [built]
[4] ./app/main.css 1.04 KiB {0} [built]
[5] ./app/Greeter.js 143 bytes {0} [built]
[6] ./app/main.js 119 bytes {0} [built]
    + 3 hidden modules

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
複製代碼

看到這樣的信息,那麼恭喜你,你的第一個 webpack 項目就完成了!

打開 public 文件夾下面的 index.html,你就能夠再瀏覽器上看到以下的效果。

Loader

loader 用於對模塊的源代碼進行轉換。loader 可使你在 import 或"加載"模塊時預處理文件。所以,loader 相似於其餘構建工具中「任務(task)」,並提供了處理前端構建步驟的強大方法。loader 能夠將文件從不一樣的語言(如 TypeScript)轉換爲 JavaScript,或將內聯圖像轉換爲 data URL。loader 甚至容許你直接在 JavaScript 模塊中 import CSS文件!

由於 webpack 自己只能處理 JavaScript,若是要處理其餘類型的文件,就須要使用 loader 進行轉換,loader 自己就是一個函數,接受源文件爲參數,返回轉換的結果。

舉個 🌰 —— css-loader

例如,咱們想在剛剛的頁面增長樣式,使文字居中顯示,那麼我在 app 文件夾下面新建一個 main.css 文件。在 webpack 中,全部的文件都是模塊,因此要使用這個 css 文件,就必需要先引入。

引入 css 文件

因此我就在 app 文件夾下面的 main.js 中引入該 css 文件

const greeter = require('./Greeter.js');
require ('./main.css')
document.querySelector("#root").appendChild(greeter());
複製代碼

從新打包

而後我從新打包一遍,藍後,發現竟然報錯了!

Hash: 179c18498fac6de89a96
Version: webpack 4.12.0
Time: 533ms
Built at: 2018-06-24 15:00:24
 1 asset
[0] ./app/Greeter.js 143 bytes {0} [built]
[1] ./app/main.js 119 bytes {0} [built]

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/

ERROR in ./app/main.js
Module not found: Error: Can't resolve 'style-loader' in '/Users/cherry/Workspace/webpack-demo' @ ./app/main.js 2:0-22 複製代碼

根據報錯信息,咱們很明顯能發現是提示咱們項目缺乏 style-loader,這是由於 webpack 原生只支持解析 js 文件,要支持非 js 類型的文件,就須要使用 loader

安裝 loader

因此咱們要安裝 style-loadercss-loader

npm i -D style-loader css-loader
複製代碼

修改配置文件

而後修改 webpack 的配置文件 webpack.config.js

module.exports = {
  entry:  __dirname + "/app/main.js",//已屢次說起的惟一入口文件
  output: {
    path: __dirname + "/public",//打包後的文件存放的地方
    filename: "bundle.js"//打包後輸出文件的文件名
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}
複製代碼

配置文件增長了 module.rules 數組,該數組是一些配置規則,告訴 webpack 符合 test 的文件須要使用 use 後面的 loader 處理。因此該規則就是對全部 .css 結尾的文件使用 style-loadercss-loader 進行處理。

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 可以產生額外的任意文件。

使用 webpack 的三種姿式

webpack 中使用 loader 有三種姿式

經過 CLI

命令行中運行

webpack --module-bind jade  --module-bind 'css=style!css'   
複製代碼

jade,style,css後面可省略-loader,他們分別對.jade使用jade-loader,對.css使用style-loader和css-loader

經過require

能夠直接在源碼中指定使用什麼 loader 去處理文件。

require('style-loader!css-loader?minimize!./main.css')
複製代碼

這樣就是對 ./main.css 文件先使用 css-loader 再使用 style-loader 進行轉換

使用配置文件 webpack.config.js

最經常使用的方式就是使用本文所使用的配置文件的方式

常見的 loader

常見的 loader

loader name loader des
babel-loader 加載 ES2015+ 代碼,而後使用 Babel 轉譯爲 ES5
buble-loader 使用 Bublé 加載 ES2015+ 代碼,而且將代碼轉譯爲 ES5
cache-loader 在一些性能開銷較大的 loader 以前添加此 loader,以將結果緩存到磁盤裏。
coffee-loader CoffeeScript 轉化爲 JS
css-loader css-loader 是將 @importurl() 引入的 css 轉換爲 import/require() 的方式而後在解析他們
exports-loader 經過添加 exports[...] = ... 語句導出文件中的變量。
expose-loader expose-loader 將模塊添加到全局對象上
file-loader file-loader 能夠解析項目中的url引入(不只限於css),根據咱們的配置,

將圖片拷貝到相應的路徑,再根據咱們的配置,修改打包後文件引用路徑,使之指向正確的文件。 gzip-loader | 能夠加載 gzip 壓縮以後的資源 html-loader | 將 html 輸出爲字符串,也能夠根據配置進行壓縮 imports-loader | imports-loader 容許使用依賴於特定全局變量的模塊,這對於依賴於像 $ 這樣的全局變量的第三方模塊很是有用 jshint-loader | 爲加載的模塊使用 jshint json-loader | 因爲 webpack >= v2.0.0 默認支持導入 JSON 文件。若是你使用自定義文件擴展名,你可能仍然須要使用此 loader json5-loader | 將 json5 文件解析成 js 對象 less-loader | 將 less 轉化爲 css null-loader | 返回一個空模塊 postcss-loader | 將 postcss 轉化爲 css raw-loader | 加載文件原始內容(utf-8格式) sass-loader | 將 SASS/SCSS 轉換爲 css source-map-loader | 從現有源文件(源代碼源URL)中提取源映射,方便調試 style-loader | 將 CSS 放在 <style> 標籤中注入到 DOM 中 script-loader | 在全局上下文中執行一次 JavaScript 文件(如在 script 標籤),不須要解析 svg-inline-loader | 將 SVG 做爲模塊嵌入 url-loader | 將文件加載爲 Base64 編碼的 URL

row 2 col 1 | row 2 col 2

Plugin

插件(plugin)是 webpack 的支柱功能。webpack 自身也是構建於,你在 webpack 配置中用到的相同的插件系統之上! 插件目的在於解決 loader 沒法實現的其餘事

Plugin 是用來擴展 Webpack 功能的,經過在構建流程裏注入鉤子實現,它給 Webpack 帶來了很大的靈活性。

經過plugin(插件)webpack能夠實 loader 所不能完成的複雜功能,使用 plugin 豐富的自定義 API 以及生命週期事件,能夠控制 webpack 打包流程的每一個環節,實現對 webpack 的自定義功能擴展。

舉個 🌰 —— ExtractTextPlugin

webpack4 已經不支持用extract-text-webpack-plugin來優化 css, 須要改爲optimize-css-assets-webpack-plugin和mini-css-extract-plugin

在剛剛的例子中,咱們查看打包以後的 index.html 文件能夠看到咱們剛剛寫的 css 代碼放在了 head 中的 style 標籤中,這是 style-loader 幫咱們處理的

可是,若是你但願打包以後 css 在單獨的文件中,那麼你就須要 ExtractTextPlugin 這個 plugin 了。

安裝 ExtractTextPlugin

npm i -D ExtractTextPlugin
複製代碼

修改配置文件

咱們須要修改配置文件:

const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
  entry:  __dirname + "/app/main.js",//已屢次說起的惟一入口文件
  output: {
    path: __dirname + "/public",//打包後的文件存放的地方
    filename: "bundle.js",//打包後輸出文件的文件名
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        // 轉換 .css 須要的 loader
        loaders: ExtractTextPlugin.extract({
          use: ['css-loader'],
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: '[name]-[contenthash:8].css'
    })
  ]
}
複製代碼

而後咱們再從新打包,就能夠發如今 public 文件夾下面多了一個 main-493a2c3c.css 文件,下面咱們要在 index.html 中自動引入這個 css 文件

html-webpack-plugin

html-webpack-plugin 能夠根據你設置的模板,在每次運行後生成對應的模板文件,同時所依賴的 CSS/JS 也都會被引入,若是 CSS/JS 中含有 hash 值,則 html-webpack-plugin 生成的模板文件也會引入正確版本的 CSS/JS 文件。

安裝 html-webpack-plugin

安裝方式咱們已經很熟悉了

npm i -D html-webpack-plugin
複製代碼

修改配置文件

const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');  

module.exports = {
  entry:  __dirname + "/app/main.js",//已屢次說起的惟一入口文件
  output: {
    path: __dirname + "/public",//打包後的文件存放的地方
    filename: "bundle.js",//打包後輸出文件的文件名
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        loaders: ExtractTextPlugin.extract({
          use: ['css-loader'],
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: '[name]-[contenthash:8].css'
    }),
    new HtmlWebpackPlugin(),
  ]
}
複製代碼

藍後咱們刪除以前咱們在 punlic 文件夾下面 index.html 的內容,在打包一次,就會生成一個 html 模板

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack App</title>
  <link href="main-493a2c3c.css" rel="stylesheet"></head>
  <body>
  <script type="text/javascript" src="bundle.js"></script></body>
</html>
複製代碼

new HtmlWebpackPlugin 的時候,咱們能夠進行一系列的配置

new HtmlWebpackPlugin({  
      // 生成的HTML模板的title,若是模板中有設置title的名字,則會忽略這裏的設置
      title: "This is the webpack config", 
      
      // 生成的模板文件的名字
      filename: "/index.html", 
      
      // 模板來源文件 
      template: "index.html", 
      
      // 引入模塊的注入位置;取值有true/false/body/head 
      // true 默認值,script標籤位於html文件的 body 底部
      // body script標籤位於html文件的 body 底部
      // head script標籤位於html文件的 head中
      // false 不插入生成的js文件,這個幾乎不會用到的
      inject: "body", 
      
      // 指定頁面圖標; 
      favicon: "", 
      
      // 是html-webpack-plugin中集成的 html-minifier ,生成模板文件壓縮配置 
      minify: {  
          caseSensitive: false, //是否大小寫敏感 
          collapseBooleanAttributes: true, // 省略布爾屬性的值 
          collapseWhitespace: true //刪除空格 
      }, 
      
      // 是否生成hash添加在引入文件地址的末尾,相似於咱們經常使用的時間戳,避免緩存帶來的麻煩
      hash: true,
      
      // 是否須要緩存,若是填寫true,則文件只有在改變時纔會從新生成
      cache: true,  
      
      // 是否將錯誤信息寫在頁面裏,默認true,出現錯誤信息則會包裹在一個pre標籤內添加到頁面上
      
      showErrors: true,  
      
      // 引入的模塊,這裏指定的是entry中設置多個js時,在這裏指定引入的js,若是不設置則默認所有引入
      chunks: "",  
      
      // 引入模塊的排序方式
      // 默認四個選項: none auto dependency {function}
      // 'dependency' 文件的依賴關係
      // 'auto' 默認值,插件的內置的排序方式
      // 'none' 無序
      // {function} 自定義
      chunksSortMode: "auto",  
      
      // 排除的模塊
      excludeChunks: "",  
      
      // 生成的模板文檔中標籤是否自動關閉,針對xhtml的語法,會要求標籤都關閉,默認false
      xhtml: false  
    
    }),
複製代碼

常見的 plugin

摘自:http://www.css88.com/doc/webpack/plugins/

Name Description
AggressiveSplittingPlugin 將原來的 chunk 分紅更小的 chunk
BabiliWebpackPlugin 基於 Babel 的裁剪工具:Babili
BannerPlugin 在每一個生成的 chunk 頂部添加 banner
CommonsChunkPlugin 提取 chunks 之間共享的通用模塊
ComponentWebpackPlugin 經過 webpack 使用組件
CompressionWebpackPlugin 預先準備的資源壓縮版本,使用 Content-Encoding 提供訪問服務
ContextReplacementPlugin 重寫 require 表達式的推斷上下文
DefinePlugin 容許在編譯時(compile time)配置的全局常量
DllPlugin 爲了極大減小構建時間,進行分離打包
EnvironmentPlugin DefinePlugin 中 process.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 進行更細粒度的控制
UglifyjsWebpackPlugin 能夠控制項目中 UglifyJS 的版本
ZopfliWebpackPlugin 經過 node-zopfli 將資源預先壓縮的版本

參考文章:

相關文章
相關標籤/搜索