专栏名称: 前端之巅
InfoQ大前端技术社群:囊括前端、移动、Node全栈一线技术,紧跟业界发展步伐。
目录
相关文章推荐
北美留学生观察  ·  马斯克查出了美国“吸血鬼”,这帮“老不死的” ... ·  10 小时前  
科技美学  ·  小鹏M03如何开启哨兵模式 ·  10 小时前  
北美留学生观察  ·  欠薪停办?深圳一DSE学校开学前网传爆雷,办 ... ·  昨天  
北美留学生观察  ·  事关100万留学家庭,中央发布《教育强国建设 ... ·  2 天前  
51好读  ›  专栏  ›  前端之巅

Qwik vs. Next.js:你的下一个Web项目应该选哪个框架?

前端之巅  · 公众号  ·  · 2024-04-29 15:00

主要观点总结

本文介绍了作者对 Qwik 和 Next.js 两个框架的比较和选择。从开发经验、服务器 vs 客户端、缓存、React生态系统、图表、状态管理、Dev服务器、服务器端渲染等方面进行了详细讨论。最后总结了Qwik框架的优势和特色。

关键观点总结

关键观点1: Qwik 和 Next.js 都是流行的 Web 开发框架,各有其优点。

两个框架都有良好的文档和广泛的使用。

关键观点2: 开发者在比较两个框架时,需要考虑自身需求和项目特点。

作者从多个方面对两个框架进行了详细比较,包括性能、开发体验等。

关键观点3: Qwik 提供更好的开发体验,特别是其可恢复性和对 React 生态系统的集成。

作者认为 Qwik 的设计宗旨使得开发过程更加顺畅和灵活。

关键观点4: Next.js 在某些方面也有其优势,特别是 React 生态系统和图表库的支持。

但作者认为 Qwik 在某些方面提供了更细粒度的控制。

关键观点5: 最终选择哪个框架取决于项目的具体需求和开发团队的偏好。

无论选择哪个框架,关键是利用其优势来实现项目的目标。


正文

作者 | Samuel Mendenhall
译者 | 平川
策划 | Tina

本文最初发布于 Outshift 官方博客。

Qwik 是我进行 Web 项目开发的首选框架,而不是 Next.js。在本文中,我将探讨 Qwik 和 Next.js 的区别、优缺点。不过,我相信,由 Builder.io 创建的 Qwik 有潜力成为 Web 开发的未来。

为什么 Qwik 成为我的首选框架

最终,我选择了 Qwik 而不是 Next.js,原因有很多,其中包括开发经验、信号、可控程度、使用广大 React 生态系统的能力,以及 Qwik 框架的前瞻性特性。Next.js 是一个非凡的框架,我会毫不犹豫地推荐它。然而,Qwik 提供的开发体验是如此的引人入胜,设计是如此的新颖,以至于每次使用它编写代码我都会感到非常兴奋!

背景:从 jQuery 到 Qwik

作为一名全栈工程师,我在软件工程领域工作已经快 20 年了。我的前端之旅始于大约 15 年前。从纯 JavaScript 和 jQuery 开始,然后转向了 KnockoutJS、AngularJS 和 GWT。2013 年,React 出现,我成了一个非常早期的使用者,并从此爱上了它。近 10 年来,React 一直是我的首选库。在这个过程中,我也使用过各种其他的框架和库,但 React 一直是我事实上的前端库,直到今年我发现了 Qwik。

Qwik 是什么?

让我们看一下,Qwik 的文档是如何定义自己的:“Qwik 是一种具有可恢复性的新框架(没有 JS 的立即执行,也没有水合),为边缘而生,为 React 开发人员所熟悉。”这是什么意思呢?让我们来分析一下。

Qwik 使用了 JSX,所以感觉和 React 很像,但它有一个非常典型的特性:可恢复性。“可恢复性是指暂停在服务器上的执行,然后在客户端上恢复,而且无需重播和下载所有应用程序逻辑。”换句话说,就是可以渲染、暂停、恢复、渲染、暂停、恢复等等。

在大多数情况下,这对开发人员来说是透明的,不会增加复杂性。Qwik 和其他框架的根本区别就在于此。举例来说,在 React 中,页面在服务器上渲染,然后在客户端上水合,等所有必要的 JavaScript 都下载完成后,页面就可以交互了。当然,有一种例外情况是使用动态导入,但那仍然与可恢复性不同。

Qwik 的设计宗旨是使客户端 / 服务器之间的边界基本不再成问题。默认情况下,一切都在服务器上渲染,除非你特别使用函数(比如搭配使用 useVisibleTask$ 和 isBrowser)强制在客户端渲染。否则,除了少数特殊情况外,一般所有服务器渲染都是奏效的。

上述内容只是冰山一角。建议通过下面的 Qwik 文档链接详细了解相关概念,因为 Qwik 真的是一个非常独特的框架,可以解决其他框架中一直在设法缓解的问题。

  • Qwik 简介

  • 可恢复性

  • 渐进式

  • 反应性

Qwik 是一个相当新的框架,刚出现没几年。到目前为止,开发人员对它的讨论还比较少。我也是最近才在 All Things Open Conference 大会上发现了它。如果这是你第一次接触 Qwik 框架,还请花时间通读下文档。你会发现,这么做是值得的。

Next.js 是什么?

关于 Next.js 的文章很多,所以我就简单地说下。Next.js 是一个封装 React 库的重要框架。它是当前 React 开发的首选框架。按照其文档的说法,“Next.js 是一个用于构建全栈 Web 应用程序的 React 框架。开发人员可以使用 React Components 构建用户界面,使用 Next.js 开发附加功能并进行优化。在底层,Next.js 做了抽象,可以自动配置 React 所需的工具,比如打包、编译等等。这使得开发人员可以专注于应用程序构建,而不用把时间花在配置上。”

Qwik vs. Next.js

下面我从 7 个方面对 Qwik 和 Next.js 做了比较。对于每一个方面,我都会说明哪个框架更好。这样你就可以根据对你而言最重要的东西来评估每一个特性。

服务器 vs. 客户端

Next.js 对服务器和客户端组件做了非常明确的区分,而在 Qwik 中,在很大程度上,这完全不是个问题。在默认情况下,所有内容基本上都是在服务器渲染的,我认为这是件好事。

胜者:Qwik。

下面是 Next.js文档 中的一个例子:

// 以下是 Next.js 代码
// SearchBar 是一个客户端组件import SearchBar from './searchbar'// Logo 是一个服务器组件import Logo from './logo'
// Layout 默认是一个服务器组件export default function Layout({ children }: { children: React.ReactNode }) { return ( <> <nav> <Logo /> <SearchBar /> nav> <main>{children}main> > )}
// ---'use client'
export default function SearchBar({ children }: { children: React.ReactNode }) { return ( <> <main>Search!main> > )}
// ---'use client'
export default function Logo({ children }: { children: React.ReactNode }) { return ( <> <main>Logo!main> > )}

在 Qwik 中则无需定义“use client”:

// 以下是 Qwik 代码import { component$ } from '@builder.io/qwik';
import SearchBar from './searchbar'import Logo from './logo'
export default component$(() => { return ( <> <nav> <Logo /> <SearchBar /> nav> <slot /> > )});
// ---// SearchBar.tsxexport default component$(() => { return ( <> <main>Search!main> > ) });
// ---// Logo.tsximport { component$ } from '@builder.io/qwik';export default component$(() => { return ( <> <main>Logo!main> > )});

代码看起来非常类似,这完全在意料之中——它们都是 JSX。主要的一点是,在 Qwik 中不必定义“use client”或“use server”,因为默认一切都是服务器渲染。这极大地简化并改善了开发体验。虽然上面的例子微不足道,但如果你用过 Next.js 就会知道,使用服务器组件还是客户端组件,是经常需要考虑的一个设计选择和实现。

缓存

Next.js 提供了更强的缓存控制能力。Qwik 有缓存功能,你可以控制持续时间,但不能直接失效缓存。这是否会成为其成败的关键因素还有待观察。在实践中,这并不是什么大问题,但可以预见,它将成为一个痛点。

胜者:Next.js。

Next.js 允许开发人员像下面这样失效缓存:

// 以下是 Next.js 代码
export default async function Page() { const res = await fetch('https://...', { next: { tags: ['collection'] } }) const data = await res.json() // ...}
'use server'
import { revalidateTag } from 'next/cache'
export default async function action() { revalidateTag('collection')}

这是个很好的特性,也是 Qwik 所缺少的一大特性。Qwik 的方法是,当发生可能导致突变的服务器操作时,重新运行所有的 routeLoader$s(在当前的页面层次结构中获取调用)。这是有效的,但是缺少细粒度控制。

React 生态系统

Next.js 生来就与整个 React 生态系统做了原生集成。Qwik 可以通过 qwikify$ 函数访问广大的 React 生态系统。但按照 Qwik 文档的说法,应该将此视为一种 迁移策略。这是因为,封装在 qwikify$ 中的任何 React 组件都是单独渲染和水合的,这可能会影响性能。不过,相应地,Qwik 为这种水合提供了很大的灵活性。例如,你可以告诉 Qwik 等到浏览器 空闲 时才水合 React 组件。除了空闲之外,Qwik 还提供许多其他的控制机制。

Qwik 另一个不错的特性是,在渲染包含该组件的页面之前,它甚至不会拉取 React 库。对于页面 B 上的 qwikified React 组件,在浏览器渲染该页面并且满足各种条件之前(比如它在页面上可见),Qwik 将永远不会加载 React 库。Qwik 提供的控制比 Next.js 多。虽然 qwikify$ 被认为是一种迁移策略,但它很有效,你可以通过各种方法来减轻任何潜在的性能问题。

胜者:Qwik。

// 以下是 Next.js 代码
'use client'
import { Carousel } from 'acme-carousel'
export default Carousel
// ---
import Carousel from './carousel'
export default function Page() { return ( <div> <p>View picturesp>
{/* Works, since Carousel is a Client Component */} <Carousel /> div> )}

你会注意到,在 Next.js 中,你不能在服务器组件中直接使用客户端组件,你必须将第三方组件封装在另一个有“use client”的组件中。这个情况与 Qwik 类似,但是可控程度更高。对于 Qwik 的方法,我真正喜欢它的地方是其对水合的控制。在这里,Next.js 控制能力弱甚至没有,而 Qwik 允许你在加载、空闲、悬停等情况下控制水合。

// 以下是 Qwik 代码
/** @jsxImportSource react */import { qwikify$ } from '@builder.io/qwik-react';import { Carousel } from 'acme-carousel'
export default qwikify$(Carousel, { eagerness: 'hover' })
// ---// SomeComponent.tsximport { component$ } from '@builder.io/qwik';import Carousel from './carousel'
export default component$(() => { return ( <div> <p>View picturesp> <Carousel /> div> )});
图表

在撰写本文时,Qwik 还没有原生图表库。在 React 中,你有大量的库可以选择,甚至是过多了。虽说把像 Chart.js 这样的东西集成到 Qwik 中非常简单,但仍然只能在客户端渲染。为了充分利用 Qwik 的强大功能,需要创建一个可以在服务器端渲染的图表库。在此之前,虽然集成任何图表库都很容易,但都只能在客户端渲染。用户体验还算不错,但怎么说还是少了原生的服务器端渲染选项。顺便说一句,你可能会使用 svg 图表库或手动 svg 来渲染服务器端,但我还没有看到一个正式的 Qwik 图表库可以做到这一点。

得益于 React 生态系统中的原生图表库,Next.js 胜出。

状态管理

Qwik 提供了原生信号。如果你用过信号和 React useState,那就没有什么可比的了。信号轻松获胜。在 Next.js 中获取信号是一个悬而未决的问题,而结论是这需要在 React 库中完成。虽然有一些用户利用“猴补丁”成功地将 Preact 信号集成到了 Next.js 中,但结果似乎好坏参半。

胜者:Qwik。

// 以下是 Next.js 代码
'use client'
function HomePage() { // ... const [likes, setLikes] = React.useState(0);
function handleClick() { setLikes(likes + 1); }
return ( <div> {/* ... */} <button onClick={handleClick}>Likes ({likes})button> div> );}// 以下是 Qwik 代码
export default component$(() => { // ... const likes = useSignal(0);
return ( <div> {/* ... */} <button onClick={() => likes += 1}>Likes ({likes})button> div> );})

你也可以将信号作为 props 传递给子组件,并在那里修改它们。在没有回调函数的 React 中,直接实现是不可能的。

// 以下是 Qwik 代码
// Parent.tsxexport default component$(() => { // ... const likes = useSignal(0);
return (
);})
// Child.tsxtype Props = { likes: Signal;};export default component$((props) => { return (
);})
Dev 服务器

Qwik 使用了 Vite,而 Vite 正成为 Dev 服务器前端工作的主要支柱之一。Vite 提供了一些令人难以置信的特性,比如内置的反向代理和非常有效的模块处理和热模块重载。要了解更多信息,请查阅为什么选择 Vite。使用 SWC、Turbo 构建和开发 Next.js 仍然非常快,但 Vite 在这方面更有优势。

胜者:Qwik。

服务器端渲染

关于这一点,虽然我在“服务器 vs. 客户端”一节中已经介绍过,但我想在这里更深入地讨论下服务器端渲染。

当考虑渲染服务器组件以及浏览器何时从框架接收第一个 HTML 时,情况就复杂了。尽管方式不同,但 Next.js 和 Qwik 完成的任务相同。从表面上看,结果是相同的,只是不同框架特有的控制机制可以提供不同的开发体验。如果你读过 Next.js 的 loading-ui-and-streaming 文档,就会发现你可以利用 React Suspense 来实现 UI 的“即时”加载和渐进式解析。这非常好,Qwik 没有提供类似的即时功能,但你仍然可以实现相同的效果。

根据 Next.js 的说法,“导航是即时的,即使是以服务器为中心的路由。”关于这一点,让我更深入地描述一下其中的核心问题。首先,在服务器端渲染组件加载产品列表,如从某些外部源(很可能)加载产品列表。接下来,框架渲染组件并生成 HTML。在后端完全加载产品列表并生成 HTML 之前,你不会看到页面。因此,如果没有缓存,缓慢的外部 API(假设 5 秒)会使用户在整整 5 秒钟内看不到产品页面的任何 HTML。我们肯定都会同意,这种用户体验很糟糕,浏览器好什么都没做或没有响应。

Next.js 的处理方法是告诉你通过 loading.js 来使用 React Suspense。Suspense 使你可以在加载数据时呈现回退组件。然后,在数据加载完成时,用实际组件替换回退组件。这是一个非常好的特性,带来了很棒的开发体验。

Qwik 的处理方式有所不同。Qwik 有一个名为 routeLoader$ 的函数,它只在服务器上运行。Promise 必须在页面渲染之前完成解析。因此,对于上述产品组件,routeLoader$ 将被调用,而 Promise 将在 5 秒后解析完成,然后才渲染页面。Qwik 中没有类似 Suspense 的概念,但你可以借助 server$ streaming 完成同样的事情。不同之处在于,你必须自己管理数据加载,但同时,你对数据加载有了更多的控制权。例如,你可以加载前 10 个产品,然后渲染页面,然后再加载其余的产品。这是一个人为设计的例子,但可以说明问题。GitHub 上有一个关于 Qwik 的有趣的问题,它演示了一个用流加载数据的例子。你会看到,在 Qwik 中执行此类操作非常复杂。因此,在这方面,Next.js 因其简单性而胜出。







请到「今天看啥」查看全文