nodejs gulp_nodejs用gulp和brotli优化

欧阳英彦
2023-12-01

nodejs gulp

An important factor that affects the performance of a website is the size of resources requested by the browser. CSS and JavaScript source files are among the top of these resources. If you do not handle them efficiently, they can considerably increase page load time. Get the app repo here.

影响网站性能的重要因素是浏览器请求的资源大小。 CSS和JavaScript源文件位于这些资源的顶部。 如果您不能有效地处理它们,则它们可能会大大增加页面加载时间。 在此处获取应用回购。

您将从这块作品中得到什么? (What you will get from this piece?)

In this article, we’re going to build a server-rendered application in Nodejs by implementing:

在本文中,我们将通过实现以下内容在Node.js中构建服务器呈现的应用程序:

  • Asset minification and compression using Gulp and Brotli.

    使用GulpBrotli进行资产压缩和压缩。

  • A development environment where assets are minified and compressed in real-time.

    实时缩小和压缩资产的开发环境。
  • A build process that can make distributable assets for deployment.

    可以使可分发资产进行部署的构建过程。

Also, you will get some familiarity with Gulp and Brotli. If your app is not server-rendered or performance is not an issue for your application then this article may not be very informative to you.

另外,您将对Gulp和Brotli有所了解。 如果您的应用程序不是服务器渲染的,或者应用程序的性能不是问题,那么本文可能对您不是很有帮助。

设置应用程序: (Setting up the Application:)

Make a new directory, initialize npm, and install express and handlebars:

新建一个目录,初始化npm,然后安装express和handlebars:

npm i express express-handlebars

Create a basic express server with handlebars configured as the templating engine.

使用配置为模板引擎的把手创建基本的Express服务器。

const express = require('express');
const expressHbs = require('express-handlebars');
const path = require('path');


// initialize app
const app = express();
const PORT = process.env.PORT || 5000;


// set template engine to handlebars
app.engine('.hbs', expressHbs({ extname: '.hbs', layoutsDir: 'views' }));
app.set('view engine', '.hbs');
app.set('views', 'views');


// set static folder
app.use(express.static(path.join(__dirname, 'dist')));


// app routes
app.get('/', (req, res) => {
  res.render('main');
});


app.listen(PORT, () => {
  console.log(`Listening on port ${PORT}`);
});

On line 15 of app.js we are setting dist as the static directory for our app. This dist directory will be automatically generated from Gulp. Add a views directory in the root of your project and create main.hbs layout for our application. Put HTML boilerplate in this template.

app.js第15行,我们将dist设置为应用程序的静态目录。 该dist目录将从Gulp自动生成。 在项目的根目录中添加一个views目录,并为我们的应用程序创建main.hbs布局。 将HTML样板文件放在此模板中。

添加一些资产: (Add some assets:)

In order to minify and compress assets, we need assets. Let’s create a folder assets in the root of the project, and add some CSS and JS files. The app folder structure will look like this:

为了最小化和压缩资产,我们需要资产。 让我们在项目的根目录中创建一个文件夹assets ,并添加一些CSS和JS文件。 应用程序文件夹结构如下所示:

my-app
├── assets
│ └── css
│ └── styles.css
│ └── js
│ └── main.js
├── node_modules
├── views
│ └── main.hbs
├── app.js
├── package.json
└── package.lock.json

Include the created assets in the head tag of main.hbs template:

将创建的资产包括在main.hbs模板的head标签中:

<link rel="stylesheet" href="/css/styles.min.css">
<script src="/js/main.min.js"></script>

Note that we are including them from the dist directory (set as static folder) which does not exist yet but it will be created by gulp. Gulp will take our assets from the assets folder minify them and put them into a new folder dist with extensions of .min.css or .min.js.

请注意,我们从dist目录(设置为静态文件夹)中将它们包括在内,该目录尚不存在,但将由gulp创建。 Gulp将从资产文件夹中提取资产,并将其缩小,然后将其放入扩展名为.min.css.min.js的新文件夹dist

Put anything you want in these asset files as our goal is to minify and compress them, so it doesn’t matter what they contain. You can also include jquery or other libraries for testing if they are minified and compressed correctly.

将您想要的任何内容放入这些资产文件中,因为我们的目标是最小化和压缩它们,因此它们包含的内容无关紧要。 您还可以包括jquery或其他库,以测试它们是否已缩小和正确压缩。

从Gulp开始: (Start with Gulp:)

Gulp is a JavaScript toolkit that allows developers to automate web development workflows. It has many plugins to accommodate all your needs. Let’s start by installing gulp and necessary plugins for the minification of CSS and JS.

Gulp是一个JavaScript工具包,允许开发人员自动执行Web开发工作流。 它有许多插件可以满足您的所有需求。 让我们从安装gulp和必要的插件开始,以缩小CSS和JS。

npm i -D gulp gulp-cli gulp-rename gulp-clean-css gulp-uglify-es

Let’s see what all these packages do:

让我们看看所有这些软件包的作用:

  • gulp: Main library

    gulp:主库

  • gulp-cli: Command Line Utility for Gulp

    gulp-cli: Gulp的命令行实用程序

  • gulp-rename: A gulp plugin to rename files easily during the build process

    gulp重命名:一个gulp插件,可在构建过程中轻松重命名文件

  • gulp-clean-css: A gulp plugin to minify CSS using clean-css

    gulp-clean-css:一个使用clean-css最小化CSS的gulp插件

  • gulp-uglify-es: A gulp plugin to minify JavaScript (es6 supported)

    gulp-uglify-es:最小化JavaScript的gulp插件(支持ES6)

建立构建管道: (Make a Build Pipeline:)

What we want from our pipeline is to take files from the assets directory, minify them and save them in a new directory dist by postfixing filenames as {filename}.min.{file_extenstion} . So if we have a CSS file in /assets/css/styles.css then the dist will contain /dist/css/styles.min.css .

我们希望从管道中获取的是从资产目录中提取文件,将其缩小并通过将文件名后缀为{filename}.min.{file_extenstion}将它们保存在新目录dist 。 因此,如果我们在/assets/css/styles.css有一个CSS文件,则dist将包含/dist/css/styles.min.css

Start by making a gulpfile.js in the root of the project and import the following modules:

首先在项目的根目录中创建一个gulpfile.js ,然后导入以下模块:

const { src, dest, parallel, watch } = require('gulp');
const cleanCSS = require('gulp-clean-css');
const uglify = require('gulp-uglify-es').default;
const rename = require('gulp-rename');

Then create an object for paths of the source and destination files:

然后为源文件和目标文件的路径创建一个对象:

const paths = {
css: {
src: 'assets/css/*.css',
dest: 'dist/css/',
},
js: {
src: 'assets/js/*.js',
dest: 'dist/js/',
},
};

Now, create a gulp task which is basically a function that takes CSS files from assets, minifies them, and saves them in the dist.

现在,创建一个gulp任务,该任务基本上是一个从资产中获取CSS文件,将其最小化并将其保存在dist中的功能。

function minifyAndCompressCSS() {
return src(paths.css.src)
.pipe(cleanCSS())
.pipe(rename({ extname: '.min.css' }))
.pipe(dest(paths.css.dest));
}

Here what is happening in the pipeline:

这是正在发生的事情:

  1. First, we are setting src of CSS files.

    首先,我们设置CSS文件的src。
  2. After that, we are minifying CSS files by calling cleanCSS() plugin.

    之后,我们通过调用cleanCSS()插件来缩小CSS文件。

  3. Then we are changing the extension of the files to min.css

    然后我们将文件的扩展名更改为min.css

  4. Finally, we are saving minified files to the dist directory by using thedest() middleware of gulp.

    最后,我们使用gulp的dest()中间件将缩小的文件保存到dist目录。

In the same manner, create a task for JavaScript files too.

同样,也为JavaScript文件创建任务。

function minifyAndCompressJS() {
return src(paths.js.src)
.pipe(uglify())
.pipe(rename({ extname: '.min.js' }))
.pipe(dest(paths.js.dest));
}

注意文件: (Watch for files:)

We want our assets to be minified and compressed in real-time during development. Let’s create a task that watches our assets for changes and reflect changes in distributable assets.

我们希望在开发过程中实时缩小和压缩资产。 让我们创建一个任务,监视我们的资产是否有变化并反映可分配资产的变化。

function watchFiles() {
watch(
paths.css.src,
{ ignoreInitial: false },
minifyAndCompressCSS
);
watch(
paths.js.src,
{ ignoreInitial: false },
minifyAndCompressJS);
}

To watch files, we’re using watch middleware provided by gulp. The ignoreInitital: false option is ensuring that the pipeline runs initially when the server is started.

为了监视文件,我们使用了gulp提供的watch中间件。 ignoreInitital: false选项可确保在启动服务器时管道最初运行。

Now, export the created tasks so they can be executed from the command line using gulp-cli

现在,导出创建的任务,以便可以使用gulp-cli从命令行执行它们

exports.watch = watchFiles;
exports.build = parallel(minifyAndCompressCSS, minifyAndCompressJS);

Here, two tasks are exported watch and build. As the name suggests watch is the task that should run in the development environment and build should run when we deploy the application. The parallel middleware runs both tasks simultaneously.

在这里,导出了两个任务watchbuild 。 顾名思义, watch是应该在开发环境中运行的任务,而build应该在我们部署应用程序时运行。 parallel中间件同时运行两个任务。

The final version of gulpfile.js that takes CSS and JavaScript files and outputs minified versions of them will look something like this:

最终版本的gulpfile.js需要CSS和JavaScript文件并输出其缩小版本,如下所示:

const { src, dest, parallel, watch } = require('gulp');
const cleanCSS = require('gulp-clean-css');
const uglify = require('gulp-uglify-es').default;
const rename = require('gulp-rename');


const paths = {
  css: {
    src: 'assets/css/*.css',
    dest: 'dist/css/',
  },
  js: {
    src: 'assets/js/*.js',
    dest: 'dist/js/',
  },
};


function minifyAndCompressCSS() {
  return src(paths.css.src)
    .pipe(cleanCSS())
    .pipe(rename({ extname: '.min.css' }))
    .pipe(dest(paths.css.dest));
}


function minifyAndCompressJS() {
  return src(paths.js.src)
    .pipe(uglify())
    .pipe(rename({ extname: '.min.js' }))
    .pipe(dest(paths.js.dest));
}


// task to watch files for change in dev environment
function watchFiles() {
  watch(paths.css.src, { ignoreInitial: false }, minifyAndCompressCSS);
  watch(paths.js.src, { ignoreInitial: false }, minifyAndCompressJS);
}


// export tasks so they are available in command line
exports.watch = watchFiles;
exports.build = parallel(minifyAndCompressCSS, minifyAndCompressJS);

自动化构建过程:(Automate the build process:)

Now, we have created the tasks we need for our pipeline, lets make them work by defining some scripts in package.json. First, add two dependencies

现在,我们创建了流水线所需的任务,通过在package.json定义一些脚本使它们工作。 首先,添加两个依赖项

npm i -D nodemon concurrently
  • nodemon: A tool that restarts the node server when file changes are detected

    nodemon:检测到文件更改时重新启动节点服务器的工具

  • concurrently: Allows to run multiple commands concurrently

    并发:允许并发运行多个命令

Add the following scripts to package.json:

将以下脚本添加到package.json

"scripts": {
"start": "node app",
"dev": "concurrently \"nodemon app\" \"gulp watch\"",
"build": "gulp build"
},

Explaining scripts:

解释脚本:

  • start: The start script will start the application with node

    start:启动脚本将使用节点启动应用程序

  • dev: This script runs multiple commands with concurrently where nodemon app restarts the server when files are changed and gulp watch executes the watch task exported from gulpfile.js

    dev:此脚本同时运行多个命令,其中nodemon app在文件更改时重启服务器,而nodemon app gulp watch执行从gulpfile.js导出的watch任务

  • build: This script can be used to make distributable assets on deployment or when you just want to make a build without running dev script.

    build:此脚本可用于在部署时或仅在不运行dev脚本的情况下进行构建时使可分发资产。

现在该进行测试了: (It’s time to test:)

Open up your command line and run npm run dev from the root of your project. When you run this command you will see that a new directory dist is added to your project with minified assets.

打开命令行,然后从项目的根目录运行npm run dev 。 当您运行此命令时,您会看到一个新目录dist被添加到您的项目中,并且资产最少。

Now change something in the /assets/js/main.js for instance, add alert('changed') and you will see in the command line that minifyAndCompressJS task is triggered and main.js is minified and saved again. Now open up the application in browser (localhost:5000) and you will see the alert message from the minified file.

现在,例如,更改/assets/js/main.js中的内容,添加alert('changed') ,您将在命令行中看到minifyAndCompressJS任务已触发,并且main.js缩小并再次保存。 现在,在浏览器(localhost:5000)中打开应用程序,您将在缩小的文件中看到警报消息。

添加压缩: (Add Compression:)

Till now we have added minification to our assets which simply alters the text but compression reduces the size of the assets further by completely rewriting the binary code within a file. The compressed file will then be decompressed by the browser. All modern browsers support gzip and brotli for the decompression of assets. Brotli is considered to be the most efficient of them so will use this.

到现在为止,我们已经在资产中添加了最小化内容,它仅改变了文本,但是压缩通过完全重写文件中的二进制代码进一步减小了资产的大小。 然后,压缩文件将被浏览器解压缩。 所有现代浏览器都支持gzipbrotli进行资产解压缩。 Brotli被认为是其中最有效的,因此将使用此功能。

Let’s install packages needed for compression:

让我们安装压缩所需的软件包:

npm i -D gulp-brotli zlib
  • gulp-brotli: A gulp plugin for file compression using the brotli compression

    gulp-brotli:一个gulp插件,用于使用brotli压缩进行文件压缩

  • zlib: a software library used for data compression (we will use this library to provide some configuration options to gulp-brotli)

    zlib:用于数据压缩的软件库(我们将使用该库为gulp-brotli提供一些配置选项)

Import these two libraries in gulpfile.js

将这两个库导入gulpfile.js

const gulpBrotli = require('gulp-brotli');
const zlib = require('zlib');

Modify the tasks we created for minification of CSS and JS assets to something like this:

修改我们为缩小CSS和JS资产而创建的任务,如下所示:

function minifyAndCompressCSS() {
return src(paths.css.src)
.pipe(cleanCSS())
.pipe(rename({ extname: '.min.css' }))
.pipe(gulpBrotli(brotliOptions()))
.pipe(rename({ extname: '.br' }))
.pipe(dest(paths.css.dest));
}

Here, I added two more stages in the pipeline, after minifying the CSS files, I am compressing them with gulpBrotli plugin, then renaming the file extension to be .br and saving them in the destination dist directory. Now if you have a file in /assets/css/styles.css then it will be minified, compressed, and saved as /dist/css/styles.min.css.br

在这里,我在管道中添加了另外两个阶段,在最小化CSS文件之后,我使用gulpBrotli插件对其进行压缩,然后将文件扩展名重命名为.br并将其保存在目标dist目录中。 现在,如果您在/assets/css/styles.css有一个文件,那么它将被缩小,压缩并另存为/dist/css/styles.min.css.br

Here I am using a function brotliOptions that passes some configuration options to the gulpBrotli plugin and it is:

在这里,我使用的是brotliOptions函数,该函数将一些配置选项传递给gulpBrotli插件,它是:

function brotliOptions() {
return {
params: {
[zlib.constants.BROTLI_PARAM_QUALITY]:
zlib.constants.BROTLI_MAX_QUALITY,
},
};
}

Finally, gulpfile.js with minification and compression both look like this:

最后,具有缩小和压缩功能的gulpfile.js都看起来像这样:

const { src, dest, parallel, watch } = require('gulp');
const cleanCSS = require('gulp-clean-css');
const uglify = require('gulp-uglify-es').default;
const rename = require('gulp-rename');
const gulpBrotli = require('gulp-brotli');
const zlib = require('zlib');


const paths = {
  css: {
    src: 'assets/css/*.css',
    dest: 'dist/css/',
  },
  js: {
    src: 'assets/js/*.js',
    dest: 'dist/js/',
  },
};


/*
 * Task to Minify and Compress CSS
 * This task takes a file suppose styles.css from assets/css
 * Minifies it and compresses the file using Brotli Algorithm
 * And saves the file as styles.min.css.br in dist/css directory
 */
function minifyAndCompressCSS() {
  return src(paths.css.src)
    .pipe(cleanCSS())
    .pipe(rename({ extname: '.min.css' }))
    .pipe(gulpBrotli(brotliOptions()))
    .pipe(rename({ extname: '.br' }))
    .pipe(dest(paths.css.dest));
}


/*
 * Task to Minify and Compress JavaScript
 * This task takes a file suppose main.js from assets/js
 * Minifies it and compresses the file using Brotli Algorithm
 * And saves the file as main.min.js.br in dist/js directory
 */
function minifyAndCompressJS() {
  return src(paths.js.src)
    .pipe(uglify())
    .pipe(rename({ extname: '.min.js' }))
    .pipe(gulpBrotli(brotliOptions()))
    .pipe(rename({ extname: '.br' }))
    .pipe(dest(paths.js.dest));
}


// task to watch files for change in dev environment
function watchFiles() {
  watch(paths.css.src, { ignoreInitial: false }, minifyAndCompressCSS);
  watch(paths.js.src, { ignoreInitial: false }, minifyAndCompressJS);
}


function brotliOptions() {
  return {
    params: {
      [zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY,
    },
  };
}


// export tasks so they are available in command line
exports.watch = watchFiles;
exports.build = parallel(minifyAndCompressCSS, minifyAndCompressJS);

服务Brotli压缩资产:(Serve Brotli compressed assets:)

In order to serve Brotli compressed assets to the browser, we need to do some configuration.

为了将Brotli压缩资产提供给浏览器,我们需要进行一些配置。

Whenever a client makes a request for a CSS or JS file, we need to modify the request URL to include .br as the extension to the request URL. Suppose a client makes a request for /dist/styles.min.css then we will modify the request URL to /dist/styles.min.css.br because our build pipeline spits out brotli compressed assets with extension .br.

每当客户请求CSS或JS文件时,我们都需要修改请求URL以包括.br作为请求URL的扩展名。 假设客户端请求/dist/styles.min.css则我们将请求URL修改为/dist/styles.min.css.br因为我们的构建管道吐出了扩展名为.br brotli压缩资产。

After mapping the correct file, tell the browser that the server is sending a brotli compressed asset. To do so, we need to set a header Content-Encoding: br which tells the browser that the asset needs to be decompressed using the brotli algorithm.

映射正确的文件后,告诉浏览器服务器正在发送brotli压缩资产。 为此,我们需要设置标头Content-Encoding: br ,该标头告诉浏览器需要使用brotli算法对资产进行解压缩。

The middlewares for achieving this will look something like this:

用于实现此目的的中间件如下所示:

/* serve Brotli compressed CSS files wheneve there is a request for .css file */app.get('*.css', (req, res, next) => {
req.url = req.url + '.br';
res.set('Content-Encoding', 'br');
res.set('Content-Type', 'text/css; charset=utf-8');
next();
});/* serve Brotli compressed JS files whenever there is a request for .js file */app.get('*.js', (req, res, next) => {
req.url = req.url + '.br';
res.set('Content-Encoding', 'br');
res.set('Content-Type', 'application/javascript; charset=UTF-8');
next();
});

Add these two middlewares in your app.js file just before you set the static folder. Now, delete the previously created dist directory and run npm run dev again and test your application in the browser.

在设置静态文件夹之前,将这两个中间件添加到app.js文件中。 现在,删除先前创建的dist目录,然后再次运行npm run dev并在浏览器中测试您的应用程序。

Success! you have built a custom pipeline for minifying and compressing your assets with Gulp and Brotli. This approach will remain the same for any number/size of assets. Play with the application repository here.

成功! 您已经建立了一个自定义管道,用于使用Gulp和Brotli最小化和压缩资产。 对于任何数量/大小的资产,此方法将保持不变。 在此处使用应用程序存储库

Read also:

另请阅读:

翻译自: https://medium.com/swlh/nodejs-optimize-with-gulp-and-brotli-52cfb70fdea

nodejs gulp

 类似资料: