• 2024年前端技术都有哪些新变化?
  • 发布于 2个月前
  • 195 热度
    0 评论
2024年2月前端技术领域再次迎来了一系列激动人心的更新和进展。无论是新兴的包仓库JSR,还是JavaScript提案中的Set方法、Array.prototype.with()的加入,都预示着前端开发的未来将更加灵活和强大。本文将为您详细介绍这些技术的最新动态,帮助您紧跟前端开发的最前沿。

一、探索JSR:Deno团队的新型JavaScript/TypeScript仓库
在JavaScript和TypeScript的世界里,一个新的仓库概念——JSR(JavaScript/TypeScript registry),由Deno团队推出,正引起广泛关注。与传统的npm等包管理器不同,JSR提出了一种全新的仓库管理方式,旨在为开发者提供更加灵活和高效的代码共享平台。

npm作为JavaScript生态中的第一个可行的包管理器和仓库,开创了依赖管理的先河。然而,尽管包管理器领域经历了诸如yarn、pnpm等诸多创新,包仓库领域的创新却相对较少,仍然以npm仓库为核心。而JSR的出现,正是为了在这一领域带来新的变革。

JSR的一大亮点是对semver(语义化版本控制)表达式的支持,这对于Deno来说是一个重大的进步,因为Deno之前一直使用精确的版本号来管理依赖。JSR的这一特性,使得依赖管理变得更加灵活,开发者可以更加方便地指定依赖版本的范围,而不仅仅是一个固定的版本号。

此外,JSR的动态适配功能也颇具特色。开发者只需发布他们的源码,无论是TypeScript还是JavaScript,JSR都能确保用户消费到正确版本的代码。这意味着,无论是在Deno、Node.js等不同的运行时环境下,JSR都能提供特定于目标运行时的版本,极大地简化了开发者的工作。

对于TypeScript开发者来说,JSR还提供了一些特有的功能,比如限制性地快速类型检查(即“zapping”),以及通过自动生成文档来完整地记录发布的代码。这些特性不仅提高了代码的质量,也为开发者间的协作提供了便利。

最引人注目的是,JSR计划不久将生成类型定义和转译后的JavaScript代码,以便更好地服务于npm生态系统。这意味着,JSR不仅仅是一个仓库,它还努力成为一个桥梁,连接不同的JavaScript生态系统,促进它们之间的互通和融合。

更多细节:https://www.kitsonkelly.com/posts/jsr-first-impressions

二、JavaScript Sets大升级:更丰富的集合操作来了!
在ES2015规范中引入的JavaScript Set对象,提供了一种管理唯一值的集合方式,但一直以来它的功能似乎有些不够完整。好消息是,这一情况即将发生改变。最近,TC39委员会(负责ECMAScript规范的工作组)提出了一系列新的Set方法,使得集合操作在JavaScript中变得更加强大和灵活。

对于喜爱探索新技术的开发者来说,这不仅仅是一项技术更新,更是一次提升开发效率和代码质量的机遇。让我们一起来看看这些新功能,以及它们如何让我们的代码更加简洁高效。

新增Set操作方法概览
Set.prototype.union(other): 返回两个集合的并集,即包含两个集合中所有元素的新集合。
Set.prototype.intersection(other): 返回两个集合的交集,即同时存在于两个集合中的元素组成的新集合。
Set.prototype.difference(other): 返回两个集合的差集,即存在于第一个集合但不在第二个集合中的元素组成的新集合。
Set.prototype.symmetricDifference(other): 返回两个集合的对称差集,即只存在于其中一个集合中的元素组成的新集合。
Set.prototype.isSubsetOf(other): 判断一个集合是否为另一个集合的子集。
Set.prototype.isSupersetOf(other): 判断一个集合是否为另一个集合的超集。
Set.prototype.isDisjointFrom(other): 判断两个集合是否不相交,即没有任何共同的元素。

这些方法大大扩展了Set的功能,使得对集合的操作变得更加直观和方便。例如,如果你想要找出两种编程语言集合的交集,只需简单地调用intersection方法即可。这些操作不仅减少了开发者需要手动实现这些功能的工作量,也使得代码更加简洁明了。

实践示例
让我们通过一些简单的示例来看看这些新方法是如何工作的:
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
// 堆代码 duidaima.com
// 并集
const allLanguages = frontEndLanguages.union(backEndLanguages);
console.log(allLanguages); // => Set {"JavaScript", "HTML", "CSS", "Python", "Java"}

// 交集
const bothEndLanguages = frontEndLanguages.intersection(backEndLanguages);
console.log(bothEndLanguages); // => Set {"JavaScript"}

// 差集
const onlyFrontEnd = frontEndLanguages.difference(backEndLanguages);
console.log(onlyFrontEnd); // => Set {"HTML", "CSS"}
这些操作不仅适用于处理编程语言这样的简单示例,它们在处理任何需要集合操作的场景中都极为有用,比如处理用户权限集合、数据标签集合等。

支持情况
截至目前,这些新的Set方法已在Chrome 122+和Safari 17+中得到支持,Edge和Firefox也即将支持。这意味着开发者可以开始在他们的项目中尝试使用这些方法,为未来的JavaScript标准做好准备。

对于那些追求效率和编码质量的开发者,这些新增的Set操作方法无疑提供了更多的可能性。它们不仅使得代码更加简洁,也促进了开发者之间的最佳实践分享。在探索这些新特性的同时,我们也期待着JavaScript生态的进一步成长和发展。

更多细节:https://www.sonarsource.com/blog/union-intersection-difference-javascript-sets/

三、JavaScript 数组的不可变更新:探索 Array.prototype.with()

在现代Web开发中,数据的不可变性(Immutability)是一个重要的概念,特别是在使用React、Vue等前端框架时。不可变性帮助我们避免了数据共享和副作用相关的复杂问题,使得状态管理更加可预测。最近,JavaScript 数组获得了一项新的方法 Array.prototype.with(),它为我们提供了一种新的不可变更新数组的方式。


如何使用 Array.prototype.with()
Array.prototype.with() 方法允许我们在不修改原始数组的情况下,返回一个新数组,其中指定索引处的元素被更新为新的值。这意味着我们可以轻松实现数组元素的更新,同时保持原数组不变,非常适合实现不可变数据模式。
const ages = [10, 15, 20, 25];
const newAges = ages.with(1, 16);
// 堆代码 duidaima.com
console.log(newAges); // 输出: [10, 16, 20, 25]
console.log(ages); // 输出: [10, 15, 20, 25] (原数组未改变)
为什么选择 Array.prototype.with()
在之前,我们可能需要使用 map() 方法或者扩展运算符(...)来实现类似的不可变更新,但这些方法要么代码复杂,要么效率不是最优。相比之下,Array.prototype.with() 提供了一种更简洁、更直观的方式来更新数组中的单个元素,而不必担心原始数据被更改。

此外,Array.prototype.with() 方法的引入,是JavaScript语言对不可变数据模式支持的一种扩展。在现代Web应用中,不可变数据模式对于提高应用性能、简化状态管理具有重要意义。

其他相关的不可变方法
值得一提的是,Array.prototype.with() 并不孤单。JavaScript还引入了其他几种操作数组的不可变方法,如
Array.prototype.toReversed()、Array.prototype.toSorted() 和 Array.prototype.toSpliced()。这些方法同样不会改变原始数组,为开发者在处理数组时提供了更多的灵活性和选择。

Array.prototype.toReversed():返回一个新数组,元素顺序与原数组相反。
Array.prototype.toSorted():返回一个新数组,元素按照指定的排序方式排列。
Array.prototype.toSpliced():返回一个新数组,可以在不改变原数组的情况下添加、删除或替换数组中的元素。

通过这些方法,JavaScript为开发者提供了强大的工具,以更加高效、简洁的方式处理数组,特别是在涉及到不可变数据结构时。对于追求代码质量和性能优化的开发者来说,了解和掌握这些新特性无疑是大有裨益的。在日益复杂的前端应用开发中,合理利用这些工具,可以帮助我们构建更加稳定、高效的应用。

更多细节:https://web.dev/blog/array-with

四、javaScript Promise 集合方法深入解析
在JavaScript的异步编程世界中,Promise 是处理异步操作的核心。不过,除了常见的单个 Promise 使用外,JavaScript 还提供了几种强大的 Promise 集合方法,它们允许我们以不同的方式处理多个 Promise。这些方法包括 Promise.all()、Promise.allSettled()、Promise.any() 和 Promise.race(),它们各自有着独特的用途和行为。

Promise.all()
Promise.all() 方法用于处理一个 Promise 数组,当所有的 Promise 都成功解决后,它会以一个包含所有结果的数组形式解决。如果其中任何一个 Promise 失败,则整个 Promise.all() 调用会立即失败,返回第一个遇到的错误。这个方法非常适合并行执行多个异步任务时,只有当所有任务都成功完成后才继续执行。

Promise.allSettled()
Promise.allSettled() 是对 Promise.all() 的补充,用于处理一个 Promise 数组,并等待所有 Promise 完成,无论是成功还是失败。它解决时返回一个对象数组,每个对象表示对应 Promise 的结果,包括一个状态(fulfilled 或 rejected)和一个值或拒绝原因。这个方法非常适合你需要等待多个异步任务完成,并且需要知道每个任务的成功或失败结果时使用。

Promise.any()
Promise.any() 方法接收一个 Promise 数组,只要数组中的任何一个 Promise 成功解决,它就会解决,并返回第一个成功的 Promise 的结果。如果所有的 Promise 都失败了,Promise.any() 将返回一个 AggregateError,包含所有 Promise 的错误信息。这个方法适用于你有多个异步任务,但只需要其中任何一个成功的结果时。

Promise.race()
Promise.race() 方法也接收一个 Promise 数组,但它的行为与 Promise.any() 有所不同。Promise.race() 会解决或拒绝与数组中第一个解决或拒绝的 Promise 相同的结果。这个方法适合竞态条件的场景,比如设置一个超时时间,或者你需要的是第一个完成的结果,而不关心其他 Promise 的结果。

应用场景示例
Promise.all():在客户端应用中,你可能需要同时发起多个API请求,并且只有当所有请求都成功返回时才更新UI。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values); // 输出: [3, 42, "foo"]
});
Promise.allSettled():在一个数据同步任务中,你需要尝试从多个数据源同步数据,即使某些源失败了,你也希望知道哪些成功了,哪些失败了,以进行相应的处理。
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, '失败'));

Promise.allSettled([promise1, promise2]).then((results) => {
  results.forEach((result) => console.log(result.status)); // 输出: "fulfilled", "rejected"
});
Promise.any():当你有多个资源可以提供同样的数据时,使用 Promise.any() 可以从中选择最快返回的资源。
const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, '快速'));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, '慢速'));

Promise.any([promise1, promise2, promise3]).then((value) => {
  console.log(value); // 输出: "快速"
});
Promise.race():在一个网络请求中设置超时,如果请求在指定时间内没有响应,就使用备用内容。
const promise1 = new Promise((resolve, reject) => setTimeout(resolve, 500, '一'));
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, '二'));

Promise.race([promise1, promise2]).then((value) => {
  console.log(value);
}).catch((reason) => {
  console.log(reason); // 输出: "二"
});
通过这些集合方法,JavaScript 开发者可以更加灵活和有效地处理多个异步操作,使代码更加简洁和可维护。了解并掌握这些方法,将有助于提升你在现代Web开发中的异步编程能力。

更多细节:https://www.raymondcamden.com/2024/02/12/looking-at-the-javascript-promise-collection-methods

五、Tempo:兼容JavaScript Date对象并支持时区的日期时间库
在现代Web开发中,处理日期和时间是一个常见且复杂的任务,尤其是当涉及到时区操作时。JavaScript原生的Date对象提供了基础的日期和时间功能,但在处理国际化和时区转换时往往显得力不从心。这就是为什么Tempo这样的日期和时间库变得尤为重要。

Tempo的核心特性
与JavaScript Date对象的无缝工作:Tempo设计之初就考虑到了与JavaScript原生Date对象的兼容性,使得开发者可以轻松地在现有项目中引入和使用Tempo。

全面支持时区操作:Tempo提供了强大的时区支持,允许开发者轻松进行日期和时间的时区转换,解决了JavaScript Date对象在处理时区时的局限性。

利用Intl.DateTimeFormat:Tempo利用JavaScript的Intl.DateTimeFormatAPI来提取复杂的数据,如时区偏移量和符合地区习惯的日期格式,为开发者提供了一个简单的API来格式化、解析和操作日期。

为什么选择Tempo
简化日期和时间操作:通过提供一个简单直观的API,Tempo大大简化了日期和时间的格式化、解析和操作过程,让开发者可以更专注于业务逻辑的实现。
提高代码的可维护性:Tempo的方法命名清晰且语义化,使得相关代码易于理解和维护。
增强国际化支持:对于需要支持多语言和多时区的应用,Tempo的强大功能可以帮助开发者轻松实现复杂的国际化需求。

使用示例
假设你需要在一个国际化的应用中显示用户的注册日期,该日期需要根据用户的时区和地区格式进行显示。使用Tempo,你可以轻松实现这一需求:
import { format } from "@formkit/tempo"

format({
  date: new Date(),
  format: "HH:mm",
  tz: "Africa/Johannesburg",
})

format({
  date: new Date(),
  format: { time: "short" },
  tz: "America/Los_Angeles",
})

format({
  date: "1999-12-31T20:00Z",
  format: "YYYY-MM-DD HH:mm",
  tz: "Pacific/Chatham",
})
此示例仅为展示Tempo基本使用方法。实际上,Tempo还提供了更多高级功能,如时间运算、时区敏感的比较等,可以满足不同场景下对日期和时间处理的需求。

对于需要进行复杂日期和时间处理的JavaScript应用,Tempo提供了一个强大且易于使用的解决方案。它不仅克服了原生Date对象在国际化和时区处理上的不足,还通过提供简洁的API简化了日期和时间的操作。无论是构建需要支持多时区的Web应用,还是处理复杂的日期时间运算,Tempo都是一个值得考虑的库。

更多细节:https://tempo.formkit.com/

六、深入理解热模块替换(HMR)原理

热模块替换(Hot Module Replacement,简称HMR)是现代Web开发中一个极为强大的特性,它允许开发者在不刷新页面的情况下替换、添加或删除模块,从而实现即时预览改动效果。这种能力极大地提高了开发效率和体验,特别是在调整组件布局或样式时,能够立即看到变化,对于加速开发流程和提升反馈速度非常有帮助。

HMR在Vite中的工作原理
虽然HMR也存在于其他打包工具如Webpack和Parcel中,但Vite对HMR的实现具有其独特之处。Vite利用现代浏览器支持的ES模块导入特性,以及服务器端的即时编译能力,提供了一个轻量级且高效的HMR实现。

基本流程
监测文件变化:当开发中的文件发生变化时,Vite服务器会被触发并检测到这一变化。
编译更新的模块:Vite只重新编译发生变化的模块,而不是整个项目,这使得HMR非常快速。
推送更新到客户端:通过WebSocket连接,Vite服务器将更新的模块推送到浏览器端。
替换旧模块:浏览器端接收到更新后,会替换掉旧的模块实例,而不需要刷新页面。

Vite中HMR的优点
即时反馈:HMR提供了几乎即时的反馈,开发者可以立即看到他们所做的更改效果。
保持应用状态:因为不需要刷新页面,应用的状态(如用户输入的数据、当前的导航状态)可以在开发过程中保持不变。
更快的开发速度:相比于传统的完全重新加载,HMR显著减少了等待时间,从而加速了开发流程。

应用HMR的考虑因素
尽管HMR带来了显著的开发体验改进,但它的实现和使用也需要考虑一些因素:
模块接受自身更新:为了使HMR工作,模块需要编写代码来接受自己的更新。在Vite中,这通常意味着需要在模块中添加特定的HMR API调用。
状态管理:在某些情况下,模块的状态(如Vuex或Redux中的状态)需要特别处理,以确保在模块替换时状态不丢失。
依赖模块的更新:如果一个模块被多个地方依赖,需要确保这些依赖也能正确处理模块的更新。

深入阅读:https://bjornlu.com/blog/hot-module-replacement-is-easy

结束
总的来说,2024年2月的前端资讯展现了技术发展的迅速与创新的不断。从JSR的推出到JavaScript新特性的提案,每一项更新都在为开发者们打开新的可能性和提高开发效率。随着技术的不断进步,我们有理由相信,前端开发的未来将更加多彩,能够创造出更加丰富和高效的用户体验。让我们共同期待并探索这一切新技术带来的更多惊喜和机遇。

用户评论