Building Vue3 Component Library from Scratch #6 Gulp Introduce
Mark
Posted on May 28, 2024
Preface
With the development of front-end tools such as Webpack, Rollup, and Vite, Gulp seems to have been replaced. However, that's not the case. It has simply moved from the forefront to the background. We can still see it in many projects, such as ElementPlus and Vant. Nowadays, Gulp is more focused on process control.
For example, if we want to put an elephant into a refrigerator, we need to follow a simple process: open the refrigerator door -> put the elephant inside -> close the refrigerator door. Using Gulp, we can define these steps and automate this process.
So we can use Gulp to automate common tasks during project development. For example, when packaging a component library, we might need to remove files, copy files, package styles, package components, execute some commands, and package multiple packages with one click. All of these tasks can be controlled by Gulp through custom workflows, making it very convenient.
This article will mainly introduce some common functionalities of Gulp.
Install Gulp
Firstly, install gulp globally.
npm install --global gulp-cli
Next, we create a new folder called gulpdemo
, then run npm init -y
to initialize the project. After that, we install Gulp as a local dependency in this project.
npm install gulp -D
At this point, Gulp is installed. Next, we create gulpfile.js
file in the root directory. When Gulp runs, it will automatically look for this file.
Create Task
Each Gulp task is an asynchronous JavaScript function. This function can accept a callback as a parameter or return a Promise or other asynchronous operation object. For example, creating a task can be done like this:
exports.default = (cb) => {
console.log("my task");
cb();
};
Or
exports.default = () => {
console.log("my task");
return Promise.resolve();
};
Then, by typing gulp
in the terminal, this task will be executed.
Series and Parallel
These two concepts are quite easy to understand. Serial execution means tasks are executed one after another, while parallel execution means all tasks are executed simultaneously. Let's first look at a demonstration of serial execution.
const { series, parallel } = require("gulp");
const task1 = () => {
console.log("task1");
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 5000);
});
};
const task2 = () => {
console.log("task2");
return Promise.resolve();
};
exports.default = series(task1, task2);
The console output:
You can see that executing task1
took 5 seconds, and then task2
was executed. Now let's look at parallel execution.
const { series, parallel } = require("gulp");
const task1 = () => {
console.log("task1");
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 5000);
});
};
const task2 = () => {
console.log("task2");
return Promise.resolve();
};
exports.default = parallel(task1, task2);
You can see that the two tasks are executed simultaneously.
src() and dest()
src()
and dest()
are two functions that we often use in our actual projects. src()
represents creating a stream to read from the file system, while dest()
creates a stream to write to the file system. Let's write a simple example for copying files.
Copy
Before writing the task, let's create a src
directory in the root of our project to store the files to be copied. Inside the src
directory, create a few files.
Next, we write our copy task in gulpfile.js
to copy all files from the src
directory to the dist
directory.
const { src, dest } = require("gulp");
const copy = () => {
return src("src/*").pipe(dest("dist/"));
};
exports.default = copy;
Then, run gulp
(which by default executes exports.default
), and you'll notice that a dist
folder has been created in the root directory.
Process Less
Next, let's write a task to process LESS files. First, we need to install gulp-less
.
npm i -D gulp-less
Next, create a style
directory inside the src
folder, and then create an index.less
file inside the style
directory. Write a piece of LESS syntax style in it. For example:
@color: #fff;
.wrap {
color: @color;
}
Next, let's write our lessTask
in gulpfile.js
to parse the LESS files in the style
directory into CSS and write them into dist/style
.
const { src, dest } = require("gulp");
const less = require("gulp-less");
const lessTask = () => {
return src("src/style/*.less").pipe(less()).pipe(dest("dist/style"));
};
exports.default = lessTask;
Then, run the gulp
command, and you'll find dist/style/index.css
.
.wrap {
color: #fff;
}
We can add prefixes to our CSS.
npm install gulp-autoprefixer -D
Update src/style/index.less
to:
@color: #fff;
.wrap {
color: @color;
display: flex;
}
Then, use gulp-autoprefixer
in gulpfile.js
.
const { src, dest } = require("gulp");
const less = require("gulp-less");
const autoprefixer = require("gulp-autoprefixer");
const lessTask = () => {
return src("src/style/*.less")
.pipe(less())
.pipe(
autoprefixer({
overrideBrowserslist: ["> 1%", "last 2 versions"],
cascade: false,
})
)
.pipe(dest("dist/style"));
};
exports.default = lessTask;
The processed dist/style/index.css
will look like this:
.wrap {
color: #fff;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
Watch File Changes with browser-sync
Browser-sync is a very useful browser synchronization testing tool. It can set up a static server, monitor file changes, and refresh the page (HMR). Let's take a look at how to use it.
Firstly, you definitely need to install it.
npm i browser-sync -D
Then, we create index.html in the root directory.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
hello world
</body>
</html>
Then, configure it in the gulpfile.js.
const browserSync = require("browser-sync");
const browserTask = () => {
browserSync.init({
server: {
baseDir: "./",
},
});
};
exports.default = browserTask;
At this point, a server will be launched on the default port 3000. Next, let's see how to monitor changes.
Firstly, we need to monitor changes in the file, which can be done using browserSync's watch. After detecting changes in the file, the page is then refreshed.
const { watch } = require("browser-sync");
const browserSync = require("browser-sync");
const { series } = require("gulp");
const reloadTask = () => {
browserSync.reload();
};
const browserTask = () => {
browserSync.init({
server: {
baseDir: "./",
},
});
watch("./*", series(reloadTask));
};
exports.default = browserTask;
At this point, if you modify a file under 'src', the browser will refresh.
Now we will import the style from 'dist/style/index.css' into 'index.html', and then simulate a simple build flow.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="../dist/style/index.css" />
</head>
<body>
<div class="wrap">hello world</div>
</body>
</html>
At this point, our process is: compile the less files -> write css into 'dist/style' -> trigger page update.
We can write our 'gulpfile.js' like this:
const { src, dest } = require("gulp");
const { watch } = require("browser-sync");
const browserSync = require("browser-sync");
const { series } = require("gulp");
const less = require("gulp-less");
const autoprefixer = require("gulp-autoprefixer");
const lessTask = () => {
return src("src/style/*.less")
.pipe(less())
.pipe(
autoprefixer({
overrideBrowserslist: ["> 1%", "last 2 versions"],
cascade: false, // format
})
)
.pipe(dest("dist/style"));
};
//refresh
const reloadTask = () => {
browserSync.reload();
};
const browserTask = () => {
browserSync.init({
server: {
baseDir: "./",
},
});
watch("./*.html", series(reloadTask));
//watch to run task
watch("src/style/*", series(lessTask, reloadTask));
};
exports.default = browserTask;
At this point, whether we change the style or the HTML, it can trigger a page update.
Finally
In the future, I will use gulp to handle the style packaging part of the Vue3 component library that I am currently developing. If you are interested in component library development, you can follow this series. I will implement some commonly used components and present them in the form of blog.
Posted on May 28, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.