At the company last month, put forward an idea for the separation of front end, and began to study about the separation before and after the end, the front-end engineering, modular something, began last week I’m going to start writing their Gulp flow based front-end engineering documents, this two days have the time, began to realize this idea, but in the process of writing, have a few problems, Because solving these problems has given me a deeper understanding of Gulp streaming, I would like to write this article to share some of the things I have learned over the past two days while writing Gulp.

The preparatory work

Install the Node

First of all, Gulp is based on Nodejs, so Nodejs installation is a prerequisite. Node is a front-end wizard, there are various tools based on Node, precisely because these tools make it very easy to build front-end projects.

Change the default Node plug-in installation location (optional)

I don’t like to have a lot of non-system related stuff on disk C, and the plugin installed via Node NPM is installed on disk C by default. However, after installing Node to disk D, I want to install the plugin in the main directory of Nodejs.

  1. Create the “node_global” and “node_cache” folders in the Node home directory

  2. Start CMD and type

npm config set prefix "D:\Program\nodejs\node_global"
npm config set cache "D:\Program\nodejs\node_cache"Copy the code
  1. Close CMD, open system dialog box, “My computer” right click “Properties” – “Advanced System Settings” – “Advanced” – “Environment Variables”.

  2. Go to the environment variables dialog box, create NODE_PATH under system variables, and enter D:\Program\nodejs\node_global\node_module. Because the default address of the module is changed, all the user variables must be changed (the user variable PATH is changed to “D:\Program\nodejs\node_global\”). Otherwise, the input command “XXX is not an internal or external command. Nor is it a runnable program or batch file “error.

After these four steps, the installed Node plug-in can be placed in the Nodejs home directory.

Install Gulp

npm install -g gulp
npm install --save-dev gulpCopy the code

Run gulp -v. If no error message is displayed, the installation is successful

Then run it from the command line

Let the project produce package.json files

Building engineering

As is known to all, in the development of engineering has two process development and launch, in development, we usually need to refresh automatically and real-time compilation, but if online, we need to consider a lot of optimization, such as file compiling compression, static resource slow processing and so on questions, I build the project will involve only compiled file compression, real-time refresh, The static resource slowdown saves these three basic processes.

The directory structure in the project is as follows

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- project | -- -- -- -- -- -- -- -- -- -- -- -- -- -- dist (generation this folder for packaging) | | | | -- -- -- -- -- -- -- -- -- -- the CSS | | | | | | -- -- -- -- -- - the index - 9 dcc24fe2e. CSS |  | | |----------js | | | | | |------index-9dcc24fe2e.js | |----------index.html |--------------src | | | |----------scss  | | | | | |------index.scss | | | |----------js | | | | | |------index.js | | | |----------index.html |--------------gulpfile.js |--------------package.jsonCopy the code

Development process

File to compile

In the project, SCSS is to be used as the precompilation of CSS, so gulp is needed to compile SCSS, so gulp-sass is installed first.

npm install --save-dev gulp-sassCopy the code

After the installation is complete, reference the configuration directly in gulpfile.js

const sass = require('gulp-sass'); 
gulp.task('scss:dev',()=>{
    gulp.src('src/scss/*.scss')
    .pipe(sass())
    .pipe(gulp.dest('dist/css')); 
    });Copy the code

Here are two gulp apis:

Gulp.src () enters a file that matches either the provided matching pattern or an array of matching patterns. Will return a stream or can be piped into another plugin. Read the file

Gulp.dest () can be entered by pipe and will write files. And re-output (emits) all the data, so it can be piped to multiple folders, which will be created automatically if they don’t exist. Write files

Real-time refresh

There are a number of tools available to implement real-time refresh. I use Browser-sync myself, and it’s a very powerful tool. For more information on how to use it, see www.browsersync.cn/.

First we install the module in the project

npm install --save-dev browser-syncCopy the code

According to the browser-sync and gulp configurations on the official website, the following configurations are obtained:

const browserSync = require('browser-sync').create(); 
const reload = browserSync.reload;
gulp.task('dev',['scss:dev'],function () {
    browserSync.init({
        server:{
            baseDir:'./'  
        logLevel: "debug",
        logPrefix:"dev",
        browser:'chrome',
        notify:false 
    });
    gulp.watch('src/scss/*.scss',['scss:dev']);
    gulp.watch(('*.html')).on('change',reload);
    });Copy the code

The result is a simple gulP development process, just a compiled SCSS and a live refresh.

Package all processes online

Package on-line, we are more concerned with static resources against caching, optimization. Compression of CSS, JS, processing of images, I wrote this simple process does not involve the processing of images, so here only for CSS, JS, HTML processing.

We can use gulp-sass to compress CSS because it has a configuration option to output the compressed CSS directly when compiling SCSS. I used gulp-uglify for js compression, gulp-rev and gulp-rev-collector for static resource anti-caching.

Handling of CSS and JS

gulp.task('css',()=> {
    gulp.src('src/scss/*.scss')
        .pipe(sass({
            outputStyle: 'compressed'               
        }))
        .pipe(rev())                                
        .pipe(gulp.dest('dist/css'))
        .pipe(rev.manifest())                       
        .pipe(gulp.dest('rev/css'));
        });
gulp.task('js', ()=> {
    gulp.src('src/js/*js')
        .pipe(uglify())
        .pipe(rev())                                
        .pipe(gulp.dest('dist/js'))
        .pipe(rev.manifest())                       
        .pipe(gulp.dest('rev/js'));
        });Copy the code

Where gulp-rev adds the hash value to the CSS file name, and rev.manifest() generates a JSON file that records a mapping between the original file name and the hashed file name, which will be used when replacing the HTML reference.

The generated JSON file is as follows:

  "index.css": "index-9dcc24fe2e.css"Copy the code

Since hashes are added to the files, the CSS and JS compiled will be different each time. This will result in many redundant files, so we can always empty the original files before generating the files.

Gulp also has a plugin for this, gulp-clean, so we can clean out the text before adding hashes to the compilation.

Clear the generated project files

const clean = require('gulp-clean');                 
gulp.task('clean', ()=> {
    gulp.src(['dist', 'rev'], {read: false}) 
        .pipe(clean());
        });Copy the code

Make hashed files automatically added to HTML

The gulp-rev plugin, gulp-rev-collector, can be used to configure a hashed reference to a file name.

gulp.task('reCollector',()=>{
    gulp.src(['rev/**/*.json','src/*.html'])
        .pipe(reCollector({
            replaceReved: true,  
            dirReplacements: {   
                'css/': '/dist/css/',
                'js/': '/dist/js/'
        }))
        .pipe(gulp.dest('dist'))
        });Copy the code
It’s not replacing normally, right?

Gulp-rename plugin is used to rename the file and add the.min file name to the file. If the file name is gulp-rename, the file name is.min, and the file name is.min. Gulp-rev-collector (gulp-rev-collector) does not have a.min file (gulp-rev-collector); gulp-rev-collector (gulp-rev-collector) does not have a.min file (gulp-rev-collector); Note that the CSS in the JSON file generated by gulp-rev is the same as that referenced in HTML. Otherwise, the replacement cannot be implemented.

Gulp-rev-collector API has a revSuffix, which seems to be similar to gulp-rename, but I don’t know how to use it, please let me know if you know…

Perform all missions

After completing the steps above we string all the tasks together so that they can be executed in one command

gulp.task('build',['clean', 'css', 'js', 'reCollector']);Copy the code

Understand GULP again

Gulp – Are its tasks executed sequentially?

I thought that at this point, even if written, run, perfect, package generated file, run again, error !!!!

[19:04:57] Finished 'default' after 7.38 μs
stream.js:74
      throw er; // Unhandled stream error in pipe.
      Error: ENOENT: no such file or directory, stat 'D:\project\dist\js\index-6045b384e6.min.js'
    at Error (native)Copy the code

Tip I can’t find this file, it makes me very depressed, and then I performed separately, is ok, you can determine the execution order has a problem, is probably behind the didn’t clean finish is executed, examined a gulp of website document just know a gulp of pipe itself is a task, are synchronous, but between each task is out of sync, So I went online to find out how to solve this problem. I found an NPM plugin called run-sequence with the following configuration file:

gulp.task('build', ()=> {
    runSequence('clean', ['css', 'js'], 'reCollector');
    });Copy the code

An error is reported in gulp

Processing of asynchronous tasks by the run-sequence plug-in

After using this plug-in to keep tasks in order, I wanted to further visually see its serialization of tasks, so I wrote a demo, as follows:

gulp.task('a',function(){ setTimeout(function () { console.log(1); }, 30); }); gulp.task('b',function() { console.log(2); }); gulp.task('ab',function(){ runSequence('a','b'); });Copy the code

Asynchronous tasks such as setTimeout, readFile, etc., need to be executed with a callback. The callback() returns a promise’s resolve() that tells future tasks that the current task has completed and can continue, so execute the callback in Task A.

gulp.task('a',function(cb){ setTimeout(function () { console.log(1); cb(); }, 30); });Copy the code

So why didn’t the previous tasks require a callback? Since the gulp pipe stream executes the small tasks in each task (each pipe) sequentially, the pipe stream is synchronous and not asynchronous, so there is no need to manually make it return a promise, and the run-sequence automatically manages it for us.

The separation of gulpfile

Dev and build are stored in gulpfile. This file can be executed, but as the project becomes larger, the maintainability of gulpfile will decrease. Gulp — gulpfile gulpfile.js build gulp — gulpfile gulpfile.js build gulp — gulpfile The former is an abbreviation of the latter, so when running gulp, type the following in the command:

gulp --gulpfile gulpfile-dev.js
gulp --gulpfile gulpfile-build.jsCopy the code

You can specify gulpfile at gulp runtime. In this way, we can run the above command to achieve the desired effect by replacing buil and dev in the original task with default.

But it’s annoying to type such a long command every time. What should I do? We can add the following JSON to the scripts of package.json:

"dev": "gulp --gulpfile gulpfile-dev.js"
"build": "gulp --gulpfile gulpfile-build.js"Copy the code

Thus, at run time, we type directly on the command line:

Can achieve packaging, is not very sour cool, ha ha!

conclusion

At this point, we’re done with a simple gulp based front-end engineering construction, a lot of things, indeed, it is not difficult to think of, it will appear all sorts of unexpected problems, gulp have long known that is a single task in writing, and then use which perform which commands, until I finish this this simple project, I have a deeper understanding of GULP.