【万字长文】通过grunt、gulp和fit,彻底搞懂前端的自动化构建(一)

简介: 【万字长文】通过grunt、gulp和fit,彻底搞懂前端的自动化构建

在上一篇文章中介绍了前端工程化中脚手架工具,这篇文章作为前端工程化的第二部分,自动化构建。


简介


一切重复工作本应自动化。

自动化构建是前端工程化中一个重要的组成部分。

自动化就是通过机器代替人工,构建就是将源代码转换为生产代码。

机器自动将源代码转换为生产代码的过程就是自动化构建工作流。

自动化构建工作流会脱离运行环境兼容带来的问题,让我们能够在开发阶段使用提高效率的语法、规范和标准。

典型的一些应用有,编写 JavaScript 时,可以使用 ECMAScript Next 语法;编写 css 时,可以使用 Sass、Less 等语法;编写 Html 时,可以使用 pug 等模板引擎。这些用法基本上都是无法在浏览器中直接运行的。

使用自动化构建之后,我们可以构建转换那些不被支持的「特性」,从而很大程度上提高我们的开发效率。


体验


使用 sass 提高 css 编程性


使用 scss 来编写样式从而提高 css 的编程性,但是浏览器不支持 scss。下面通过将 scss 转换成 css 来体验自动化构建。

首先创建一个 index.html。


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Demo</title>
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    hello, sass
  </body>
</html>

然后创建一个 style.scss 文件。


$bg: #e2e2e2;
$color: #d0d;
body {
background-color: color;
}
body {
background-color: color;
}

我们需要将 scss 转换为 css 就可以让页面正常使用样式。

编译 scss 需要使用官方提供的 sass 模块。

首先初始化 node 项目。


npm init -y

安装 sass。


npm i -D sass

然后就可以通过 npx 运行 sass 模块了。


npx sass style.scss style.css

npx 是 node5.2 版本之后推出的命令,可以帮助我们自动调用项目内部安装的模块。

安装完 sass 后,会在项目根目录的 node_modules 下的.bin 目录中多出来一个 sass 可执行脚本文件。

如果不使用 npx,就需要指定路径来执行。


node_modules/.bin/sass style.scss style.css

这样会很麻烦。

如果你使用 yarn 的话,可以使用 yarn 来执行 sass,这和 npx 很相似。


yarn sass style.scss style.css

sass 的两个参数分别是源文件和目标文件,执行过后会生成两个文件,一个是 style.css,另一个是 style.css.map。

css 文件就是我们要使用的生产代码,map 文件是将源代码和生产代码做出的映射,方便我们调试。

这时候在浏览器中打开 index.html 就可以看到正常应用样式的页面了。


NPM Scripts


但是如果这个项目让别人来接手时,他可能并不理解 sass 的编译命令。

这时可以通过 npm script 中定义命令来包装项目开发过程中需要的命令。

定义 npm script 的方式就是在 package.json 中添加 scripts 字段,并在其中添加一个属性。属性名是 scripts 的名称,值就是需要执行的命令。


{
  "scripts": {
    "build:style": "sass style.scss style.css"
  }
}

这样就可以通过npm run build:style来执行同样的操作。

npm run 的操作会自动在项目依赖中寻找命令,所以不需要再去写 npx 了,当然写了也不会报错。


添加开发服务器和自动化构建


直接在浏览器中打开文件的方式有很多弊端,最大的一个问题就是每当我们修改完代码后,都需要手动刷新浏览器,这样会大大降低开发效率。

我们可以安装一个开发服务器帮助我们完成热更新的功能。


npm i -D browser-sync

browser-sync 是一个非常流行的 web 开发服务器,它可以帮助我们实现很多开发阶段需要的功能。

在 package.json 中添加对应的脚本。


{
  "scripts": {
    "serve": "browser-sync . --files ./**/*.html ./**/*.css"
  }
}

第一个.是项目的根目录,--files 参数是监听哪些目录的变化,从而刷新浏览器。

这时可以修改 index.html 中的内容,浏览器会自动刷新。

但是修改 style.scss 的内容,浏览器不会自动应用样式。


pre hook


一般来说,在启动服务之前,我们都应该把代码编译一遍,这样保证服务启动后一切都是正常的。

npm scripts 提供了钩子机制,可以在脚本名前面添加 pre 和 post 来创建钩子脚本。

pre 前缀的脚本会在脚本执行之前执行,post 前缀的脚本会在脚本执行之后执行。

比如在 serve 运行之前执行 build。


{
  "scripts": {
    "build:style": "sass style.scss style.css",
    "preserve": "npm run build:style",
    "serve": "browser-sync . --files ./**/*.html ./**/*.css"
  }
}

这样就可以在每次启动服务之前都对样式文件进行编译了。


多任务并行执行


让 scss 文件变化后自动刷新浏览器也很简单,sass 提供了--watch 参数,每当 sass 文件修改,重新编译 css 文件。

但是 sass 的--watch 会阻塞当前的命令行窗口,导致后续的任务无法继续执行。

这时正常现象,大部分命令行运行平台默认都是继发执行,就是一个命令接一个命令的执行,只有当前一个命令执行成功,才会执行下一个命令。

可以使用&来让两个命令同时执行。

这样用的话,就需要修改脚本,将 preserve 删除掉。


npm run build:style & npm run serve

除了这种 bash 平台默认的功能以外,还可以借助 node 的多任务模块来实现。

安装 npm-run-all 模块。


npm i -D npm-run-all

修改脚本。


{
  "scripts": {
    "build:style": "npx sass style.scss style.css --watch",
    "serve": "browser-sync . --files ./**/*.html ./**/*.css",
    "start": "run-p build:style serve"
  }
}

这样就完成了一个简单的自动化构建工作流。


自动化构建工具简介


npm scripts 适合一些相对简单的构建任务。

当构建任务变得复杂时,npm scripts 就会显得非常吃力,这时就需要借助更加专业的构建工具。

现代开发最为流行的工具还是 webpack。但是严格意义上来说,webpack 并不算是一款自动化构建工具,而是模块打包工具。当然 webpack 也可以借助 plugins 来完成一些构建任务。但是自定义程度不高,不如 grunt 和 gulp。

webpack 的成功,是一直跟随潮流。React 和 Vue 等单页面框架的成功,意味着模块化成为了必须解决的问题。

在最早的前端开发中,所谓的构建就是将 js 文件和 css 文件进行压缩混淆。那时最流行的方式就是在网上找到一些在线工具,通过代码上传,在线压缩,拷贝本地的方式来完成。当文件过多时,这将会是一项非常枯燥的工作。

07 年左右,雅虎推出了 YUI Build Tool,它可以结合 Java 的 ant 做一些代码处理工作,算是自动化构建最早的起点。

YUI Build Tool 的流行持续了好几年,之后由于 AMD、CMD 等 js 模块化的流行,演变出了 grunt 工具。grunt 曾经是最为流行、最为强大、生态圈最为繁华的前端构建工具,官方甚至自称可以使用 grunt 完成你所有想要完成的任务。

grunt 的做法是将构建文件存储在本地磁盘,当项目足够大的时候构建速度会非常慢,所以出现了 gulp,gulp 使用文件流的概念,将临时文件存储在内存中,大大提高了构建速度。

grunt 的很多插件的更新维护都停留在 15 年左右,所以目前来说,新项目中推荐使用 gulp 来取代 grunt。

fit 是百度推出的一款构建工具,由于在当时有很多先进的构建理念,开源后在国内非常流行。和 grunt、gulp 不同的是,它不像是一个构建平台,而是一个构建体统。使用它可以非常轻松的完成资源加载、模块化部署、性能优化等。相比较 gulp,fit 对新手更加友好,但是官方团队已经停止对 fit 的维护了。

关于构建工具的总结,就是一句话: 新手需要规则,老手渴望自由。 这正是由于这样,才使得一些小而美的工具流行开来。


Grunt 用法


基本用法


初始化项目:


npm init -y

安装 grunt:


npm i grunt -D

在项目根目录下创建 grunt 的入口文件:


touch gruntfile.js

gruntfile.js 默认导出一个函数,这个函数会有一个形式参数,就是 grunt。

通过 registerTask 来注册任务,第一个参数是任务名,第二个参数是任务的回调。

如果传递三个参数,那么第二个参数就可以是任务的描述。


module.exports = (grunt) => {
  grunt.registerTask("foo", "test task", () => {
    console.log("hello grunt~");
  });
};

通过 npx grunt --help 来查看任务描述。


npx grunt --help

通过 npx grunt --help 来执行任务。


npx grunt foo


默认任务


任务名称为 default 时,会作为 grunt 的默认任务。


module.exports = (grunt) => {
  grunt.registerTask("default", "default task", () => {
    console.log("default task~");
  });
};

这样在命令行中就不需要指定任务名。


npx grunt


组合任务


通常情况下,default 会组合一些其他任务。这时可以传递一个数组。


module.exports = (grunt) => {
  grunt.registerTask("foo", "foo task", () => {
    console.log("foo task~");
  });
  grunt.registerTask("bar", "bar task", () => {
    console.log("bar task~");
  });
  grunt.registerTask("default", ["foo", "bar"]);
};

这样运行 npx grunt 时,会先运行 foo 任务,再运行 bar 任务。


异步任务


grunt 默认支持同步任务,对于异步任务,需要手动标记完成。


grunt.registerTask("async", "async", function () {
  setTimeout(() => {
    console.log("async task~");
  }, 1000);
});

运行 npx grunt async,不会输出 async task~。

如果要正常输出,需要使用 this.async,返回一个函数,调用这个函数,就意味着这个异步任务完成了。


grunt.registerTask("async", "async", function () {
  const done = this.async();
  setTimeout(() => {
    console.log("async task~");
    done();
  }, 1000);
});

这样 grunt 会一直等待 done 函数的执行。


标记任务失败


任务返回 false 就代表任务失败。


grunt.registerTask("bad", "bad task", () => {
  console.log("bad task~");
  return false;
});

但是不会影响当前任务的执行。


bad task~
Warning: Task "bad" failed. Use --force to continue.
Aborted due to warnings.

如果一个任务执行失败的话,就会阻止后续的任务。


grunt.registerTask("default", "default task", ["foo", "bad", "bar"]);

如果是异步任务,标记失败可以给 done 函数传递一个 false 参数。


grunt.registerTask("async", "async", function () {
  const done = this.async();
  setTimeout(() => {
    console.log("async task~");
    done(false);
  }, 1000);
});


配置方法


grunt 提供了一个添加配置选项的 API,叫做 initConfig。

initConfig 可以方便我们把配置集中起来,在使用的时候更好的获取到配置项。


module.exports = (grunt) => {
  grunt.initConfig({
    foo: "bar",
    obj: {
      hello: "world",
    },
  });
  grunt.registerTask("foo", () => {
    console.log(grunt.config("foo"));
    // 如果是个对象,可以继续.
    console.log(grunt.config("obj.hello"));
  });
};

运行 npx grunt foo,控制台输出:


bar
world


多目标模式任务


多任务模式可以让任务根据配置形成多个任务。

注册多任务需要使用 grunt.registerMultiTask。

多任务的配置需要在 grunt.initConfig 中定义一个和任务同名的属性,这个属性必须是个对象。对象中的每个属性就是该任务下的子任务。

options 属性是例外,它会作为该任务的配置选项,而不会作为一个任务。

如果子任务中也存在 options 属性,会覆盖掉外层的 options。

示例代码如下:


module.exports = (grunt) => {
  grunt.initConfig({
    build: {
      options: {
        env: "es6",
      },
      weapp: "foo",
      h5: {
        options: {
          target: "chrome",
        },
      },
    },
  });
  grunt.registerMultiTask("build", function () {
    console.log(
      `target: ${this.target}, data: ${this.data}, options: ${JSON.stringify(
        this.options()
      )}`
    );
  });
};

这样就模拟注册了一个叫做 build 的任务。build 任务下还有两个任务,分别是微信平台的 weapp 和 web 平台的 h5。

在 build 任务的回调函数中,可以通过 this.target 拿到当前执行任务的属性名,通过 this.data 拿到当前执行任务的属性值,通过 this.options 函数调用拿到当前执行任务的配置选项。

现在这个 build 命令就有了三种运行方式。

两个平台同时编译。


npx grunt build

只编译微信平台。


npx grunt build:weapp

只编译 h5 平台。


npx grunt build:h5


插件用法


插件是 grunt 的核心机制。有很多构建任务是通用的,和业务无关,只和特定技术有关。

使用插件的过程比较简单,通常做法有 3 步。

  1. 安装插件。
  2. 导入插件。
  3. 配置插件。

标准的插件命名规范是 grunt-contrib-xx。

下面演示一下 grunt-contrib-clean 插件的用法。

clean 插件的作用是清除掉临时目录。

假设项目下有一个 temp 文件夹是用于存放临时目录的,我们要通过 clean 命令删除掉它。

你可以在项目根目录下创建一个 temp 目录,并在其中创建一些文件。

首先安装插件。


npm i -D grunt-contrib-clean

编写配置:


module.exports = (grunt) => {
  grunt.initConfig({
    clean: {
      temp: "temp",
    },
  });
  grunt.loadNpmTasks("grunt-contrib-clean");
};

然后运行 npx grunt clean,就可以将 temp 文件夹删除掉了。


常用插件


实际上大部分自动化构建工具的插件核心包都是相同的。

比如编译 sass 所用到的 sass、编译 ES Next 语法所用到的 babel、检查 js 代码的 eslint 等。

下面演示下 sass 和 babel 的用法。

首先安装 sass。


npm i -D sass grunt-sass

再安装 babel 所需要的依赖。


npm i -D @babel/core @babel/preset-env grunt-babel

然后编写配置文件。


const sass = require("sass");
module.exports = (grunt) => {
  grunt.initConfig({
    sass: {
      options: {
        sourceMap: true,
        implementation: sass,
      },
      main: {
        files: {
          "dist/css/main.css": "src/scss/*.scss",
        },
      },
    },
    babel: {
      options: {
        sourceMap: true,
        presets: ["@babel/preset-env"],
      },
      main: {
        files: {
          "dist/js/app.js": "src/js/*.js",
        },
      },
    },
  });
  grunt.loadNpmTasks("grunt-sass");
  grunt.loadNpmTasks("grunt-babel");
};

这时虽然可以编译文件,但是修改了源代码后并不会重新编译。这时候可以使用一个 grunt-contrib-watch 插件来监听源代码的变化,从而自动执行编译。


npm i -D grunt-contrib-watch

在配置文件中添加 watch 的任务配置。


grunt.initConfig({
  // ... other code
  watch: {
    js: {
      files: ["src/js/*.js"],
      tasks: ["babel"],
    },
    css: {
      files: ["src/scss/*.scss"],
      tasks: ["sass"],
    },
  },
});
grunt.loadNpmTasks("grunt-contrib-watch");

由于 watch 只是会监听文件变化,才会触发对应的任务。所以应该创建一个组合命令,先执行 sass 和 babel 的编译,再执行文件监听。


grunt.registerTask("default", ["sass", "babel", "watch"]);

这时会有一个问题,当任务过多时,需要执行很多遍 grunt.loadNpmTasks。我们可以通过 load-grunt-tasks 库来解决这个问题。


npm i -D load-grunt-tasks

安装完成后修改代码。


module.exports = (grunt) => {
  // ... other code
  require("load-grunt-tasks")(grunt);
  // 不再需要 grunt.loadNpmTasks
  // grunt.loadNpmTasks('grunt-sass')
  // grunt.loadNpmTasks('grunt-babel')
};

目前来说,grunt 已经退出历史舞台,目前它的替代者是 gulp。但是仍然在一些老项目中会碰到 grunt。

学习 grunt 的目的有两点,第一是当碰到老项目时可以有能力去修改编译配置。第二是为接下来学习 gulp 做铺垫。



相关文章
|
13天前
|
人工智能
LangGraph:构建多代理动态工作流的开源框架,支持人工干预、循环、持久性等复杂工作流自动化
LangGraph 是一个基于图结构的开源框架,专为构建状态化、多代理系统设计,支持循环、持久性和人工干预,适用于复杂的工作流自动化。
50 12
LangGraph:构建多代理动态工作流的开源框架,支持人工干预、循环、持久性等复杂工作流自动化
|
1月前
|
人工智能 自然语言处理 JavaScript
Agent-E:基于 AutoGen 代理框架构建的 AI 浏览器自动化系统
Agent-E 是一个基于 AutoGen 代理框架构建的智能自动化系统,专注于浏览器内的自动化操作。它能够执行多种复杂任务,如填写表单、搜索和排序电商产品、定位网页内容等,从而提高在线效率,减少重复劳动。本文将详细介绍 Agent-E 的功能、技术原理以及如何运行该系统。
110 5
Agent-E:基于 AutoGen 代理框架构建的 AI 浏览器自动化系统
|
29天前
|
Serverless 决策智能 UED
构建全天候自动化智能导购助手:从部署者的视角审视Multi-Agent架构解决方案
在构建基于多代理系统(Multi-Agent System, MAS)的智能导购助手过程中,作为部署者,我体验到了从初步接触到深入理解再到实际应用的一系列步骤。整个部署过程得到了充分的引导和支持,文档详尽全面,使得部署顺利完成,未遇到明显的报错或异常情况。尽管初次尝试时对某些复杂配置环节需反复确认,但整体流程顺畅。
|
1月前
|
缓存 监控 安全
公司电脑监控软件的 Gradle 构建自动化优势
在数字化办公环境中,公司电脑监控软件面临代码更新频繁、依赖管理和构建复杂等挑战。Gradle 构建自动化工具以其强大的依赖管理、灵活的构建脚本定制及高效的构建缓存与增量构建特性,显著提升了软件开发效率和质量,支持软件的持续更新与优化,满足企业对员工电脑使用情况的监控与管理需求。
37 3
|
1月前
|
数据库 Docker 容器
Docker在现代软件开发中扮演着重要角色,通过Dockerfile自动化构建Docker镜像,实现高效、可重复的构建过程。
Docker在现代软件开发中扮演着重要角色,通过Dockerfile自动化构建Docker镜像,实现高效、可重复的构建过程。Dockerfile定义了构建镜像所需的所有指令,包括基础镜像选择、软件安装、文件复制等,极大提高了开发和部署的灵活性与一致性。掌握Dockerfile的编写,对于提升软件开发效率和环境管理具有重要意义。
71 9
|
2月前
|
JavaScript 前端开发 测试技术
构建高效可维护的前端应用
构建高效可维护的前端应用
|
1月前
|
运维 jenkins Java
Jenkins 自动化局域网管控软件构建与部署流程
在企业局域网管理中,Jenkins 作为自动化工具,通过配置源码管理、构建及部署步骤,实现了高效、稳定的软件开发与部署流程,显著提升局域网管控软件的开发与运维效率。
48 5
|
2月前
|
缓存 JavaScript 前端开发
JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用
本文深入讲解了 JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用。
68 5
|
2月前
|
jenkins 测试技术 持续交付
自动化测试框架的构建与优化:提升软件交付效率的关键####
本文深入探讨了自动化测试框架的核心价值,通过对比传统手工测试方法的局限性,揭示了自动化测试在现代软件开发生命周期中的重要性。不同于常规摘要仅概述内容,本部分强调了自动化测试如何显著提高测试覆盖率、缩短测试周期、降低人力成本,并促进持续集成/持续部署(CI/CD)流程的实施,最终实现软件质量和开发效率的双重飞跃。通过具体案例分析,展示了从零开始构建自动化测试框架的策略与最佳实践,包括选择合适的工具、设计高效的测试用例结构、以及如何进行性能调优等关键步骤。此外,还讨论了在实施过程中可能遇到的挑战及应对策略,为读者提供了一套可操作的优化指南。 ####
|
2月前
|
敏捷开发 监控 测试技术
探索自动化测试框架的构建与优化####
在软件开发周期中,自动化测试扮演着至关重要的角色。本文旨在深入探讨如何构建高效的自动化测试框架,并分享一系列实用策略以提升测试效率和质量。我们将从框架选型、结构设计、工具集成、持续集成/持续部署(CI/CD)、以及最佳实践等多个维度进行阐述,为软件测试人员提供一套系统化的实施指南。 ####

热门文章

最新文章