通过 100 多个技巧合集学习 Nuxt!
文章·  

Nuxt 在边缘运行

了解我们如何使 Nuxt 3 能够在边缘运行时上运行,以在靠近用户的服务器端渲染。

简介

在 2017 年 9 月,Cloudflare 推出了 Cloudflare Workers,使其能够在他们的 边缘网络 上运行 JavaScript。这意味着您的代码将在全球一百多个地点的整个边缘网络中大约 30 秒内完成部署。这项技术允许您专注于编写更接近用户的应用程序,无论他们身处世界何处(延迟约为 50 毫秒)。

Worker 的运行时与 Node.js 或浏览器不同,它使用 Google Chrome 开发的 JavaScript 引擎 V8 执行代码。到目前为止,您可以在其平台上运行的只是在到达您的服务器之前在边缘运行的小脚本,以提高性能或添加一些基于请求头部的逻辑,例如。

在 2020 年 11 月,在开发 Nuxt 3 时,我们决定在边缘运行时/V8 隔离区中生产环境运行 Nuxt

当使用像 CloudFlare Workers 这样的平台时,它解锁了能够在全球各地约 50 毫秒内进行服务器端页面渲染的能力,而无需处理服务器、负载均衡和缓存,每个百万请求大约花费 $0.3。截至今天,新的平台正在出现,例如 Deno Deploy,允许在 V8 隔离区上运行应用程序。

2024 年更新: 我发布了 NuxtHub,让您可以在您的 Cloudflare 账户上,零配置地使用 Nuxt 在边缘构建全栈应用程序。它包括数据库、blob 存储、KV、远程存储等。

挑战

为了使 Nuxt 能够在 Worker 中运行,我们必须重写 Nuxt 的某些部分,使其环境不可知(在 Node.js、浏览器或 V8 中运行)。

我们从服务器开始,创建了 unjs/h3:一个为高性能和可移植性而构建的最小 http 框架。它取代了我们在 Nuxt 2 中使用的 Connect,但具有兼容性,因此您可以继续使用 Connect/Express 中间件。在 Worker 中,对于每个传入的请求,它都会在生产环境中启动 Nuxt,将请求发送给它,然后发回响应。

在 Nuxt 2 中,在内存中启动生产环境中的服务器(也称为冷启动)的持续时间约为 ~300 毫秒,因为我们必须加载服务器和应用程序的所有依赖项才能处理请求。

通过在 h3 上工作,我们决定对附加到服务器的每个处理程序进行代码拆分,并且仅在请求时才延迟加载它们。当您启动 Nuxt 3 时,我们只在内存中加载 h3 和相应的处理程序。当请求传入时,我们加载与路由对应的处理程序并执行它。

通过采用这种方法,我们将冷启动时间从 ~300 毫秒缩短到 ~2 毫秒

为了在边缘运行 Nuxt,我们面临另一个挑战:生产包的大小。这包括服务器、Vue 应用程序和 Node.js 依赖项的组合。Cloudflare Workers 目前对 Worker 大小有限制,免费计划为 1MB,每月 5 美元的计划为 5MB。

为了实现这一点,我们创建了 unjs/nitro,我们的服务器引擎,当运行 nuxt build 命令时,它会将您的整个项目捆绑在一起,并将所有依赖项都包含在最终输出中。它使用 Rollupvercel/nft 来仅跟踪 node_modules 使用的代码,从而删除不必要的代码。基本 Nuxt 3 应用程序生成的输出总大小约为 700kB gzip。

最后,为了在开发(Node.js)和 Cloudflare 上的生产环境(边缘运行时)之间提供相同的开发人员体验,我们创建了 unjs/unenv:一个通过模拟或为已知依赖项添加 polyfill 来转换 JavaScript 代码以在任何地方(平台无关)运行的库。

在 Nuxt,我们认为您应该有自由选择最适合您的托管提供商。

这就是为什么您可以在以下位置部署具有边缘端渲染的 Nuxt 应用程序:

我们还支持许多其他部署提供商,包括静态托管传统的 Node.js 无服务器和服务器主机

推动全栈功能

现在我们已经在边缘运行时上运行了 Nuxt,我们可以做的不仅仅是渲染 Vue 应用程序。感谢服务器目录,创建一个 API 路由只需一个 TypeScript 文件。

要添加 /api/hello 路由,请创建一个 server/api/hello.ts 文件

server/api/hello.ts
export default defineEventHandler((event) => {
  return {
    hello: 'world'
  }
})

您现在可以在您的页面和组件中全局调用此 API

pages/index.vue
<script setup>
const { data } = await useFetch('/api/hello')
</script>

<template>
  <pre>{{ data }}</pre>
</template>

当我们创建 useFetch$fetch 时,需要注意的一件重要的事情是,在服务器端渲染期间,如果您调用您的 API 路由,它将模拟请求并直接调用函数代码:避免 HTTP 请求并减少页面渲染时间

在开发人员体验方面,您会注意到在创建服务器文件时,Nuxt 服务器会保持运行,而无需重建 Vue 应用程序。这是因为在创建 API 和服务器路由时,Nuxt 3 支持热模块替换 (HMR)。

此外,通过利用对象关系映射 (ORM),如 drizzle-orm,开发人员可以连接边缘和无服务器数据库,例如 D1TursoNeonPlanetscale 等。

我创建了 Atidone,一个开源演示,展示了在边缘运行的具有身份验证和数据库的全栈应用程序。源代码以 MIT 许可证在 GitHub 上提供,网址为 atinux/atidone

结论

我们对边缘端渲染及其解锁的功能感到兴奋。我们的 Nuxt 团队迫不及待地想看看您将在此基础上构建什么!

请随时加入我们的 Discord 服务器,或在 Twitter 上提及 @nuxt_js 来分享您的作品。