Webpack入门指南

来源:https://www.sitepoint.com/webpack-beginner-guide/

现在,我们被迫使用许多辅助工具来方便、加速和优化我们的web开发工作流程。不过,这些工具通常会给堆栈增加额外的复杂性。因此,我们需要利用额外的时间和精力来正确地学习、理解和使用这些工具。webpack也是如此。

第一次使用webpack时,可能很难理解它是如何工作的以及应该如何使用它。尽管它有很好的文档,但对于新手来说,它可能会让人望而生畏,而且它有一个陡峭的学习曲线。然而,webpack是值得学习的,从长远来看,它可以节省大量的时间和精力。在本教程中,我将介绍所有的核心概念,以帮助您入门。

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

一、Webpack是什么?

作为其核心,webpack是一个静态模块捆绑器。在一个特定的项目中,webpack将所有的文件和资源都视为模块。在底层,它依赖于一个依赖图。依赖关系图描述了模块如何使用文件之间的引用(require和import语句)相互关联。通过这种方式,webpack静态遍历所有模块来构建图形,并使用它生成一个bundle(或多个bundle)——一个JavaScript文件,包含所有模块的代码,并按正确的顺序组合在一起。" static "的意思是,当webpack构建它的依赖关系图时,它不会执行源代码,而是将模块及其依赖关系缝合到一个包中。然后可以将其包含在HTML文件中。

现在,为了扩展上面的概述,让我们探讨一下webpack使用的主要概念。

二、Webpack主要概念

Webpack有一些主要的概念,在深入了解它的实际实现之前,我们需要清楚地理解它们。让我们逐一检查它们:

Entry:入口点是webpack用来开始构建其内部依赖图的模块。从那里,它确定入口点(直接和间接地)依赖哪些其他模块和库,并将它们包含在图中,直到没有任何依赖关系。默认情况下,entry属性被设置为./src/index.js,但我们可以在webpack配置文件中指定不同的模块(甚至多个模块)。

Output: Output属性指示webpack在哪里发出包以及文件的名称。这个属性的默认值是主包的./dist/main.js,其他生成的文件的./dist -例如图像。当然,我们可以根据需要在配置中指定不同的值。

Loaders:默认情况下,webpack只理解JavaScript和JSON文件。为了处理其他类型的文件并将它们转换成有效的模块,webpack使用了加载器。加载器转换非javascript模块的源代码,允许我们在将这些文件添加到依赖关系图之前对它们进行预处理。例如,加载器可以将CoffeeScript语言的文件转换为JavaScript,或者将内联图像转换为数据url。有了加载器,我们甚至可以直接从JavaScript模块中导入CSS文件。

Plugins:插件用于加载器不能完成的任何其他任务。它们为我们提供了关于资产管理、bundle最小化和优化等广泛的解决方案。

Mode:通常,当我们开发应用程序时,我们使用两种类型的源代码——一种用于开发构建,另一种用于生产构建。Webpack允许我们通过将mode参数更改为development、production或none来设置我们想要生成哪一个。这允许webpack使用对应于每个环境的内置优化。默认值为production。none模式意味着不使用默认的优化选项。要了解更多关于webpack在开发和生产模式中使用的选项,请访问模式配置页面。

三、Webpack是如何工作的

在本节中,我们将研究webpack是如何工作的。即使是一个简单的项目也包含HTML、CSS和JavaScript文件。此外,它还可以包含字体、图像等资产。所以,一个典型的webpack工作流应该包括用适当的CSS和JS链接建立一个index.html文件,以及必要的资源。此外,如果您有许多相互依赖的CSS和JS模块,则需要对它们进行优化,并将它们适当地组合到一个单元中,以便投入生产。

要做到这一点,webpack依赖于配置。从版本4及以上开始,webpack提供了合理的默认值,因此不需要创建配置文件。然而,对于任何重要的项目,您都需要提供一个特殊的webpack.config.js文件,该文件描述了应该如何转换文件和资产,以及应该生成什么样的输出。这个文件可能很快就会变成一个整体,这使得理解webpack是如何工作的变得很困难,除非你知道它工作背后的主要概念。

基于所提供的配置,webpack从入口点开始,并在构建依赖关系图时解析它遇到的每个模块。如果模块包含依赖项,则会针对每个依赖项递归地执行进程,直到遍历完成。然后webpack将所有的项目模块打包成少量的包——通常只有一个——由浏览器加载

四、Webpack 5有什么新内容

webpack 5于2020年10月发布。这篇文章很长,探讨了webpack的所有变化。不可能提到所有的更改,对于像这样的初学者指南来说也没有必要。相反,我将试着列出一个小列表,包含一些一般的重点:

(1)持久性缓存提高了构建性能。开发人员现在可以启用基于文件系统的缓存,这将加快开发构建。

(2)长期缓存也得到了改进。在webpack 5中,对代码所做的改变不会影响最小化的bundle版本(注释、变量名),这不会导致缓存失效。此外,还添加了新的算法,以确定的方式将短数字id分配给模块和块,并将短名称分配给导出。在webpack 5中,它们在生产模式下默认是启用的。

(3)改进了包的大小,这要归功于更好的摇树和代码生成。多亏了新的嵌套摇树功能,webpack现在能够跟踪对导出的嵌套属性的访问。CommonJs摇树让我们可以消除未使用的CommonJs导出。

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

(5)代码库被清理干净。所有webpack 4中标记为弃用的项目都将被移除。

(6)自动的Node.js腻子脚本被移除。以前的webpack版本包含了原生Node.js库的腻子脚本,比如crypto。在许多情况下,它们是不必要的,并且会极大地增加包的大小。这就是为什么webpack 5会自动停止填充这些核心模块,而专注于前端兼容模块。

(7)作为开发的一个改进,webpack 5允许我们传递目标列表,也支持target的版本。它提供了公共路径的自动确定。此外,它还提供了自动的、唯一的命名,这可以防止多个webpack运行时使用相同的全局变量进行块加载时发生冲突。

(8)webpack-dev-server命令现在是webpack serve。

(9)引入了资产模块,它取代了文件加载器、原始加载器和url加载器的使用。

五、开始

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

现在我们有了坚实的理论基础,让我们在实践中实现它。

首先,我们将创建一个新目录并切换到它。然后我们将初始化一个新项目:

mkdir learn-webpack

cd learn-webpack

npm init -y

接下来,我们需要在本地安装webpack和webpack CLI(命令行界面):

npm install webpack webpack-cli --save-dev

现在,生成的package.json的内容应该类似于以下内容:

{

  "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"

  }

}

除了作为一个包管理器,npm还可以作为一个简单的任务运行器。我们可以创建webpack任务,方法是在package.json文件的scripts部分中加上任务的名称和指令。现在让我们试试这个。打开package.json,将scripts对象更改为如下内容:

"scripts": {

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

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

  "build": "webpack --mode production"

},

在scripts属性中,npm允许我们引用本地安装的Node.js包的名称。我们使用这个和——mode标志来定义开发和构建任务,它们将分别在开发(npm run dev)和生产(npm run build)模式下运行webpack。

在测试我们刚刚创建的任务之前,让我们创建一个src目录,并在其中放入一个index.js文件,这样它就包含了console.log("Hello, Webpack!");现在我们已经可以运行dev任务来启动webpack在开发模式:

$ 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将默认入口点设置为./src/index.js,将默认输出设置为./dist/main.js。因此,当我们运行dev任务时,webpack所做的就是从index.js文件中获取源代码,并将最终代码打包到main.js文件中。

太棒了!它像预期的那样工作。但是为了验证我们得到了正确的输出,我们需要在浏览器中显示结果。为了做到这一点,让我们在dist目录下创建一个index.html文件:

<!doctype html>

<html>

  <head>

    <title>Getting Started With Webpack</title>

  </head>

  <body>

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

  </body>

</html>

现在,如果我们在浏览器中打开这个文件,我们应该会看到Hello, Webpack!消息。


到目前为止,一切顺利。但在某些情况下,手动编写index.html文件可能会有问题。例如,如果我们改变入口点的名称,生成的bundle将被重命名,但是我们的index.html文件仍然会引用旧的名称。因此,每次重命名一个入口点或添加一个新入口点时,我们都需要手动更新HTML文件。幸运的是,我们可以用html-webpack-plugin轻松地解决这个问题。让我们现在安装它:

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

注意:注意我输入的是html-webpack-plugin@next而不是html-webpack-plugin。在撰写本文时,前者是webpack 5的合适版本,而后者是webpack 4的合适版本。这在将来可能会改变,所以对于实际版本,请检查html-webpack-plugin repo。

现在,为了激活这个插件,我们需要在根目录下创建一个webpack.config.js文件,包含以下内容:

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

const path = require('path');

module.exports = {

  plugins: [

    new HtmlWebpackPlugin({

      title: "Webpack Output",

    }),

  ],

};

如你所见,要激活webpack插件,我们需要把它包含在文件中,然后把它添加到plugins数组中。如果需要,我们也将选项传递给插件。请参阅html-webpack-plugin repo了解所有可用的选项以及编写和使用自己模板的能力。

现在运行webpack看看会发生什么:

$ 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

让我们打开index.html。如我们所见,插件会自动为我们创建一个更新后的index.html文件,该文件使用了配置文件中的title选项:

<!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>

现在让我们展开项目,并为输入和输出属性指定自定义名称。在webpack.config.js中,我们在plugins属性前添加以下内容:

entry: {

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

},

output: {

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

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

},

在这里,我们将输入文件更改为app.js,并将输出文件夹更改为要部署的文件夹。我们还稍微调整了生成的bundle文件的名称。现在它将以条目的名称(“main”)开始,后面是单词“bundle”和.js文件扩展名。

现在,我们要创建一个src/component.js文件:

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

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

  element.innerHTML = text;

  return element;

};

接下来,我们将index.js重命名为app.js,以反映我们的改变,并将其内容替换为以下内容:

import component from './component';

document.body.appendChild(component());

现在,让我们再次运行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)及其依赖项(component.js)。

现在,在deploy文件夹中,我们有了新生成的包文件main.bundle.js。如果我们在浏览器中打开index.html文件,我们会看到Hello, Webpack!界面显示。


同样,如果我们检查index.html的源代码,我们会看到脚本标记中的src属性的值被更新为main.bundle.js。

现在,我们可以删除webpack最初生成的dist文件夹,因为我们不再需要它了。

六、将现代JavaScript编译为ES5

在本节中,我们将了解如何将ES6编译成兼容es5的代码,这些代码可以在所有浏览器中运行。让我们先运行以下命令:

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

在这里,我运行webpack并将devtool选项设置为inline-source-map,以便使代码更具可读性。这样我就可以更清楚地演示从ES6到ES5的代码转换过程。

接下来,让我们打开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;

});

/***/ })

如你所见,默认情况下,component.js模块中的现代ES6特性(箭头函数和const声明)不会转换为兼容es5的代码。为了让我们的代码能在旧的浏览器上运行,我们必须添加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']

        }

      }

    },

  ]

},

当我们为webpack加载器定义规则时,通常需要定义三个主要的属性:

test,它描述了应该转换什么样的文件。

exclude,它定义了不应该被加载器处理的文件(如果有这些文件的话)。

use,它告诉哪个加载器应该针对匹配的模块使用。在这里,我们还可以设置加载器选项,就像我们刚刚完成的预置选项一样。

重新执行如下命令:

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;});/***/})

完美的。现在我们可以使用现代的JS特性了,webpack会转换我们的代码,这样它就可以被旧的浏览器执行了。

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

https://flaviocopes.com/webpack/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容