使用 Gulp和Grunt分别完成项目的自动化构建

Study Notes

本博主会持续更新各种前端的技术,如果各位道友喜欢,可以关注、收藏、点赞下本博主的文章。

使用 Gulp 完成项目的自动化构建

使用

gulpfile.js 使用 module.exports 导出了 build、dev、clean,所以我们可以通过 gulp 调用这三个任务

gulp build
gulp dev
gulp clean

详解 gulpfile.js

处理 html 文件

我们需要安装gulp-htmlminswig两个插件

首先我们使用 swig 编译 html,并将数据对象中的变量注入模板,设置不缓存页面;后续我们将使用gulp-htmlmin,对 html 文件进行压缩

const page = () => {
  return src('src/*html', { base: 'src' })
    .pipe(plugins.swig({ data, defaults: { cache: false } })) // 编译html,并将数据对象中的变量注入模板,不缓存
    .pipe(dest('temp'));
};

处理 scss 文件

我们需要安装gulp-sassgulp-clean-css两个插件

首先我们使用 gulp-sass 编译 scss 文件,将 scss 转换为 css;后续我们将使用gulp-clean-css,对 css 文件进行压缩

const style = () => {
  return src('src/assets/styles/*.scss', { base: 'src' })
    .pipe(plugins.sass({ outputStyle: 'expanded' })) // 将scss转换为css
    .pipe(dest('temp'));
};

处理 js 文件

我们需要安装@babel/core@babel/preset-envgulp-babelgulp-uglify四个插件

首先我们使用 @babel/core@babel/preset-envgulp-babel 编译 js 文件,将 es6 转换为 es5;后续我们将使用gulp-uglify,对 js 文件进行压缩

const script = () => {
  return src('src/assets/scripts/*.js', { base: 'src' })
    .pipe(plugins.babel({ presets: [require('@babel/preset-env')] })) // 提供babel将es6转换为es5
    .pipe(dest('temp'));
};

处理 image 文件

我们需要安装gulp-imagemin插件

gulp-imagemin插件内置了几个 git 上的插件,拉取极易失败;可以采取以下方法,将可 install 成功

C:\Windows\System32\drivers\etc\hosts
找到上面路径的文件,进行编辑
添加以下内容:
52.74.223.119     github.com
192.30.253.119    gist.github.com
54.169.195.247    api.github.com
185.199.111.153   assets-cdn.github.com
151.101.76.133    raw.githubusercontent.com
151.101.76.133    gist.githubusercontent.com
151.101.76.133    cloud.githubusercontent.com
151.101.76.133    camo.githubusercontent.com
151.101.76.133    avatars0.githubusercontent.com
151.101.76.133    avatars1.githubusercontent.com
151.101.76.133    avatars2.githubusercontent.com
151.101.76.133    avatars3.githubusercontent.com
151.101.76.133    avatars4.githubusercontent.com
151.101.76.133    avatars5.githubusercontent.com
151.101.76.133    avatars6.githubusercontent.com
151.101.76.133    avatars7.githubusercontent.com
151.101.76.133    avatars8.githubusercontent.com

添加后使用cmd,运行ipconfig/flushdns

我们使用 gulp-imagemin将图片文件进行压缩

const image = () => {
  return src('src/assets/images/**', { base: 'src' })
    .pipe(plugins.imagemin()) // 压缩图片
    .pipe(dest('dist'));
};

处理字体文件

我们使用 gulp-imagemin将字体文件夹中的图片文件进行压缩

const font = () => {
  return src('src/assets/fonts/**', { base: 'src' })
    .pipe(plugins.imagemin()) // 压缩图片
    .pipe(dest('dist'));
};

处理无需编译的文件

将无需编译的文件直接写入到目标目录

const static = () => {
  return src('public/**', { base: 'public' }).pipe(dest('dist')); // 无需编译的文件直接写入到目标目录
};

资源合并

gulp-useref这是一款可以将 HTML 引用的多个 CSS 和 JS 合并起来,减小依赖的文件个数,从而减少浏览器发起的请求次数。gulp-useref 根据注释将 HTML 中需要合并压缩的区块找出来,对区块内的所有文件进行合并。gulp-useref只负责合并,不负责压缩!这里我们使用 gulp-if 插件,来判断文件类型,并压缩对应文件.

const useref = () => {
  return (
    src('temp/*.html', { base: 'temp' })
      .pipe(plugins.useref({ searchPath: ['temp', '.'] }))
      // html js css三种流
      // 压缩js文件
      .pipe(plugins.if('*.js', plugins.uglify()))
      // 压缩css文件
      .pipe(plugins.if('*.css', plugins.cleanCss()))
      // 压缩html文件
      .pipe(
        plugins.if(
          '*.html',
          plugins.htmlmin({
            conservativeCollapse: true,
            collapseWhitespace: true,
            minifyCSS: true,
            minifyJS: true,
          }),
        ),
      )
      .pipe(dest('dist'))
  );
};

浏览器同步测试工具

安装browser-sync

使用browser-sync,我们的任何一次代码保存,浏览器都会同时显示我们的改动,提供我们的开发效率

我们也将使用 gulp 自带的 watch 方法,监听文件变化时,对其进行编译处理

const serve = () => {
  bs.init({
    // browser-sync可以在工作中监听文件。您所做的更改要么被注入到页面(CSS和图像),或将导致所有浏览器做一个完整的页面刷新
    files: ['temp', 'src', 'public'],
    open: false, // browser-sync启动时是否自动打开的网址
    notify: false, // 是否在浏览器中弹出通知
    server: {
      baseDir: ['temp', 'src', 'public'],
      routes: {
        '/node_modules': 'node_modules',
      },
    },
  });

  // 监听文件变化,对其进行编译处理
  watch('src/*.html', page);
  watch('src/assets/styles/*.scss', style);
  watch('src/assets/scripts/*.js', script);
  watch(
    ['src/assets/fonts/**', 'src/assets/images/**', 'public/**'],
    bs.reload,
  );
};

删除目录

使用 del 插件,我们在编译前,将原先编译后的文件目录删除

const clean = () => {
  return del(['temp', 'dist']);
};

完整示例

// 实现这个项目的构建任务
const { src, dest, series, parallel, watch } = require('gulp');
const plugins = require('gulp-load-plugins')();

const del = require('del');

const bs = require('browser-sync');

const data = require('./data');

const page = () => {
  return src('src/*html', { base: 'src' })
    .pipe(plugins.swig({ data, defaults: { cache: false } })) // 编译html,并将数据对象中的变量注入模板,不缓存
    .pipe(dest('temp'));
};

const style = () => {
  return src('src/assets/styles/*.scss', { base: 'src' })
    .pipe(plugins.sass({ outputStyle: 'expanded' })) // 将scss转换为css
    .pipe(dest('temp'));
};

const script = () => {
  return src('src/assets/scripts/*.js', { base: 'src' })
    .pipe(plugins.babel({ presets: [require('@babel/preset-env')] })) // 提供babel将es6转换为es5
    .pipe(dest('temp'));
};

const image = () => {
  return src('src/assets/images/**', { base: 'src' })
    .pipe(plugins.imagemin()) // 压缩图片
    .pipe(dest('dist'));
};

const font = () => {
  return src('src/assets/fonts/**', { base: 'src' })
    .pipe(plugins.imagemin()) // 压缩图片
    .pipe(dest('dist'));
};

const static = () => {
  return src('public/**', { base: 'public' }).pipe(dest('dist')); // 无需编译的文件直接写入到目标目录
};

// 删除目录
const clean = () => {
  return del(['temp', 'dist']);
};

const useref = () => {
  return (
    src('temp/*.html', { base: 'temp' })
      .pipe(plugins.useref({ searchPath: ['temp', '.'] }))
      // html js css三种流
      // 压缩js文件
      .pipe(plugins.if('*.js', plugins.uglify()))
      // 压缩css文件
      .pipe(plugins.if('*.css', plugins.cleanCss()))
      // 压缩html文件
      .pipe(
        plugins.if(
          '*.html',
          plugins.htmlmin({
            conservativeCollapse: true,
            collapseWhitespace: true,
            minifyCSS: true,
            minifyJS: true,
          }),
        ),
      )
      .pipe(dest('dist'))
  );
};

// 浏览器同步测试工具
const serve = () => {
  bs.init({
    files: ['temp', 'src', 'public'],
    open: false,
    notify: false,
    server: {
      baseDir: ['temp', 'src', 'public'],
      routes: {
        '/node_modules': 'node_modules',
      },
    },
  });

  // 监听文件变化,对其进行编译处理
  watch('src/*.html', page);
  watch('src/assets/styles/*.scss', style);
  watch('src/assets/scripts/*.js', script);
  watch(
    ['src/assets/fonts/**', 'src/assets/images/**', 'public/**'],
    bs.reload,
  );
};

// 三个任务编译,互不干扰,所以我们使用异步组合任务
const compile = parallel(page, style, script);
// 这三个任务,我们需要先删除编译后文件目录,再执行编译,最后才执行浏览器同步测试工具,所以我们使用同步组合任务
const dev = series(clean, compile, serve);
// 我们需要先删除编译后文件目录,所以需要用同步组合任务
// 需要先编译后,才能资源合并,所以需要用同步组合任务
// 而image等任务互不干扰,我们使用异步组合任务
const build = series(
  clean,
  parallel(series(compile, useref), image, font, static),
);

module.exports = {
  build,
  dev,
  clean,
};

使用 Grunt完成项目的自动化构建

使用

grunt build
grunt dev
grunt clean
...

详解 gruntfile.js

处理 html 文件

我们需要安装grunt-swigtemplatesgrunt-contrib-htmlmin两个插件

首先我们使用 grunt-swigtemplates 编译 html,并将数据对象中的变量注入模板;后续我们将使用grunt-contrib-htmlmin,对 html 文件进行压缩

swigtemplates: {
    options: {
        defaultContext: require('./data'),
        templatesDir: 'src',
    },
    build: {
        src: ['src/*.html'],
        dest: 'dest/',
    }
},

处理 scss 文件

我们需要安装grunt-sassgrunt-contrib-cssmin两个插件

首先我们使用 grunt-sass 编译 scss 文件,将 scss 转换为 css;后续我们将使用grunt-contrib-cssmin,对 css 文件进行压缩

sass: {
    options: {
        implementation: sass,
        sourceMap: false,
        outputStyle: 'expanded',
    },
    build: {
        expand: true,
        ext: '.css',
        cwd: 'src/assets/styles',
        src: '*.scss',
        dest: 'dest/assets/styles'
    }
},

处理 js 文件

我们需要安装@babel/core@babel/preset-envgrunt-babelgrunt-contrib-uglify四个插件

首先我们使用 @babel/core@babel/preset-envgrunt-babel 编译 js 文件,将 es6 转换为 es5;后续我们将使用grunt-contrib-uglify,对 js 文件进行压缩

babel: {
    options: {
        sourceMap: false,
        presets: ['@babel/preset-env']
    },
    build: {
        expand: true,
        cwd: 'src/assets/scripts',
        src: '*.js',
        dest: 'dest/assets/scripts'
    }
},

处理 image 文件和字体文件

我们需要安装grunt-contrib-imagemin插件

我们使用 grunt-contrib-imagemin将图片文件进行压缩

imagemin: {
    build: {
        expand: true,
        cwd: 'src/assets/images',
        src: '**',
        dest: 'dest/assets/images'
    },
    buildFont: {
        expand: true,
        cwd: 'src/assets/fonts',
        src: '**',
        dest: 'dest/assets/fonts'
    },
},

处理无需编译的文件

我们需要安装grunt-contrib-copy插件

将无需编译的文件直接复制到目标目录

copy: {
    build: {
        expand: true,
        cwd: 'public',
        src: '**',
        dest: 'dest'
    }
},

资源合并

grunt-usemin这是一款可以将 HTML 引用的多个 CSS 和 JS 合并起来,减小依赖的文件个数,从而减少浏览器发起的请求次数。
grunt-usemin 根据注释将 HTML 中需要合并压缩的区块找出来,并改写引用链接。
grunt-usemin不负责合并,不负责压缩!这里我们使用useminPrepare任务,
该任务解析 HTML 标记以查找每个块,并在 type = js 时,为concatuglify任务初始化相应的 Grunt 配置,在 type = css 时,初始化concatcssmin任务。
不需要在initConfig里配置concatuglifycssmin这三个任务

// 资源合并
useminPrepare: {
    html: 'dest/*.html',
    options: {
        dest: 'dest',
        root: ['dest', '.']
    }
},
// 资源合并
usemin: {
    html: 'dest/*.html',
},

浏览器同步测试工具

安装grunt-browser-syncgrunt-contrib-watch

使用grunt-browser-sync,我们的任何一次代码保存,浏览器都会同时显示我们的改动,提供我们的开发效率

我们也将使用 grunt-contrib-watch 任务,监听文件变化时,对其进行编译处理

执行任务时,watch 必须在 browserSync 之后执行

browserSync: {
    build: {
        open: false,
        notify: false,
        bsFiles: {
            src: ['dest', 'src', 'public']
        },
        options: {
            watchTask: true,
            server: {
                baseDir: ['dest', 'src', 'public'],
                routes: {
                    "/node_modules": "node_modules"
                }
            }
        }
    }
},
watch: {
    bulidScss: {
        files: 'src/assets/styles/*.scss',
        tasks: ['sass']
    },
    bulidJs: {
        files: 'src/assets/scripts/*.js',
        tasks: ['babel']
    }
},

删除目录

使用 grunt-contrib-clean 插件,我们在编译前,将原先编译后的文件目录删除

 clean: {
    build: {
        src: ['dest', '.tmp']
    }
},

完整示例

module.exports = (grunt) => {
  // 在这里写与grunt有关的功能
  const sass = require('node-sass');

  require('load-grunt-tasks')(grunt);
  // 项目配置
  grunt.initConfig({
    // 删除目录
    clean: {
      build: {
        src: ['dest', '.tmp'],
      },
    },
    // 将无需编译的文件直接复制到目标目录
    copy: {
      build: {
        expand: true,
        cwd: 'public',
        src: '**',
        dest: 'dest',
      },
    },
    // 将es6转换为es5
    babel: {
      options: {
        sourceMap: false,
        presets: ['@babel/preset-env'],
      },
      build: {
        expand: true,
        cwd: 'src/assets/scripts',
        src: '*.js',
        dest: 'dest/assets/scripts',
      },
    },
    // 将scss转换为css
    sass: {
      options: {
        implementation: sass,
        sourceMap: false,
        outputStyle: 'expanded',
      },
      build: {
        expand: true,
        ext: '.css',
        cwd: 'src/assets/styles',
        src: '*.scss',
        dest: 'dest/assets/styles',
      },
    },
    // 编译html,并将数据对象中的变量注入模板
    swigtemplates: {
      options: {
        defaultContext: require('./data'),
        templatesDir: 'src',
      },
      build: {
        src: ['src/*.html'],
        dest: 'dest/',
      },
    },
    // 压缩图片
    imagemin: {
      build: {
        expand: true,
        cwd: 'src/assets/images',
        src: '**',
        dest: 'dest/assets/images',
      },
      buildFont: {
        expand: true,
        cwd: 'src/assets/fonts',
        src: '**',
        dest: 'dest/assets/fonts',
      },
    },
    // 资源合并
    useminPrepare: {
      html: 'dest/*.html',
      options: {
        dest: 'dest',
        root: ['dest', '.'],
      },
    },
    // 资源合并
    usemin: {
      html: 'dest/*.html',
    },
    htmlmin: {
      options: {
        removeComments: true,
        collapseWhitespace: true,
        minifyCSS: true,
        minifyJS: true,
      },
      build: {
        expand: true,
        cwd: 'dest',
        src: '*.html',
        dest: 'dest',
      },
    },
    // 监听文件变化,对其进行编译处理
    watch: {
      bulidScss: {
        files: 'src/assets/styles/*.scss',
        tasks: ['sass'],
      },
      bulidJs: {
        files: 'src/assets/scripts/*.js',
        tasks: ['babel'],
      },
    },
    // 浏览器同步测试工具
    browserSync: {
      build: {
        open: false,
        notify: false,
        bsFiles: {
          src: ['dest', 'src', 'public'],
        },
        options: {
          watchTask: true,
          server: {
            baseDir: ['dest', 'src', 'public'],
            routes: {
              '/node_modules': 'node_modules',
            },
          },
        },
      },
    },
  });

  // 三个任务编译,互不干扰,不分前后顺序
  grunt.registerTask('compile', ['babel', 'swigtemplates', 'sass']);

  // 'copy', 'compile', 'imagemin',这三个任务执行,互不干扰,不分前后顺序
  // 必须先执行'compile'任务后,依次执行'useminPrepare'、'concat'、'cssmin'、 'uglify'、 'usemin'、 'htmlmin'任务
  grunt.registerTask('build', [
    'clean',
    'copy',
    'compile',
    'imagemin',
    'useminPrepare',
    'concat',
    'cssmin',
    'uglify',
    'usemin',
    'htmlmin',
  ]);
  // 必须先执行'compile'任务后,才能'browserSync'任务
  // 切记'watch'不能在'browserSync'任务之前执行
  grunt.registerTask('dev', ['clean', 'compile', 'browserSync', 'watch']);
};
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352