跳到主要内容

Next.js

Next.js 是一个基于 React 的现代全栈开发框架,由 Vercel 团队开发和维护。它提供了开箱即用的功能,包括服务器端渲染(SSR)、静态站点生成(SSG)、增量静态再生(ISR)、API 路由、文件系统路由等,帮助开发者快速构建高性能的 Web 应用。

本指南将详细介绍如何在 CloudBase HTTP 云函数上开发和部署 Next.js 应用程序,充分利用 Next.js 的全栈能力。

前提条件

在开始之前,请确保您已具备以下条件:

  • Node.js 环境:版本 18.18 或更高(推荐 20.x)
  • npm、yarn 或 pnpm:包管理工具
  • React 基础知识:熟悉 React 组件开发
  • CloudBase 账号:已注册腾讯云账号并开通云开发服务

环境准备

1. 检查 Node.js 版本

# 检查 Node.js 版本
node --version

# 检查 npm 版本
npm --version

2. 创建 Next.js 应用

如果您已有 Next.js 应用,可以跳过此步骤。

# 使用官方 CLI 创建 Next.js 应用
npx create-next-app@latest my-nextjs-app

# 进入项目目录
cd my-nextjs-app

创建过程中的配置选项:

✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes

推荐配置:

  • TypeScript: Yes(更好的类型安全)
  • ESLint: Yes(代码质量保证)
  • Tailwind CSS: Yes(快速样式开发)
  • App Router: Yes(Next.js 13+ 推荐)

应用开发

1. 项目结构

使用 App Router 的项目结构:

my-nextjs-app/
├── app/
│ ├── globals.css # 全局样式
│ ├── layout.tsx # 根布局
│ ├── page.tsx # 首页
│ ├── api/ # API 路由
│ │ └── users/
│ │ └── route.ts # /api/users 端点
│ └── about/
│ └── page.tsx # /about 页面
├── components/ # 可复用组件
├── lib/ # 工具函数
├── public/ # 静态资源
├── next.config.js # Next.js 配置
└── package.json

2. 配置 Next.js

编辑 next.config.js 文件,为 CloudBase 部署进行优化:

/** @type {import('next').NextConfig} */
const nextConfig = {
// 输出模式设置为 standalone,便于云函数部署
output: 'standalone',

// 禁用 x-powered-by 头
poweredByHeader: false,

// 压缩配置
compress: true,

// 图片优化配置
images: {
domains: ['example.com'], // 添加外部图片域名
unoptimized: true, // 在云函数环境中禁用图片优化
},

// 环境变量配置
env: {
CUSTOM_KEY: process.env.CUSTOM_KEY,
},

// 重定向配置
async redirects() {
return [
{
source: '/old-path',
destination: '/new-path',
permanent: true,
},
];
},

// 头部配置
async headers() {
return [
{
source: '/api/:path*',
headers: [
{
key: 'Access-Control-Allow-Origin',
value: '*',
},
{
key: 'Access-Control-Allow-Methods',
value: 'GET, POST, PUT, DELETE, OPTIONS',
},
{
key: 'Access-Control-Allow-Headers',
value: 'Content-Type, Authorization',
},
],
},
];
},
};

module.exports = nextConfig;

3. 创建页面组件

首页组件

编辑 app/page.tsx

import Link from 'next/link';
import { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Next.js on CloudBase',
description: 'A Next.js application running on CloudBase HTTP Functions',
};

export default function HomePage() {
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
<div className="container mx-auto px-4 py-16">
<div className="text-center">
<h1 className="text-4xl font-bold text-gray-900 mb-6">
欢迎使用 Next.js on CloudBase
</h1>
<p className="text-xl text-gray-600 mb-8">
这是一个运行在 CloudBase HTTP 云函数上的 Next.js 应用
</p>

<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-12">
<FeatureCard
title="服务器端渲染"
description="享受 Next.js 的 SSR 能力,提升 SEO 和首屏加载速度"
icon="🚀"
/>
<FeatureCard
title="API 路由"
description="内置 API 路由功能,轻松构建全栈应用"
icon=""
/>
<FeatureCard
title="云原生部署"
description="一键部署到 CloudBase,享受弹性伸缩和高可用"
icon="☁️"
/>
</div>

<div className="mt-12 space-x-4">
<Link
href="/about"
className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
>
了解更多
</Link>
<Link
href="/api/users"
className="bg-gray-600 text-white px-6 py-3 rounded-lg hover:bg-gray-700 transition-colors"
>
测试 API
</Link>
</div>
</div>
</div>
</div>
);
}

interface FeatureCardProps {
title: string;
description: string;
icon: string;
}

function FeatureCard({ title, description, icon }: FeatureCardProps) {
return (
<div className="bg-white p-6 rounded-lg shadow-md">
<div className="text-3xl mb-4">{icon}</div>
<h3 className="text-xl font-semibold mb-2">{title}</h3>
<p className="text-gray-600">{description}</p>
</div>
);
}

关于页面

创建 app/about/page.tsx

import { Metadata } from 'next';

export const metadata: Metadata = {
title: '关于我们 - Next.js on CloudBase',
description: '了解更多关于 Next.js 和 CloudBase 的信息',
};

export default function AboutPage() {
return (
<div className="min-h-screen bg-gray-50 py-16">
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl font-bold text-gray-900 mb-8">关于 Next.js on CloudBase</h1>

<div className="bg-white rounded-lg shadow-md p-8 mb-8">
<h2 className="text-2xl font-semibold mb-4">技术栈</h2>
<ul className="space-y-2 text-gray-700">
<li><strong>Next.js 14+</strong>: React 全栈框架</li>
<li><strong>TypeScript</strong>: 类型安全的 JavaScript</li>
<li><strong>Tailwind CSS</strong>: 实用优先的 CSS 框架</li>
<li><strong>CloudBase</strong>: 腾讯云原生应用开发平台</li>
</ul>
</div>

<div className="bg-white rounded-lg shadow-md p-8">
<h2 className="text-2xl font-semibold mb-4">特性</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 className="font-semibold mb-2">前端特性</h3>
<ul className="space-y-1 text-gray-700">
<li>• 服务器端渲染 (SSR)</li>
<li>• 静态站点生成 (SSG)</li>
<li>• 增量静态再生 (ISR)</li>
<li>• 自动代码分割</li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-2">后端特性</h3>
<ul className="space-y-1 text-gray-700">
<li>• API 路由</li>
<li>• 中间件支持</li>
<li>• 数据库集成</li>
<li>• 身份认证</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

4. 创建 API 路由

用户 API

创建 app/api/users/route.ts

import { NextRequest, NextResponse } from 'next/server';

// 模拟用户数据
let users = [
{ id: 1, name: 'Alice Johnson', email: 'alice@example.com', role: 'admin' },
{ id: 2, name: 'Bob Smith', email: 'bob@example.com', role: 'user' },
{ id: 3, name: 'Charlie Brown', email: 'charlie@example.com', role: 'user' },
];

// GET /api/users - 获取所有用户
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const role = searchParams.get('role');

let filteredUsers = users;
if (role) {
filteredUsers = users.filter(user => user.role === role);
}

return NextResponse.json({
success: true,
data: filteredUsers,
total: filteredUsers.length,
});
} catch (error) {
return NextResponse.json(
{ success: false, error: 'Failed to fetch users' },
{ status: 500 }
);
}
}

// POST /api/users - 创建新用户
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { name, email, role = 'user' } = body;

// 简单验证
if (!name || !email) {
return NextResponse.json(
{ success: false, error: 'Name and email are required' },
{ status: 400 }
);
}

// 检查邮箱是否已存在
const existingUser = users.find(user => user.email === email);
if (existingUser) {
return NextResponse.json(
{ success: false, error: 'Email already exists' },
{ status: 409 }
);
}

const newUser = {
id: Math.max(...users.map(u => u.id), 0) + 1,
name,
email,
role,
};

users.push(newUser);

return NextResponse.json(
{ success: true, data: newUser },
{ status: 201 }
);
} catch (error) {
return NextResponse.json(
{ success: false, error: 'Failed to create user' },
{ status: 500 }
);
}
}

健康检查 API

创建 app/api/health/route.ts

import { NextResponse } from 'next/server';

export async function GET() {
return NextResponse.json({
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
environment: process.env.NODE_ENV || 'development',
version: process.env.npm_package_version || '1.0.0',
});
}

5. 添加中间件

创建 middleware.ts(项目根目录):

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
// 添加安全头
const response = NextResponse.next();

response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('Referrer-Policy', 'origin-when-cross-origin');

// API 路由的 CORS 处理
if (request.nextUrl.pathname.startsWith('/api/')) {
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');

// 处理预检请求
if (request.method === 'OPTIONS') {
return new Response(null, { status: 200, headers: response.headers });
}
}

return response;
}

export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico).*)',
],
};

本地开发

1. 安装依赖

# 安装项目依赖
npm install

# 或使用 yarn
yarn install

# 或使用 pnpm
pnpm install

2. 启动开发服务器

# 开发模式启动
npm run dev

# 指定端口启动
npm run dev -- -p 9000

3. 测试应用

访问以下 URL 测试应用:

# 首页
curl http://localhost:3000

# 关于页面
curl http://localhost:3000/about

# 健康检查 API
curl http://localhost:3000/api/health

# 用户 API
curl http://localhost:3000/api/users

# 创建用户
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"David Wilson","email":"david@example.com","role":"admin"}'

# 按角色筛选用户
curl "http://localhost:3000/api/users?role=admin"

部署配置

1. 构建应用

# 构建生产版本
npm run build

# 启动生产服务器(本地测试)
npm start

2. 创建 scf_bootstrap 文件

在项目根目录创建 scf_bootstrap 文件(无扩展名):

#!/bin/sh
# 设置端口为 9000(CloudBase HTTP 云函数要求)
export PORT=9000

# 设置 Node.js 环境为生产模式
export NODE_ENV=production

# 启动 Next.js 应用
npm start

3. 设置文件权限

chmod +x scf_bootstrap

4. 优化 package.json

确保 package.json 包含正确的脚本:

{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "14.0.0",
"react": "^18",
"react-dom": "^18"
}
}

5. 环境变量配置

创建 .env.local 文件(本地开发):

# 数据库配置
DATABASE_URL=your_database_url

# API 密钥
API_SECRET_KEY=your_secret_key

# 外部服务配置
EXTERNAL_API_URL=https://api.example.com

部署到 CloudBase

方式一:控制台部署

  1. 打开 CloudBase 控制台 访问 腾讯云 CloudBase 控制台

  2. 创建 HTTP 云函数

    • 函数类型:选择「HTTP 云函数」
    • 函数名称:输入 nextjs-app(或自定义名称)
    • 提交方法:选择「本地上传文件夹」
  3. 配置部署参数

    • 函数代码:选择您的 Next.js 项目根目录
    • 运行环境:选择 Node.js 20.19(推荐)或 Node.js 18.15
    • 自动安装依赖:开启
    • 内存配置:建议 512MB 或更高
  4. 完成部署 点击「创建」按钮,等待部署完成

方式二:CLI 部署(敬请期待)

访问应用

部署成功后,您可以参考通过 HTTP 访问云函数设置自定义域名访问HTTP 云函数

您可以测试以下接口:

  • 根路径:/ - Express 欢迎页面
  • 健康检查:/health - 查看应用状态

性能优化

1. 代码分割

// 动态导入组件
import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() => import('../components/HeavyComponent'), {
loading: () => <p>Loading...</p>,
ssr: false, // 禁用服务器端渲染
});

export default function Page() {
return (
<div>
<DynamicComponent />
</div>
);
}

2. 图片优化

import Image from 'next/image';

export default function OptimizedImage() {
return (
<Image
src="/hero-image.jpg"
alt="Hero Image"
width={800}
height={600}
priority // 优先加载
placeholder="blur" // 模糊占位符
blurDataURL="data:image/jpeg;base64,..." // 占位符数据
/>
);
}

3. 缓存策略

// app/api/data/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
const data = await fetchData();

return NextResponse.json(data, {
headers: {
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
},
});
}

最佳实践

1. 错误处理

创建 app/error.tsx

'use client';

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h2 className="text-2xl font-bold mb-4">出错了!</h2>
<p className="text-gray-600 mb-4">{error.message}</p>
<button
onClick={reset}
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
>
重试
</button>
</div>
</div>
);
}

2. 加载状态

创建 app/loading.tsx

export default function Loading() {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-blue-600"></div>
</div>
);
}

3. 404 页面

创建 app/not-found.tsx

import Link from 'next/link';

export default function NotFound() {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h1 className="text-6xl font-bold text-gray-900 mb-4">404</h1>
<h2 className="text-2xl font-semibold mb-4">页面未找到</h2>
<p className="text-gray-600 mb-8">抱歉,您访问的页面不存在。</p>
<Link
href="/"
className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
>
返回首页
</Link>
</div>
</div>
);
}

常见问题

Q: 部署后页面样式丢失?

A: 检查以下几点:

  • 确认 next.config.js 中的 output: 'standalone' 配置
  • 检查静态资源路径是否正确
  • 验证 CSS 文件是否正确打包

Q: API 路由返回 404 错误?

A: 常见原因:

  • 检查 API 路由文件命名是否为 route.tsroute.js
  • 确认文件位置在 app/api/ 目录下
  • 验证 HTTP 方法是否正确导出

Q: 应用启动缓慢?

A: 优化建议:

  • 使用 output: 'standalone' 减少包大小
  • 启用代码分割和懒加载
  • 优化图片和静态资源
  • 考虑使用 ISR 或 SSG

Q: 如何连接数据库?

A: 推荐使用 CloudBase 数据库或其他云数据库:

// lib/database.ts
import { MongoClient } from 'mongodb';

const client = new MongoClient(process.env.DATABASE_URL!);

export async function connectToDatabase() {
if (!client.topology?.isConnected()) {
await client.connect();
}
return client.db('nextjs_app');
}

// app/api/users/route.ts
import { connectToDatabase } from '@/lib/database';

export async function GET() {
const db = await connectToDatabase();
const users = await db.collection('users').find({}).toArray();
return NextResponse.json({ data: users });
}

Q: 如何处理环境变量?

A: 在 CloudBase 控制台配置环境变量,或使用 .env 文件:

// 在组件中使用环境变量
const apiUrl = process.env.NEXT_PUBLIC_API_URL; // 客户端可访问
const secretKey = process.env.SECRET_KEY; // 仅服务器端可访问

总结

通过本指南,您已经学会了如何在 CloudBase HTTP 云函数上开发和部署 Next.js 应用。Next.js 的全栈能力结合 CloudBase 的云原生特性,可以帮助您快速构建现代化的 Web 应用。

建议在实际项目中根据业务需求进一步优化应用性能、添加数据库集成、实现身份认证等功能,充分发挥 Next.js 和 CloudBase 的优势。