『前端工程化』gulp(gulp3 && gulp4)

Gulp

什么是Gulp

  • Gulp是基于Node.js的构建工具,我们使用它来集成前端开发环境,来构建自动化工作流程。
  • 通常一些常见、重复的任务我们可以使用Gulp来代替我们完成,例如压缩代码文件/图片等资源,给CSS添加兼容性前缀,ES6语法转换成ES5,打包代码以及部署环境等。
  • gulp的核心概念是流,可以简单类比为工厂的流水线,从原材料到最后的商品,会经过很多道工序,每一道工序的输入都是前一道工序的输出。gulp的任务就是把源文件进行各种加工处理最终输出到指定位置,省略了途中的中间文件,相比grunt的频繁IO操作,gulp的流操作,能更快更便捷地完成构建工作。
  • 流与流之间是通过管道(pipe)连接的,pipe接受一个函数作为参数,将当前流的内容传给这个函数让其加工。可以想象成让流中的所有文件一个接一个地流过有着某种加工方法的管道。
  • gulp虽然是基于node的,但是它并没有直接使用node中fs模块里的文件系统和流,而是包装了一层vinyl。
    vinyl是一个用来描述文件的简单的数据格式。所以gulp中的流其实是一种vinyl流,与我们通常所见的流其实不是一种流。

安装入门

1. 首先全局安装gulp

$ npm install -g gulp

2. 作为项目的开发依赖安装(需要在开发项目的目录下安装)

$ npm install --save-dev gulp

3.在项目的根目录下创建一个名为gulpfile.js的文件

目录结构如下
project
│   README.md
│   gulpfile.js    
│   package.json
│   package-lock.json
│
└───src
│   │   assets
│   │   js
│   │   page
│   │
│   └───scss
│       │   test.scss
│       │   ...
│   
└───dist
    │   
    └───css_output
         │   test.css
     

4. 在gulpfile.js中编写你的任务

let gulp = require('gulp'),
    sass = require('gulp-sass'); //SASS编译插件

gulp.task('default', function() {
  return gulp.src('src/scss/**/*.scss')
    .pipe(sass()) //通过gulp-sass插件将scss文件编译成css文件
    .pipe(gulp.dest('dist/css_output'))
});
5. 运行gulp

$ gulp


API介绍

  • gulp.src(globs[, options])

说明:可以读取符合匹配规则路径下(包括数组形式)的文件,并将这些文件返回到一个流中,以便后续使用操作。

第一个参数类型可以是String或Array,表示所要读取的并且符合匹配规则的文件或数组的文件
第二个参数并不是必须的,类型是Object,表示一些参数的配置。通常我们选择默认配置即可
return: 返回的是一个Vinyl流,它可以被piped到别的插件中

备注:

在gulp4中gulp.src()接收的文件匹配字符串会顺序解析,所以你可以写成这样
gulp.src([ '.js', '!b.js', 'bad.js' ]) //表示匹配除了所有以b开头的JS文件但是除了bad.js


  • gulp.dest(paths[, options])

说明: 可以将文件写入到paths路径下,并且返回的还是一个流,可以继续操作使用。

第一个参数类型是String或function,表示的是文件将被写入的路径(输出目录)
第二个参数不是必须的,类型是Object,其中值得一提的是options.mode表示的是所创建的目录的权限,用八进制权限字符表示,默认值是077
return: 返回的是一个Vinyl流,它可以继续被piped到别的插件中


  • gulp.task(name[, deps], fn)

说明: 可以定义一个任务。在gulp3中,可以接受3个参数,在gulp4中,只能接受两个参数。

第一个参数类型是String,表示的是任务的名字,避免名字中带空格
第二个参数不是必须的,类型是Array,表示的是一个包含任务列表的数组,这些任务会在你当前任务运行之前完成,但并不能很好的控制它们的执行顺序。
第三个参数类型是function,表示的是定义该任务所要执行的一些操作

  • gulp.task(name, fn)

说明:这是在gulp4中的格式,不在接受任务列表的数组。但是可以通过别的方式更好的实现相同的功能

第一个参数类型是String,表示的是任务的名字,避免名字中带空格
第二个参数类型是function,表示的是定义该任务所要执行的一些操作。这个函数可以是匿名函数,也可以是在别的地方已经声明过的函数

备注:

gulp.task用字符串注册的任务必须是直接在命令行中调用的任务
gulp.task可以接受单参数语法,这个参数必须是一个命名函数,函数名会被作为任务名
添加了gulp.series和gulp.parallel方法来用于组合任务代替了以前使用任务列表数组的方式

demo

##在gulp3中定义
gulp.task(name, function() { ... });
gulp.task(name, ['task1', 'task2', 'task3'], function() { ... });

##在gulp4中定义
gulp.task(name, () => { ... });
gulp.task(name, gulp.series('task1', 'task2', 'task3')); //顺序执行
gulp.task(name, gulp.parallel('task1', 'task2', 'task3')); //并行执行
//当然gulp.series和gulp.parallel两者可以随意组合使用

  • gulp.watch(glob[, opts, cb])

说明:监听符合匹配规则路径下的文件变动,并在变动后执行callback里的内容

第一个参数类型是String或Array,表示指定具体监控哪些文件的变动。
第二个参数类型是Object,表示用来传给gaze的参数。通常无需配置,使用默认即可。
第三个参数cb(event)类型是Function,表示每次变动需要执行的callback。callback会被传入一个名为event的对象,这个对象描述了所监控到的变动。
event.type -- 发生变动的类型 added ,changed,deleted
event.path -- 触发了该事件的文件路径

demo

gulp.wathc('js/**/.js', function(event) {
  console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});

下面是在gulp4中新增的API:

  • gulp.series(fn)

说明:以串行的方式运行指定的任务,并会返回一个函数。

接受任意数量的参数,参数可以是已经定义了的任务名,也可以是函数。
由于它返回一个函数,因此它在调用时通常是作为参数传递给其他方法,比如gulp.task中使用它。


  • gulp.parallel(fn)

说明:以并行的方式运行指定的任务,并会返回一个函数。

接受任意数量的参数,参数可以是已经定义了的任务名,也可以是函数。
由于它返回一个函数,因此它在调用时通常是作为参数传递给其他方法,比如gulp.task中使用它。


  • gulp.symlink() – 与dest相似,但是使用软连接形式
  • gulp.lastRun(taskName[,timeResolution]) – 获得某任务上次成功运行的时间戳
  • gulp.tree(options) – 返回一个数组,该数组由已定义的任务名字字符串所组成
  • gulp.registry() – 获取或者设置任务名和任务函数之间的映射关系

心得

以上便是gulp的语法相关内容,包含gulp3和gulp4的相关内容。
但是私以为仅仅学会这些语法,不亲自去应用是无法真正掌握的,因为语法相对而言非常简单,gulp的关键是在它有许多丰富多彩的插件,我们如何去搭配配置使用它们,并应用到我们的项目中,才是我们应该去思考并实践的问题,所以,让我们去带着目的去实践吧!

gulp实践

目标需求:

自动添加CSS兼容性前缀,并且能将SASS文件编译成CSS文件
能监听文件变化,并且在文件变化后自动编译
在浏览器上实时刷新变化

gulpfile.js

/**
 * 此为gulp4.0的配置文件
 */
var gulp = require('gulp'),
    gutil = require('gulp-util'), //打印日志工具
    clean = require('gulp-clean'), //清理文件工具
    del = require('del'), //更好用的清理工具
    htmlmin = require('gulp-htmlmin'), //HTML压缩
    sass = require('gulp-sass'), //SASS编译
    cleanCSS = require('gulp-clean-css'), //压缩CSS
    autoprefixer = require('gulp-autoprefixer'), //CSS自动添加前缀
    postcss = require('gulp-postcss'), //对css处理的插件系统
    pxtorem = require('postcss-pxtorem'), //将px转成rem
    inlineSource = require('gulp-inline-source'), //html外链文件复制到HTML
    browserSync = require('browser-sync').create(), //浏览器实时刷新
    reload = browserSync.reload,

    fileName = "sakura-new-website",
    srcDir = "src", //源文件存放路径
    srcPath = {
        scss: [srcDir + "/scss/**/*.scss"],
        js: srcDir + "/**/*.js",
        html: [
            srcDir + "/**/*.html"
        ],
        other: [srcDir + "/**/*+(png|jpg|gif)"]
    },
    
    buildDir = "dist", //编译后文件存放路径
    buildPath = {
        css: buildDir + "/css_output",
        js: buildDir + "/js"
    },
    end = "";

//编译SCSS && 添加CSS前缀  && 将px转成rem
gulp.task('styles', function() {
    var processors = [
        pxtorem({
            propList: ['*'],
            selectorBlackList: ['ignore'],
            replace: true,
            mediaQuery: true,
            minPixelValue: 1
        })
    ];
    
    return(
        gulp
        .src(srcPath.scss)
        .pipe(sass().on("error", sass.logError))
        .pipe(
            autoprefixer({
                browsers: ['last 4 versions'],
                cascade: false
            })
        )
        .pipe(postcss(processors))
        .pipe(gulp.dest(buildPath.css))
        .pipe(reload({ stream: true }))
    );
});

/**
 * 删除所有编译文件
 */
gulp.task('clean', function() {
    return del([buildDir]);
});

/**
 * 监听文件变化
 */
gulp.task('watch', function(done) {
    gulp.watch(srcPath.scss, gulp.series('styles')).on(
        'change', function(event) {
            console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');        
            done
        }
    );
    gulp.watch(srcPath.html, reload).on(
        '监听--HTML变化了--styles end',
        done
    );
    gulp.watch(srcPath.js, reload).on(
        '监听--JS变化了--styles end',
        done
    );
});

/**
 * 浏览器实时刷新
 */
gulp.task('server', function() {
    browserSync.init({
        server: './'
    });
});

/**
 * gulp 默认指令
 */
gulp.task('default', gulp.series('clean', gulp.parallel('server', 'watch', 'styles')));

运行截图

$ gulp

编译文件对比

浏览器运行效果

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容