前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从零开始构建你的 Gulp

从零开始构建你的 Gulp

作者头像
Nian糕
修改2024-03-19 14:22:54
1K0
修改2024-03-19 14:22:54
举报
Unsplash
Unsplash

本篇博文的内容根据 Introduction to Gulp.js 系列文章 拓展而来,其代码、依赖包及目录结构部分均有所更改,更多详细内容,敬请参考原文及作者 Github

我们在上一篇博文 Gulp 前端自动化构建工具 中,已经对 Gulp 有了初步的了解,我们通过将所有任务写到 gulpfile.js 文件中进行编译,这当然是最直观的方法,但当我们需要执行的任务过多时,gulpfile.js 文件就会变的特别的巨大,这很不利于我们之后的维护及修改,所以我们要做的第一件事就是将 gulpfile.js 文件进行分割,分成一个个小的任务文件,每一个文件只完成特定的任务,这也是我们常说的模块化处理,每一任务文件不与其他文件产生直接交互,并通过赋值的方式在文件内部调用全局变量,下图是我们整个项目的目录结构,在文章的接下来部分,将会给大家详细讲解

文件目录结构
文件目录结构

文件结构

我们先来简单介绍下我们的文件目录结构,node_modules 文件夹下为依赖包,gulp 文件夹下为任务文件,src 文件夹下为项目的引用文件,该目录下的文件均为测试文件,各位童鞋可根据自身需求进行修改替换,build 文件夹为 gulp 过后的生产文件

因为 package.json 文件里所罗列的依赖包太多,在这里就不再具体展示,童鞋们可先自行下载 package.json 文件,运行 npm install 命令进行项目依赖包的下载,亦可通过下载整个项目进行学习,需要注意的是,插件的更新或是依赖包的缺少都可能导致项目无法正常运行,可根据报错信息进行依赖包的更新或修改

gulpfile.js 文件非常的短,只有短短两行,我们通过 require-dir 依赖包的作用,将 ./gulp/tasks 目录下的所有任务载入

代码语言:javascript
复制
// gulpfile.js
const requireDir = require('require-dir'); 
requireDir('./gulp/tasks', { recurse: true });
gulp文件夹
gulp文件夹

根据上图我们可以看到,gulp 文件夹下有一个 config.js 文件,主要是各任务的路径匹配及文件配置,具体如下图所示

config.js
config.js

default 默认任务

当我们运行 gulp 命令时,Gulp 将会执行 default 默认任务,而该任务具体代码如下所示:

代码语言:javascript
复制
// default.js
const gulp = require('gulp');
gulp.task('default', ['watch']);

可以看到 default 任务并没有执行任何操作,但执行 defalut 任务前,我们需要先执行 watch 任务,我们再来看看 watch 任务里的具体代码

代码语言:javascript
复制
// watch.js
const gulp   = require('gulp'),
      config = require('../../config').watch;

gulp.task('watch', ['browsersync'], () => {
    gulp.watch(config.styles, ['styles', 'lint-styles']);
    gulp.watch(config.scripts, ['scripts', 'jshint']);
    gulp.watch(config.images, ['optimize-images']);
    gulp.watch(config.sprites, ['sprites']);
})
运行结果
运行结果

可以看到,watch 任务监听了四个文件路径下的文件更改,涉及到了 9 个任务的运行,并没有涵盖我们定义的所有任务,这是因为这 9 个任务已经满足了我们日常的开发需求,至于其他任务,可以通过运行指定任务名来完成相应的操作,当然,各位童鞋也可以根据自身需求来对 watch 文件进行更改,在这里只是提供一个示例方法

CSS 依赖包

接下来我将根据作用的文件类型不同,来对所引入的依赖包来作简单的介绍,而关于各插件的更多用配置及用法,还请查看相应插件的 Github 主页

代码语言:javascript
复制
// lint-styles.js
const gulp      = require('gulp'),
      postcss   = require('gulp-postcss'),
      stylelint = require('stylelint'),
      reporter  = require('postcss-reporter'),
      config    = require('../../config');

gulp.task('lint-styles', () => {
    return gulp.src(config.lintStyles.src)
               .pipe(postcss([
                   stylelint(config.lintStyles.options.stylelint),
                   reporter(config.lintStyles.options.reporter)
               ]))
})

可以看到,我们除了引入 stylelintpostcss-reporter 这两个插件之外,还引入了 gulp-postcss 这一插件集合,在这里想要跟大家介绍的是,PostCSS 是一个使用 JS 解析样式的插件集合,它可以用来审查 CSS 代码,也可以增强 CSS 的语法(比如变量和混合宏),还支持未来的 CSS 语法、行内图片等等,而本文所使用到的大部分 CSS 插件,均是来自 PostCSS,关于更多的 PostCSS 的介绍,而通过 w3cplusPostCSS 深入学习系列文章 进行学习

stylelint 是一个代码审查插件,除了审查 CSS 语法外,还能审查类 CSS 语法,帮助我们审查出重复的 CSS 样式、不规范的代码、无效颜色值、无意义的浏览器前缀以及我们所配置的一些审查规则,我们可以根据自身项目的需求来设置不同的规则

config.js
config.js

rules 使用 0, 1, 2 来代表规则启用状态不同,具体的规则可在 Rules.md 中查找,当然,如果你觉得手动配置规则太麻烦,也可以直接使用 stylelint 官方的配置文档

代码语言:javascript
复制
"extends": "stylelint-config-standard"

审查完之后,我们通过 postcss-reporter 插件在控制台记录 PostCSS 的消息

postcss-reporter
postcss-reporter
config.js
config.js

我们在 CSS 样式这部分引入了大量的 PostCSS 插件,各插件的部分功能如下所示,demo 运行效果就不在这里详细展示,童鞋们可在文章末尾下载项目代码运行测试即可

  • autoprefixer 处理浏览器私有前缀
  • cssnext 使用 CSS 未来的语法
  • precss 预处理插件包,可实现像 Less、Sass 预处理器的功能
  • postcss-color-rgba-fallbackrgba() 颜色添加一个十六进制的颜色作为降级处理,在 IE8 中是不支持 rgba() 颜色的
  • postcss-opacity 给 IE 浏览器添加滤镜属性,IE8 不支持 opacity 属性
  • postcss-pseudoelements 将伪元素的 :: 转换为 :
  • postcss-vmin 使用 vmvmin 做降级处理,IE9+
  • pixremrem 添加 px 作为降级处理,IE8+
  • postcss-import 使用 @import 合并样式表
  • cssnano 删除空格和最后一个分号,删除注释,优化字体权重,丢弃重复的样式规则,优化 calc(),压缩选择器,减少手写属性,合并规则
  • postcss-font-magician 使用自定义字体
代码语言:javascript
复制
// styles.js
const gulp              = require('gulp'),
      postcss           = require('gulp-postcss'),
      autoprefixer      = require('autoprefixer'),
      cssnext           = require('cssnext'),
      precss            = require('precss'),
      colorRgbaFallback = require('postcss-color-rgba-fallback')
      opacity           = require('postcss-opacity'),
      pseudoelements    = require('postcss-pseudoelements'),
      vmin              = require('postcss-vmin'),
      pixrem            = require('pixrem'),
      atImport          = require('postcss-import'),
      mqpacker          = require('css-mqpacker'),
      cssnano           = require('cssnano'),
      fontMagician      = require('postcss-font-magician'),
      config            = require('../../config').styles;

gulp.task('styles',() => {
    const processors = [
        autoprefixer,
        cssnext,
        precss,
        colorRgbaFallback,
        opacity,
        pseudoelements,
        vmin,
        pixrem,
        atImport,
        mqpacker,
        cssnano,
        fontMagician
    ];
    return gulp.src(config.src)
               .pipe(postcss(processors))
               .pipe(gulp.dest(config.dest));
});
config.js
config.js

我们之前介绍过 Less 在 Gulp 的用法,这里再贴一下 Sass 的部分,相对于直接将 Sass 转换成 CSS,我们还加入了 PostCSS 的一些插件

代码语言:javascript
复制
// sass.js
const gulp         = require('gulp'),
      postcss      = require('gulp-postcss'),
      sass         = require('gulp-sass'),
      autoprefixer = require('autoprefixer'),
      cssnano      = require('cssnano'),
      config       = require('../../config').sass;

gulp.task('sass',() => {
    const processors = [
        autoprefixer,
        cssnano
    ];
    return gulp.src(config.src)
               .pipe(sass().on('error',sass.logError))
               .pipe(postcss(processors))
               .pipe(gulp.dest(config.dest))
});

images 依赖包

gulp-base64 插件,能够把一些小的 icon 转换成 base64 编码,因为图片转换后会比原尺寸大 30% 左右,所以不推荐将尺寸较大的图片进行 base64 编码转换

代码语言:javascript
复制
// base64.js
const gulp    = require('gulp'),
      base64  = require('gulp-base64'),
      config  = require('../../config').base64;

gulp.task('base64', ['styles'], () => {
    return gulp.src(config.src)
               .pipe(base64(config.options))
               .pipe(gulp.dest(config.dest));
});

在这里,srcdest 路径相同的意义在于,我们将经过审查编译压缩过后的代码进行编码,而不会影响之前已执行的操作,若是任务执行的顺序相反,则会导致编码过后的文件无法执行后续的操作,同样的,在 build.js 中,我们也是先执行其他任务,最后才执行 base64 任务

config.js
config.js
build.js
build.js

imagemin 插件,将目录下的所有 jpg ,png 格式的图片进行压缩,我们还利用了 gulp-cache 插件,该插件的作用是代理 Gulp 的缓存,所以我们通过利用缓存,保存已经压缩过的图片,以保证只有新建或者修改过的图片才会被压缩,最后通过 gulp-size 显示压缩过后的图片大小

代码语言:javascript
复制
// optimize-images.js
const gulp     = require('gulp'),
      imagemin = require('gulp-imagemin'),
      cache    = require('gulp-cache'),
      size     = require('gulp-size'),
      config   = require('../../config').optimize.images;

gulp.task('optimize-images', () => {
    return gulp.src(config.src)
               .pipe(cache(imagemin(config.options)))
               .pipe(gulp.dest(config.dest))
               .pipe(size())
})
config.js
config.js
运行结果
运行结果

细心的童鞋可能发现了,在 production 目录下有 4 个 optimize.js 文件,分别是对应 HTML CSS JS Images 文件,尽管我们建立这些任务,但在项目中并没有全都使用到,这里只是给大家多一种选择方式

production 目录
production 目录

生成精灵图的插件有很多,我们在这里选择的是 sprity 插件,反正我折腾了这么多个插件之后,这一个是最友好的,我是在 Windows 7 环境下进行测试的,不管你使用的是哪个精灵图生成插件,都必须要安装图片引擎,我们在这里安装的是 sprity-gm 图片引擎,同时还需要下载安装 GraphicsMagick 和 Imagemagick 引擎,安装成功之后,电脑重启,下载地址请戳 >>> 图片引擎 | Download

若是在 Windows 10 环境下,只需安装 sprity-lwip 图片引擎即可,Mac 环境下没有测试过

代码语言:javascript
复制
// sprites.js
const gulp   = require('gulp'),
      gulpif = require('gulp-if'),
      sprity = require('sprity'),
      config = require('../../config').sprites;

gulp.task('sprites',() => {
      return sprity.src(config.options)
                   .pipe(gulpif('*.png', gulp.dest(config.dest.image), gulp.dest(config.dest.css)))
})
config.js
config.js
sprites_1
sprites_1
sprites_2
sprites_2
sprites.css
sprites.css

JS 依赖包

在 CSS 部分我们使用到了 stylelint 代码审查插件,而在 JS 部分也有类似的代码审查插件 gulp-jshint,需要注意的是,gulp-jshintjshnt 要一起下载安装,其他一些插件也有类似的要求,具体以 Github 主页为准,JS 代码审查完成之后,通过 jshint-stylish 插件指定一个外部报告器

代码语言:javascript
复制
const gulp    = require('gulp'),
      jshint  = require('gulp-jshint'),
      stylish = require('jshint-stylish'),
      config  = require('../../config').jshint;

gulp.task('jshint', () => {
    return gulp.src(config.src)
               .pipe(jshint())
               .pipe(jshint.reporter(stylish))
})
config.js
config.js
运行结果
运行结果

通过引入 browserify 插件,使得我们可以在浏览器中加载 Node.js 模块,而 watchify 插件可以加速 browserify 的编译,而 vinyl-source-stream 把普通的 Node Stream 转换为 Vinyl File Object Stream,我们在之前的文章有提到过,Gulp 使用的 Stream 并不是普通的 Node Stream,而是一种名为 Vinyl File Object Stream 的虚拟文件格式,主要包含了路径 path 及内容 contents 两个属性,此外,我们还引入了 bundleLogger.jshandleErrors.js 两个文件,处理错误信息及记录绑定的过程,而 browserify-shim 插件则是能够帮助我们加载类似 jQuery 或 Modernizr 的非 CommonJS 文件

代码语言:javascript
复制
// script.js
const gulp         = require('gulp'),
      browsersync  = require('browser-sync'),
      browserify   = require('browserify'),
      source       = require('vinyl-source-stream'),
      watchify     = require('watchify'),
      bundleLogger = require('../../util/bundleLogger'),
      handleErrors = require('../../util/handleErrors'),
      config       = require('../../config').browserify;

gulp.task('scripts', callback => {
    browsersync.notify('Compiling JavaScript');
    var bundleQueue = config.bundleConfigs.length;
    var browserifyThis = bundleConfig => {
        var bundler = browserify({
            cache: {}, packageCache: {}, fullPaths: false,
            entries: bundleConfig.entries,
            // Add file extentions to make optional in your requires
            extensions: config.extensions,
            debug: config.debug
        })

        var bundle = () => {
            bundleLogger.start(bundleConfig.outputName);
            return bundler
                  .bundle()
                  .on('error', handleErrors)
                  .pipe(source(bundleConfig.outputName))
                  .pipe(gulp.dest(bundleConfig.dest))
                  .on('finish', reportFinished)

        }
        if(global.isWatching) {
            bundler = watchify(bundler);
            bundler.on('update', bundle);
        }

        var reportFinished = () => {
            bundleLogger.end(bundleConfig.outputName)
            if(bundleQueue) {
                bundleQueue--;
                if(bundleQueue === 0) {
                    callback();
                }
            }
        }
        return bundle();
    }
    config.bundleConfigs.forEach(browserifyThis);
})
代码语言:javascript
复制
// bundleLogger.js
const gutil        = require('gulp-util'),
      prettyHrtime = require('pretty-hrtime');
var   startTime;

module.exports = {
    start: filepath => {
        startTime = process.hrtime();
        gutil.log('Bundling', gutil.colors.green(filepath));
    },
    // watch: bundleName => {
    //     gutil.log('Watching files required by', gutil.colors.yellow(bundleName));
    // },
    end: filepath => {
        var taskTime     = process.hrtime(startTime),
            prettyTime = prettyHrtime(taskTime);
        gutil.log('Bundled', gutil.colors.green(filepath), 'in', gutil.colors.magenta(prettyTime));
    }
}
代码语言:javascript
复制
// handleErrors.js
const notify = require("gulp-notify");

module.exports = function() {    
    var args = Array.prototype.slice.call(arguments);

    // Send error to notification center with gulp-notify
    notify.onError({
        title: "Compile Error",
        message: "<%= error.message %>"
    }).apply(this, args);

    // Keep gulp from hanging on this task
    this.emit('end');
}

每增加一个需要 Gulp 的 JS 文件,建议在 bundleConfigs 中进行配置

config.js
config.js

还需要在 packfile.json 文件里进行配置,具体代码如下

packfile.json
packfile.json

喜欢使用 ES6 的童鞋一定不能忘了引入 gulp-babel 插件

代码语言:javascript
复制
// babel.js
const gulp       = require('gulp'),
      babel      = require('gulp-babel'),
      uglify     = require('gulp-uglify'),
      browserify = require('browserify'),
      source     = require('vinyl-source-stream'),
      config     = require('../../config').babel;

gulp.task('babel', () => {
    gulp.src(config.src)
        .pipe(babel(config.options))
        .pipe(uglify())
        .pipe(gulp.dest(config.dest))
})
config.js
config.js

Browsersync

browser-sync 插件,其作用是能让浏览器实时、快速响应 HTML、CSS、JS、Sass、Less 等文件更改并自动刷新页面,更重要的是,可以同时在 PC、平板、手机等设备下进项调试,我们可以使用 Browsersync 提供的静态服务器,对我们的 html 文件进行测试,也可以使用代理服务器,来对 php 文件进行测试,而我们在这里使用的静态服务器

代码语言:javascript
复制
// browser-sync.js
const gulp        = require('gulp'),
      browsersync = require('browser-sync'),
      config      = require('../../config').browsersync;

gulp.task('browsersync', ['build'], () => {
    browsersync.init(config.development);
    // browsersync.init(config.production);
})
config.js
config.js
运行结果
运行结果
思维导图
思维导图

该章节的内容到这里就全部结束了,源码及思维导图我已经发到了 GitHub Gulp_Niangao 上了,有需要的同学可自行下载

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.08.08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文件结构
  • default 默认任务
  • CSS 依赖包
  • JS 依赖包
  • Browsersync
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档