前端工程化之构建工具

简介: 构建解决的问题包管理工具任务式构建工具模块化:模块定义与模块化的构建工具


自我的核心就在于独立之人格,自由之精神

👉 成熟的自我,就必然是孤独的

大家好,我是柒八九

最近,本来是想写一篇关于Promise的文章,在文章规划的时候,发现牵扯的东西有点多,需要再准备一下。

前几天,我们开辟了一个新的模块 前端工程化,今天我们继续来讨论和介绍这方面的知识。 因为,工程化的一些概念还是有些繁杂的,但是如果梳理得当,会对工程化有一个更深的认识。

那话不多说,开搞?!

文章概要

  1. 构建解决的问题
  2. 包管理工具
  3. 任务式构建工具
  4. 模块化:模块定义与模块化的构建工具

构建解决的问题

Grunt /Gulp/ webpack 等前端工具出现之前,前端资源的构建需要借助于其他开发领域的工具实现,比如 Ant(主要用 Java 项目的编译、构建、部署等), Make(主要用于 言的项目构建) 。

甚至专业构建 jscss 具也需要特殊的编程语言执行环境,比如 YUI Composer 最初需要依赖 Java 运行环境。

然而Node.js 的诞生和发展令前端工具生态不断壮大,目前我们所熟知的 Grunt /Gulp/webpack 等工具均是由 Node.js 为底层驱动平台的

Node.js 诞生之前,对于前端资源的构建工作只是进行基本的压缩和打包,因为当时前端项目自身的复杂度并不高,没有模块化开发、规范转译、css 预编译等现在看来非常普遍的需求。

简单介绍一下远古时期的文件压缩和合并工具。

文件压缩与合并工具

为了获得更好的访问体验,开发者需要更少的资源连接数更小的文件体积,这就分别对应了两类工具:

  • 文件压缩工具
  • 合并工具

压缩工具主要有

  1. JSMin
    用于去除 JS 代码中的注释和空格
  2. YUI Compressor
    基于 Java 的代码压缩工具
  3. Closure Compiler
    提供了比 YUI Compressor更多的代码优化功能
    并支持 Source Map 和多文件合并
  4. UglifyJS

从上到下,压缩与优化的性能不断完善


合并工具主要从图片资源代码层面两方面进行处理。

  • 图片合并
    CSS Sprite 技术的提出解决了网页中大量素材图片的加载性能问题
  • 代码文件的合并:
    利用Closure Compiler 工具中将多个文件合并为一个

而随着前端工程复杂度的不断提升,对网站中资源也不仅仅停留在压缩和打包上。需要利用构建工具,将源代码转化为宿主浏览器可执行的代码核心是资源的管理

前端的产出资源包括js/ css /HTML 等,分别对应的源代码则是

  • 领先于浏览器实现的 ECMAScript 规范编写的 JS 代码
  • LESS/SASS预编译语法编写的 css 代码
  • Jade/EJS/Mustache模板语法编写 HTML 代码

以上源代码是无法在浏览器环境下运行的,构建工作的核心便是将其转化为宿主可执行代码,分别对应:

  • ECMAScript 规范的转译
  • css 预编译语法转译
  • HTML 模板渲染

这些功能可以说是为了弥补浏览器自身功能的缺陷和不足,可以理解为面向语言的

除了语言本身,前端资源的构建处理还需要要考虑Web应用的性能因素

比如开发阶段使用模块化开发每个模块有独立 JS/CSS/ 图片等文件。 如果不做处理将每个文件独立上线, 无疑会增加客户端 HTTP 请求的数量 ,从而影响 Web 应用性能和用户体验。

我们可以从以下几点来窥探,构建阶段在性能方面的工作方向

  • 依赖打包
    1. 分析文件依赖关系,将同步依赖的文件打包在一起
    2.减少 HTTP 请求数量
  • 资源嵌入
    1. 比如小于 10KB 的图片编译为 base6 格式嵌入文档
    2. 减少HTTP 请求
  • 文件压缩
    1. 减小文件体积
    2. 缩短请求时间
  • hash 指纹
    1. 通过给文件名加入 hash 指纹
    2. 利用浏览器缓存策略

这些功能的目的是为了提高 Web 应用的性能和用户体验,可以理解为面向优化的

html 文件与js/ css /图片等资源是引用与被引用关系。 被引用的资源经过构建后通常有以下改动

  • 域名/路径改变
    1. 开发环境线上环境的域名肯定是不同的
    2. 不同类型的资源部署于不同的 CDN 服务器上
  • 文件名改变
    1. 经过构建之后文件名被加上 hash 指纹
    2. 内容的改动导致 hash 的改变

以上的改动最终会影响 html 文件对被引用资源的 URL 改变。 所以对于 html 件的构建工作需要注意在其引用资源 URL 改变时同步更新,这个功能通常被称为资源定位

之所以将被引用资源进行上文所述的改动,是由于测试环境生产环境的要借助部署策略应对。 构建在其中的作用可以理解为面向部署

构建需要解决的问题有三类

  1. 面向语言 (转译工作)
  2. 面向优化 (性能和用户体验)
  3. 面向部署 (不同环境下的资源定位)

包管理工具

在上文说到,在Node.js出现之前,前端针对资源的处理,只局限在压缩文件合并上,更悲惨的是,有时候还需要低三下四的去借用别的语言的运行时环境(runtime)。

随着Node.js 的发布,许多原先基于其他语言开发的工具包如今可以通过 Node.js 来实现,并通过 npm(Node Package Manager,即 node 包管理器)来安装使用。

现在主流的包管理器,主要有npm/yarn/pnpm

  • npm:
    1. 在 npm 安装依赖的过程中会引入大量的子包
    2. 在早期版本npm 3 之前)中会产生相同依赖包的大量重复拷贝
    产生node_modules hell
  • yarn:和 npm 相比,Yarn 的主要优点有
    1.  安装速度: Yarn 在安装依赖时采用的是并行操作,它在初次与重复安装依赖时,普遍都会比 npm 更快
    2. 稳定性:npm5 引入的 package-lock 文件,在每次执行 npm install时仍然会检查更新符合语义规则的依赖包版本,yarn.lock 则会严格保证版本的稳定性
    3. Plug'n'PlayPnP):Yarn 2.0 发布了 PnP的功能, PnP 方案具有提升项目安装与解析依赖的速度,以及多项目共享缓存(与普通缓存相比,免去了读写 node_modules 的大量 I/O 操作),节省占用空间等优势
  • pnpm:
    1. 节约磁盘空间提升安装速度
    2. 创建非扁平化的 node_modules 文件夹


任务式构建工具

使用自动化任务式构建工具来替代手工执行各种处理命令。

任务式构建工具主要由两类:

  • Grunt: 基于任务的构建工具(2012年发布)
  • Gulp: 流式构建工具(2013年发布)

GruntGulp 这两种任务式的构建工具的基本组成包括

  • 核心的处理工具(grunt-cli/gulp-cli
  • 配置文件(Gruntfile/Gulpfile
  • 一系列常用的任务插件
  • Clean
  • Watch
  • Copy
  • Concat
  • Uglify

在项目里通过编写配置文件,就可以定义工作流程中的各种自动化构建处理。

Grunt vs Gulp

  • 读写速度
    1. Gulp 在处理任务的过程中基于 NodeJS 的数据流,本质上是读写内存
    2. Grunt 则是基于临时文件, 读写速度上 Gulp 要快于 Grunt
  • 社区使用规模
    npmjs.com 的周下载量方面,Gulp 大约是 Grunt 的两倍
  • 配置文件的易用性
    相比描述不同插件配置信息的 Gruntfile 而言,使用 pipe 函数描述任务处理过程的方式通常更易于阅读

任务式构建工具的出现解决了开发流程中自动化执行预设任务的问题,但不能解决项目中代码如何组织成不同功能的代码包、不同代码之间如何相互依赖等问题


模块化:模块定义与模块化的构建工具

我们简单来描述下,从前端的莽荒时代,到现在ESModule一统天下,都经历了哪些模块化解决方案。

模块化的不同规范

CommonJS

CommonJS 出现之前,一个 JS 类库只能通过暴露全局对象的方式,供其他 JS 文件使用,这种处理方式,极易造成变量污染

CommonJS 作为非浏览器端JS 规范,从几个方面来描述该规范。

  • 模块定义
    一个模块即是一个 JS 文件,代码中module指向当前模块对象
  • 模块引用
    1. 通过引用 require() 函数来实现模块的引用
    2. 参数可以是相对路径也可以是绝对路径
    3. 在绝对路径的情况下,会按照 node_modules 规则递归查找,在解析失败的情况下,会抛出异常
  • 模块加载:
    1. require() 的执行过程是同步的
    2. 执行时即进入到被依赖模块的执行上下文中,执行完毕后再执行依赖模块的后续代码

AMD

适用于浏览器端异步加载模块化规范

  • 模块定义
    1. 通过define(id?, dependencies?, factory) 函数定义模块
    2.  id 为模块标识
    3.  dependencies 为依赖的模块
    4.  factory 为工厂函数
  • 模块引用
    1. 最早需要通过 require([id], callback) 方式引用
    2. 之后也支持了类似 CommonJSvar a = require('a') 的写法

UMD

UMD 本质上是兼容 CommonJSAMD 这两种规范的代码语法糖

通过判断执行上下文中是否包含 definemodule 来包装模块代码,适用于需要跨前后端的模块

ES Module

ES6版本中提出JS模块化

  • 模块定义
  • 模块内支持两种导出方式
  • 通过 export 关键字导出任意个数的变量
  • 通过 export default 导出
    一个模块中只能包含一个 default 的导出类型
  • 模块引用1. 通过import关键字引用其他模块
  • 引用方式分为
  • 静态引用:
    静态引用格式为import importClause from ModuleSpecifier
    import 表达式需要写在文件最外层上下文中
  • 动态引用
    动态引用的方式则是 import(),返回 promise 对象

模块化的构建工具

构建工具 作用&目标
RequireJS 支持 AMD 风格的模块化代码运行
Browserify CommonJS 风格的代码也运行在浏览器端
除了提供语法糖外
还提供了一些经过处理后且在浏览器端运行的 NodeJS 的核心模块
Babel 定位一直是 Transformer:即语法转换器
承担着将 ES6、JSX 等语法转换为 ES5 语法的核心功能
SystemJS 兼容各种模块化规范的运行时工具
Webpack 1. 一方面兼容各种模块化规范的标识方法
2. 另一方面将模块化的概念延伸到其他类型的文件中, 创造性地打造了一种完全基于模块的新的构建体系
Rollup 1. 率先实现了 Tree Shaking 功能
2. 天然支持 ES6 模块的打包

后记

分享是一种态度

参考资料:效率工程化/前端工程化体系设计与实践

全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。



相关文章
|
1月前
|
缓存 监控 前端开发
前端工程化:Webpack与Gulp的构建工具选择与配置优化
【10月更文挑战第26天】前端工程化是现代Web开发的重要趋势,通过将前端代码视为工程来管理,提高了开发效率和质量。本文详细对比了Webpack和Gulp两大主流构建工具的选择与配置优化,并提供了具体示例代码。Webpack擅长模块化打包和资源管理,而Gulp则在任务编写和自动化构建方面更具灵活性。两者各有优势,需根据项目需求进行选择和优化。
69 7
|
1月前
|
缓存 前端开发 JavaScript
前端工程化:Webpack与Gulp的构建工具选择与配置优化
【10月更文挑战第27天】在现代前端开发中,构建工具的选择对项目的效率和可维护性至关重要。本文比较了Webpack和Gulp两个流行的构建工具,介绍了它们的特点和适用场景,并提供了配置优化的最佳实践。Webpack适合大型模块化项目,Gulp则适用于快速自动化构建流程。通过合理的配置优化,可以显著提升构建效率和性能。
39 2
|
5月前
|
JSON 前端开发 JavaScript
前端工程化:Webpack配置全攻略
【7月更文挑战第14天】
76 6
|
5月前
|
JSON 缓存 前端开发
前端工程化:Webpack配置全攻略
【7月更文挑战第18天】
57 1
|
6月前
|
缓存 前端开发 JavaScript
前端性能优化实践与工程化
前端性能优化实践与工程化
|
5月前
|
缓存 前端开发 JavaScript
前端 JS 经典:构建工具
前端 JS 经典:构建工具
49 0
|
7月前
|
资源调度 JavaScript 前端开发
【前端开发---Vue2】史上最详细的Vue入门教程(六) --- 工程化开发和脚手架、组件注册
【前端开发---Vue2】史上最详细的Vue入门教程(六) --- 工程化开发和脚手架、组件注册
【前端开发---Vue2】史上最详细的Vue入门教程(六) --- 工程化开发和脚手架、组件注册
|
6月前
|
前端开发 安全 JavaScript
前端工程化实战 - 日程管理
前端工程化实战 - 日程管理
47 0
|
6月前
|
存储 JavaScript 前端开发
前端工程化
前端工程化
50 0
|
6月前
|
运维 前端开发 JavaScript
什么是前端工程化❓
什么是前端工程化❓
75 0