你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

在 Azure Static Web Apps 中部署混合 Next.js 网站(预览)

本教程介绍如何利用对 Next.js 功能(例如服务器端呈现 (SSR) 和 API 路由)的支持将 Next.js 网站部署到 Azure Static Web Apps

注意

Next.js 混合支持为预览版。

先决条件

资源 说明
Azure 帐户 如果没有一个订阅处于活动状态的 Azure 帐户,可以免费创建一个
GitHub 帐户 如果没有 GitHub 帐户,可以免费创建一个帐户
Node.js 安装最新版本的 Node.js。
Next.js CLI 安装最新版本的 Next.js CLI。 有关详细信息,请参阅 Next.js 入门指南

预览中不支持的功能

混合呈现的 Next.js 中不支持 Static Web Apps 的以下功能:

  • 选择 Azure 服务:使用 Azure Functions、Azure 应用服务、Azure 容器应用或 Azure API 管理的链接 API。
  • SWA CLI 功能:SWA CLI 本地仿真和部署。
  • 部分功能支持:不支持 staticwebapp.config.json 文件中的以下属性:
    • 不支持导航回退。
    • 必须在 next.config.js 内配置对 Next.js 应用程序中路由的路由重写。
    • staticwebapp.config.json 文件中的配置优先于 next.config.js 中的配置。
    • 应使用 next.config.js 来处理 Next.js 站点的配置,以便实现功能完全兼容。
  • 跳过生成:对于 skip_api_build=true 情况下的 Next.js 应用程序,静态 Web 应用不会删除开发依赖项,也不会默认添加 sharp 包。 如果需要这些优化,请在传递 skip_app_build=true 之前将它们添加到自定义生成步骤。
  • 增量静态重新生成 (ISR):不支持映像缓存。

注意

混合 Next.js 应用程序的最大应用大小为 250 MB。 通过 Next.js 使用独立功能来优化应用大小。 如果这还不够,而你的应用大小要求超过 250 MB,请考虑使用静态 HTML 导出的 Next.js

创建存储库

本文使用 GitHub 模板存储库,使你能够轻松入门。 该模板具有一个部署到 Azure Static Web Apps 的入门应用。

  1. 导航到以下位置以创建新存储库。

    https://github.com/staticwebdev/nextjs-hybrid-starter/generate

  2. 将存储库命名为 my-first-static-web-app

  3. 选择“从模板创建存储库”。

    “根据模板创建存储库”按钮的屏幕截图。

创建静态 Web 应用

现在,已经创建了存储库,可以从 Azure 门户创建静态 Web 应用。

  1. 转到 Azure 门户
  2. 选择“创建资源”。
  3. 搜索“静态 Web 应用”。
  4. 选择“静态 Web 应用”。
  5. 选择“创建”

在“基本信息”部分中,首先配置新应用,并将其链接到 GitHub 存储库。

Azure 门户中“基本信息”部分的屏幕截图。

设置
订阅 选择 Azure 订阅。
资源组 选择“新建”链接并在文本框中输入 static-web-apps-test。
名称 在文本框中输入 my-first-static-web-app
计划类型 选择“免费”。
Source 选择 GitHub 并在必要时登录到 GitHub。

选择“使用 GitHub 登录”,然后向 GitHub 进行身份验证。

登录 GitHub 后,输入存储库信息。

设置 Value
组织 选择你的组织。
存储库 选择“my-first-web-static-app”。
分支 选择“主要”。

Azure 门户中存储库详细信息的屏幕截图。

注意

如果看不到任何存储库:

  • 则可能需要在 GitHub 中授权 Azure Static Web Apps。 浏览到 GitHub 存储库,转到“设置”>“应用程序”>“授权 OAuth 应用”,选择“Azure Static Web Apps”,然后选择“授予”。
  • 可能需要在 Azure DevOps 组织中授权 Azure Static Web Apps。 必须是组织的所有者才能授予权限。 通过 OAuth 请求第三方应用程序访问。 有关详细信息,请参阅 使用 OAuth 2.0 授权访问 REST API

在“生成详细信息”部分中,添加特定于首选前端框架的配置详细信息。

  1. 从“生成预设”下拉列表中选择“Next.js”

  2. 保留“应用位置”框中的默认值。

  3. 将“API 位置”框留空。

  4. 将“输出位置”框留空

选择“查看 + 创建”。

“创建”按钮的屏幕截图。

查看网站

通过两个方面来部署静态应用。 第一、创建构成应用的基础 Azure 资源。 第二、生成和发布应用程序的工作流。

在转到新静态站点之前,必须先完成部署生成的运行。

Static Web Apps“概述”窗口显示了一系列链接,可帮助你与 Web 应用交互。

Azure Static Web Apps 概述窗口的屏幕截图。

选择“选择此处以查看 GitHub Actions 运行的状态”横幅,即可转到针对存储库运行的 GitHub Actions。 确认部署作业完成后,即可通过生成的 URL 转到网站。

GitHub Actions 工作流完成后,可以选择该 URL 链接以在新选项卡中打开网站。

在本地设置 Next.js 项目以进行更改

  1. 将新存储库克隆到计算机。 确保将 <GITHUB_ACCOUNT_NAME> 替换为你的帐户名称。

    git clone http://github.com/<GITHUB_ACCOUNT_NAME>/my-first-static-web-app
    
  2. 在 Visual Studio Code 或首选代码编辑器中打开项目。

设置服务器端呈现

托管后端会自动应用于所有计划中的每个混合 Next.js 部署。 但可以通过向站点分配自定义后端来微调性能并对后端进行更多控制。 如果在托管后端与链接后端之间切换,则站点不会停机。

引入自己的后端

引入自己的后端时,可提高性能并获得对 Next.js 服务器端渲染的更多控制。 使用以下步骤,为站点设置自定义后端。

以下步骤演示了如何将自定义后端关联到标准计划及更高版本的静态 Web 应用。

注意

链接后端仅适用于使用标准计划或更高版本的网站。

  1. 前往 Azure 门户中的静态 Web 应用。

  2. 从侧菜单中选择“设置”,然后选择“API”。

  3. 选择“配置链接后端”。

  4. 可创建新的应用服务计划,或选择现有的应用服务计划。

    所选应用服务计划必须至少使用一个 S1 SKU。

  5. 单击“链接”。

使用服务器组件添加服务器呈现的数据

若要使用应用路由器在 Next.js 项目中添加服务器呈现的数据,需编辑 Next.js 组件,添加服务器端操作以在组件中呈现数据。 默认情况下,Next.js 组件是可由服务器呈现的服务器组件

  1. 打开 app/page.tsx 文件并添加一个操作,用于设置服务器端计算变量的值。 示例包括提取数据或其他服务器操作。

    export default function Home() {
        const timeOnServer = new Date().toLocaleTimeString('en-US');
        return(
            ...
        );
    }
    
  2. next/cache 导入 unstable_noStore,并在 Home 组件中调用它,以确保动态呈现路由。

    import { unstable_noStore as noStore } from 'next/cache';
    
    export default function Home() {
        noStore();
    
        const timeOnServer = new Date().toLocaleTimeString('en-US');
        return(
            ...
        );
    }
    

    注意

    此示例强制此组件的动态呈现,以演示服务器当前时间的服务器呈现。 Next.js 的应用路由器模型建议缓存单个数据请求,以优化 Next.js 应用的性能。 阅读 Next.js 中的数据提取和缓存,了解更多信息。

  3. 更新 app/pages.tsx 中的 Home 组件以呈现服务器端数据。

    import { unstable_noStore as noStore } from 'next/cache';
    
    export default function Home() {
        noStore();
    
        const timeOnServer = new Date().toLocaleTimeString('en-US');
        return(
            <main className="flex min-h-screen flex-col items-center justify-between p-24">
                <div>
                    This is a Next.js application hosted on Azure Static Web Apps with hybrid rendering. The time on the server is <strong>{timeOnServer}</strong>.
                </div>
            </main>
        );
    }
    

添加 API 路由

除了服务器组件,Next.js 还提供路由处理程序,可用于创建到 Next.js 应用程序的 API 路由。 可以在客户端组件中提取这些 API。

首先添加 API 路由。

  1. app/api/currentTime/route.tsx 创建新文件。 此文件保存新 API 终结点的路由处理程序。

  2. 添加处理程序函数以从 API 返回数据。

    import { NextResponse } from 'next/server';
    
    export const dynamic = 'force-dynamic';
    
    export async function GET() { 
        const currentTime = new Date().toLocaleTimeString('en-US');
    
        return NextResponse.json({ 
            message: `Hello from the API! The current time is ${currentTime}.`
        });
    }
    
  3. app/components/CurrentTimeFromAPI.tsx 创建新文件。 此组件为从浏览器提取 API 的客户端组件创建容器。

  4. 添加一个客户端组件,用于提取此文件中的 API。

    'use client';
    
    import { useEffect, useState } from 'react';
    
    export function CurrentTimeFromAPI(){
        const [apiResponse, setApiResponse] = useState('');
        const [loading, setLoading] = useState(true);
    
        useEffect(() => {
            fetch('/api/currentTime')
                .then((res) => res.json())
                .then((data) => {
                setApiResponse(data.message);
                setLoading(false);
                });
            }, 
        []);
    
        return (
            <div className='pt-4'>
                The message from the API is: <strong>{apiResponse}</strong>
            </div>
        )
    }
    

此客户端组件使用 useEffect React 挂钩提取 API,以在加载完成后呈现该组件。 'use client' 指令将此元素标识为客户端组件。 有关详细信息,请参阅客户端组件

  1. 编辑 app/page.tsx 以导入和呈现 CurrentTimeFromAPI 客户端组件。

    import { unstable_noStore as noStore } from 'next/cache';
    import { CurrentTimeFromAPI } from './components/CurrentTimeFromAPI';
    
    export default function Home() {
        noStore();
    
        const timeOnServer = new Date().toLocaleTimeString('en-US');
        return(
            <main className="flex min-h-screen flex-col items-center justify-between p-24">
                <div>
                    This is a Next.js application hosted on Azure Static Web Apps with hybrid rendering. The time on the server is <strong>{timeOnServer}</strong>.
                </div>
                <CurrentTimeFromAPI />
            </main>
        );
    }
    
  2. 页面上显示了 API 路由的结果。

显示 API 路由输出的屏幕截图。

配置 Next.js 的运行时版本

某些 Next.js 版本需为具体的 Node.js 版本。 若要配置具体 Node 版本,可通过设置 package.json 文件的 engines 属性指定一个版本。

{
  ...
  "engines": {
    "node": "18.17.1"
  }
}

设置 Next.js 的环境变量

Next.js 在生成时和请求时使用环境变量,以同时支持服务器端呈现的静态页面生成和动态页面生成。 因此,应同时在生成和部署任务中以及 Azure Static Web Apps 资源的“环境变量”中设置环境变量。

...
      - name: Build And Deploy
        id: builddeploy
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
          repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for GitHub integrations (i.e. PR comments)
          action: "upload"
          app_location: "/" 
          api_location: ""
          output_location: "" 
        env:
          DB_HOST: ${{ secrets.DB_HOST }}
          DB_USER: ${{ secrets.DB_USER }}
          DB_DATABASE: ${{ secrets.DB_DATABASE }}
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
          DB_PORT: ${{ secrets.DB_PORT }}
...

启用独立功能

当应用程序大小超过 250 MB 时,Next.js 输出文件跟踪功能可帮助优化应用大小并提高性能。

输出文件跟踪会创建整个应用程序的压缩版本,其中包含必要包依赖项。 此包内置到名为 .next/standalone 的文件夹中。 有了此包,你的应用便可以自行部署,无需 node_modules 依赖项。

若要启用 standalone 功能,请将以下属性添加到 next.config.js

module.exports ={
    output:"standalone",
}

接下来,在 package.json 文件中配置 build 命令,以便将静态文件复制到独立输出。

{
  ...
  "scripts": {
    ...
    "build": "next build && cp -r .next/static .next/standalone/.next/ && cp -r public .next/standalone/"
    ...
  }
  ...
}

为部署配置路由和中间件

可以使用自定义重定向、重写和中间件配置 Next.js 项目的路由处理。 这些处理程序通常用于身份验证、个性化、路由和国际化。 自定义处理会影响 Next.js 站点的默认路由,并且配置必须与 Static Web Apps 上的托管兼容。

Static Web Apps 会通过在生成时向网站添加页面来验证是否成功部署了 Next.js 网站。 该页面将命名为 public/.swa/health.html,Static Web Apps 会通过导航到 /.swa/health.html 并验证成功响应来验证是否成功启动和部署了网站。 中间件和自定义路由(包括重定向和重写)可能会影响 /.swa/health.html 路径的访问,这可以阻止 Static Web Apps 的部署验证。 要为成功部署到 Static Web Apps 配置中间件和路由,请执行以下步骤:

  1. 排除中间件配置的 middleware.ts(或 .js)文件中以 .swa 开头的路由。

    export const config = {
      matcher: [
        /*
         * Match all request paths except for the ones starting with:
         * - .swa (Azure Static Web Apps)
         */
        '/((?!.swa).*)',
      ],
    }
    
  2. next.config.js 中配置重定向,以排除以 .swa 开头的路由。

    module.exports = {
        async redirects() {
            return [
              {
                source: '/((?!.swa).*)<YOUR MATCHING RULE>',
                destination: '<YOUR REDIRECT RULE>', 
                permanent: false,
              },
            ]
        },
    };
    
  3. next.config.js 中配置重写规则,以排除以 .swa 开头的路由。

    module.exports = {
        async rewrites() {
            return {
                beforeFiles: [
                    {
                        source: '/((?!.swa).*)<YOUR MATCHING RULE>',
                        destination: '<YOUR REWRITE RULE>', 
                    }
                ]
            }
        },
    };
    

这些代码片段可排除以 .swa 开头的路径,使自定义路由或中间件停止处理这些请求。 这些规则可确保路径在部署验证期间按预期方式解析。

为 Next.js 启用日志记录

遵循 Next.js 服务器 API 故障排除的最佳实践,将日志记录添加到 API 以捕获这些错误。 在 Azure 上登录将使用 Application Insights。 为了预加载此 SDK,你需要创建一个自定义启动脚本。 若要了解详细信息,请访问以下链接:

清理资源

如果不打算继续使用此应用程序,可按以下步骤删除 Azure 静态 Web 应用实例:

  1. 打开 Azure 门户
  2. 在顶部搜索栏中搜索“my-first-web-static-app”。
  3. 选择应用名称。
  4. 选择“删除” 。
  5. 选择“是”以确认删除操作(此操作的完成可能需要一些时间)。

后续步骤