Front-end automation with gulp

Target

  • js module packaging
  • sass compilation
  • Sprite
  • css js automatically add MD5 suffix
  • css js files are automatically replaced to html
  • css js html compression
  • Automatically run after file changes
  • Use @@include() to load common html modules like: header bottom

code

Use framework plugins

gulp task flow

  • js package and merge -> compress -> add MD5 stamp -> insert the file that replaces html -> send to the target directory
  • css less compile Sprite image -> add MD5 stamp -> compress -> insert file to replace html -> send to target directory
  • html header top loading -> compression -> target path
  • Copy the image to the target directory

code gulpfile

const gulp = require('gulp');
const clean = require('gulp-clean');
const fileinclude = require('gulp-file-include');
const rev = require('gulp-rev');
const revReplace = require('gulp-rev-replace');
const uglify = require('gulp-uglify');
const cssmin = require('gulp-cssmin');
const spriter = require('gulp-css-spriter');
const plumber = require('gulp-plumber');
const htmlmin = require('gulp-htmlmin');

const sass = require('gulp-sass');
const runSequence = require('run-sequence');
const webpackStream = require('webpack-stream');
const named = require('vinyl-named');
const path = require('path');

const config = {
    'ENV': 'dev',//dev or product
    'public': path.resolve(__dirname, '../public'),     //发布静态资源 css js img 路径
    'view': path.resolve(__dirname, '../views')         //发布'html路径'
};
const webpackConfig = {
    module: {
        loaders: [
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel-loader',
                query: {
                    presets: ['es2015']
                }
            }
        ],
        externals:['$','zepoto']
    }
};

const tempdir = path.resolve(__dirname, 'temp');//临时目录

//清理目标文件夹/文件
gulp.task('clean', function () {
    return gulp.src([config.public, config.view, tempdir])
        .pipe(clean({force: true}));
});

//用于在html文件中直接include文件  并保存到目标路径
gulp.task('fileinclude', function () {
    return gulp.src(['html/*.html'])
        .pipe(plumber())
        .pipe(fileinclude({
            prefix: '@@',
            basepath: '@file'
        }))
        .pipe(gulp.dest(config.view));
});

//将图片拷贝到目标目录
gulp.task('copy:img', function () {
    return gulp.src('img/**/*')
        .pipe(gulp.dest(path.join(config.public, 'img')));
});

//css 压缩 文件名添加MD5
gulp.task('build:css', function () {
    const timestamp = +new Date();
    return gulp.src(['css/*.css', 'css/*.scss'])
        .pipe(plumber())
        .pipe(rev()) //添加MD5
        .pipe(sass().on('error', sass.logError))
        .pipe(spriter({
            includeMode: 'implicit',//explicit:默认不加入雪碧图,,implicit:默认加入雪碧图/* @meta {"spritesheet": {"include": true}} */
            spriteSheet: config.public + '/img/spritesheet' + timestamp + '.png',//img保存路径
            pathToSpriteSheetFromCSS: '../img/spritesheet' + timestamp + '.png'//在css文件中img的路径
        }))
        .pipe(cssmin()) //压缩
        .pipe(gulp.dest(path.join(config.public, 'css')))
        .pipe(rev.manifest())
        .pipe(gulp.dest(path.join(tempdir, 'rev/css')));
});

//js 打包 压缩 混淆 文件名添加MD5
gulp.task('build:js', function () {
    return gulp.src('js/*.js')
        .pipe(plumber())
        .pipe(named())
        .pipe(webpackStream(webpackConfig))
        .pipe(rev())  //添加MD5
        .pipe(uglify()) //压缩 混淆
        .pipe(gulp.dest(path.join(config.public, 'js')))
        .pipe(rev.manifest())
        .pipe(gulp.dest(path.join(tempdir, 'rev/js')));
});

// 将html的css js 引用路径 替换为  修改(增加MD5)后的路径   并压缩
gulp.task("revreplace", function () {
    const manifest = gulp.src(path.join(tempdir, 'rev/**/rev-manifest.json'));
    //noinspection JSUnusedGlobalSymbols
    const revReplaceOptions = {
        manifest: manifest,
        replaceInExtensions: ['.js', '.css', '.html', '.scss'],
        modifyUnreved: (filename) => {
            if (filename.indexOf('.js') > -1) {
                return '../js/' + filename;
            }
            if (filename.indexOf('.scss') > -1) {
                return '../css/' + filename;
            }
        },
        modifyReved: (filename) => {
            if (filename.indexOf('.js') > -1) {
                return '/js/' + filename;
            }
            if (filename.indexOf('.css') > -1) {
                return '/css/' + filename;
            }
        }
    };
    return gulp.src(config.view + '/*.html')
        .pipe(revReplace(revReplaceOptions))
        .pipe(htmlmin({collapseWhitespace: true}))
        .pipe(gulp.dest(config.view));
});


const watchFiles = ['js/**/*.js', 'css/*.css', 'css/*.sass', 'css/**/*.scss', 'html/**/*.html'];

gulp.task('watch', function () {
    gulp.watch(watchFiles, function (event) {
        gulp.start('default', function () {
            console.log('File ' + event.path + ' was ' + event.type + ', build finished');
        });
    });
});


gulp.task('dev', ['default', 'watch']);

gulp.task('default', function (done) {
    runSequence('clean',
        ['fileinclude', 'copy:img', 'build:css', 'build:js'],
        'revreplace',
        done);
});


Note

  • Build tools are just to assist us in development and production. Do not configure these cumbersome frameworks, which increases the complexity of the project and makes it difficult to use and maintain.
  • Third-party libraries are not packaged, use link javascript alone because you can use third-party cdn All pages load a website theme css modular CSS
  • We should always make it clear that we are for the convenience of management, modification, and multi-person cooperation, rather than simple division. If there is any suggestion, I think the modularity of CSS should be as consistent as possible with the modularity of HTML. The consensus here is that whether it is in the division of files or in the division of CSS content, it is consistent with the modularity of HTML.

gulp task

Run tasks serially, i.e., task-dependent

By default, tasks will run concurrently with maximum concurrency - that is, it will start all tasks at the same time without any waiting. If you want to create a sequence of tasks in a specific order, you need to do two things:

  • give it a hint to tell when the task is done,
  • Then, give a prompt to inform that a task needs to depend on the completion of another task.

gulp's task runs cannot be well controlled using run-sequence

gulp file monitoring

When the gulp running task fails, the program will exit. The program is no longer monitored. Use gulp-plumber to prevent css and js from exiting when there is an error.

monitor js css html img

The task default is a copy, compression and packaging of a complete process file. During
monitoring , the corresponding tasks should be executed according to different file changes. This is more efficient
. However, each file change causes other tasks to be executed, or it is simple and difficult to execute a complete task. If the error is not efficient, replace the computer

Create pages automatically

Every page has something in common: there will be a css with the same name as the page at the head and bottom, the js file will load the base.css of a website, base.js, etc. use a nodejs script to complete this.

Command form node new

const readline = require('readline');
const fs = require("fs") ;
let pagename = null;

//创建readline接口实例
const  rl = readline.createInterface({
  input:process.stdin,
  output:process.stdout
});

rl.question("页面名是什么?",function(answer){      
  rl.close();
  pagename = answer;

  if(fs.existsSync(__dirname + `/html/${pagename}.html`)){
    console.log(`页面${pagename}已经存在`);
    process.exit(0);
  }


  let basehtml=`<!DOCTYPE html>
<html>
  <head>
    <title>base page</title>
    <meta charset="utf-8"/>
    <meta name='description' content=''>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <meta name="keywords" content=""/>  
    <meta name="author" content="" />  
    <link rel="icon" href="/favicon.ico" type="image/x-icon"/>
    <link href="//cdn.bootcss.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="../css/base.scss">
    <link rel="stylesheet" type="text/css" href="../css/${pagename}.scss">
  </head>
  <body>
    @@include('./common/head.html')
    <div class='container'>

    </div>
    @@include('./common/foot.html')
    <script type="text/javascript" src='../js/base.js'></script>
    <script type="text/javascript" src='../js/${pagename}.js'></script>
  </body>
</html>`;

  fs.writeFile(__dirname + `/html/${pagename}.html`, basehtml, {flag: 'a'}, function (err) {
     if(err) {
      console.error(err);
      } else {
         console.log(`创建${pagename}.html`);
      }
  });

  fs.writeFile(__dirname + `/css/${pagename}.scss`, '', {flag: 'a'}, function (err) {
     if(err) {
      console.error(err);
      } else {
         console.log(`创建${pagename}.scss`);
      }
  });

  fs.writeFile(__dirname + `/js/${pagename}.js`, '', {flag: 'a'}, function (err) {
     if(err) {
      console.error(err);
      } else {
         console.log(`创建${pagename}.js`);
      }
  });

});


problem dicovered

  1. After using gulp-minify-html, the ejs syntax "<%=aa %>" error: <%= aa=”” %=””>

solve

  1. Change to gulp- htmlmin
  2. Change to gulp -rev-replace and put the rev (add MD5) execution step in the build task in less (before compilation); ensure that the name is still .less and add it to rev-mainfest.json.
    Add configuration items

    modifyUnreved: (filename) => {
    if (filename.indexOf('.js') > -1) {
    return '../js/' + filename;
    }
    if (filename.indexOf('.less') > -1) {
    return '../css/' + filename;
    }
    },
    modifyReved: (filename) => {
    return '/' + filename
    }

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325726229&siteId=291194637