Webpack入門(mén)指南

來(lái)源:https://www.sitepoint.com/webpack-beginner-guide/

現(xiàn)在,我們被迫使用許多輔助工具來(lái)方便、加速和優(yōu)化我們的web開(kāi)發(fā)工作流程。不過(guò),這些工具通常會(huì)給堆棧增加額外的復(fù)雜性。因此,我們需要利用額外的時(shí)間和精力來(lái)正確地學(xué)習(xí)、理解和使用這些工具。webpack也是如此。

第一次使用webpack時(shí),可能很難理解它是如何工作的以及應(yīng)該如何使用它。盡管它有很好的文檔,但對(duì)于新手來(lái)說(shuō),它可能會(huì)讓人望而生畏,而且它有一個(gè)陡峭的學(xué)習(xí)曲線。然而,webpack是值得學(xué)習(xí)的,從長(zhǎng)遠(yuǎn)來(lái)看,它可以節(jié)省大量的時(shí)間和精力。在本教程中,我將介紹所有的核心概念,以幫助您入門(mén)。

注意:在本教程中,我使用的是webpack 5.9.0。

一、Webpack是什么?

作為其核心,webpack是一個(gè)靜態(tài)模塊捆綁器。在一個(gè)特定的項(xiàng)目中,webpack將所有的文件和資源都視為模塊。在底層,它依賴于一個(gè)依賴圖。依賴關(guān)系圖描述了模塊如何使用文件之間的引用(require和import語(yǔ)句)相互關(guān)聯(lián)。通過(guò)這種方式,webpack靜態(tài)遍歷所有模塊來(lái)構(gòu)建圖形,并使用它生成一個(gè)bundle(或多個(gè)bundle)——一個(gè)JavaScript文件,包含所有模塊的代碼,并按正確的順序組合在一起。" static "的意思是,當(dāng)webpack構(gòu)建它的依賴關(guān)系圖時(shí),它不會(huì)執(zhí)行源代碼,而是將模塊及其依賴關(guān)系縫合到一個(gè)包中。然后可以將其包含在HTML文件中。

現(xiàn)在,為了擴(kuò)展上面的概述,讓我們探討一下webpack使用的主要概念。

二、Webpack主要概念

Webpack有一些主要的概念,在深入了解它的實(shí)際實(shí)現(xiàn)之前,我們需要清楚地理解它們。讓我們逐一檢查它們:

Entry:入口點(diǎn)是webpack用來(lái)開(kāi)始構(gòu)建其內(nèi)部依賴圖的模塊。從那里,它確定入口點(diǎn)(直接和間接地)依賴哪些其他模塊和庫(kù),并將它們包含在圖中,直到?jīng)]有任何依賴關(guān)系。默認(rèn)情況下,entry屬性被設(shè)置為./src/index.js,但我們可以在webpack配置文件中指定不同的模塊(甚至多個(gè)模塊)。

Output: Output屬性指示webpack在哪里發(fā)出包以及文件的名稱。這個(gè)屬性的默認(rèn)值是主包的./dist/main.js,其他生成的文件的./dist -例如圖像。當(dāng)然,我們可以根據(jù)需要在配置中指定不同的值。

Loaders:默認(rèn)情況下,webpack只理解JavaScript和JSON文件。為了處理其他類型的文件并將它們轉(zhuǎn)換成有效的模塊,webpack使用了加載器。加載器轉(zhuǎn)換非javascript模塊的源代碼,允許我們?cè)趯⑦@些文件添加到依賴關(guān)系圖之前對(duì)它們進(jìn)行預(yù)處理。例如,加載器可以將CoffeeScript語(yǔ)言的文件轉(zhuǎn)換為JavaScript,或者將內(nèi)聯(lián)圖像轉(zhuǎn)換為數(shù)據(jù)url。有了加載器,我們甚至可以直接從JavaScript模塊中導(dǎo)入CSS文件。

Plugins:插件用于加載器不能完成的任何其他任務(wù)。它們?yōu)槲覀兲峁┝岁P(guān)于資產(chǎn)管理、bundle最小化和優(yōu)化等廣泛的解決方案。

Mode:通常,當(dāng)我們開(kāi)發(fā)應(yīng)用程序時(shí),我們使用兩種類型的源代碼——一種用于開(kāi)發(fā)構(gòu)建,另一種用于生產(chǎn)構(gòu)建。Webpack允許我們通過(guò)將mode參數(shù)更改為development、production或none來(lái)設(shè)置我們想要生成哪一個(gè)。這允許webpack使用對(duì)應(yīng)于每個(gè)環(huán)境的內(nèi)置優(yōu)化。默認(rèn)值為production。none模式意味著不使用默認(rèn)的優(yōu)化選項(xiàng)。要了解更多關(guān)于webpack在開(kāi)發(fā)和生產(chǎn)模式中使用的選項(xiàng),請(qǐng)?jiān)L問(wèn)模式配置頁(yè)面。

三、Webpack是如何工作的

在本節(jié)中,我們將研究webpack是如何工作的。即使是一個(gè)簡(jiǎn)單的項(xiàng)目也包含HTML、CSS和JavaScript文件。此外,它還可以包含字體、圖像等資產(chǎn)。所以,一個(gè)典型的webpack工作流應(yīng)該包括用適當(dāng)?shù)腃SS和JS鏈接建立一個(gè)index.html文件,以及必要的資源。此外,如果您有許多相互依賴的CSS和JS模塊,則需要對(duì)它們進(jìn)行優(yōu)化,并將它們適當(dāng)?shù)亟M合到一個(gè)單元中,以便投入生產(chǎn)。

要做到這一點(diǎn),webpack依賴于配置。從版本4及以上開(kāi)始,webpack提供了合理的默認(rèn)值,因此不需要?jiǎng)?chuàng)建配置文件。然而,對(duì)于任何重要的項(xiàng)目,您都需要提供一個(gè)特殊的webpack.config.js文件,該文件描述了應(yīng)該如何轉(zhuǎn)換文件和資產(chǎn),以及應(yīng)該生成什么樣的輸出。這個(gè)文件可能很快就會(huì)變成一個(gè)整體,這使得理解webpack是如何工作的變得很困難,除非你知道它工作背后的主要概念。

基于所提供的配置,webpack從入口點(diǎn)開(kāi)始,并在構(gòu)建依賴關(guān)系圖時(shí)解析它遇到的每個(gè)模塊。如果模塊包含依賴項(xiàng),則會(huì)針對(duì)每個(gè)依賴項(xiàng)遞歸地執(zhí)行進(jìn)程,直到遍歷完成。然后webpack將所有的項(xiàng)目模塊打包成少量的包——通常只有一個(gè)——由瀏覽器加載。

四、Webpack 5有什么新內(nèi)容

webpack 5于2020年10月發(fā)布。這篇文章很長(zhǎng),探討了webpack的所有變化。不可能提到所有的更改,對(duì)于像這樣的初學(xué)者指南來(lái)說(shuō)也沒(méi)有必要。相反,我將試著列出一個(gè)小列表,包含一些一般的重點(diǎn):

(1)持久性緩存提高了構(gòu)建性能。開(kāi)發(fā)人員現(xiàn)在可以啟用基于文件系統(tǒng)的緩存,這將加快開(kāi)發(fā)構(gòu)建。

(2)長(zhǎng)期緩存也得到了改進(jìn)。在webpack 5中,對(duì)代碼所做的改變不會(huì)影響最小化的bundle版本(注釋、變量名),這不會(huì)導(dǎo)致緩存失效。此外,還添加了新的算法,以確定的方式將短數(shù)字id分配給模塊和塊,并將短名稱分配給導(dǎo)出。在webpack 5中,它們?cè)谏a(chǎn)模式下默認(rèn)是啟用的。

(3)改進(jìn)了包的大小,這要?dú)w功于更好的搖樹(shù)和代碼生成。多虧了新的嵌套搖樹(shù)功能,webpack現(xiàn)在能夠跟蹤對(duì)導(dǎo)出的嵌套屬性的訪問(wèn)。CommonJs搖樹(shù)讓我們可以消除未使用的CommonJs導(dǎo)出。

(4)最小支持Node.js版本從6增加到10.13.0 (LTS)。

(5)代碼庫(kù)被清理干凈。所有webpack 4中標(biāo)記為棄用的項(xiàng)目都將被移除。

(6)自動(dòng)的Node.js膩?zhàn)幽_本被移除。以前的webpack版本包含了原生Node.js庫(kù)的膩?zhàn)幽_本,比如crypto。在許多情況下,它們是不必要的,并且會(huì)極大地增加包的大小。這就是為什么webpack 5會(huì)自動(dòng)停止填充這些核心模塊,而專注于前端兼容模塊。

(7)作為開(kāi)發(fā)的一個(gè)改進(jìn),webpack 5允許我們傳遞目標(biāo)列表,也支持target的版本。它提供了公共路徑的自動(dòng)確定。此外,它還提供了自動(dòng)的、唯一的命名,這可以防止多個(gè)webpack運(yùn)行時(shí)使用相同的全局變量進(jìn)行塊加載時(shí)發(fā)生沖突。

(8)webpack-dev-server命令現(xiàn)在是webpack serve。

(9)引入了資產(chǎn)模塊,它取代了文件加載器、原始加載器和url加載器的使用。

五、開(kāi)始

注意:你可以在GitHub repo中找到我們項(xiàng)目的文件(https://github.com/sitepoint-editors/learn_webpack)。

現(xiàn)在我們有了堅(jiān)實(shí)的理論基礎(chǔ),讓我們?cè)趯?shí)踐中實(shí)現(xiàn)它。

首先,我們將創(chuàng)建一個(gè)新目錄并切換到它。然后我們將初始化一個(gè)新項(xiàng)目:

mkdir learn-webpack

cd learn-webpack

npm init -y

接下來(lái),我們需要在本地安裝webpack和webpack CLI(命令行界面):

npm install webpack webpack-cli --save-dev

現(xiàn)在,生成的package.json的內(nèi)容應(yīng)該類似于以下內(nèi)容:

{

? "name": "learn-webpack",

? "version": "1.0.0",

? "description": "",

? "main": "index.js",

? "scripts": {

? ? "test": "echo \"Error: no test specified\" && exit 1"

? },

? "keywords": [],

? "author": "",

? "license": "ISC",

? "devDependencies": {

? ? "webpack": "^5.9.0",

? ? "webpack-cli": "^4.2.0"

? }

}

除了作為一個(gè)包管理器,npm還可以作為一個(gè)簡(jiǎn)單的任務(wù)運(yùn)行器。我們可以創(chuàng)建webpack任務(wù),方法是在package.json文件的scripts部分中加上任務(wù)的名稱和指令?,F(xiàn)在讓我們?cè)囋囘@個(gè)。打開(kāi)package.json,將scripts對(duì)象更改為如下內(nèi)容:

"scripts": {

? "test": "echo \"Error: no test specified\" && exit 1",

? "dev": "webpack --mode development",

? "build": "webpack --mode production"

},

在scripts屬性中,npm允許我們引用本地安裝的Node.js包的名稱。我們使用這個(gè)和——mode標(biāo)志來(lái)定義開(kāi)發(fā)和構(gòu)建任務(wù),它們將分別在開(kāi)發(fā)(npm run dev)和生產(chǎn)(npm run build)模式下運(yùn)行webpack。

在測(cè)試我們剛剛創(chuàng)建的任務(wù)之前,讓我們創(chuàng)建一個(gè)src目錄,并在其中放入一個(gè)index.js文件,這樣它就包含了console.log("Hello, Webpack!");現(xiàn)在我們已經(jīng)可以運(yùn)行dev任務(wù)來(lái)啟動(dòng)webpack在開(kāi)發(fā)模式:

$ npm run dev

> learn-webpack@1.0.0 dev C:\WEBDEV\learn-webpack

> webpack --mode development

[webpack-cli] Compilation finished

asset main.js 874 bytes [emitted] (name: main)

./src/index.js 31 bytes [built] [code generated]

webpack 5.9.0 compiled successfully in 122 ms

正如我之前提到的,webpack將默認(rèn)入口點(diǎn)設(shè)置為./src/index.js,將默認(rèn)輸出設(shè)置為./dist/main.js。因此,當(dāng)我們運(yùn)行dev任務(wù)時(shí),webpack所做的就是從index.js文件中獲取源代碼,并將最終代碼打包到main.js文件中。

太棒了!它像預(yù)期的那樣工作。但是為了驗(yàn)證我們得到了正確的輸出,我們需要在瀏覽器中顯示結(jié)果。為了做到這一點(diǎn),讓我們?cè)赿ist目錄下創(chuàng)建一個(gè)index.html文件:

<!doctype html>

<html>

? <head>

? ? <title>Getting Started With Webpack</title>

? </head>

? <body>

? ? <script src="main.js"></script>

? </body>

</html>

現(xiàn)在,如果我們?cè)跒g覽器中打開(kāi)這個(gè)文件,我們應(yīng)該會(huì)看到Hello, Webpack!消息。


到目前為止,一切順利。但在某些情況下,手動(dòng)編寫(xiě)index.html文件可能會(huì)有問(wèn)題。例如,如果我們改變?nèi)肟邳c(diǎn)的名稱,生成的bundle將被重命名,但是我們的index.html文件仍然會(huì)引用舊的名稱。因此,每次重命名一個(gè)入口點(diǎn)或添加一個(gè)新入口點(diǎn)時(shí),我們都需要手動(dòng)更新HTML文件。幸運(yùn)的是,我們可以用html-webpack-plugin輕松地解決這個(gè)問(wèn)題。讓我們現(xiàn)在安裝它:

npm install html-webpack-plugin@next --save-dev

注意:注意我輸入的是html-webpack-plugin@next而不是html-webpack-plugin。在撰寫(xiě)本文時(shí),前者是webpack 5的合適版本,而后者是webpack 4的合適版本。這在將來(lái)可能會(huì)改變,所以對(duì)于實(shí)際版本,請(qǐng)檢查html-webpack-plugin repo。

現(xiàn)在,為了激活這個(gè)插件,我們需要在根目錄下創(chuàng)建一個(gè)webpack.config.js文件,包含以下內(nèi)容:

const HtmlWebpackPlugin = require("html-webpack-plugin");

const path = require('path');

module.exports = {

? plugins: [

? ? new HtmlWebpackPlugin({

? ? ? title: "Webpack Output",

? ? }),

? ],

};

如你所見(jiàn),要激活webpack插件,我們需要把它包含在文件中,然后把它添加到plugins數(shù)組中。如果需要,我們也將選項(xiàng)傳遞給插件。請(qǐng)參閱html-webpack-plugin repo了解所有可用的選項(xiàng)以及編寫(xiě)和使用自己模板的能力。

現(xiàn)在運(yùn)行webpack看看會(huì)發(fā)生什么:

$ npm run dev

> learn-webpack@1.0.0 dev C:\WEBDEV\learn-webpack

> webpack --mode development

[webpack-cli] Compilation finished

asset main.js 874 bytes [compared for emit] (name: main)

asset index.html 234 bytes [emitted]

./src/index.js 31 bytes [built] [code generated]

webpack 5.9.0 compiled successfully in 151 ms

讓我們打開(kāi)index.html。如我們所見(jiàn),插件會(huì)自動(dòng)為我們創(chuàng)建一個(gè)更新后的index.html文件,該文件使用了配置文件中的title選項(xiàng):

<!DOCTYPE html>

<html>

? <head>

? ? <meta charset="utf-8">

? ? <title>Webpack Output</title>

? ? <meta name="viewport" content="width=device-width, initial-scale=1">

? ? <script defer src="main.js"></script>

? </head>

? <body>

? </body>

</html>

現(xiàn)在讓我們展開(kāi)項(xiàng)目,并為輸入和輸出屬性指定自定義名稱。在webpack.config.js中,我們?cè)趐lugins屬性前添加以下內(nèi)容:

entry: {

? main: path.resolve(__dirname, './src/app.js'),

},

output: {

? filename: '[name].bundle.js',

? path: path.resolve(__dirname, 'deploy')

},

在這里,我們將輸入文件更改為app.js,并將輸出文件夾更改為要部署的文件夾。我們還稍微調(diào)整了生成的bundle文件的名稱?,F(xiàn)在它將以條目的名稱(“main”)開(kāi)始,后面是單詞“bundle”和.js文件擴(kuò)展名。

現(xiàn)在,我們要?jiǎng)?chuàng)建一個(gè)src/component.js文件:

export default (text = "Hello, Webpack!") => {

? const element = document.createElement("h1");

? element.innerHTML = text;

? return element;

};

接下來(lái),我們將index.js重命名為app.js,以反映我們的改變,并將其內(nèi)容替換為以下內(nèi)容:

import component from './component';

document.body.appendChild(component());

現(xiàn)在,讓我們?cè)俅芜\(yùn)行webpack:

$ npm run dev

> learn-webpack@1.0.0 dev C:\WEBDEV\learn-webpack

> webpack --mode development

[webpack-cli] Compilation finished

asset main.bundle.js 4.67 KiB [emitted] (name: main)

asset index.html 241 bytes [emitted]

runtime modules 668 bytes 3 modules

cacheable modules 230 bytes

? ./src/app.js 79 bytes [built] [code generated]

? ./src/component.js 151 bytes [built] [code generated]

webpack 5.9.0 compiled successfully in 194 ms

讓我們檢查并澄清webpack輸出的信息。在“編譯完成”消息之后,您可以看到在deploy目錄中生成的文件(main.bundle.js和index.html)。在它們下面,你可以看到源文件:入口模塊(app.js)及其依賴項(xiàng)(component.js)。

現(xiàn)在,在deploy文件夾中,我們有了新生成的包文件main.bundle.js。如果我們?cè)跒g覽器中打開(kāi)index.html文件,我們會(huì)看到Hello, Webpack!界面顯示。


同樣,如果我們檢查index.html的源代碼,我們會(huì)看到腳本標(biāo)記中的src屬性的值被更新為main.bundle.js。

現(xiàn)在,我們可以刪除webpack最初生成的dist文件夾,因?yàn)槲覀儾辉傩枰恕?/p>

六、將現(xiàn)代JavaScript編譯為ES5

在本節(jié)中,我們將了解如何將ES6編譯成兼容es5的代碼,這些代碼可以在所有瀏覽器中運(yùn)行。讓我們先運(yùn)行以下命令:

npm run dev -- --devtool inline-source-map

在這里,我運(yùn)行webpack并將devtool選項(xiàng)設(shè)置為inline-source-map,以便使代碼更具可讀性。這樣我就可以更清楚地演示從ES6到ES5的代碼轉(zhuǎn)換過(guò)程。

接下來(lái),讓我們打開(kāi)main.bundle.js:

/***/ "./src/component.js":

/*!**************************!*\

? !*** ./src/component.js ***!

? \**************************/

/*! namespace exports */

/*! export default [provided] [no usage info] [missing usage info prevents renaming] */

/*! other exports [not provided] [no usage info] */

/*! runtime requirements: __webpack_exports__, __webpack_require__.r, __webpack_require__.d, __webpack_require__.* */

/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);

/* harmony export */ __webpack_require__.d(__webpack_exports__, {

/* harmony export */? "default": () => __WEBPACK_DEFAULT_EXPORT__

/* harmony export */ });

/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((text = "Hello, Webpack!") => {

? const element = document.createElement("h1");

? element.innerHTML = text;

? return element;

});

/***/ })

如你所見(jiàn),默認(rèn)情況下,component.js模塊中的現(xiàn)代ES6特性(箭頭函數(shù)和const聲明)不會(huì)轉(zhuǎn)換為兼容es5的代碼。為了讓我們的代碼能在舊的瀏覽器上運(yùn)行,我們必須添加Babel加載器:

npm install babel-loader @babel/core @babel/preset-env --save-dev

然后,在webpack.config.js中,在output屬性后添加module:

module: {

? rules: [

? ? {

? ? ? test: /\.js$/,

? ? ? exclude: /node_modules/,

? ? ? use: {

? ? ? ? loader: 'babel-loader',

? ? ? ? options: {

? ? ? ? ? presets: ['@babel/preset-env']

? ? ? ? }

? ? ? }

? ? },

? ]

},

當(dāng)我們?yōu)閣ebpack加載器定義規(guī)則時(shí),通常需要定義三個(gè)主要的屬性:

test,它描述了應(yīng)該轉(zhuǎn)換什么樣的文件。

exclude,它定義了不應(yīng)該被加載器處理的文件(如果有這些文件的話)。

use,它告訴哪個(gè)加載器應(yīng)該針對(duì)匹配的模塊使用。在這里,我們還可以設(shè)置加載器選項(xiàng),就像我們剛剛完成的預(yù)置選項(xiàng)一樣。

重新執(zhí)行如下命令:

npm run dev -- --devtool inline-source-map

這一次,main.bundle.js中的代碼被編譯:

/***/ "./src/component.js":

/*!**************************!*\

? !*** ./src/component.js ***!

? \**************************//*! namespace exports *//*! export default [provided] [no usage info] [missing usage info prevents renaming] *//*! other exports [not provided] [no usage info] *//*! runtime requirements: __webpack_exports__, __webpack_require__.r, __webpack_require__.d, __webpack_require__.* *//***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) =>{__webpack_require__.r(__webpack_exports__);/* harmony export */__webpack_require__.d(__webpack_exports__,{/* harmony export */"default":()=>__WEBPACK_DEFAULT_EXPORT__/* harmony export */});/* harmony default export */const__WEBPACK_DEFAULT_EXPORT__=(function(){vartext=arguments.length>0&&arguments[0]!==undefined?arguments[0]:"Hello, Webpack!";varelement=document.createElement("h1");element.innerHTML=text;returnelement;});/***/})

完美的?,F(xiàn)在我們可以使用現(xiàn)代的JS特性了,webpack會(huì)轉(zhuǎn)換我們的代碼,這樣它就可以被舊的瀏覽器執(zhí)行了。

https://www.freecodecamp.org/news/an-intro-to-webpack-what-it-is-and-how-to-use-it-8304ecdc3c60/

https://flaviocopes.com/webpack/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容