前端构建系统阐述

​​​​​ 开发人员编写 JavaScript;浏览器运行 JavaScript。
从根本上来说,前端开发中不需要任何构建步骤。那么为什么我们在现代前端有一个构建步骤呢?

随着前端代码库变得越来越大,并且开发人员的人体工程学变得越来越重要,将 JavaScript 源代码直接发送给客户端会导致两个主要问题:

  1. 不支持的语言功能:由于 JavaScript 在浏览器中运行,并且存在许多不同版本的浏览器,因此您使用的每种语言功能都会减少可以执行 JavaScript 的客户端数量。此外,像 JSX 这样的语言扩展不是有效的 JavaScript,并且不会在任何浏览器中运行。

  2. 性能:浏览器必须单独请求每个 JavaScript 文件。在大型代码库中,这可能会导致数千个 HTTP 请求来呈现单个页面。过去,在 HTTP/2 之前,这也会导致数千次 TLS 握手。

    此外,在加载所有 JavaScript 之前,可能需要多次连续的网络往返。例如,如果 index.js 导入 page.js 且 page.js 导入 button.js ,则需要三个连续的网络往返才能完全加载 JavaScript。这称为瀑布问题。

    由于长变量名称和空白缩进字符,源文件也可能变得不必要的大,从而增加了带宽使用和网络加载时间。

前端构建系统处理源代码并发出一个或多个针对发送到浏览器而优化的 JavaScript 文件。由此产生的可分发内容通常对于人类来说是难以辨认的。

1. 构建步骤

前端构建系统通常包含三个步骤:转译、捆绑和压缩。

某些应用程序可能不需要全部三个步骤。例如,较小的代码库可能不需要捆绑或缩小,并且开发服务器可以为了性能而跳过捆绑和/或缩小。还可以添加其他自定义步骤。

有些工具实现多个构建步骤。值得注意的是,捆绑器通常会实现所有三个步骤,并且单独的捆绑器可能足以构建简单的应用程序。复杂的应用程序可能需要为每个构建步骤提供专门的工具来提供更大的功能集。

1.1.转译

转译通过将以现代版本的 JavaScript 标准编写的 JavaScript 转换为旧版本的 JavaScript 标准来解决不支持的语言功能的问题。如今,ES6/ES2015 是一个共同目标。

框架和工具也可能引入转译步骤。例如,JSX 语法必须转换为 JavaScript。如果一个库提供 Babel 插件,通常意味着它需要一个转译步骤。此外,TypeScript、CoffeeScript 和 Elm 等语言必须转换为 JavaScript。

CommonJS 模块 (CJS) 还必须转换为浏览器兼容的模块系统。 2018 年浏览器广泛支持 ES6 模块 (ESM) 后,普遍建议转译为 ESM。此外,ESM 更容易优化和进行树更新,因为其导入和导出是静态定义的。

目前常用的转译器有 Babel、SWC 和 TypeScript Compiler。

  1. Babel (2014) 是标准转译器:用 JavaScript 编写的慢速单线程转译器。许多需要转译的框架和库都是通过 Babel 插件来实现的,要求 Babel 成为构建过程的一部分。然而,Babel 很难调试并且经常会令人困惑。

  2. SWC (2020) 是一个用 Rust 编写的快速多线程转译器。它声称比 Babel 快 20 倍;因此,它被较新的框架和构建工具使用。它支持转译 TypeScript 和 JSX。如果您的应用程序不需要 Babel,SWC 是一个更好的选择。

  3. TypeScript Compiler (tsc) 还支持转译 TypeScript 和 JSX。它是 TypeScript 的参考实现,也是唯一功能齐全的 TypeScript 类型检查器。然而,它非常慢。虽然 TypeScript 应用程序必须使用 TypeScript 编译器进行类型检查,但对于其构建步骤,替代转译器的性能会更高。

如果您的代码是纯 JavaScript 并使用 ES6 模块,也可以跳过转译步骤。

针对不支持的语言功能子集的替代解决方案是 polyfill。 Polyfill 在运行时执行,并在执行主应用程序逻辑之前实现任何缺少的语言功能。但是,这会增加运行时成本,并且某些语言功能无法进行多填充。请参阅 core-js。

所有捆绑器本质上也是转译器,因为它们解析多个 JavaScript 源文件并发出新的捆绑 JavaScript 文件。这样做时,他们可以选择在发出的 JavaScript 文件中使用哪些语言功能。一些捆绑器还能够解析 TypeScript 和 JSX 源文件。如果您的应用程序有简单的转译需求,您可能不需要单独的转译器。

1.2.绑定

绑定解决了发出大量网络请求的需要和瀑布问题。绑定器将多个 JavaScript 源文件连接成一个 JavaScript 输出文件(称为捆绑包),而不改变应用程序行为。浏览器可以在单个往返网络请求中有效地加载该包。

目前常用的绑定器有 Webpack、Parcel、Rollup、esbuild 和 Turbopack。

  1. Webpack(2014)在 2016 年左右获得了极大的流行,后来成为标准捆绑器。与当时通常与 Gulp 任务运行器一起使用的 Browserify 不同,Webpack 开创了“加载器”,可以在导入时转换源文件,从而允许 Webpack 编排整个构建管道。

    加载器允许开发人员透明地将静态资源导入 JavaScript 文件中,将所有源文件和静态资源组合到单个依赖关系图中。使用 Gulp 时,每种类型的静态资源都必须作为单独的任务构建。 Webpack 还支持开箱即用的代码拆分,从而简化了其设置和配置。

    Webpack 速度慢且是单线程,用 JavaScript 编写。它具有高度可配置性,但其许多配置选项可能会令人困惑。

  2. Rollup (2016) 利用了 ES6 模块的广泛浏览器支持及其启用的优化,即树摇动。它产生的包大小比 Webpack 小得多,导致 Webpack 后来采用了类似的优化。 Rollup 是一个用 JavaScript 编写的单线程打包器,性能仅比 Webpack 稍高一些。

  3. Parcel (2018) 是一个低配置捆绑器,旨在“开箱即用”,为构建过程的所有步骤和开发人员工具需求提供合理的默认配置。它是多线程的,并且比 Webpack 和 Rollup 快得多。 Parcel 2 在底层使用 SWC。

  4. Esbuild (2020) 是一个为并行性和最佳性能而设计的捆绑器,用 Go 编写。它的性能比 Webpack、Rollup 和 Parcel 提高了数十倍。 Esbuild 实现了一个基本的转译器和一个压缩器。然而,它的功能不如其他捆绑器,提供的插件 API 有限,无法直接修改 AST。无需使用 esbuild 插件修改源文件,而是可以在将文件传递到 esbuild 之前对其进行转换。

  5. Turbopack (2022) 是一个快速的 Rust 捆绑器,支持增量重建。该项目由 Vercel 构建,并由 Webpack 的创建者领导。它目前处于测试阶段,可能会在 Next.js 中选择加入。

如果您的模块很少或网络延迟非常低(例如在本地主机上),则跳过捆绑步骤是合理的。一些开发服务器还选择不为开发服务器捆绑模块。

1.2.1.代码分割

默认情况下,客户端 React 应用程序会转换为单个包。对于具有许多页面和功能的大型应用程序,捆绑包可能非常大,从而抵消了捆绑的原始性能优势。

将捆绑包分成几个较小的捆绑包或代码拆分可以解决此问题。一种常见的方法是将每个页面拆分为单独的包。使用 HTTP/2,共享依赖项也可以分解到它们自己的包中,以避免以很少的成本进行重复。此外,大型模块可能会拆分为单独的捆绑包并按需延迟加载。

代码分割后,每个包的文件大小大大减小,但现在需要额外的网络往返,可能会重新引入瀑布问题。代码分割是一种权衡。

由 Next.js 普及的文件系统路由器优化了代码分割权衡。 Next.js 为每个页面创建单独的包,仅包含该页面在其包中导入的代码。加载页面会并行预加载该页面使用的所有包。这优化了包大小,而不会重新引入瀑布问题。文件系统路由器通过为每页创建一个入口点 ( pages/**/*.jsx ) 来实现这一点,而不是传统客户端 React 应用程序的单个入口点 ( index.jsx )。

1.2.2.摇树

一个包由多个模块组成,每个模块包含一个或多个导出。通常,给定的包只会使用它导入的模块的导出子集。捆绑器可以在称为树摇动的过程中删除其模块中未使用的导出。这优化了包的大小,缩短了加载和解析时间。

Tree Shaking 依赖于源文件的静态分析,因此当静态分析变得更具挑战性时,它就会受到阻碍。影响摇树效率的两个主要因素:

  1. 模块系统:ES6 模块具有静态导出和导入,而 CommonJS 模块具有动态导出和导入。因此,捆绑器在摇树 ES6 模块时能够更加积极和高效。

  2. 副作用: package.json 的 sideEffects 属性声明模块在导入时是否有副作用。当存在副作用时,由于静态分析的限制,未使用的模块和未使用的导出可能不会进行树摇动。

1.2.3.静态资源

静态资产(例如 CSS、图像和字体)通常在捆绑步骤中添加到可分发文件中。它们还可以在缩小步骤中针对文件大小进行优化。

在 Webpack 之前,静态资产是作为独立的构建任务在构建管道中与源代码分开构建的。要加载静态资源,应用程序必须通过其在可分发文件中的最终路径来引用它们。因此,围绕 URL 约定(例如 /assets/css/banner.jpg 和 /assets/fonts/Inter.woff2 )仔细组织资源是很常见的。

Webpack“加载器”允许从 JavaScript 导入静态资产,将代码和静态资产统一到单个依赖关系图中。在捆绑过程中,Webpack 将静态资源导入替换为可分发文件中的最终路径。此功能使静态资产能够与其源代码中的关联组件一起组织,并为静态分析创造了新的可能性,例如检测不存在的资产。

重要的是要认识到静态资产(非 JavaScript 或转换为 JavaScript 文件)的导入不是 JavaScript 语言的一部分。它需要一个配置为支持该资产类型的捆绑器。幸运的是,Webpack 之后的捆绑器也采用了“loader”模式,使得这个功能变得司空见惯。

1.3.压缩

压缩解决了不必要的大文件问题。缩小器可以减小文件的大小而不影响其行为。对于 JavaScript 代码和 CSS 资产,压缩器可以缩短变量、消除空格和注释、消除死代码并优化语言功能的使用。对于其他静态资源,压缩器可以执行文件大小优化。压缩器通常在构建过程结束时在捆绑包上运行。

目前常用的几个 JavaScript 压缩器是 Terser、esbuild 和 SWC。 Terser 是从无人维护的 uglify-es 分叉出来的。它是用 JavaScript 编写的,速度有点慢。前面提到的 Esbuild 和 SWC 除了其他功能之外还实现了压缩器,并且比 Terser 更快。

目前常用的几个 CSS 压缩器是 cssnano、csso 和 Lightning CSS。 Cssnano 和 CSSo 是用 JavaScript 编写的纯 CSS 压缩器,因此速度有些慢。 Lightning CSS 是用 Rust 编写的,据称比 cssnano 快 100 倍。 Lightning CSS 还支持 CSS 转换和捆绑。

2. 开发者工具

上述基本前端构建管道足以创建优化的生产可分发文件。有几类工具可以增强基本的构建管道并改善开发人员的体验。

2.1.元框架

前端领域因选择要使用的“正确”包的挑战而臭名昭著。例如,在上面列出的五个捆绑程序中,您应该选择哪一个?

元框架提供了一组精心挑选的软件包,包括构建工具,可以协同并支持专门的应用程序范例。例如,Next.js 专注于服务器端渲染(SSR),Remix 专注于渐进增强。

元框架通常提供一个预配置的构建系统,无需您将其拼接在一起。他们的构建系统具有适用于生产和开发服务器的配置。

与元框架一样,Vite 等构建工具为生产和开发提供预配置的构建系统。与元框架不同,它们不强制采用专门的应用程序范例。它们适用于通用前端应用程序。

2.2.源图Sourcemaps 

构建管道发出的可分配信息对于大多数人来说是难以辨认的。这使得调试发生的任何错误变得困难,因为它们的回溯指向难以辨认的代码。

Sourcemaps 通过将可分发文件中的代码映射回其在源代码中的原始位置来解决此问题。浏览器和分类工具(例如 Sentry)使用源映射来恢复和显示原始源代码。在生产中,源映射通常对浏览器隐藏,仅上传到分类工具以避免公开源代码。

构建管道的每个步骤都可以发出源映射。如果使用多个构建工具来构建管道,则源映射将形成一条链(例如 source.js -> transpiler.map -> bundler.map -> minifier.map

然而,大多数工具无法解释源映射链;他们期望可分发文件中的每个文件最多有一个源映射。源映射链必须扁平化为单个源映射。预配置的构建系统将解决这个问题(参见Vite的 combineSourcemaps 功能)。

2.3.热加载

开发服务器通常提供热重载功能,该功能可以在源代码更改时自动重建新的包并重新加载浏览器。虽然大大优于手动重建和重新加载,但它仍然有点慢,并且所有客户端状态在重新加载时都会丢失。

热模块替换通过替换正在运行的应用程序中更改的包(就地更新)来改进热重载。这保留了未更改模块的客户端状态,并减少了代码更改和更新应用程序之间的延迟。

但是,每次代码更改都会触发导入它的所有包的重建。这具有相对于包大小的线性时间复杂度。因此,在大型应用中,由于重新捆绑成本不断增加,热模块更换可能会变得缓慢。

目前由 Vite 倡导的无捆绑模式通过不捆绑开发服务器来解决这一问题。相反,Vite 直接向浏览器提供 ESM 模块,每个模块对应一个源文件。在此范例中,每次代码更改都会触发前端中的单个模块替换。这导致相对于应用程序大小而言,刷新时间复杂度接近恒定。但是,如果您有很多模块,则初始页面加载可能需要更长的时间。

2.4.莫诺回购Monorepo

在拥有多个团队或多个应用程序的组织中,前端可能会分为多个 JavaScript 包,但保留在单个存储库中。在这种架构中,每个包都有自己的构建步骤,它们一起形成包的依赖关系图。应用程序驻留在依赖图的根部。

Monorepo 工具协调依赖图的构建。它们通常提供增量重建、并行性和远程缓存等功能。借助这些功能,大型代码库可以享受小型代码库的构建时间。

更广泛的行业标准 monorepo 工具(例如 Bazel)支持广泛的语言、复杂的构建图和密封执行。然而,前端 JavaScript 是最难与这些工具完全集成的生态系统之一,目前几乎没有现有技术。

幸运的是,有几种专门为前端设计的 monorepo 工具。不幸的是,它们缺乏 Bazel 等人的灵活性和稳健性,尤其是封闭式执行。

目前常用的前端专用 monorepo 工具是 Nx 和 Turborepo。 Nx 更加成熟且功能丰富,而 Turborepo 是 Vercel 生态系统的一部分。过去,Lerna 是将多个 JavaScript 包链接在一起并将其发布到 NPM 的标准工具。 2022 年,Nx 团队接管了 Lerna,Lerna 现在在底层使用 Nx 来为构建提供动力。

较新的构建工具是用编译语言编写的,并且强调性能。 2019 年的前端构建速度非常慢,但现代工具大大加快了速度。然而,现代工具的功能集较小,有时与库不兼容,因此遗留代码库通常无法轻松切换到它们。

Next.js 兴起后,服务器端渲染 (SSR) 变得更加流行。 SSR 不会给前端构建系统带来任何根本差异。 SSR 应用程序还必须向浏览器提供 JavaScript,因此它们执行相同的构建步骤。

附:

1、Monorepo 是 mono-repository 的缩写,指的是将多个项目的代码存储在同一个代码仓库中的一种方式。与之相对的是 polyrepo,即每个项目都有各自独立的代码仓库。Monorepo 并不是一个新的概念,很多大型科技公司,如 Google、Facebook、Microsoft 等,早已采用这种代码管理方式。

在 Monorepo 中,所有项目共享同一个代码库,可能会包含多个不同的应用程序、库和服务。每个项目在代码库中都有自己独立的目录结构,但所有项目共享同一套版本控制系统和代码管理工具。这种方法可以促进代码的重用、统一管理依赖以及更高效的协作。

文章来源:https://sunsetglow.net/posts/frontend-build-systems.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/387492.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3后台管理系统 vue3+vite+pinia+element-plus+axios上

前言 项目安装与启动 使用vite作为项目脚手架 # pnpm pnpm create vite my-vue-app --template vue安装相应依赖 # sass pnpm i sass # vue-router pnpm i vue-router # element-plus pnpm i element-plus # element-plus/icon pnpm i element-plus/icons-vue安装element-…

C++第一篇 入门基础

目录 1.C的第一个程序 2.c历代版本 3.命名空间 3.1 namespace关键字 namespace的用法: namespace中定义函数 namespace中定义结构体 C中的域: 3.2就近原则 4.命名空间的使用 5.C输入输出 6.缺省参数 全缺省: 半缺省:必须从右往左连续缺省(也…

爆“卷”的AI视频,大厂向左,创企向右

文|白 鸽 编|王一粟 “生成的人物一转身就变成老外,怎么解决呢?” “没有办法,10s中动作大的,人物一致性有问题,只能抽卡,多刷几个,选择一个变化不大的。” 在一个以…

RocketMQ Server Windows安装

RocketMQ阿里开发 开源给apache 官网:RocketMQ 官方网站 | RocketMQ 下载后解压 配置环境变量 注意启动顺序 双击 注意 4.9.0这个版本必须 jdk 8 高了用不了 namesrv是注册中心的作用 broke是核心用于接收生产者消息 存储消息 发送给消费者消息 类似DubboZookeeper…

Java红娘相亲交友平台系统源码小程序

💕遇见真爱,从“红娘相亲交友平台系统”开始!👫 🌹【精准匹配,缘分不再擦肩而过】 还在为茫茫人海中找不到那个TA而烦恼吗?“红娘相亲交友平台系统”利用先进的大数据分析技术,根据…

匿名内部类

一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类,嵌套其他的类称为外部类。 类的五大成员:属性、方法、构造器、代码块、内部类 内部类最大的特点的就是直接访问私有属性,并且可以体现类鱼类之间的包含关系。 基本…

北斗三号海上人员落水报警及示位搜救系统升级方案

随着海洋经济的快速发展,海上作业活动日益频繁,人员安全问题也日益凸显。传统的海上救援手段存在诸多不足,如救援响应时间长、定位不准确等。为了提高海上救援的效率和成功率,北斗三号海上人员落水报警及示位搜救系统应运而生。该…

微波传感器 - 从零开始认识各种传感器【第二十期】

微波传感器|从零开始认识各种传感器 1、什么是微波传感器 微波传感器是一种利用微波技术进行探测和测量的传感器。 一般来说,微波是波长为1到1000毫米的电磁波。使用微波传感器,在不接触目标物体的情况下,通过检测和分析微波信号的反射、散…

Matplotlib柱形图大揭秘:让数据‘站’起来,比增高鞋垫还管用!

1. Matplotlib绘制柱形图/柱状图/条形图 柱状图是一种用矩形柱来表示数据分类的图表,柱状图可以垂直绘制,也可以水平绘制,它的高度与其表示的数据成正比关系 # 导包 import numpy as np import pandas as pd import matplotlib.pyplot as p…

机械学习—零基础学习日志(高数16——函数极限性质)

零基础为了学人工智能,真的开始复习高数 这里我们继续学习函数极限的性质。 局部有界性 充分条件与必要条件 极限存在是函数局部有界的充分条件。什么是充分条件,什么是必要条件呢?我这里做了一点小思考,和大家分享&#xff0c…

Windows11下 Visual Studio 2022 + Qt6 的 WebSocket 线程池异步 客户端

Windows11下 Visual Studio 2022 + Qt6 的 WebSocket 线程池异步 客户端 1 开发 WebSocket 客户端1.1 开发环境1.1.1 为Qt 6安装 websockets1.2 .基于Qt6的 QWebSocket 客户端示例1.2.1 实现 WebSocket 客户端1.2.2 创建 QtQWesocketClient1.2.3 创建QWebsocket对象1.2.3.1 添加…

iOS object-C 解答算法:找到所有数组中消失的数字(leetCode-448)

找到所有数组中消失的数字(leetCode-448) 题目如下图:(也可以到leetCode上看完整题目,题号448) 光看题看可能有点难以理解,我们结合示例1来理解一下这道题. 有8个整数的数组 nums [4,3,2,7,8,2,3,1], 求在闭区间[1,8]范围内(即1,2,3,4,5,6,7,8)的数字,哪几个没有出现在数组 …

新装centos7虚拟机如何配置网络,NAT配置固定IP

首先声明,我想使用的是NAT连接模式,并且设置完IP之后,使得这个IP固定住,以后不会再变了。 文章目录 1,打开Vmware软件的【编辑】-【虚拟网络编辑器】2,先选择VMnet8(画1处)&#xf…

解析capl文件生成XML Test Module对应的xml工具

之前一直用的CAPL Test Module来写代码,所有的控制都是在MainTest()函数来实现的,但是有一次,代码都写完了,突然需要用xml的这种方式来实现,很突然,之前也没研究过,整理这个xml整的一身汗&#…

Hive3:库操作常用语句

1、创建库 create database if not exists myhive;2、选择库 use myhive;3、查看当前选择的库 SELECT current_database();4、查看库详细信息 desc database myhive;可以查看数据文件在hdfs集群中的存储位置 5、创建库时制定hdfs的存储位置 create database myhive2 …

安全防护软件的必要性:从微软蓝屏事件谈起

最近微软遭遇了的大规模蓝屏事件,让全球很多用户措手不及。这次事件告诉我们,保护我们的电脑和数据,安全防护软件是多么重要。 微软蓝屏事件源于网络安全公司CrowdStrike的技术更新错误,导致全球范围内大量Windows用户系统崩溃&a…

什么是五力分析?5分钟带你了解它在企业财务经营中的应用与价值!

如今,随着全球化进程的不断加速,市场环境复杂多变,市场竞争日益激烈,财务经营已经成为了企业应对复杂市场环境、保持自身竞争力的关键。体系化的五力分析平台能够为企业提供一套全面的解决方案,帮助企业在盈利能力、偿…

HamonyOS性能优化工具和方法

性能优化,如何做到更快的启动、更流畅的使用,概括图如下 ArkTS高性能编程: 1. ArkTS规则:有利于方舟编译运行时进行编译优化 2. 使用AOT(Ahead Of Time)模式对应用进行编译优化:方舟编译运行时通过采用PGO(Profile-Gui…

React 学习——组件内通信(兄弟之间)

A组件 > B组件 核心思路&#xff1a; 1、A组件先通过子传父的方式把数据传给父组件App 2、App拿到数据后通过父传子的方式再传递给B组件 import { useState } from "react" function A({onGetMsg}){const AMsg 我是A组件的消息return (<div><button…

ESP-ADF适配到自定义开发板中

ESP-ADF适配到自定义开发板中 前言:项目开发完了,来记录一下开发过程。 安装: 这里采用vscode+ESP-IDF+ESP-ADF的开发方式。 安装esp-idf的方法很简单,网上都是,这里不说了。想用esp-adf那么你idf的环境肯定是已经搭建好了。 安装adf也很简单,一步完成。 按下F1,选…