×

如何使用Webpack捆绑简单的静态站点

作者:Terry2020.10.23来源:Web前端之家浏览:5870评论:0
关键词:jswebpack

500.jpg

Webpack已将自身确立为JavaScript工具链中不可或缺的一部分。它在GitHub上拥有超过55,000个星,并且被JavaScript世界中的许多大型公司使用,例如React和Angular。

但是,您无需使用前端框架,也无需进行大型项目来利用它。Webpack主要是一个捆绑器,因此,您也可以使用它来捆绑您想考虑的任何资源或资产。

在本文中,我将向您展示如何安装和配置webpack,然后使用它为具有少量资产的简单静态站点创建缩小的包。

但是为什么要这么做呢?

这样做的原因之一是最大程度地减少了您向服务器发出的HTTP请求的数量。随着平均网页的增长,您可能会包括jQuery(是的,它在2020年仍然很流行),一些字体,一些插件以及各种样式表和您自己的一些JavaScript。如果您要针对这些资产中的每一个发出网络请求,那么事情很快就会加起来,您的页面可能会变得很呆滞。将您的代码捆绑在一起可以缓解此问题。

Webpack还使简化代码变得更容易,进一步减小了代码的大小,并且使您可以按自己的喜好编写资产。例如,在本文中,我将演示如何将webpack转换为现代JavaScript到ES5。这意味着您可以使用最新的最新语法编写JavaScript(尽管可能尚不完全支持),然后为几乎可在所有地方运行的浏览器ES5提供服务。

最后,这是一个有趣的学习练习。是否在自己的项目中使用这些技术取决于您自己,但是随着学习的进行,您将对webpack的功能,如何执行以及它是否很适合您有深刻的了解。

起床并跑步

您需要做的第一件事是在计算机上安装Node和npm。如果尚未安装Node,则可以从Node网站下载它,也可以在版本管理器的帮助下下载并安装它。就个人而言,我更喜欢第二种方法,因为它允许您在多个版本的Node之间切换,并且可以消除一堆权限错误,否则可能会看到您使用管理员权限安装Node软件包。

我们还需要一个框架项目。这是我早些时候做的。要使其在您的计算机上运行,您应该从GitHub克隆项目并安装依赖项:

git clone https://github.com/sitepoint-editors/webpack-static-site-examplecd webpack-static-site-examplenpm install

这将把jQuery以及Slick Slider和Lightbox2(我们将在网站上使用的两个插件)安装node_modules到项目根目录中的文件夹中。

之后,您可以index.html在浏览器中打开并浏览该站点。您应该会看到以下内容:

QQ截图20201023163715.jpg

将Webpack引入项目

接下来,我们需要安装webpack。我们可以使用以下命令执行此操作:

npm install webpack webpack-cli --save-dev

这将安装webpack和webpack CLI,并将它们添加到文件devDependency部分package.json

"devDependencies": {
  "webpack": "^5.1.3",
  "webpack-cli": "^4.0.0"}

接下来,我们将创建一个dist文件夹,其中包含捆绑的JavaScript:

mkdir dist

现在,我们可以尝试从命令行运行webpack,以查看其设置是否正确:

./node_modules/webpack/bin/webpack.js ./src/js/main.js --output-filename=bundle.js --mode=development

我们在这里所做的就是告诉webpack将其中的内容捆绑src/js/main.js到中dist/bundle.js。如果一切都正确安装,您应该在命令行中看到类似以下输出:

asset bundle.js 1.04 KiB [emitted] (name: main)
./src/js/main.js 192 bytes [built] [code generated]
webpack 5.1.3 compiled successfully in 45 ms

然后webpack将bundle.js在该dist文件夹中创建一个文件。如果您在选择的文本编辑器中查看该文件,则会main.js在底部看到一堆样板文件和内容。

自动化我们的设置

如果每次我们要运行webpack时都必须在终端中键入以上所有内容,那会很烦人。因此,让我们创建一个可以运行的npm脚本。

在中package.json,将scripts属性更改为如下所示:

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "build": "webpack ./src/js/main.js --output-filename=bundle.js --mode=development"
},

创建一个Webpack配置文件

注意我们如何将文件路径传递到bundle并将输出文件路径作为webpack的参数?好吧,我们可能应该更改它,并在配置文件中指定它们。以后再使用装载机时,这将使我们的生活更轻松。

webpack.config.js在项目根目录中创建一个文件:

touch webpack.config.js

并添加以下代码:

module.exports = {
  entry: './src/js/main.js',
  mode: 'development',
  output: {
	path: `${__dirname}/dist`,
	filename: 'bundle.js',
  },};

并将npm脚本更改为以下内容:

"scripts": {
  ...
  "build": "webpack"},

webpack.config.js我们导出配置对象的过程中,该对象指定了入口点,应该在其中运行webpack模式(稍后会详细介绍)以及包的输出位置。再次运行所有内容,它应该仍然像以前一样工作。

捆绑

现在我们已经有了webpack为我们生成一个包,接下来我们要做的就是将它包含在某个地方。但首先,让我们创建一个不同的入口点,以便我们可以列出我们希望webpack捆绑的资产。这将是app.jssrc/js目录中命名的文件:

touch src/js/app.js

将以下内容添加到app.js

require('./main.js');

并因此更改webpack配置:

entry: './src/js/app.js',

npm run build再次运行以重新创建捆绑包。一切都应该像以前一样工作。

现在,如果您看一看,index.html会发现在JavaScript方面没有太多进展。在文件的底部,我们包括jQuery和一个名为的文件main.js,该文件负责在您单击Read more…链接时显示更多信息。

让我们进行编辑,index.html以包含包而不是main.js。查看文件的底部。您应该看到:

    <script src="./node_modules/jquery/dist/jquery.min.js"></script>
	<script src="./src/js/main.js"></script>
  </body></html>

更改为:

    <script src="./node_modules/jquery/dist/jquery.min.js"></script>
	<script src="./dist/bundle.js"></script>
  </body></html>

在浏览器中刷新页面,并让自己确信 Read more…链接仍然有效。

捆绑jQuery

接下来,让我们将jQuery添加到包中。这将减少页面发出的HTTP请求的数量。为此,我们必须app.js像这样更改文件:

window.$ = require('jquery');require('./main.js');

这里我们需要jQuery,但是当我们使用npm安装它时,我们不必包含完整路径。我们还将其通常的$别名添加到全局window对象,以便其他脚本可以访问它。我们需要main.jsjQuery,因为前者取决于后者,顺序很重要。

更改index.html以删除jQuery脚本标签:

    <script src="./dist/bundle.js"></script>
  </body></html>

运行,npm run build然后再次在浏览器中刷新页面,以使自己确信“ 阅读更多...”链接仍然有效。是吗 好!

衡量我们的进步

谈论性能固然很好,但如果您不建立某种可衡量的指标,那么意义就很小。在我们的案例中,我们试图减少浏览器发出的HTTP请求的数量,我们可以从浏览器的开发人员工具中查看这些请求。我将以Chrome为例,但其原理对于任何现代浏览器都是相同的。


按F12打开开发人员工具,然后确保关键网络被选中的标签。然后单击并按住地址栏(带有箭头的圆圈)旁边的重新加载符号,然后选择空缓存和硬重新加载。您应该看到类似于下图的内容。

222.jpg

正如您在窗口底部的栏中看到的那样,正在发出八个请求(通过将jQuery添加到我们的捆绑包中,我们已经减少了一个请求),并且总共557kB的请求通过电线传输。

捆绑CSS

看一下index.html,我们向网络发出请求的唯一另一件事是CSS。如您所见,我们将其包含main.css在页面顶部,该文件又将导入另外四个CSS文件。

尽管在标准配置中,webpack只能处理JavaScript,但是我们也可以使用称为loader的东西来捆绑CSS。从webpack文档:

加载程序是应用于模块源代码的转换。它们允许您在处理文件时对其进行预处理import或“加载”。因此,加载程序有点像其他构建工具中的“任务”,并提供了一种强大的方式来处理前端构建步骤。加载程序可以将文件从其他语言(例如TypeScript)转换为JavaScript或将嵌入式图像加载为数据URL。加载程序甚至允许您import直接从JavaScript模块执行CSS文件之类的操作!

因此,让我们改变app.js

// CSSrequire('../css/main.css');// JavaScriptwindow.$ = require('jquery');require('./main.js');

而且webpack.config.js,当遇到一个以结尾的文件时,我们需要进行修改以告诉它要运行哪个加载器.css

module.exports = {
  ...
  module: {
	rules: [
	  {
		test: /\.css$/,
		use: [
		  'style-loader',
		  'css-loader',
		],
	  },
	],
  },};

如您所见,我指定了两个加载器:css-loader和style-loader。在这两者之间,css-loader将CSS转换为JavaScript模块,而style-loader<style>在运行时将JavaScript模块导出的CSS注入标签中。让我们同时安装:

npm install --save-dev css-loader style-loader

现在,让我们再次使用运行webpack npm run build,看看会发生什么:

> Webpack-static-site-example@1.0.0 build /home/jim/Downloads/webpack-static-site-example> webpackasset bundle.js 349 KiB [emitted] (name: main)runtime modules 931 bytes 4 modulesmodules by path ./src/ 356 KiB
  modules by path ./src/css/*.css 3.96 KiB 6 modules
  modules by path ./src/js/*.js 294 bytes
	./src/js/app.js 102 bytes [built] [code generated]
	./src/js/main.js 192 bytes [built] [code generated]
  ./src/fonts/open-sans/OpenSans-ExtraBold.ttf 352 KiB [built] [code generated] [1 error]modules by path ./node_modules/ 290 KiB
  modules by path ./node_modules/css-loader/dist/runtime/*.js 2.38 KiB
	./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
	./node_modules/css-loader/dist/runtime/getUrl.js 830 bytes [built] [code generated]
  ./node_modules/jquery/dist/jquery.js 281 KiB [built] [code generated]
  ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.67 KiB [built] [code generated]ERROR in ./src/fonts/open-sans/OpenSans-ExtraBold.ttf 1:0Module parse failed: Unexpected character '' (1:0)You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders(Source code omitted for this binary file)
 @ ./node_modules/css-loader/dist/cjs.js!./src/css/fonts.css 4:0-86 6:73-102
 @ ./node_modules/css-loader/dist/cjs.js!./src/css/main.css 3:0-104 8:26-59
 @ ./src/css/main.css 2:12-89 9:17-24 13:15-29
 @ ./src/js/app.js 2:0-26webpack 5.1.3 compiled with 1 error in 292 ms

哦,不!炸了。检查输出,似乎中存在错误src/css/fonts.css。如果打开该文件并查看第5行,您会看到我们包含了自定义字体(src/fonts/open-sans/OpenSans-ExtraBold.ttf),而webpack不知道该如何处理。

但是不用担心,我们已经做到了!我们只需要使用另一个加载器。这次是url-loader,它可以将诸如字体和图像之类的资产转换为数据URL,然后可以将其添加到捆绑软件中:

module.exports = {
  ...
  module: {
	rules: [
	  {
		test: /\.css$/,
		use: [
		  'style-loader',
		  'css-loader',
		],
	  },
	  {
		test: /\.ttf$/,
		use: [
		  'url-loader',
		],
	  },
	],
  },};

当然,我们需要安装它:

npm install url-loader --save-dev

现在构建应该运行了。通过<link>从中删除CSS标记来进行测试index.html,重新创建分发包并刷新页面。

捆绑第三方库

现在,我们将注意力转移到photos.html。由于我们正在使用两个库-Slick Slider和Lightbox2-这两个库都依赖jQuery,因此此页面上还有更多事情要做。幸运的是,我们可以应用我们学到的技术将它们包括在捆绑软件中。

app.js像这样更改:

// CSS
require('slick-carousel/slick/slick.css');require('slick-carousel/slick/slick-theme.css');
require('lightbox2/dist/css/lightbox.min.css');
require('../css/main.css');

// JS
window.$ = require('jquery');
window.slick = require('slick-carousel');
window.lightbox = require('lightbox2');
require('./main.js');

另外,从文档的开头删除CSS include,从脚注删除脚本包括。这应该给我们:

<!DOCTYPE html><html>
  <head>
 <meta charset="UTF-8">
 <title>I Can Haz Cheeseburger?</title>
  </head>
  <body>
 ...
 <script src="dist/bundle.js"></script>
 <script>
   $('.slick-slider').slick({
 dots: true,
 arrows: false,
 infinite: true,
 speed: 500,
 fade: true,
 cssEase: 'linear'
   });
 </script>
  </body></html>

尽管没有什么可以阻止我们在捆绑包中包含Slick初始化代码,但我将其保留在此页面上,因为我们只想在这里使用它。

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

...ERROR in ./node_modules/slick-carousel/slick/ajax-loader.gif 1:7Module parse failed: Unexpected character '' (1:7)You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders(Source code omitted for this binary file)
 @ ./node_modules/css-loader/dist/cjs.js!./node_modules/slick-carousel/slick/slick-theme.css 4:0-62 10:73-102
 @ ./node_modules/slick-carousel/slick/slick-theme.css 2:12-83 9:17-24 13:15-29
 @ ./src/js/app.js 3:0-47....

糟糕,还有更多错误!这次似乎slick-theme.css文件有问题,该文件正在引用GIF格式的图像。Webpack不知道如何处理GIF,因此它会举起手臂并停止工作。但是我们知道该怎么办,对吗?

将第二条规则更改webpack.config.js为以下内容:

{
  test: /\.(svg|gif|png|eot|woff|ttf)$/,
  use: [
	'url-loader',
  ],},

您会注意到我已经更改了正则表达式以匹配其他几种文件类型。Slick或Lightbox2都需要这些。再次运行webpack,并确保它完整无误。

重新运行build命令,刷新页面,并确保一切正常。

一些优化改进

我们差不多完成了,但是有几件事我们可以改进。

处理未样式化内容的闪烁

如果您在服务器上尝试此操作(仅在浏览器中打开文件可能无法使用),则在页面加载时会看到闪烁的未样式化内容。让我们在本地重现此内容。

首先,在您的系统上全局安装http-server软件包:

npm install -g http-server

然后导航到项目的根目录并发出以下命令:

http-server

这将在您的PC上启动HTTP服务器。导航至http://127.0.0.1:8080,您将像以前一样看到该站点。接下来,跳至浏览器开发工具的“网络”选项卡,然后找到用于限制连接速度的菜单。选择快速3G预设(或等效设置),然后硬刷新页面。您将看到HTML的加载方式,然后再过一两秒钟应用CSS。显然这不是最佳的。

注意:在所有现代浏览器中,应该都可以模拟慢速连接。这是有关如何在Chrome中执行操作的说明,以及这是如何在Firefox中进行操作的说明。

可以解决此问题的一种方法是利用<script>标签的阻塞特性,并将包含内容移到文件顶部。

<!DOCTYPE html><html>
  <head>
 <meta charset="UTF-8">
 <title>I Can Haz Cheeseburger?</title>
 <script src="dist/bundle.js"></script>
  </head>
  <body>
 ...
  </body></html>

这种方法可行,但是现在需要几秒钟来加载站点,这也不是完美的。

提取CSS

我们可以通过将CSS提取到其自己的包中并将其加载到页面顶部,而将JavaScript包保留在底部的位置来稍微改善这种情况。为此,我们需要mini-css-extract-plugin,所以让我们先安装它:

npm install --save-dev mini-css-extract-plugin

然后webpack.config.js像这样更改:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');module.exports = {
  entry: './src/js/app.js',
  mode: 'development',
  output: {
	path: `${__dirname}/dist`,
	filename: 'bundle.js',
  },
  plugins: [new MiniCssExtractPlugin()],
  module: {
	rules: [
	  {
		test: /\.css$/,
		use: [
		  MiniCssExtractPlugin.loader,
		  'css-loader',
		],
	  },
	  {
		test: /\.(svg|gif|png|eot|woff|ttf)$/,
		use: [
		  'url-loader',
		],
	  },
	],
  },};

在这里,我们需要在文件顶部添加新插件并将其添加到plugins数组中,然后再用MiniCssExtractPlugin的加载器替换样式加载器。现在,当您运行时npm run build,将在dist文件夹bundle.js和中生成两个捆绑包main.css

更改index.htmlphotos.html像这样包含它们:

<!DOCTYPE html><html>
  <head>
 <meta charset="UTF-8">
 <title>I Can Haz Cheeseburger?</title>
 <link rel="stylesheet" href="./dist/main.css">
  </head>
  <body>
 ...
 <script src="./dist/bundle.js"></script>
 <!-- Only photos.html -->
 <script>$('.slick-slider').slick({ ... });</script>
  </body></html>

现在我们避开了FOUC,站点加载速度加快了几秒钟,这无疑是一个进步。

不同页面的不同组合

您可能已经注意到,无论用户访问哪个页面,我们都将所有资产都包含在捆绑包中并为这些捆绑包提供服务。严格来说,如果用户仅访问索引页面,则无需下载照片页面上滑块的样式和代码。

根据您要采用这种方法的程度,完全有可能为照片页面和网站上的其他页面创建单独的捆绑包。为此,您可以使用HtmlWebpackPlugin,它可以简化HTML文件的创建以服务于Webpack捆绑包。

不幸的是,该技术稍微超出了本教程的范围,但是您可以在此处找到有关如何执行此操作的出色指南。

缩小捆绑

如果您要采用“一捆一包”的方法,那么一个简单的胜利就是在生产模式下运行webpack,这将使其输出较小的缩小的包。

为此,请webpack.config.js像这样更改:

module.exports = {
  entry: './src/js/app.js',
  mode: 'production',
  ...};

现在,当您运行build命令时,webpack将输出一个缩小和优化的包。这使大小bundle.js从821.8kB减小到485.9kB。不错,考虑了所有事情。

如果您决定将捆绑软件分为JavaScript和CSS,事情会变得更加复杂。为了优化CSS,我们需要一个额外的插件—optimize-css-assets-webpack-plugin。要使用此功能,我们必须重写webpack的默认Minimalizer,这反过来意味着我们也需要指定一个JavaScript最小化器。对于此任务,terser-webpack-plugin是一个不错的选择。

让我们安装这两个:

npm install --save-dev optimize-css-assets-webpack-plugin terser-webpack-plugin

然后webpack.config.js像这样更改 :

const MiniCssExtractPlugin = require('mini-css-extract-plugin');const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');const TerserPlugin = require('terser-webpack-plugin');module.exports = {
  entry: './src/js/app.js',
  mode: 'production',
  output: {
	path: `${__dirname}/dist`,
	filename: 'bundle.js',
  },
  plugins: [new MiniCssExtractPlugin()],
  module: {
	rules: [
	  {
		test: /\.css$/,
		use: [
		  MiniCssExtractPlugin.loader,
		  'css-loader',
		],
	  },
	  {
		test: /\.(svg|gif|png|eot|woff|ttf)$/,
		use: [
		  'url-loader',
		],
	  },
	],
  },
  optimization: {
	minimize: true,
	minimizer: [
	  new TerserPlugin({
		extractComments: false,
	  }),
	  new OptimizeCssAssetsPlugin(),
	],
  },};

查看代码,您可以看到我们在文件顶部需要两个新插件,并且已向optimization导出的配置对象添加了密钥。这使我们可以将Terser指定为JavaScript的最小化器,并将Optimize CSS Assets插件指定为CSS的最小化器。现在,当您运行时npm run build,应该输出两个最小化的包。

这将束大小分别从446.6kB和338.8kB减小到144kB和336kB。对于某些CSS,JS和其他一些资产,总计480kB的捆绑包似乎有点多余,但请记住,其中的222kB是字体。

将ES6转换为ES5

您还可以安装babel-loader并让webpack通过它运行JavaScript文件,从而将现代JavaScript转换为ES5:

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

然后在中为Javascript文件定义新规则webpack.config.js

{
  test: /\.js$/,
  exclude: /(node_modules|bower_components)/,
  use: {
	loader: 'babel-loader',
	options: {
	  presets: ['@babel/preset-env'],
	},
  },},

现在,当您运行时npm run build,JavaScript文件将通过Babel通过管道传输,并因此转换为ES5语法,该语法将在几乎所有浏览器中运行。

当然,您可以将Babel换成您喜欢的几乎所有其他JavaScript编译语言。例如,这是一个TypeScript加载器,使您可以将TypeScript添加到项目中。

结论

在本文中,我们演示了如何使用webpack捆绑一个简单的静态站点,该过程减少了HTTP请求的数量,从而使该站点可能更灵活,响应更迅速。我还演示了如何使webpack最小化结果包,从而减小文件大小,以及如何使用babel-loader将现代JavaScript转换为ES5。

但是,在我注销之前,让我们将原始配置与最终配置进行比较。以index.html为例,该页面最初九次请求,并有319KB的有效载荷。使用Chrome中的Fast 3G预设,页面加载时间为4.14秒。相反,使用两捆设置,页面发出两个请求,有效载荷为472kB,加载时间为4.34s。

嗯……这可能会让您想知道为什么您会不厌其烦。但是,请不要忘记两个捆绑软件都由浏览器缓存,因此当您访问照片页面时,原始设置必须从服务器获取所有滑块代码,并且需要6.5秒才能完全加载。捆绑的设置已经满足了很多需求,并且可以在3.32秒内准备就绪。

即使这种策略并不适合每个人,但希望您能通过遵循该指南深入了解webpack的功能以及它的工作方式。对于那些希望进一步探索Webpack的人,我推荐“ Webpack初学者指南”,它对一些重要概念进行了更深入的介绍,例如webpack dev服务器,它将为您介绍热模块的美好世界重装。

您的支持是我们创作的动力!
温馨提示:本文作者系Terry ,经Web前端之家编辑修改或补充,转载请注明出处和本文链接:
https://jiangweishan.com/article/webpack20201023.html

网友评论文明上网理性发言 已有0人参与

发表评论: