【万字长文】通过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 做铺垫。



相关文章
|
1天前
|
消息中间件 运维 Kubernetes
构建高效自动化运维体系:Ansible与Kubernetes的融合实践
【5月更文挑战第9天】随着云计算和微服务架构的普及,自动化运维成为确保系统可靠性和效率的关键。本文将深入探讨如何通过Ansible和Kubernetes的集成,构建一个强大的自动化运维体系。我们将分析Ansible的配置管理功能以及Kubernetes容器编排的优势,并展示如何将二者结合,以实现持续部署、快速扩展和高效管理现代云原生应用。文章还将涵盖实际案例,帮助读者理解在真实环境下如何利用这些工具优化运维流程。
|
1天前
|
运维 监控 算法
构建高效自动化运维体系的实践与思考
【5月更文挑战第15天】 随着信息技术的飞速发展,企业对IT运维管理的要求越来越高。传统的手动运维已无法满足日益增长的业务需求,因此,构建一个高效、可靠且易于管理的自动化运维体系变得至关重要。本文将探讨在现代企业环境中,如何通过一系列策略和技术手段实现运维自动化,以及在此过程中可能遇到的挑战和解决方案。文章将基于实际案例分析,提供一种系统性的思考框架,帮助读者理解和构建适合自己的自动化运维体系。
|
1天前
|
运维 资源调度 监控
构建高效自动化运维流程的策略与实践
【5月更文挑战第15天】 在现代IT基础设施管理中,自动化运维已成为提高效率、确保稳定性和快速响应变化的关键。本文将探讨构建高效自动化运维流程的策略与实践,重点在于如何通过一系列切实可行的步骤实现从人工密集型到自动化驱动的转变。我们将讨论工具选择、流程设计、最佳实践以及持续改进的重要性,旨在帮助读者构建一个既灵活又可靠的自动化运维环境。
15 3
|
1天前
|
运维 监控 Kubernetes
构建高效自动化运维体系:基于容器技术的持续集成与持续部署(CI/CD)实践
【5月更文挑战第15天】 随着云计算和微服务架构的普及,传统的IT运维模式面临转型压力。为提高软件交付效率并降低运维成本,本文探讨了利用容器技术实现自动化运维的有效策略。重点分析了在持续集成(CI)和持续部署(CD)流程中,容器如何发挥作用,以及它们如何帮助组织实现敏捷性和弹性。通过具体案例研究,文章展示了容器化技术在自动化测试、部署及扩展中的应用,并讨论了其对系统稳定性和安全性的影响。
|
1天前
|
Java Maven
Maven 自动化构建
**Maven自动化构建确保依赖稳定性:当bus-core-api(1.0-SNAPSHOT)构建完成时,自动触发app-web-ui(依赖1.0)的构建,保证上下游项目同步。**
|
1天前
|
运维 监控 安全
构建高效自动化运维系统:基于容器技术的持续集成与持续部署(CI/CD)实践
【5月更文挑战第14天】 随着DevOps文化的深入人心,持续集成与持续部署(CI/CD)已成为现代软件工程不可或缺的组成部分。本文将探讨如何利用容器技术,尤其是Docker和Kubernetes,构建一个高效、可扩展的自动化运维系统。通过深入分析CI/CD流程的关键组件,我们将讨论如何整合这些组件以实现代码从提交到生产环境的快速、无缝过渡。文章还将涉及监控、日志管理以及安全性策略等运维考量,为读者提供一个全面的自动化运维解决方案蓝图。
|
1天前
|
运维 Kubernetes 持续交付
构建高效自动化运维体系:基于容器技术的持续集成与部署实践
【5月更文挑战第13天】 在现代软件开发周期中,持续集成(CI)和持续部署(CD)已成为提升开发效率、保障产品质量的关键环节。随着云计算和微服务架构的普及,容器技术如Docker和Kubernetes为运维领域带来了革命性的变革。本文旨在探讨如何利用容器技术构建一个高效、可靠的自动化运维体系,实现从代码提交到产品发布的全过程自动化管理。通过深入分析容器化技术的核心原理,结合实际案例,我们将阐述如何优化持续集成流程、确保自动化测试的覆盖率、以及实现无缝的持续部署。
14 2
|
1天前
|
运维 安全 API
构建高效自动化运维体系:Ansible与Docker的协同实践
【5月更文挑战第13天】在现代IT基础设施管理中,自动化运维已成为提升效率、确保一致性和降低人为错误的关键。本文通过深入探讨Ansible和Docker的集成实践,揭示了如何构建一个灵活、可扩展且高效的自动化运维体系。我们将从理论到实践,展示如何利用这两种技术实现自动化部署、管理和扩展应用服务,以及它们如何帮助运维团队应对快速变化的业务需求和复杂的IT环境。
16 1
|
1天前
|
存储 JavaScript 前端开发
使用Vue.js构建交互式前端的技术探索
【5月更文挑战第12天】Vue.js是渐进式前端框架,以其简洁和强大的特性深受开发者喜爱。它聚焦视图层,采用MVVM模式实现数据与视图的双向绑定,简化开发。核心特性包括响应式数据绑定、组件化、模板系统和虚拟DOM。通过创建Vue实例、编写模板及定义方法,可以构建交互式前端,如计数器应用。Vue.js让复杂、交互式的前端开发变得更加高效和易维护。
|
1天前
|
Linux Shell C语言
Linux:自动化构建 - make
Linux:自动化构建 - make
13 1

热门文章

最新文章