Building Vue3 Component Library from Scratch #6 Gulp Introduce
Tags: gulp
Published: 2024-05-28 09:35:51

## 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.

```bash
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.

```bash
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:

```javascript
exports.default = (cb) => {
  console.log("my task");
  cb();
};
```

Or

```javascript
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.


```javascript
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:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzvk6b6rrx9j0qhqvz9s.png)

You can see that executing `task1` took 5 seconds, and then `task2` was executed. Now let's look at parallel execution.

```javascript
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);
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ttxdcaijwyb71mj9zwq.png)

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.

```javascript
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`.

```bash
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:

```css
@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`.

```javascript
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`.

```css
.wrap {
  color: #fff;
}
```

We can add prefixes to our CSS.

```bash
npm install gulp-autoprefixer -D
```

Update `src/style/index.less` to:

```css
@color: #fff;
.wrap {
  color: @color;
  display: flex;
}
```

Then, use `gulp-autoprefixer` in `gulpfile.js`.

```javascript
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:

```css
.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.

```bash
npm i browser-sync -D 
```

Then, we create index.html in the root directory.

```html
<!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.

```javascript
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.

```javascript
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.


```html
<!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:

```javascript
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.