一、先聊聊网页为什么慢

每个访问者打开一个 Nuxt 项目时,真正花时间的往往不是后台接口,而是浏览器要下载那一堆 JS、CSS、图片。如果这些资源体积大、文件多,还都堆在自己服务器上,加载速度自然就下来了。我们要解决的事情其实很简单:把静态资源打包得规整一点,再把它们放到离用户更近的 CDN 上,让浏览器能更快拿到。

这里说的静态资源,是指那些不会频繁变化的内容,比如样式表、脚本文件、Logo、页面里的公共图片。它们和接口数据不一样,接口每次请求都不一样,但静态资源构建一次后内容就固定了,特别适合用 CDN 缓存。

二、先搞清楚 Nuxt 里静态资源放在哪里

2.1 assets 和 public 的分工

Nuxt 项目里有两个经常让人纠结的目录,一个是 assets,一个是 public。很多新手会把图片随便丢一个目录,结果打包后路径找不到,或者文件被重复处理。

assets 里的文件是给构建工具看的。比如你在组件的 script 里引入了 CSS 或图片,Nuxt 会把它作为模块参与打包。它会经历压缩、加 hash、按需加载这一套流程。处理完之后,文件名会变成类似 logo-1a2b3c.png 的样子。

而 public 目录里的文件更像传统网站里的根目录静态文件。你放一个 logo.png,然后直接用 /logo.png 就能访问。它不会被打包器处理,也不会改文件名,构建后原样复制到输出目录。

下面是一个最简单的目录示例:

技术栈:Nuxt 3(Vue 3 + JavaScript)
└── my-nuxt-app/
    ├── assets/
    │   └── css/
    │       └── main.css
    ├── public/
    │   ├── logo.png
    │   └── favicon.ico
    ├── app.vue
    └── nuxt.config.js

如果你想在组件里直接引用一张公共图片,把它放到 public 下面即可:

<!-- 技术栈:Nuxt 3(Vue 3 + JavaScript) -->
<template>
  <header>
    <!-- /logo.png 指向 public/logo.png -->
    <img src="/logo.png" alt="站点 Logo">
  </header>
</template>

上面的注释特意写清楚了路径对应关系,因为 public 目录不会出现在 URL 里。

2.2 assets 里的文件要怎么用

如果你希望图片经过压缩、加 hash,方便 CDN 做永久缓存,可以把它当成模块导入。

<script>
// 技术栈:Nuxt 3(Vue 3 + JavaScript)
// 导入 assets/img 下面的图片
import logo from '@/assets/img/logo.png'

export default {
  name: 'AppHeader',
  setup() {
    // 返回给模板使用
    return { logo }
  }
}
</script>

<template>
  <header>
    <!-- 这里使用的是打包处理后的地址 -->
    <img :src="logo" alt="站点 Logo">
  </header>
</template>

这段代码里 @/assets 是 Nuxt 常用的别名,指向 assets 目录。构建后,logo.png 会变成带 hash 的文件名。这一步很关键,带 hash 的文件名能帮我们判断浏览器要不要重新下载资源。

三、构建配置里的关键参数

3.1 配置 CDN 地址和构建资源目录

Nuxt 3 中,我们可以直接在 nuxt.config.js 里设置 CDN 地址。CDN 地址可以理解成静态资源的主机头,也就是 URL 前面那一段。

// nuxt.config.js
// 技术栈:Nuxt 3(Vue 3 + JavaScript)
export default defineNuxtConfig({
  app: {
    // 如果你的站点不在域名根目录,可以改成 /子目录/
    baseURL: '/',

    // 打包后的 JS、CSS 放在这个目录下,默认是 _nuxt
    buildAssetsDir: '_nuxt',

    // CDN 域名,部署时请换成你自己的
    cdnURL: 'https://static.my-site.com'
  }
})

这里 cdnURL 主要影响的是通过打包生成的那批 JS、CSS 文件。配置好后,构建出来的 HTML 里,脚本和样式地址会以 https://static.my-site.com/_nuxt/ 开头。

为了让大家看得更清楚,可以对比一下配置前后的效果。假设你执行构建命令:

# 技术栈:Nuxt 3(Vue 3 + JavaScript)
npm run build

构建完成后的 HTML 里,你会找到类似下面的标签:

<!-- 配置了 cdnURL 之后,浏览器会从这里加载资源 -->
<script src="https://static.my-site.com/_nuxt/main-abc123.js"></script>
<link rel="stylesheet" href="https://static.my-site.com/_nuxt/main-abc123.css">

而没配置之前,这些地址通常是 / 开头的相对路径。

3.2 想要真正让 CDN 生效,还需要考虑部署方式

这里要分两类场景。第一种是纯静态站点。你可以用 nuxi generate 生成静态文件,然后把生成的 dist 或 .output/public 目录上传到对象存储,再套一层 CDN。此时访问者获取 HTML、JS、CSS、图片都可以走 CDN 节点,速度提升非常明显。

第二种是 Node 服务端部署。假如你在服务器上运行 node .output/server/index.mjs,那么页面 HTML 由 Node 服务生成,而 JS、CSS 这类静态文件仍然可以交给 CDN。配置好 cdnURL 后,浏览器加载这些文件时会直接请求 CDN,服务器压力就会小很多。

需要提醒的是,不要忘记把构建出来的静态文件同步到 CDN 源站。很多平台提供了自动同步或者回源功能,如果没有,就要在发版流程里加上上传静态文件这一步。

四、应用场景:什么时候最需要这样优化

我见过最典型的场景是图片资源多、营销活动页面、面向全国用户的产品站点。用户分布在不同地区,自己单独一台服务器扛不住带宽。尤其遇到活动时,大量 JS 和图片同时请求,服务器出口带宽很容易被打满。

另一个明显场景是每次发版都会更新文件名。配置了带 hash 的文件名后,没有变化的文件,CDN 可以继续缓存很久;只有内容变化的那个文件,文件名才会变化,CDN 就会重新回源获取。这种精确失效的缓存策略,是纯手工管理静态资源很难做到的。

如果你的项目只是内部管理后台,用户几十个,且都集中在一个网络环境,其实没必要折腾 CDN。不要为了技术看起来高级而上 CDN,这是很多团队踩过的坑。静态资源优化有收益,但也有成本和复杂度。

五、这样做的好处和坏处

5.1 优点

第一,加载速度更快。CDN 节点离用户近,网络延迟低。第二,服务器压力小。原本所有 JS、CSS、图片都从你的服务器下载,现在 CDN 扛掉了大头。第三,缓存策略更清晰。文件名带 hash,版本变化一目了然,线上问题排查时也更容易。

5.2 缺点

首先,配置和发布流程会变复杂。你不能再只执行本地启动命令就完事,构建后要上传资源、刷新 CDN 缓存,出错概率增加。其次,如果 CDN 配置得不好,比如没有清理旧文件,或者回源设置错误,很容易出现线上资源更新了,但用户还在用旧版。另外,CDN 服务是需要费用的,流量特别大时,费用会变成一个需要关注的因素。

这些缺点不是让你放弃优化,而是建议在动手前想清楚:你的用户分布、流量规模、团队运维能力是否合适。

六、注意事项:这些坑一定要避开

6.1 public 目录下的资源不会被处理

public 里的文件不会加 hash,不会压缩,也不会改名字。如果修改了 public/logo.png,图片访问地址还是 /logo.png。如果 CDN 缓存了这张图,用户短时间内还是看到旧图。这时你需要手动更新 CDN 缓存,或者把文件名改掉。

6.2 尽量使用相对稳定的 CDN 域名

千万不要今天用一个域名,明天换另一个域名。每次换域名,所有资源链接都会变化,浏览器缓存等于全部失效。如果一定要换,最好能提前规划好下线和过渡方案。

6.3 注意跨域问题

如果 CDN 域名和当前站点不是同一个域名,而你的字体文件、Canvas 绘制操作需要读取资源内容,就会遇到跨域问题。一般来说,静态资源通过 script、img、link 标签加载不受跨域影响,但如果你用 fetch 去请求,或者引入 Web Font 时需要进行跨域读取,就需要在 CDN 上配置 CORS 响应头。

6.4 请把缓存时间设置正确

带 hash 的资源文件名基本不变,所以可以设置很长的缓存时间。不带 hash 的文件,比如 public 里的图片、HTML 页面本身,应该设置短缓存或者不缓存,避免更新后用户拿不到最新页面。

一个常见的配置思路是:

// 这个只是便于理解的规则,不是实际代码
// 带 hash 的 JS/CSS:一年
// HTML 页面:不缓存
// public 里会变动的图片:30 天或更短

如果你的 CDN 平台不支持这种规则,就要在边缘服务或源站里手动配置。

七、文章总结

Nuxt 项目要提速,比较大的收益点就是管理好静态资源。先把 assets 和 public 的用途分清楚,再决定哪些文件参与打包、哪些文件原样发布。然后通过 nuxt.config.js 里的 baseURL、buildAssetsDir、cdnURL 几个配置,把打包后的资源地址指向 CDN,同时配合合理的缓存时间,就能让用户在打开页面时更快拿到样式和脚本。

最后再强调一遍:配置 CDN 不是终点。你还需要在发布流程里保证构建文件和 CDN 内容是同步的,在线上出问题时能迅速判断用户拉到的到底是哪个版本。把这些基础工作做扎实,网站加载速度才能稳定地好起来。