nuxt.js的优点
- 解决SEO难题:传统VUE等SPA应用是客户端渲染(CSR),页面初始化HTML内容为空,依赖JS执行后动态生成,很难被爬虫引擎抓取;而nuxt.js默认支持服务端渲染,在服务端就生成完整的HTML内容发送给浏览器,解决了SEO的痛点;
- 提升首屏加载速度:由于用户一请求获得的就是完整的页面,所以展现更快;
- 灵活的渲染模式:除了服务端渲染(SSR),还支持静态站点生成(SSG),可以在构建时就生成静态HTML文件,访问速度快,适合不经常修改的页面。
- 开发效率快:简化的路由配置、位于components下的组件无需导入就可直接使用。
打包命令
nuxt build: 为生产环境构建应用, 包含服务端代码;nuxt generate: 静态站点生成(SSG)的核心命令,只输出纯静态文件。(如果你的项目是纯静态站点,用改命令即可。)
渲染模式
- SSR(服务端渲染):用户请求时,服务器动态生成,适合内容频繁变化、需要实时数据的页面;
- CSR(客户端渲染):像VUE等传统SPA一样,仅返回空的HTML等待用户打开之后再加载JS渲染,适用于不用SEO的内容;
- SSG(静态站点生成):在构建的时候就预先生成所有静态HTML页面,适合博客等经常不变的内容;
- 混合渲染:通过routeRules为不同路由指定不同的渲染模式,适合一个项目中既有产品详情页(SSR)和帮助中心(SSG)。
以下是routeRules的说明:
js
// nuxt.config.ts
export default defineNuxtConfig({
// 全局启用 SSR
ssr: true,
routeRules: {
// 增量静态再生:当用户访问该动态页面的时候,会缓存3600秒(如果想更精细化缓存可以使用cache),如果超过这个时间,会回重新生成新页面,适用于内容定期更新的新闻列表、博客首页等,如果设置为false就是仅缓存不验证
'/blog/**': { swr: 3600 },
// 管理后台走 CSR(客户端渲染),用户打开页面之后才会加载js,不具备SEO能力
'/admin/**': { ssr: false },
// 采用SSG模式(静态站点生成),即在构建阶段就生成该HTML,适用于内容几乎不变的网页
'/': { prerender: true },
}
})
重要提醒 :在使用
nuxt generate时,routeRules中的ssr、swr等选项不会生效 ,因为nuxt generate只生成静态文件,不启动服务端逻辑
预渲染(SSG的实现方式)
前面提到了SSG,会在构建的时候先生成所有静态的HTML页面。那如果我希望页面路由虽然是动态的(/blog/:id),但是又希望它在构建的时候可以自动生成该怎么办?
借助a标签
html
<nuxt-link to='/blog/1'>帖子1</nuxt-link>
<nuxt-link to='/blog/2'>帖子2</nuxt-link>
在 nuxt.config.ts 中开启 crawlLinks: true。如果在a标签已经写好了要预渲染的帖子路径,那么在构建的时候,会自动将这两个帖子进行静态构建。
手动指定路由
提高seo的时候,可以提供一份sitemap.xml文件,一般情况下可以直接放在public文件夹下,这样打包的时候会自动复制到打包的文件夹中,但是如果sitemap.xml中的内容是动态生成的话,那就可以在预渲染的时候动态生成,然后更新其内容:
js
export default defineNuxtConfig({
nitro: {
prerender: {
routes: ['/sitemap.xml'], // 手动添加路由
},
},
});
当爬虫无法进入的页面(比如登录后才会显示、路由参数太复杂),但是其需要预渲染的时候,就可以使用这种方法。
路由规则
你可以利用 routeRules 进行更精细的控制,按路由模式来决定是否预渲染。
js
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true }, // 预渲染首页
'/blog/**': { prerender: true }, // 预渲染 /blog 下的所有页面
'/admin/**': { prerender: false }, // 跳过 /admin 下的页面
},
});
动态/编程式添加
之前动态生成帖子的数量比较熟,手动配置问题不大,但是如果是数量较多的情况下呢?
- 使用
prerenderRoutes工具函数:
js
<script setup>
// 在页面组件中动态添加路由,适合在运行时才知道的动态路由,仅在构建时会生成静态页面
prerenderRoutes(['/dynamic/1', '/dynamic/2']);
</script>
- 使用
prerender:routes钩子: 在构建的时候先请求,再生成:
js
export default defineNuxtConfig({
hooks: {
async 'prerender:routes'(ctx) {
// 从 API 获取所有博客文章ID
const posts = await $fetch('https://api.example.com/posts');
for (const post of posts) {
ctx.routes.add(`/blog/${post.slug}`);
}
}
}
});
- 使用
nitro:config钩子:
js
export default defineNuxtConfig({
hooks: {
async 'nitro:config'(nitroConfig) {
if (nitroConfig.dev) return;
const routes = await fetchDynamicRoutes(); // 你的获取数据逻辑
nitroConfig.prerender = nitroConfig.prerender || {};
nitroConfig.prerender.routes = [...(nitroConfig.prerender.routes || []), ...routes];
}
}
});
提高SEO
用nuxt一般也是为了提高SEO,下面小结一下提高SEO的常见方式
内容与元信息优化
- 设置全局默认标题/模板(未单独配置的页面默认使用这里的配置)
js
// nuxt.config.ts
export default defineNuxtConfig({
app: {
head: {
. // %s 会被页面具体标题替换
titleTemplate: '%s - 我的电商网站',
// 没有页面标题时使用
defaultTitle: '网站名称',
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
]
}
}
})
- 给页面单独设置
js
<script setup>
useHead({
title: '商品列表 - 我的电商网站',
meta: [
{ name: 'description', content: '全场商品限时折扣,正品保证' },
{ name: 'keywords', content: '手机, 电脑, 家电' },
]
})
</script>
- 规范链接:同一商品可以通过
/product/123和/product/123?ref=ad访问,需要告诉搜索引擎当前页面的"标准"网址是哪个
js
<script setup>
useHead({
link: [
{ rel: 'canonical', href: 'https://example.com/current-page-path' }
]
})
</script>
打包与性能优化
(搜索引擎会评估页面加载速度和用户体验,所以需要进行优化。)
-
评估之后选择渲染模式;
-
图片优化:使用
@nuxt/image模块,自动转换为现代格式(webp/avif)、懒加载、响应式图片。js// nuxt.config.js modules: ['@nuxt/image'] // 页面使用 <NuxtImg src="/photo.jpg" format="webp" loading="lazy" /> -
代码分割与懒加载:nuxt会自动按照路由分隔代码,也会在
components/目录下的每个组件生成一个懒加载版本,只需在组件名前面加上Lazy前缀,即可获得一个异步加载的组件、使用第三方库按需导入(比如lodash中的某些方法) -
预加载某些关键资源
js
useHead({
link: [
{ rel: 'preload', href: '/fonts/main.woff2', as: 'font', crossorigin: '' }
]
})
- 进一步启用进一步启用
gzip或brotli压缩代码(需服务器配置)。
向搜索引擎主动提交文件
sitemap.xml:动态生成(通过server/routes/sitemap.xml.ts或@nuxtjs/sitemap模块)并提交到 Google Search Console / 百度资源平台。robots.txt:放在public/robots.txt,允许搜索引擎爬取。
补充
sitemap.xml
sitemap.xml 的核心作用是主动告诉搜索引擎你的网站上有哪些重要页面,以及它们的更新频率、优先级和最后修改时间 ,从而引导爬虫更高效、更完整地抓取和索引内容。
有时候sitemap.xml的内容会过多,所以会拆分成多个子xml,比如当前sitemap.xml为:
xml
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://name.com/sitemap-main.xml</loc>
</sitemap>
<!-- 可以是子域名下的 -->
<sitemap>
<loc>https://aa.name.com/sitemap-news.xml</loc>
</sitemap>
<sitemap>
<loc>https://name.com/sitemap-2026-01-15.xml</loc>
</sitemap>
</sitemapindex>
这时候我会将这个网站静态的页面写在sitemap-main.xml中。
xml
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://name.com/index.html</loc>
</url>
<url>
<loc>https://name.com/about/index.html</loc>
</url>
</urlset>
动态生成sitemap.xml
运营希望我每天可以新增一个sitemap-日期文件.xml,这样就不仅是生成新的xml文件,也要更新插入到sitemap.xml,即:
xml
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://name.com/sitemap-main.xml</loc>
</sitemap>
<!-- 省略中间 -->
<sitemap>
<loc>https://name.com/sitemap-2026-01-15.xml</loc>
</sitemap>
</sitemapindex>
我的sitemap-日期文件.xml是一个涵盖了动态路由的文件,内容太多,不能使用ssg一次性构建,不然一天几百个页面,n天后会炸的,而且seo效果肯定也不好。另外我还需要将每天的生成的sitemap-日期文件.xml提交到搜索引擎,所以就需要写定时任务,刚好nuxt.js就可以让你写服务端的内容,在项目的server/api下新建generateSite.js
js
export async function generateSitemapCore(customDate = null) {
try {
// 1. 这里就根据自身逻辑去编写xml的内容
xmlContent= `...`;
// 2. 写入sitemap-当前日期.xml文件并放到正确的位置
const publicDir = path.resolve(process.cwd(), '.output', 'public');
const sitemapFileFullPath = path.join(publicDir, sitemapFileName);
const sitemapIndexPath = path.join(publicDir, 'sitemap.xml');
// 确保目录存在
try {
await fs.access(publicDir);
} catch {
await fs.mkdir(publicDir, { recursive: true });
}
// 写入日期Sitemap
await fs.writeFile(sitemapFileFullPath, xmlContent, 'utf8');
console.log(`[定时/手动] 日期Sitemap生成成功:${sitemapFileName}`);
// 2.更新sitemap.xml
const newSitemapNode = ` <sitemap>\n <loc>${newSitemapLoc}</loc>\n </sitemap>`;
let indexContent = '';
try {
indexContent = await fs.readFile(sitemapIndexPath, 'utf8');
indexContent = indexContent.trim();
} catch {
indexContent = `<?xml version="1.0" encoding="UTF-8"?>\n<sitemapindex xmlns="${SITEMAP_XMLNS}">\n</sitemapindex>`;
console.log(`[定时/手动] 首次生成Sitemap索引文件:sitemap.xml`);
}
if (indexContent.includes(newSitemapLoc)) {
console.log(`[定时/手动] 索引文件已存在该节点,无需追加:${newSitemapLoc}`);
} else {
const closeTag = `</sitemapindex>`;
indexContent = indexContent.replace(closeTag, `${newSitemapNode}\n${closeTag}`);
await fs.writeFile(sitemapIndexPath, indexContent, 'utf8');
console.log(`[定时/手动] 索引文件更新成功:已追加 ${newSitemapLoc}`);
}
return {
success: true,
msg: `Sitemap生成成功(${sitemapFileName}),索引文件已更新,URL提交已触发`,
data: { sitemapFileName, newSitemapLoc, urlCount: generateUrlList.length }
};
} catch (err) {
console.error(`[定时/手动] Sitemap生成失败:`, err.message, err.stack);
return {
success: false,
msg: 'Sitemap生成失败',
error: err.message
};
}
}
接下来我写定时任务,只要文件放在 server/plugins/ 目录下并正确使用 defineNitroPlugin,它就会在服务器启动时自动加载和执行:
js
// src/server/plugins/sitemap-cron.js
import schedule from 'node-schedule';
export default defineNitroPlugin(async () => {
// 服务启动时,导入抽离的Sitemap核心生成函数(路径和你实际文件一致)
const { generateSitemapCore } = await import('../api/generateSite.js');
// ======================================
// 配置定时任务规则:每天凌晨1点执行(推荐低峰期)
// Cron表达式:分 时 日 月 周
// ======================================
// 规则1:每天凌晨1点执行(生产环境推荐)
const cronRule = '0 1 * * *';
// 测试规则:每分钟执行一次(开发时用,验证成功后改回上面的生产规则)
// const cronRule = '*/5 * * * *';
// 注册定时任务
schedule.scheduleJob(cronRule, async () => {
console.log('==================== 定时任务开始执行:生成Sitemap并提交URL ====================');
try {
// 调用核心函数,不传参则自动使用「当天日期」生成(和手动接口逻辑一致)
const result = await generateSitemapCore();
if (result.success) {
console.log('定时任务执行成功:', result.msg);
} else {
console.error('定时任务执行失败:', result.error);
}
} catch (err) {
console.error('定时任务执行异常:', err.message, err.stack);
}
console.log('==================== 定时任务执行结束 ====================\n');
});
// 服务启动时打印日志,确认定时任务已注册
console.log(`✅ Sitemap定时任务已注册成功,执行规则:${cronRule}(分 时 日 月 周)`);
});
服务器的部署问题
- 执行
npm run build之后,将整个output文件夹丢到线上服务器去; - 如果每次都更新了
package.json则需要执行npm install --omit=dev --ignore-scripts; - 在服务器中安装
pm2 - 在项目文件夹中执行
pm2 start .output/server/index.mjs --name 自定义项目名称 - 记得每次更新的时候执行
pm2 stop 项目名称 - 重启命令为
pm2 restart 项目名称
小结
- 了解
nuxt的优点,主要是为了实现SEO; - 了解
nuxt的渲染模式:服务端渲染(SSR)、静态渲染(SSG)、客户端渲染(CSR)以及他们的区别,通常情况下实际业务选择混合模式,不常变更的页面使用SSG(且可以搭配缓存进行使用),需要变更的页面使用SSR; - 了解提高SEO的常用方式:页面信息配置、页面性能/代码体积优化、主动提交给搜索引擎;
- 学会动态生成并提交
sitemap和服务端部署。