I wanted to write about front-end automation tools for several reasons:

  1. The importance of automated build tools for front-end development: efficiency, reduced repetitive operations, and a variety of powerful plugins.

  2. There is a certain cost to getting started with the build tools, and there are also a lot of drawbacks. In addition to mastering HTML/JS/CSS, you also need to understand Node.js, NPM package manager, the configuration of the build tools, syntax sugar and the use of plug-ins. Learn how to organize your code elegantly and efficiently as your build tools grow in complexity and complexity, reducing the likelihood of bugs when using them.

  3. In my work, I encounter some common examples and strange skills related to Grunt, which can be used to play and interpret, which is helpful for me to get started more quickly and customize a powerful automatic working mode.

  4. Competent build tools like gulp, Webpack (which is technically a module management tool, but can still do some of the building work), and even the NPM scripts from Grunt and gulp have their own advantages and have refreshed my understanding of build tools. In my opinion, instead of arguing about which one is better, it’s better to use a tool library that you feel comfortable with and that fits the needs of your project better.

Automated build tool – Grunt

Here are some of the pain points of writing front-end code before these tools were created:

  • “CSS is so laborious to write. Can those reusable styles be stored in a variable or function and called directly?”

  • “Also remember to prefix your styles with browser compatibility, Ctrl +C/V.”

  • “After changing the code, I have to press F5 to refresh the browser every time. If I want to debug multiple devices, I have to refresh each device manually. I feel tired to think about it.”

  • “After the code is written, we have to borrow tools to merge it manually, compress it, and then copy it into the product catalog. We have to repeat the operation every time we release it…”

  • “Great, the combined code now has only one script tag on the page, greatly reducing the number of requests, but also introducing code that would only be used on other pages. Can we split it into their respective page views?”

  • etc…

There are too many pain points to enumerate. For small projects, it doesn’t hurt to do so manually, but when it comes to large and medium-sized projects, I can’t imagine that I still do it by hand. In order to make the front-end work less boring, all kinds of heroes have taken measures. Under the shine of Node, JS construction tools emerge as The Times require, and gradually become an indispensable part of the front-end ecology. Automated build tools are meant to let you say no to the repetitive, boring work of writing front-end code.

About Grunt

After all that gossip, let’s introduce today’s main character. Grunt. (To be honest, what I thought of at the first sight of this word was the big G of my orc clan in Warcraft.) Why do I choose Grunt as the first choice of construction tool?

The Grunt ecosystem is huge and growing all the time. With a large number of plugins to choose from, you can automate anything with Grunt and do it with minimal cost. If you can’t find what you need, create your own Grunt plugin and publish it to NPM. —- from Grunt

To date, Grunt has over 5,500 plugins. Owning these plugins is like owning a Swiss Army Knife. To do the right thing, you need the right tools. Without going into details here, please refer to the Quick Start guide on our website. Let’s take a look at how Grunt sharpened our weapons in Top100.

Grunt’s basic kit

Grunt’s own tools :(officially maintained by Grunt plugins)

Package name instructions
contrib-watch Monitor file changes, and you can specify the tasks to be performed when the changes occur
contrib-clean Clear the files in the specified directory
contrib-jshint JS grammar specification prompts, can be written into the specification configuration file, does not conform to the specification of the code to be prompted
contrib-copy Copy the file to the specified directory
contrib-uglify Compress the specified JS code
contrib-concat Merges the specified JavaScript or CSS code
contrib-cssmin Compress the specified CSS code
contrib-less Compile the LESS file to CSS
contrib-htmlmin Compress the specified HTML code
contrib-imagemin Compress the specified image

Home essential artifact :(commonly used third-party plugins, better with official plugins)

Package name instructions
postcss CSS preprocessor tool, can achieve less or SCSS or stylus CSS preprocessor effect, can also use its powerful auto-prefix plug-in to automatically add compatibility browser manufacturer prefix for CSS code
babel ES6 syntax to ES5 JS converter
sync Similar to contrib-copy, but only copy those files that have been changed
webpack Powerful module management tool, with its featured loader function allows you to import almost any type of file into your JS code
jsdoc A Grunt plug-in that automatically generates documentation by writing comments that follow a well-established syntactic format
sails-linker Automatically inserts a CSS or JS (one or more) file into the specified location on the page
assets-linker Similar to Sails-Linker, but with a more concise configuration syntax
browser-sync A lightweight HTTP development server that supports simultaneous testing and debugging across multiple devices
time-grunt We can intuitively see the time consuming of each Grunt task, which can effectively optimize the build tool
grunt-cdn Specify the CDN path to add CSS, JS resource CDN path
load-grunt-configs Registrated Grunt tasks can be split into separate files to make it easier to organize and manage tasks when the number of tasks is large
load-grunt-tasks Automatically load tasks into Grunt. LoadNPMTasks, saving code

The use of Grunt buckets

Json file, use the Grunt plugin, and register a Grunt task. If you are still not sure, please click on the official description. Following on from the dozen or so common Grunt plugins I’ve described above, I want to go through the two project modes (development and production) in detail, but before I do that, it’s worth starting with a basic project example that looks something like this:

├ ─ ─ your project │ ├ ─ ─ Gruntfile. Js │ ├ ─ ─ package. The json │ ├ ─ ─ grunt │ │ ├ ─ ─ watch. Js │ │ ├ ─ ─ the clean, js │ │ ├ ─ ─... │ ├ ─ ─ assets │ │ ├ ─ ─ js │ │ │ ├ ─ ─ index. The js │ │ │ ├ ─ ─... │ │ │ ├─ less │ │ ├─ Index. Less │ │ ├─ ├─ Bass Exercises -... │ ├ ─ ─ WWW │ │ ├ ─ ─ js │ │ │ ├ ─ ─ index. The js │ │ │ ├ ─ ─... │ │ │ ├─ CSS │ │ ├─ Index. CSS │ │ ├─ Bass Exercises -... │ ├ ─ ─ build | | ├ ─ ─ min │ │ | ├ ─ ─ js │ │ │ | ├ ─ ─ index. The js │ │ │ | ├ ─ ─... │ │ | ├ ─ ─ CSS │ │ │ | ├ ─ ─ index. The CSS │ │ │ | ├ ─ ─...

In the gruntfile.js file, the configuration of the plugin and the loading of the task are written in a manner similar to this:

module.exports = function(grunt) { // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, build: { src: 'src/<%= pkg.name %>.js', dest: 'build/<%= pkg.name %>.min.js' } } }); // Load the plug-in that contains the "uglify" task. grunt.loadNpmTasks('grunt-contrib-uglify'); // The list of tasks performed by default. grunt.registerTask('default', ['uglify']); };

It’s just a plug-in that introduces a task (uglify). Imagine if you had dozens of plugins writing, gruntfile.js wouldn’t look so good. In order to split each plugin into different files and manage them separately, load-grunt-configs and load-grunt-tasks plug-ins are introduced, which can split the grunt tasks into separate files and automatically load the corresponding grunt tasks. With their help, the amount of code will be greatly reduced and the maintainability of Grunt tasks will be greatly improved. In contrast to the official writing, the current code could be as elegant as this:

module.exports = fucntion(grunt){
    var options = {
        config : {
            src: "grunt/*.js"
        }
    };

    var configs = require('load-grunt-configs')(grunt, options);
    grunt.initConfig(configs);
    
    // Load grunt tasks automatically
    require('load-grunt-tasks')(grunt);
}

Write each Grunt task to a separate JS file. Take Watch Task as an example, like this:

module.exports.tasks = {
    watch: {
        js: {
            files: [
                'assets/js/**/*.js',
                'routes/**/*.js'
            ],
            tasks: ['copy:dev'],
            options: {
                livereload: true
            }
        },
        less: {
            files: ['assets/styles/**/*.less'],
            tasks: ['less:dev', 'postcss'],
            options: {
                livereload: true
            }
        },
        view: {
            files: ['templates/**/*'],
            options: {
                livereload: true
            }
        }
    }
};

By placing these files in the grunt directory and specifying the location of the grunt directory in the load-grunt-configs options configuration, you can easily write the grunt tasks to separate files. With load-grunt-tasks, we only need one line of code:

// Load grunt tasks automatically
require('load-grunt-tasks')(grunt);

I can replace the following n rows!

grunt.loadNpmTasks('grunt-shell'); grunt.loadNpmTasks('grunt-sass'); grunt.loadNpmTasks('grunt-recess'); grunt.loadNpmTasks('grunt-sizediff'); grunt.loadNpmTasks('grunt-svgmin'); grunt.loadNpmTasks('grunt-styl'); grunt.loadNpmTasks('grunt-php'); grunt.loadNpmTasks('grunt-eslint'); grunt.loadNpmTasks('grunt-concurrent'); grunt.loadNpmTasks('grunt-bower-requirejs'); .

Another plugin that can be used with GruntFile.js is Time-Grunt, which outputs the time of each Grunt task in a very intuitive way so that you can optimize the build time for a particular task, as shown below:

Use Grunt in development mode

Grunt tasks in development mode mainly include source code precompilation, code modification, code specification check, code tag automatic injection, etc. These are like equipped with an all-around Swiss Army Knif experience, which can completely solve the pain points mentioned above. Combined with Grunt family, Here’s how to configure a set of automated processes for development:

First of all, if you go back to the previous project directory, you can see that there are three directories containing similar files: Assets, WWW and Build.

  • Assets are used to hold the source code of the project’s front-end code

  • The WWW contains compiled, modified code and static resources that are directly accessible to Web pages on the local debug server

  • Build contains all the packaged code and resources in production mode to be placed under the CDN service

The reason for this division is to make the responsibilities and division of labor of Grunt more clear, and also facilitate the easy switching and management of the two modes.

To get to the point, here is a list of tasks commonly used in development mode:

[
    'clean',
    'less',
    'postcss',
    'jshint',
    'copy',
    'asset-linker',
    'browserSync',
    'watch'
]

The above tasks translated into natural language are:

  • First, clear the target directory. Make sure that the next time you execute the grunt task, clear the files generated by the last task and write them into a clean directory

  • Compile LESS (CSS precompiled language, here also SCSS, stylus) into CSS

  • Modify compiled CSS (e.g., simplified CSS, CSS with compatibility prefixes via auto-prefix)

  • Check the standard of JS code, whether there is a mistake in writing, whether it is enough standard

  • Copy the processed code to the target directory (for example, WWW, build)

  • Automatically add link and script tags to HTML or template files

  • Detects the files in the specified directory. If there is any modification, the browser will be refreshed automatically. The modification effect is what you see is what you get

Next, we just need to register the list of tasks with the Grunt Dev directive and execute each task in the order in which it is arranged:

grunt.registerTask('dev', [
    'clean:dev',
    'less',
    'postcss',
    'jshint',
    'copy:dev',
    'asset-linker:linkCssDev',
    'asset-linker:linkJsDev',
    'browserSync',
    'watch'
]);

PS: Most grunt tasks are multithreading. You can run multiple subtasks at the same time. You can also run a single subtask, such as 'clean:dev'. Therefore, this can be divided into dev and build based on the environment

To get a better sense of the subtasks of the Grunt task, we can use an example:

module.exports.tasks = {
    clean: {
        dev: ['www'],
        build: ['build-res']
    }
};

Note: When we type “grunt clean” in Terminal, all subtasks under “clean” will be executed by default: dev and build

In the example above, with the task cluster registered via RegisterTask, we simply type Grunt Dev at the terminal and let the tool take care of the rest

Use Grunt in production mode

In my opinion, the product model is much more rigorous and streamlined than the development model. The development model emphasizes that developers can quickly debug and track their code and the WYSIWYG effects of code changes, in order to more efficiently and conveniently complete the development and testing of feature points. Production mode requires that the code in development mode be less error-prone, smaller in size (file size), and more suitable for network transmission. It also requires that the code should be included in the version number of each release to ensure a smooth transition of version updates. Here’s a step-by-step guide to getting Grunt to do this for us:

Start with a copy of the Tasks List in the product mode:

grunt.registerTask('build', [
    'clean:dev',
    'less',
    'postcss',
    'jshint',
    'copy:dev',
    'asset-linker:linkCssDev',
    'asset-linker:linkJsDev',
    'cdn',
    'concat',
    'uglify',
    'cssmin',
    'asset-linker:linkCssProd',
    'asset-linker:linkJsProd',
    'clean:build',
    'copy:build'
    ]);

This list includes all tasks in development mode. To do this, we can register the shared tasks in a separate compileAssets:

grunt.registerTask('compileAssets', [
    'clean:dev',
    'less',
    'postcss',
    'jshint',
    'copy:dev',
    'asset-linker:linkCssDev',
    'asset-linker:linkJsDev'
]);

grunt.registerTask('dev', [
    'compileAssets',
    'browserSync',
    'watch'
]);

grunt.registerTask('build', [
    'compileAssets',
    'cdn',
    'concat',
    'uglify',
    'cssmin',
    'asset-linker:linkCssProd',
    'asset-linker:linkJsProd',
    'clean:build',
    'copy:build'
]);
Add version number

As is known to all, package.json in each project has a version field to indicate the version number of the project, and all we need to do is add this version number to the relevant task:

Related tasks

  • cdn

  • asset-linker

  • copy

Regarding the position of the added version number, we can add the version number to the end of the file, such as the index, 1.0.0. Js, but think carefully, the release, in order to ensure that the old and new version of the file can keep to online at the same time, will appear a folder has a lot of a file with the version number (when you keep the version number of the more). This is obviously not convenient to organize, so the wisest choice is to put the version number in the root directory, such as http://your-web-site/1.0.1/index.js, so that a version is a directory, both beautiful and convenient version management, if you want to delete one of the versions, as long as the whole directory can be removed.

Gulp and NPM scripts

This article was originally intended to be an introduction to Grunt, but since we are all automated build tools, we have to bring them out for discussion. Not long ago, I read a translation of “Why I gave up Gulp and Grunt and switched to NPM Scripts”. It was an eye-opening experience, and I was enlightened. Now that I’ve written so far, let me briefly introduce both of them

gulp

My biggest experience with Gulp is:

  • The configuration code is cleaner and more intuitive

  • The Node.js Streams stream works in a way that makes it faster to process tasks

Gulp allows you to feed source files into a pipe and configure a series of plugins to process the files in the pipe before exporting them to the target location. Just like a factory pipeline, gulp directly takes the output completed by the previous assembly line task as the input of the next assembly line task. This means that compared with grunt, we don’t need to specify the input and output of this task in each grunt task, which saves a lot of code. No amount of verbosity can beat a stark example:

Grunt

sass: { dist: { options: { style: 'expanded' }, files: { 'dist/assets/css/main.css': 'src/styles/main.scss', } } }, autoprefixer: { dist: { options: { browsers: [' Last 2 version', 'Safari 5',' IE 8', 'IE 9',' Opera 12.1', 'iOS 6',' Android 4']}, SRC: 'dist/assets/css/main.css', dest: 'dist/assets/css/main.css' } }, grunt.registerTask('styles', ['sass', 'autoprefixer']);

Let’s see how the same configuration is implemented in Gulp:

Gulp

gulp.task('sass', function() {
  return sass('src/styles/main.scss', { style: 'expanded' })
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(gulp.dest('dist/assets/css'))
});

Have a sense of a bright! This will make a lot of old Grunt players decide to jump into gulp. For more information on configuration and how to get started with Gulp, see this excellent introductory article, and the code examples above are also referenced from this article: Getting Started with Gulp

npm scripts

To be honest, after reading the article “Why I gave up Gulp and Grunt for NPM Scripts”, the most vivid feeling for me was that I heard a great god say, “I only use Vim for the editor, please let me admire three times.” Of course, overall, NPM scripts are very good and powerful, and cost a lot to develop. As mentioned in the article, you may need to learn some command line instructions and operations to use NPM scipts, which is more like a game for advanced gamers. Here are some of the advantages mentioned in the article:

The NPM scripts themselves are actually quite powerful. It provides convention-based pre and post hooks:

{name: "NPM script-example ", version: "1.0.0", description:" NPM scripts example", scripts: {prebuild: "echo I run before the build script", build: "cross-env NODE_ENV=production webpack", postbuild: "echo I run after the build script" } }

In addition, you can break down big problems by calling another script in a script:

{"name": "NPM script-example ", "version": "1.0.0", "description":" NPM scripts example", "scripts": {"clean": "rimraf ./dist && mkdir dist", "prebuild": "npm run clean", "build": "cross-env NODE_ENV=production webpack" } }

If a command is complex, you can also call a separate file:

{"name": "NPM script-example ", "version": "1.0.0", "description":" NPM scripts example", "scripts": {"build": "node build.js" } }

conclusion

Learning to use the right tools to solve problems is a great way to be happy and make work more fun and playable. The three kinds of automatic construction tools mentioned in the paper are basically essential to the front-end engineering work in addition to JS, CSS, HTML work skills. Grunt is backed by a large number of plug-ins that can be combined to support more complex builds. Gulp is smaller, more beautiful, faster, less is more, and gets a lot of thumb ups on GitHub (much more than Grunt!). , is a rising star. However, NPM scripts are free from a layer of unnecessary abstraction and do not need to rely on the maintenance of plugin authors like Grunt and Gulp. Most of the construction work can be completed directly through the instructions of NPM, which provides a new way of thinking for the automated build process and has a sense of getting back to basics. So, when it comes to choosing a tool that will be the focus of your work, stick with the one that works for you