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中构建服务器呈现的应用程序:
- 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:
这是正在发生的事情:
- First, we are setting src of CSS files. 首先,我们设置CSS文件的src。
After that, we are minifying CSS files by calling
cleanCSS()
plugin.之后,我们通过调用
cleanCSS()
插件来缩小CSS文件。Then we are changing the extension of the files to
min.css
然后我们将文件的扩展名更改为
min.css
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.
在这里,导出了两个任务watch
和build
。 顾名思义, 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 andgulp watch
executes thewatch
task exported fromgulpfile.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.
到现在为止,我们已经在资产中添加了最小化内容,它仅改变了文本,但是压缩通过完全重写文件中的二进制代码进一步减小了资产的大小。 然后,压缩文件将被浏览器解压缩。 所有现代浏览器都支持gzip
和brotli
进行资产解压缩。 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