• 快速掌握Gulp自动化构建工具


    Gulp是什么?

    Gulp是一个自动化构建工具,类似Webpack,目的是把开发环境的代码转换为生产环境的代码,比如ES6转ES5、Saas转CSS、文件压缩等。

    Gulp的整体设计思路是通过文件读写和一系列Stream插件实现文件内容处理,完成构建工作。完整的构建工作可以拆分为多个构建步骤,每一个构建步骤称之为任务,一个大型的项目构建由多个任务组合完成。

    Gulp基本使用

    1.Gulp执行依赖Node环境,需要安装Node
    2.安装命令行工具gulp-cli
    3.安装核心依赖库gulp
    4.创建gulpfile.js,在其中书写任务代码
    5.导出要执行的任务
    6.使用命令行工具执行任务

    下面是代码演示:

    # 安装node环境
    # 安装命令行工具
    npm install -g gulp-cli@2.3.0 
    # 安装核心依赖库
    npm install -D gulp@4.0.2
    # 创建gulpfile.js
    touch gulpfile.js
    # 书写并导出任务,见下文
    # 执行任务
    gulp xxxx # 导出任务函数名称/default 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在gulpfile.js中书写并导出任务:

    // vim gulpfile.js
    
    const { src, dest } = require("gulp");
    
    // 任务函数
    function copy() {return src("src/index.html").pipe(dest("dist/"));
    }
    
    // 导出任务
    module.exports = {copy,
    };
    
    // gulp copy 执行任务 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Gulp核心概念

    基于Stream流的任务创建

    一个任务就是一个函数,创建任务就是创建一个函数,然后将要执行的函数导出即可。

    任务函数的内部通常由一个一个的流处理器构成的流处理管道,起点和终点是文件读写,中间是各种流处理插件,流处理器之间则通过stream.pipe连接,后面我们重点看看这几个部分。

    文件读写流

    gulp.src / gulp.dest

    Gulp本身是非常简单的,在流处理器方面只提供了gulp.src和gulp.dest两个函数用于文件读写,中间的处理器全部由外部插件提供,这些插件全部按照Gulp提供的规范编写,可以无缝嵌入Gulp流处理过程。这种基于组合的设计思路使得Gulp本身非常精简,同时也让Gulp的生态越来越丰富。

    我个人非常喜欢这种轻量化的设计思路,只需要提供一套规范和主流程框架就可以实现各种功能,同时还能保证项目的健壮性、可维护性和可扩展性。

    gulp.src和gulp.dest是一个读写流,继承自stream.Duplex,为什么是读写流而不是一边只读,一边只写呢?因为src和dest不仅用于两端,还会用在中间部分,比如先读一个文件处理一下,然后再读一个文件进行合并压缩等等。

    这两个函数的使用也比较简单,最常用的就是像上文copy函数那样开头读一个文件,结尾将文件写入一个目录。

    function copy() {return src("src/index.html").pipe(dest("dist/"));
    } 
    
    • 1
    • 2

    src接收一个路径字符串或者路径数组,用于读取一个或者多个文件,而dest则接收一个目录路径字符串,用于将内容写入文件。

    src("src/index.html")
    src(["src/index.html", "src/index.css"])
    dest("dist/") 
    
    • 1
    • 2
    • 3

    值得注意的是,这里的路径字符串是Glob路径匹配符而不是普通的字符串,通过Glob的方式可以方便的匹配任意层级、任意格式的文件。

    Glob路径匹配符

    Glob用于匹配文件路径,由普通字符、特殊字符和分隔符三个部分组成,即基本用法如下:

    • 分隔符只能是"/"
    • 普通字符就是正常的文本,没有特殊含义
    • 特殊字符包括:* *: 匹配单级目录下任意数量的字符* **: 匹配任意级目录下任意数量的字符* !: 取反,必须在数组中跟在一个正向的glob后面* {}: 取或,同时匹配多个模式,如{ab,cd}

    接下来我们对特殊字符举例说明:

    首先*通常用来批量匹配文件,比如*.js,*.png等等,或者dist/*匹配所有文件。

    其次**通常用来匹配跨越多级目录,比如images/**/*.png,如果直接写images/**.png,那大概率只能匹配images单级目录下面的png文件,因为子目录的名字里没有.png后缀,**虽然可以跨层级,但也是一级一级往下匹配的,如果中间一级不能匹配上,那就会不会继续向下匹配了。

    !通常用来表示在批量匹配之后排除其中的个别文件,所以需要跟在正向匹配后面,比如["src/*.js", "!src/abc.js"]

    {}通常用来匹配多个文件后缀,比如*.{png,jpg},注意{png,jpg}的逗号两边不能有空格。

    好了,理解Glob之后那文件读写就变得非常轻松了,接下来就看看如何处理这些流。

    流处理插件

    在Gulp中,文件流的处理是通过各种流处理插件完成的,这些插件继承自stream.Transform,本质就是一个转换流。不同的插件之间通过stream.pipe()相连,由此构成了一个处理流水线,实现整个构建任务。

    Gulp提供了一个插件查询的网址:gulpjs.com/plugins ,我们要做的就是下载这些插件,然后把他放置在需要的pipe()之中进行执行。

    src('src/index.html').pipe(插件1).pipe(插件2).pipe(dest("dist/")) 
    
    • 1

    对于前端开发来说,常用的插件如下:

    1.HTML相关:* gulp-htmlmin: 压缩HTML文件* gulp-file-include:引入HTML代码片段
    2.CSS相关:* gulp-sass/gulp-less: SAAS、LESS转CSS* gulp-autoprefixer: 自动添加厂商前缀* gulp-cssmin: 压缩CSS文件
    3.JS相关:* gulp-babel: ES6转ES5* gulp-uglify: 压缩JS文件
    4.其它:* gulp-rename: 文件重命名* gulp-concat: 文件合并* gulp-webserver: 搭建本地服务器* webpack-stream: 在gulp中使用webpack

    通过组合任务实现Gulp执行

    大型项目的构建往往要拆分为多个构建任务,而任务就是gulp的执行单元。任务之间可以通过组合形成新的任务,这种组合方式可以实现任意复杂度的构建流程。

    单个任务

    上文我们介绍了一个任务的本质就是pipe连接的stream处理流水线,这边再补充几点关于任务的知识:

    1.一个任务就是一个函数。
    2.只有导出的任务函数才能被执行,执行命令为gulp [任务函数名称]
    3.通过exports.default导出的任务称为默认任务,可以通过gulp直接执行。
    4.任务完成后需要通知Gulp,否则控制台就是报错说没有收到任务结束的通知。对于组合任务来说,通知尤其重要,如果多个任务之间是线性执行的,那么只有当收到前一个任务完成的通知之后,Gulp才会执行下一个任务。任务结束通知有三种方式:* 任务函数可以接收一个cb参数,调用cb回调进行通知* 任务函数返回一个Promise* 任务函数返回一个Stream

    接下来我们演示一下这三种任务结束通知方式:

    // 执行回调函数
    function start(cb) {console.log("task start");cb();
    }
    
    // 返回Promise
    function clean() {return new Promise((resolve, reject) => {setTimeout(() => {console.log("dir clean finished");resolve();}, 1000);});
    }
    
    // 返回Stream
    function copy() {return src("src/index.html").pipe(dest("dist/"));
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    组合任务

    当我们需要将多个任务按照顺序进行组合,依次执行或者并行执行时就可以使用组合任务。Gulp提供了series和parallel两个API用于创建组合任务。

    series和parallel都会返回一个新的任务,我们可以像使用普通任务那样使用组合任务,两者的基本使用方法如下:

    const {series, parallel} = require("gulp");
    
    series(task1, task2, ...);
    parallel(task1, task2, ...); 
    
    • 1
    • 2
    • 3
    • 4

    series是线性执行,其中的任务会一个接一个的执行,只有当前面的任务通知Gulp执行完成后,后面的任务才会执行。

    paralle是并行执行,任务之间没有先后关系,这种执行方式可以提高整体的构建效率。

    文件监听

    在开发过程中,我们希望构建系统能够监听文件的变化自动构建,实现页面的热更新。为此,Gulp提供了一个watchAPI实现文件变化的监听,当文件发生变化后,Gulp将自动执行注册的任务。

    const { watch } = require("gulp");
    
    function watchJS() {watch('src/index.js', task1);
    }
    
    exports.default = watchJS; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    一个完整的案例

    最后,我们用一个完整的案例把上面的知识串起来。在这个案例中,我们将监听js模块文件的变化,实现如下几个构建动作:

    1.清理dist目录
    2.使用gulp-babel将ES6转为ES5
    3.使用gulp-concat将多个模块组合为单个文件
    4.使用gulp-uglify进行JS文件压缩
    5.使用gulp-rename重命名最终生成的JS文件

    让我们开始吧!

    在src目录下有两个JS模块文件moduleA.js,moduleB.js,我们的目的就是把他打包为module.min.js。

    // moduleA.js
    
    const animal = "dog";
    // 跑得更快
    function run() {console.log("run faster!");
    }
    
    // moduleB.js
    
    const animal = "bird";
    // 飞得更高
    function fly() {console.log("run higher!");
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1.安装所有需要的插件

    npm install -D gulp-babel@8.0.0 @babel/core@7.14.8 @babel/preset-env@7.14.8
    npm install -D gulp-uglify@3.0.2 gulp-rename@2.0.0 gulp-concat@2.6.1
    # 这个不是插件,用于删除文件
    npm install -D del@6.0.0 
    
    • 1
    • 2
    • 3
    • 4

    2.编写gulpfile.js

    const { src, dest, series, watch } = require("gulp");
    const babel = require("gulp-babel");
    const concat = require("gulp-concat");
    const uglify = require("gulp-uglify");
    const rename = require("gulp-rename");
    const del = require("del");
    
    function clean() {// 返回Promisereturn del("dist");
    }
    
    function build() {return src("src/module*.js").pipe(babel({ presets: ["@babel/env"] })).pipe(concat("module.js")).pipe(uglify()).pipe(rename({ extname: ".min.js" })).pipe(dest("dist/"));
    }
    
    function watchJS() {watch("src/module*.js", series(clean, build));
    }
    
    exports.default = watchJS;
    
    // 命令行执行gulp 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    最终我们构建得到的module.min.js内容为:

    "use strict";var animal="dog";function run(){console.log("run faster!")}animal="bird";function fly(){console.log("run higher!")} 
    
    • 1
  • 相关阅读:
    气膜球幕影院:科技创新的结晶—轻空间
    浅谈霍尔电流传感器在汽车电池管理系统中的应用
    中文人物关系知识图谱(含码源):中文人物关系图谱构建、数据回标、基于远程监督人物关系抽取、知识问答等应用.
    中国软件三季度业绩预测,中国软件股票趋势预测
    GeographicLib 的VS2013项目生成以及Geoid Height 提取
    Android—Surface,BufferQueue
    k8s之创建基于sa的访问凭据kubeconfig文件
    C语言——文件操作(2)文件的读写操作
    Leetcode 26-30题
    Eureka注册中心以及Ribbon负载均衡
  • 原文地址:https://blog.csdn.net/web2022050901/article/details/126879149