普通视图

发现新文章,点击刷新页面。
昨天 — 2026年1月12日首页

Next.js第二十一章(环境变量)

作者 小满zs
2026年1月12日 20:54

环境变量

环境变量一般是指程序在运行时,所需要的一些配置信息,例如数据库连接字符串,API密钥,端口号等。其次就是环境变量跟我们的操作系统有关,例如Linux,Windows,Mac等。

基本用法

我先带大家熟悉各种操作系统(Linux,Windows /cmd/powershell,Mac)的临时环境变量的命令:

查询环境变量
  1. Linux / MacOS / wsl (通用命令):

提示:wsl是Windows Subsystem for Linux的缩写,是Windows 10/11操作系统的一个子系统,允许用户在Windows上运行Linux命令行工具。

echo $PATH #查询PATH环境变量
  1. Windows /cmd:
echo %PATH% #查询PATH环境变量
echo %USERNAME% #查询用户名环境变量
  1. Windows /powershell:
dir Env:* #查询所有环境变量

输出展示:

RlsSvcPort                     22112
SESSIONNAME                    Console
SystemDrive                    C:
SystemRoot                     C:\WINDOWS
TEMP                           C:\Users\11955\AppData\Local\Temp
TERM_PROGRAM                   vscode
TERM_PROGRAM_VERSION           2.1.25
TMP                            C:\Users\11955\AppData\Local\Temp
USERDOMAIN                     XIAOMAN
USERDOMAIN_ROAMINGPROFILE      XIAOMAN
USERNAME                       11955
USERPROFILE                    C:\Users\11955
设置临时环境变量
  1. Linux / MacOS / wsl (通用命令) 提示:这个必学,后期进行部署的时候也很常用。:
export XM=123 #设置XM环境变量为123
echo $XM #查询XM环境变量
  1. Windows /cmd:
set XM=123 #设置XM环境变量为123
echo %XM% #查询XM环境变量
  1. Windows /powershell:
$env:XM='123' #设置XM环境变量为123
echo $env:XM #查询XM环境变量

script-shell

那么我们学会临时环境变量之后有什么用呢? 我们可以应用到项目中,例如本地环境连接数据库localhost,root,12346,生产环境就会变成8.8.8.8,xiaoman,^&TG*H#**P,等等诸如此类。

打开package.json文件,找到scripts配置项,添加一个脚本:

set DB_HOST=localhost(本地环境 自定义环境变量)

set DB_HOST=8.8.8.8(生产环境 自定义环境变量)

这样我们就可以根据不同的环境变量,连接不同的数据库

"scripts": {
    "dev": "set DB_HOST=localhost && next dev",
    "build": "set DB_HOST=8.8.8.8 && next build",
    "start": "next start"
},

为什么我在powershell终端,编写cmd的命令,而且是可以运行的!!!

image.png

原理解释:script-shell, 是因为npm运行script脚本的时候会单独开一个线程,这个线程在Linux/MacOs下是/bin/sh,在Windows下是cmd,所以我们在script脚本中要编写cmd的命令。

cross-env

我们可以观察上一小节的问题,我们在script脚本中编写了cmd的命令,但是如果他在powershell终端/MacOs终端/Linux终端,他就无法运行,也就是跨平台问题。

所以我们可以使用cross-env来解决这个问题,cross-env是一个跨平台的环境变量设置工具,他可以让我们在不同的操作系统下,使用相同的命令来设置环境变量。

npm install cross-env -D #安装cross-env

然后我们就可以在package.json文件中使用cross-env来设置环境变量:

"scripts": {
    "dev": "cross-env DB_HOST=localhost next dev",
    "build": "cross-env DB_HOST=8.8.8.8 next build",
    "start": "next start"
},

这样我们就可以在不同的操作系统下,使用相同的命令来设置环境变量了。

最佳实践

因为上述方式依旧麻烦,如果有很多的环境变量,我们的命令就会变得非常长,所以我们可以使用.env文件来存储环境变量。

Next.js 环境变量查找规则(官方规定),如果在其中一个链路中找到了环境变量,那么就不会继续往下找了。

  1. process.env
  2. .env.$(NODE_ENV).local
  3. .env.local(未检查的情况NODE_ENV。test)
  4. .env.$(NODE_ENV)
  5. .env

提示:NODE_ENV是Next.js自动注入的环境变量,开发模式他会注入development,生产模式他会注入production

所以我们就可以创建两个不同的env文件,一个是开发环境,一个是生产环境。

创建 .env.development.local文件(表示开发环境),并添加环境变量:

DB_HOST=localhost
DB_USER=root
DB_PASSWORD=123456

创建 .env.production.local文件(表示生产环境),并添加环境变量:

DB_HOST=8.8.8.8
DB_USER=xiaoman
DB_PASSWORD=^&TG*H#**P

创建/src/app/home/page.tsx文件,并添加环境变量:

//服务器组件 不要增加`use client`
export default function Home() {
  return <div>
    <h1>Home</h1>
    <p>DB_HOST: {process.env.DB_HOST}</p>
    <p>DB_USER: {process.env.DB_USER}</p>
    <p>DB_PASSWORD: {process.env.DB_PASSWORD}</p>
  </div>;
}

当我们执行npm run dev时,他会自动读取.env.development.local文件中的环境变量,当我们执行npm run build时,他会自动读取.env.production.local文件中的环境变量。

image.png

nextjs学习9:数据获取fetch、缓存与重新验证

2026年1月12日 10:35

在 Next.js 中如何获取数据呢?

Next.js 优先推荐使用原生的 fetch 方法,因为 Next.js 拓展了原生的 fetch 方法,为其添加了缓存和更新缓存(重新验证)的机制。

这样做的好处在于可以自动复用请求数据,提高性能。坏处在于如果你不熟悉,经常会有一些“莫名奇妙”的状况出现……

服务端使用 fetch

Next.js 拓展了原生的 fetch Web API,可以为服务端的每个请求配置缓存(caching)和重新验证( revalidating)行为。

你可以在服务端组件、路由处理程序、Server Actions 中搭配 async/await 语法使用 fetch。

基本用法

// app/page.js
async function getData() {
  const res = await fetch('https://jsonplaceholder.typicode.com/todos')
  if (!res.ok) {
    // 由最近的 error.js 处理
    throw new Error('Failed to fetch data')
  }
  return res.json()
}

export default async function Page() {
  const data = await getData()
  return <main>{JSON.stringify(data)}</main>
}

默认缓存

默认情况下,Next.js 会自动缓存服务端 fetch 请求的返回值(背后用的是数据缓存(Data Cache))。

// fetch 的 cache 选项用于控制该请求的缓存行为
// 默认就是 'force-cache', 平时写的时候可以省略
fetch('https://...', { cache: 'force-cache' })

但这些情况默认不会自动缓存:

  1. 在 Server Action 中使用的时候
  2. 在定义了非 GET 方法的路由处理程序中使用的时候

简单的来说,在服务端组件和只有 GET 方法的路由处理程序中使用 fetch,返回结果会自动缓存。

logging 配置项

在写代码之前,先让我们修改下 next.config.mjs 的配置:

const nextConfig = {
  logging: {
    fetches: {
      fullUrl: true
    }
  }
};

export default nextConfig;

目前 logging 只有这一个配置,用于在开发模式下显示 fetch 请求和缓存日志。

服务端组件使用

第一种在服务端组件中使用,修改 app/fetch/page.tsx,代码如下:

async function getData() {
  // 接口每次调用都会返回一个随机的猫猫图片数据
  const res = await fetch('https://api.thecatapi.com/v1/images/search')
  if (!res.ok) {
    throw new Error('Failed to fetch data')
  }

  return res.json()
}

export default async function Page() {
  const data = await getData()

  return <img src={data[0].url} width="300" />
}

在开发模式下,为了方便调试,可以使用浏览器的硬刷新(Command + Shift + R)清除缓存,此时数据会发生更改(cache: SKIP)。普通刷新时因为会命中缓存(cache: HIT),数据会保持不变。

image.png

命中缓存时 6ms 就返回了。

image.png

不命中缓存,需要912ms。

运行 npm run build && npm run start 开启生产版本。因为 fetch 请求的返回结果被缓存了,无论是否硬刷新,图片数据都会保持不变。

路由处理程序 GET 请求

第二种在路由处理程序中使用,新建 app/api/cats/route.js,代码如下:

export async function GET() {
  const res = await fetch('https://dog.ceo/api/breeds/image/random')
  
  const data = await res.json()
  return Response.json({ data })
}

开发模式下,浏览器硬刷新的时候会跳过缓存,普通刷新的时候则会命中缓存。可以看到第一次硬刷新的时候,请求接口时间为 5418ms,后面普通刷新的时候,因为使用缓存中的数据,数据返回时间都是 0ms 左右。

image.png

运行 npm run build && npm run start 开启生产版本,因为 fetch 请求的返回结果被缓存了,无论是否硬刷新,接口数据都会保持不变。

重新验证

在 Next.js 中,清除数据缓存并重新获取最新数据的过程就叫做重新验证(Revalidation)。

Next.js 提供了两种方式重新验证:

一种是基于时间的重新验证(Time-based revalidation) ,即经过一定时间并有新请求产生后重新验证数据,适用于不经常更改且新鲜度不那么重要的数据。

一种是按需重新验证(On-demand revalidation) ,根据事件手动重新验证数据。按需重新验证又可以使用基于标签(tag-based)和基于路径(path-based)两种方法重新验证数据。适用于需要尽快展示最新数据的场景。

基于时间的重新验证

使用基于时间的重新验证,你需要在使用 fetch 的时候设置 next.revalidate 选项(以秒为单位):

fetch('https://...', { next: { revalidate: 3600 } })

或者通过路由段配置项进行配置,使用这种方法,它会重新验证该路由段所有的 fetch 请求。

那什么是路由段呢?

image.png

在这张图中,/dashboard/settings由三段组成:

  • /:根段(Root Segment)
  • dashboard:段(Segment)
  • settings:叶段(Leaf Segment)

路由段配置选项可以配置页面、布局、路由处理程序的行为。比如我们使用 fetch 的时候可以单独配置某个请求的 revalidate ,借助路由段配置,我们可以配置这个路由下所有 fetch 请求的 revalidate

所以可以这么设置:

// layout.jsx | page.jsx | route.js
export const revalidate = 3600

按需重新验证

使用按需重新验证,在路由处理程序或者 Server Action 中通过路径( revalidatePath) 或缓存标签 revalidateTag 实现。

revalidatePath

新建 app/api/revalidatePath/route.js,代码如下:

import { revalidatePath } from 'next/cache'
 
export async function GET(request) {
  const path = request.nextUrl.searchParams.get('path')
 
  if (path) {
    revalidatePath(path)
    return Response.json({ revalidated: true, now: Date.now() })
  }
 
  return Response.json({
    revalidated: false,
    now: Date.now(),
    message: 'Missing path to revalidate',
  })
}

在上面的例子中,访问/api/cats页面内容都不变的,因为被缓存了,现在我如果访问下/api/revalidatePath?path=/api/cats,因为这个接口里面有revalidatePath(path),所以会更新/api/cats这个接口的数据,当再次访问/api/cats时,内容就变了。

注意:在开发模式下,用 revalidatePath 确实更新了对应路径上的 fetch 缓存结果。但如果大家部署到生产版本,你是发现 revalidatePath 只对页面生效,对路由处理程序并不生效。

这是因为 /api/cats 被静态处理了(不同于页面的静态渲染),静态处理表示响应内容在 npm run build 构建阶段生成并固化,部署后直接返回缓存的响应,无需实时计算。

首先你要将 /api/cats 转为动态处理(响应内容在用户每次请求时实时生成,不提前固化,每次请求都执行处理程序逻辑),然后才能测试 revalidatePath 的效果。

但是转为动态处理,比如使用 cookies 等函数,又会触发 Next.js 的自动逻辑,让 fetch 请求退出缓存。

简而言之,如果你想在生产环境测试 revalidatePath 对路由处理程序的影响,你需要多做一些配置:

// 路由动态处理, 每次请求都会返回新的内容
export const revalidate = 0
// fetch 强制缓存,这里有进行了强制缓存
export const fetchCache = 'force-cache'

export async function GET() {
  const res = await fetch('https://dog.ceo/api/breeds/image/random')
  
  const data = await res.json()
  return Response.json({ data, now: Date.now() })
}
revalidateTag

Next.js 有一个路由标签系统,可以跨路由实现多个 fetch 请求重新验证。具体这个过程为:

  1. 使用 fetch 的时候,设置一个或者多个标签标记请求
  2. 调用 revalidateTag 方法重新验证该标签对应的所有请求
// app/page.js
export default async function Page() {
  const res = await fetch('https://...', { next: { tags: ['collection'] } })
  const data = await res.json()
  // ...
}

在这个例子中,为 fetch 请求添加了一个 collection标签。在 Server Action 中调用 revalidateTag,就可以让所有带 collection 标签的 fetch 请求重新验证。

// app/actions.js
'use server'
 
import { revalidateTag } from 'next/cache'
 
export default async function action() {
  revalidateTag('collection')
}

客户端使用路由处理程序

如果你需要在客户端组件中获取数据,可以在客户端调用路由处理程序。

路由处理程序会在服务端被执行,然后将数据返回给客户端,适用于不想暴露敏感信息给客户端(比如 API tokens)的场景。

如果你使用的是服务端组件,无须借助路由处理程序,直接获取数据即可。

昨天以前首页
❌
❌