Webpack飛行手冊

SimonMa發表於2017-12-08

前言

在學習 Webpack 之前,我們需要了解一個概念:模組

何為模組?

如果你曾學過 Java , C# 之類的語言,一定會知道 Java 中的 import 或 C# 中的 using 吧?

比如:我想在 C# 中進行資料庫操作,我只需要在程式碼頭部加上 下面這兩段程式碼即可。

using System.Data;
using System.Data.SqlClient;
複製程式碼

這兩段程式碼可以看成 兩個與資料庫操作相關的模組。 當我們需求是資料庫,或者是讀取 IO 等其他操作,我們便載入其他不同的模組。

很明顯,這實現了程式設計中的一個非常重要的功能 按需載入

在前端中 模組又該如何定義呢? 按照我個人的理解:

  • 在 HTML 中 模組 便是一個元件
<div class="layer">
    <div><%= name %></div>
    <% for(var i = 0; i < People.length;++i) { %>
        <%= People[i] %>
    <% } %>
</div>
複製程式碼
  • 在 CSS 中 模組 便是一個區域性樣式
header{
    display:block;
}
header h1{
    font-size: 60px;
}
複製程式碼
  • 在 Javascript 中 模組 便是一個封裝著方法或資料的指令碼檔案
let People = { name: "Simon" } ;
module.exports = People;

複製程式碼

而我們又該怎樣實現 在前端中載入模組呢?

下面是兩個很常見的例子:

在 Less 中

@import "header";
@import "layout";
@import "footer";
複製程式碼

在 Javascript

// CommonJS
const $ = require("jQuery");

//es6
let People = { name: "Simon" } ;
module.exports = People;

import "./layer.less";
import tpl from "./layer.ejs";
複製程式碼

如果你直接執行以上程式碼,瀏覽器並不會解析,這個時候,就要依靠 Webpack 了!

Webpack是什麼

Webpack 是一款目前非常流行的前端模組打包工具,可以將專案中所載入的模組進行打包,以及將 一些瀏覽器不支援的語言進行轉換。

Webpack 的打包原理是 先找到入口檔案,遞迴探索出所有依賴的模組,最後 利用 Loader 進行不同檔案型別的處理,打包成一個 Javascript 檔案。

其中,Webpack 的兩個最核心原理分別是:

  1. 一切皆模組
  2. 按需載入

當然 Webpack 的作用不止載入模組這麼簡單,前端的常用需求通常都可以實現:利用 Loader 轉換 es6 、 Less 、 Typescript ,還可利用外掛 開發多頁面應用,等等諸多強大功能。

正文

下面,我將講解 Webpack 的具體使用和配置。

安裝

我一般在專案中使用 Webpack,都是先執行下面這四條命令進行 Webpack 的安裝

  1. npm install -g webpack

    在全域性安裝 Webpack,第一次使用時 執行

  2. npm install --save-dev webpack

    將 Webpack 安裝到你的專案

  3. npm init

    npm初始化,會詢問你的專案資訊,可以回車跳過

  4. npm install --save-dev webpack-dev-server

    在當前專案,安裝 Webpack 伺服器

安裝完成後,便是建立配置檔案了。

基本配置

在專案根目錄下新建名為 webpack.config.js 的檔案,

基本上 一個配置檔案的大體結構就是下面這樣:

modules.export={
    entry:{
        /* 入口檔案 */
    },
    output:{
        /* 出口檔案 */
    },
    module:{
        /* Loader */
        rules:[{},{},{}]
    },
    plugins:[ 
        /* 外掛 */ 
    ],
    devtool: ...
    devServer: {...}
    resolve:{...}
}
複製程式碼

我們下面就先分析 modules.export 各個屬性

入口

entry 代表是入口檔案,Webpack 工作的開始。

Webpack 會遞迴的探索出 入口檔案中所依賴的模組,並按照順序 利用 Loader 進行處理。

官網給出了其 3 種資料型別:

  1. 字串
    entry: "app.js";
複製程式碼
  1. 陣列

    陣列中的每一項都會被打包,形成互不依賴的檔案

    entry: ["app.js","main.js"];
複製程式碼
  1. 物件

    物件中的每一個屬性都會被打包,形成互不依賴的檔案

    entry:{
        app: "./src/js/app.js",
        main: "./src/js/main.js"
    }
複製程式碼

一般入口檔案中多是 import 或者 require 等模組匯入命令。

出口

output 顧名思義,Webpack打包後檔案的具體配置 常用的屬性有 4 個

  1. path: ${__dirname }/dist

    打包後檔案所在路徑

  2. filename: "js/[name].js"

    打包後檔案的名字,這裡有 4 種常用的寫法

    1. 自定義

    2. [name].js

      代表的便是入口的檔名

    3. [hash].js

      此次打包後的hash值

    4. [chunkhash]

      該塊打包後的hash值

  3. publicPath: "http://cdn.com/"

    上線時的公共路徑,主要應用於線上

  4. chunkFilename: 'js/[name].js'

    按需載入模組時輸出的檔名稱

Loader

Loader 是 Webpack 中最振奮人心的東西了!

將一切瀏覽器不支援的語言,處理成 瀏覽器可以支援。 針對各個檔案型別,都有各種的 Loader 等你去挖掘。

Loader 的工作方式 是從右向左執行,鏈式地按照順序進行編譯。 loader 鏈中的第一個返回值給下一個 loader,在最後一個 loader,返回所預期的結果。

loader 可以是同步或非同步函式,也可使用 options 物件去接受配置引數。

基礎結構

module:{
 	rules:[
 	   	{
  			test:/\.xxx$/,//以xxx結尾的檔案
  			loader: "xxx-loader",
 	 		exclude: {排除的路徑},
 	 		include: {包含的路徑},
 	 		options: {Loader配置}
 	 	}
 	]
}
複製程式碼

可以很清楚的看到,Loader 利用 test 的正則 找到各個型別檔案,然後使用 loader 進行處理,便可轉換成瀏覽器支援的檔案。

其中我知道的 loader 的寫法有兩種:

  1. 每一個 loader 都是一個物件
loaders:[
 	{loader:"style-loader"},
 	{ loader: "css-loader?modules", options: { importLoaders: 1 } },
 	{loader: "less-loader"}
]
複製程式碼
  1. 使用 ! 號拼接的寫法
loader: "style-loader!css-loader?importLoaders=1!less-loader"
複製程式碼

下面介紹三個 前端必備的 Loader 方式

css

  1. style-loader

    通過注入<style>標籤將 CSS 新增到 DOM

    npm install style-loader --save-dev
    複製程式碼
  2. css-loader

    css-loader像import / require()一樣解釋@import和url()並解析它們。

    npm install css-loader --save-dev
    複製程式碼
  3. postcss-loader

    補充 不相容的css屬性 的瀏覽器字首

    npm install post-loader --save-dev
    複製程式碼
  4. less-loader

    將Less 轉換成 CSS

    npm install less --save-dev
    npm install less-loader --save-dev
    複製程式碼

javascript

babel

主要用於將 es6 轉換成 es2015
複製程式碼
npm install --save-dev babel-core babel-loader babel-preset-es2015
複製程式碼

圖片 & 字型

  1. file-loader

    用於壓縮檔案

    npm install --save-dev file-loader
    複製程式碼
  2. url-loader

    如果檔案下於 規定限制,將會轉換成 二進位制編碼

    npm install --save-dev url-loader
    複製程式碼

ejs

另外 我想介紹一下 自己常用的 ejs-loader

  1. 配置
npm install --save-dev ejs-loader
複製程式碼
test:/\.ejs$/ , loader:"ejs-loader",
複製程式碼
  1. 使用
<div class="layer">
    <div><%= name %></div>
    <% for(let i = 0; i < Array.length;++i) { %>
        <%= Array[i] %>
    <% } %>
</div>
複製程式碼
//入口檔案
import tpl from "./layer.ejs";

document.body.innerHTML = tpl({
 	name:"Simon",
 	arr:["Apple","Xiaomi"]
});
複製程式碼

執行 生成後的頁面 ,便會發現 ejs 元件已經被加進去了, 想象一下,我們在平時工作中是否可以把 一個輪播圖,或者 排行榜 、評論 當成一個元件呢?

外掛

plugins

在日常工作中,我們使用 Loader 處理不同型別的檔案,當有某種其他方面的需求時,比如 抽離 CSS 、生成多頁面 HTML ,plugins 便派上了用場。

外掛的使用,一般都要先 require 出來,然後在 plugins 屬性中 進行初始化

const htmlWebpackPlugin = require("html-webpack-plugin");
......
plugins: [ new htmlWebpackPlugin({/* options */}) ]
複製程式碼

下面將介紹 一些工作中常用的外掛

  1. clean-webpack-plugin

    主要用於 打包之前 先清空 打包目錄下的檔案,防止檔案混亂。

    npm install --save-dev clean-webpack-plugin 
    複製程式碼
  2. html-webpack-plugin

    主要用於生成HTML,可以規定 模板HTML,也可以為 模板傳入引數,壓縮檔案等

    npm install --save-dev html-webpack-plugin 
    複製程式碼

這個外掛可謂是 前端必備的,它的配置有很多

new htmlWebpackPlugin({
 	//打包後的檔名
 	filename: "index.html",
 	
 	//模板
 	template: "index.html",
 
 	//為true自動生成script標籤新增到html中
 	//或者寫 body/head 標籤名
 	inject: false,//js的注入標籤
 
 	//通過<%= htmlWebpackPlugin.options.title  %>引用
 	title: "引數title",
 
 	//通過<%= htmlWebpackPlugin.options.date %> 引用
 	date: new Date()
 
 	//網站的圖示
 	favicon: 'path/to/yourfile.ico'
 
 	//生成此次打包的hash
 	//如果檔名中有雜湊,便代表有 合理的緩衝
 	hash: true,
 
  	//排除的塊
  	excludeChunks: [''],
  
 	//選中的塊 與入口檔案相關
 	chunks: ['app','people'],
 
 	//壓縮
 	minify:{ 
 		removeComments: true,collapseWhitespace: true,
 		minifyJS: true,	minifyCSS: true,minifyURLs: true,
 	}

        
}),
複製程式碼

那麼問題來了,我們在模板檔案中 又該怎樣使用引數呢? 直接按照 ejs 的語法寫入 html 檔案即可!

<!DOCTYPE html>
<html lang="en">
<%= htmlWeb
    packPlugin.options.date %>
</html>
複製程式碼

生成後的模板檔案

<!DOCTYPE html>
<html lang="en">
Thu Dec 07 2017 10:01:58 GMT+0800 (中國標準時間)
</html>
複製程式碼

另外,如果想生成 多頁面應用,只需 將上面的配置,多複製幾遍即可。

new htmlWebpackPlugin({ filename: "index1.html", }
new htmlWebpackPlugin({ filename: "index2.html", }
new htmlWebpackPlugin({ filename: "index3.html", }
複製程式碼
  1. UglifyJsPlugin

    主要用於壓縮 Javascript 檔案

    npm i -D uglifyjs-webpack-plugin
    複製程式碼
  2. webpack.ProvidePlugin

    自動載入模組,全域性使用變數,下面藉助 官網的DEMO

new webpack.ProvidePlugin({
  $: 'jquery',
  jQuery: 'jquery'
})
// in a module
$('#item'); // <= 起作用
jQuery('#item'); // <= 起作用
// $ 自動被設定為 "jquery" 輸出的內容
複製程式碼
  1. open-browser-webpack-plugin

    開啟伺服器後 會自動開啟瀏覽器埠,用起來 很方便

  2. HotModuleReplacementPlugin

    熱更新外掛

常用命令

  • webpack

    最基本的啟動webpack命令

  • webpack -w

    監控程式碼變化,實時進行打包更新

  • webpack -p

    對打包後的檔案進行壓縮,利用線上釋出

  • webpack -d

    提供SourceMaps,方便除錯程式碼

  • webpack --colors

    輸出結果帶彩色,可以更詳細的檢視資訊

  • webpack --profile

    輸出效能資料,可以看到每一步的耗時

devtool

不知道你現在時候有沒有一個想法? webpack 打包後的檔案就一定正確無誤嗎? 如果發生錯誤的話,該怎麼辦呢?

devtool 屬性 便提供了生成 sourcemap 的功能,具體有下面這些選項。

  1. source-map

    此選項具有最完備的source map,但會減慢打包的速度;

  2. cheap-module-source-map

    生成一個不帶列對映的map

  3. eval-source-map

    使用eval打包原始檔模組,生成一個完整的source map。

  4. cheap-module-eval-source-map

    這是最快生成source map的方法,生成後的Source Map 會和打包後的 JavaScript 檔案同行顯示,但沒有列對映,所以慎用

devServer

  1. contentBase: "./dist",

    本地伺服器所載入的頁面所在的目錄

  2. historyApiFallback: true,

    再找不到檔案的時候預設指向index.html

  3. inline: true,

    當原始檔改變時會自動重新整理頁面

  4. hot: true,

    熱載入開啟

  5. port:8080

    設定預設監聽埠

resolve

  1. extensions: [".js", ".html", ".css", ".txt","less","ejs","json"],

    自動擴充套件檔案字尾名,意味著我們require模組可以省略不寫字尾名

  2. alias: { Temp: path.resolve(__dirname, "src/templates/") }

    模組別名定義,直接 require('AppStore') 即可,方便後續直接引用別名

其他功能

path

常用於字串拼接路徑。

const path = require("path");
複製程式碼

有兩個 API

  1. path.resolve()

    將相對路徑轉換成絕對路徑

const aPath = path.resolve("__dirname","js","main.js");
// aPath = 當前目錄下的 js 資料夾的 main.js 檔案的路徑
複製程式碼
  1. path.join()

    對路徑進行拼接

const rPath = path.join("source","js","main.js");
// aPath = //source/js/main.js
複製程式碼
  1. __dirname Node.js 中的全域性變數,代表的是 專案的當前路徑。常與 path 結合使用。

熱更新

上面我們已經提過了 webpack -w 命令,它可以實時的監控 程式碼的改變,從而自動進行打包,但是 有個缺點 在於它不能及時的重新整理介面。

在我們 開啟伺服器後,是無法使用 此命令的,這個時候,如果你還想進行 自動打包,又想自動重新整理介面,熱更新 便是不二之選,另外 Webpack 只會熱更新 發生改變的模組,不會重新載入整個頁面,便可加快開發速度。

開啟步驟:

  1. 修改 devServer屬性
devServer: {
 	hot: true,//熱載入開啟
 	inline: true,//檔案改變時會自動重新整理頁面
}
複製程式碼
  1. 增加熱更新外掛
const webpack = require("webpack");
//Other property
plugins: [
 	new webpack.HotModuleReplacementPlugin()
]

複製程式碼

另外,只有修改 依賴的專案,才會進行實時更新。

結束語

現在是一個 Web 技術蓬髮的時代,一定要把握住時代潮流。

在寫這篇文章前,我已經花了一整天的時間,把工作所需要 幾乎所有的 Loader , plugins 收集了一遍,專門寫了個原始檔。但因為我的原始檔太大 必須可顯示,可隱藏,不然影響閱讀 ,可掘金的 Markdown 不支援自定義樣式 ,所以我貼在了我的部落格,希望它能對你有幫助。

最後,再送你個福利,如果你是 個人部落格愛好者,我最近新開發了一款美觀開源的主題,期待你的使用!

相關文章