• 作者:老汪软件技巧
  • 发表时间:2024-09-01 21:04
  • 浏览量:

系列

前言

作为一名 Vue Developer,我习惯使用 Vue 组件 和 组合式 API 以及 渐进式的生态 来构建各类单页面应用(SPA),也喜欢在业余时间捣鼓开源项目和技术博客。随着项目各类需求的迭代和升级,我开始探索学习服务端渲染(SSR)和 静态站点生成(SSG)的场景和技术。

而在这探索和学习中,Nuxt 基于 Vue 的元框架,天然对 Vue 开发者的友好性,吸引了我的关注。它不仅支持多种渲染模式,集成了路由管理、状态管理、SEO优化,还提供了更丰富的生态(图层、插件、中间件、模块)以及支持多种部署环境(Github Pages、Nodejs、Cloudflare、Netlify、Vercel)

单页应用 SPA

单页应用是一种 Web 应用架构,其中应用的所有交互和内容都在一个 HTML 页面中进行,只有必要的部分才会动态加载或更新。

缺点

服务端渲染 SSR

服务端渲染(Server-Side Rendering)是在服务器端生成完整的 HTML 页面,然后将这些页面发送到客户端。

缺点

静态站点生成 SSG

静态站点生成是在构建时生成所有页面的静态 HTML 文件,这些文件在用户请求时直接提供。

缺点

初识 Nuxt概述简介

Nuxt 是一个基于 Vue 的元框架,专注于构建现代化的 Web 应用。它提供了一系列功能和工具,旨在简化开发过程,提高应用性能,并支持多种渲染模式。无论是服务端渲染、静态站点生成、还是单页面应用都能提供出色的支持,并具有如下特点:

应用场景

企业级应用

企业级应用通常需要高性能、可扩展和可维护的解决方案。Nuxt 的服务端渲染功能可以提高应用的首屏加载速度和 SEO 性能,同时也可以支持复杂的业务逻辑和大规模的应用结构。

例: 公司官网、企业门户网站、内部管理系统、客户管理系统

内容管理系统

内容管理系统需要高效的内容发布和管理功能。Nuxt 的静态站点生成功能适合生成静态的内容页面,提高加载速度和减少服务器负担。

例: 博客、新闻网站、技术文档网站 (由 @nuxt/content 模块实现)

电子商务网站

电子商务网站需要快速的页面加载速度和良好的用户体验。Nuxt 可以通过 SSR 提高页面加载性能,并通过静态生成技术预渲染产品页面以提高访问速度。

例: 在线商店、产品目录、电子商务平台

......

关键概念

在不断学习 Nuxt 旅途中,了解了 Nuxt 通过 图层(Layers)、插件(Plugins)、中间件(Middleware)、模块(Modules)等概念,为开发者提供了一个高度灵活和可扩展的开发环境,所以就图层、插件、中间件、模块、服务端渲染、以及运行时配置,做进一步说明和讲解。

图层 Layers

图层是一个包含 Nuxt 配置、模块和其他资源的目录。每个图层可以包含自己的 nuxt.config.js 配置文件、模块、插件等。这种结构使得不同的图层可以独立开发和管理。

具有如下作用

通过将应用拆分为多个图层,可以实现更好的模块化,每个图层专注于应用的一个特定方面。图层之间可以继承和覆盖配置,使得共享配置可以集中管理,同时允许对特定图层定制化设置。通过图层可以在多个项目中复用相同模块和配置,提高开发效率和一致性,图层可以是 npm 包

其他说明

图层内文件将被 Nuxt 自动扫描并在项目中使用,而无需手到导入。这意味着,图层内的目录和文件等同于你在项目的手动编写开发一样。当然就近原则,项目中同名组件、布局、页面等覆盖于图层中同名文件内容。如果以 JavaScript 的作用域中变量去理解项目所引用的图层内容,还挺 "适用"。每个 Nuxt 项目都可以视为图层,并根据需要加载不同图层,也可以被其他图层加载

应用场景

多主题支持:

通过图层,你可以为应用创建多个主题,每个主题作为一个独立的图层,并根据用户的选择或配置动态加载不同的主题图层。多租户应用:

在多租户应用中,不同租户可能需要不同的配置或功能。你可以为每个租户创建一个图层,这些图层可以包含特定的路由、组件和服务端逻辑。插件系统:

开发一个可扩展的插件系统,每个插件作为一个独立的图层,实现特定功能,如身份验证、支付集成、数据分析等。渐进式迁移:

如果你正在将一个大型遗留项目迁移到 Nuxt,可以将不同的部分逐步迁移到图层中,确保平稳过渡。

参考文档

官方文档:/docs/guide/…视频讲解:…插件 Plugins

Nuxt 插件是一个功能强大的机制,它允许开发者在 Vue 应用初始化时执行自定义代码,以扩展或定制应用的行为,如果是服务端插件则允许开发者配置身份验证和授权、数据上的预处理以及处理请求拦截、重定向、日志记录等功能。

Nuxt 插件是分 客户端插件 和 服务端插件 两类:

客户端插件 (Nuxt4 目录结构: ~/app/plugins/xxx.ts)

// 例. 全局注册 Ant Design Vue 组件
import Antd from 'ant-design-vue'
export default defineNuxtPlugin((nuxtApp) => { 
  nuxtApp.vueApp.use(Antd) 
})

// 例. 全局注册指令
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.directive('action', {
    mounted (el) {
      //...
    },
  })
})

官方文档:/docs/guide/…

服务端插件 (Nuxt4 目录结构: ~/server/plugins/xxx.ts)

// 例. 返回 html 页面时,注入  信息
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('render:html', (html) => {
    html.head.push('')
  })
})

Nitro Plugin 文档:nitro.unjs.io/guide/plugi…

中间件 Middleware

在 Nuxt 框架中,中间件也分 客户端路由中间件 和 服务端路由中间件。 客户端路由中间件通常用于 Vue 路由的权限验证和重定向,而服务端路由中间件则通常用于来自客户端API请求认证和授权,以及数据和异常的预处理。

客户端路由中间件 (Nuxt4 目录结构: ~/app/middleware/xxx.ts)

// 例. Vue 路由拦截处理 => ~/app/middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
  if (to.params.id === '1') {
    return abortNavigation() // 阻止路由访问
  }
  if (to.path == '/index') {
    return navigateTo('/') // 路由重定向
  }
})

 <template>
   <div class="container">
     Render Page Content
   div>
 template>
 <script setup lang="ts">
 definePageMeta({
   middleware: 'auth' // 使用中间件
 })
 script>

官方文档 /docs/guide/…

推荐视频 …

服务端路由中间件 (Nuxt4 目录结构: ~/server/middleware/xxx.ts)

// 例. Api Token 校验
export default defineEventHandler((event) => {
  const token = getRequestHeader(event, 'token')
  if (!token) {
    throw createError({
      statusCode: 403,
      statusMessage: 'Token expired',
    })
  }
})

官方文档

模块 Modules

Nuxt 模块是一个扩展和定制 Nuxt 应用的核心机制,它可以为你的项目导入组件库,集成第三方服务、自动化配置等,而无需你手动编写所有代码。

模块与插件区别

模块主要作用如下

常用的官方和社区模块

如何使用模块?(以 Ant Design Vue 为例)

添加依赖项 @ant-design-vue/nuxt

npx nuxi@latest module add ant-design-vue

完善 nuxt.config.ts 配置选项

 export default defineNuxtConfig({
   modules: [
     '@ant-design-vue/nuxt'
   ],
   antd:{
     // Options
   }
 })

项目中使用组件,无需手动导入

 <template>
   <AButton @click="doClick">
     按钮
   AButton>
 template>
 
 <script lang="ts" setup>
 const doClick = () => {
   message.info("按钮已触发");
 }
 script>

社区现有模块:/modules

开发模块文档:/docs/guide/…

服务端渲染 SSR

在 Nuxt SSR 模式下,客户端向服务器发出请求后,服务器会先执行相关的逻辑(如获取数据、渲染模板等),生成完整的 HTML 页面,并将其返回给客户端。客户端接收到页面后,立即展示给用户,同时在后台加载并激活 Vue 代码以进行后续的交互。

具体流程如下:

请求阶段:用户在浏览器中访问页面时,浏览器会向服务器发出请求。服务器渲染:服务器接收到请求,执行数据获取、模板渲染,生成 HTML 页面。返回 HTML:服务器将生成的 HTML 页面返回给浏览器。浏览器展示:浏览器接收到 HTML 后,立即展示页面内容。客户端激活:接下来 Vue 客户端脚本会加载激活,使页面具备交互能力。

这里服务端获取数据传输给客户端,Nuxt 称之为预渲染页面的有效负载,它一般是随着 html 页面一起返回。那么怎么样的数据获取才是真正的有效负载呢?

运行时配置 Runtime

在 Nuxt 中,运行时配置是指在应用运行时,动态设置或调整配置参数的功能。运行时配置通常用于设置环境相关的变量,例如 API 密钥、数据库连接字符串、或根据不同的部署环境(如开发、生产、测试等)切换配置。

推荐视频:…

运行时配置的用途

环境区分:根据不同的环境(开发、生产等)设置不同的选项,如 API 的基础 URL。安全性:将敏感信息(密钥、数据库配置)存储在环境变量中,而不是代码硬编码。灵活性:在不需要重新构建应用的情况下,可以动态调整某些配置参数。

运行时配置的示例

设置运行时配置

 export default defineNuxtConfig({
   runtimeConfig: {
     public: {
       apiBase: '/api' // 服务端和客户端都可用, 默认 '/api'
     },
     apiSecret: 'key', // 仅服务端可用, 默认 'key' 
   }
 })

项目中使用运行时配置

 <template>
   <div class="container">
     <div class="look-base-url">
       {{ config.public.apiBase }}
     div>
     <div class="look-user-data">
       {{ data }}
     div>
   div>
 template>
 <script setup lang="ts">
 const config = useRuntimeConfig()
 const { data } = await useFetch('/api/user', {
   method: 'post',
   body: {
     apiSecret: import.meta.server 
       ? config.apiSecret // 仅在服务端渲染时有值
       : ''
   }
 })
 script>

如果构建了 node-server 模式下的前端项目,如何设定运行不同部署环境下的变量

# 测试环境 (apiBase: `/test/api`, apiSecret: `test-key`)
NUXT_PUBLIC_API_BASE=/test/api NUXT_API_SECRET=test-key node .output/server/index.mjs
# 正式环境 (apiBase: `/prod/api`, apiSecret: `prod-key`)
NUXT_PUBLIC_API_BASE=/prod/api NUXT_API_SECRET=prod-key node .output/server/index.mjs

如果是构建了 node-server 模式下的前端项目,我们一般是通过 PM2 来设定变量和后台运行

// ecosystem.config.cjs
module.exports = {
  apps: [
    {
      name: "NuxtApp", // 表示 PM2 运行实例的唯一名称
      autorestart: true, // 默认为 true, 发生异常的情况下自动重启
      exec_mode: "cluster", // 多核 CPU 上运行多个实例
      instances: "max", // max表示最大的 应用启动实例个数,仅在 cluster 模式有效
      script: ".output/server/index.mjs", // 应用程序的启动脚本
      
      // 生产环境运行时变量
      env_production: {
        NUXT_PUBLIC_API_BASE: "/prod/api",
        NUXT_API_SECRET: "prod-key",
      },
      
      // 测试环境运行时变量
      env_test: {
        NUXT_PUBLIC_API_BASE: "/test/api",
        NUXT_API_SECRET: "test-key",
      },
    },
  ],
};

# 运行 测试环境
pm2 start ecosystem.config.cjs --env test 
# 运行 正式环境
pm2 start ecosystem.config.cjs --env production

与 Vue 相比搭建 Template 模版

使用 Nuxt 搭建模版相比于直接使用 Vue 搭建,具有以下几个优势:

Nuxt 提供了服务端渲染的能力,可以减少首屏加载时间,并能提供非常好的SEO优化。另外还可以借助于 来实现组件纯服务端渲染的能力,为一些涉及密钥等安全的页面提供保障。

Nuxt 提供了服务端引擎的能力,可以灵活地管理和扩展项目的服务端逻辑(API 路由的定义和代理等),同时允许为不同的部署环境构建输出 (传统服务器、无服务器环境、静态站点等)。

Nuxt 提供了强大的布局系统,可以轻松管理不同的页面布局,支持在页面中通过 definePageMeta 中的 layout 定义不同的布局适配,同时还支持 setPageLayout API 动态更改当前页面的布局。

Nuxt 提供文件系统路由可以自动生成页面路由,减少了手动配置的工作量。这在构建具有大量页面的后台管理系统时尤为便利。而在适配技术文档的模版上,Nuxt官方也提供了 @nuxt/content 功能上更胜于 vitepress 的模块,很轻松地实现由 Markdown 文件编写的技术文档网站。

Nuxt 内置了许多功能,如中间件、插件机制、模块系统等,这些功能可以极大地简化开发流程,使得开发各类模版更加高效。(如 开发后台管理系统模块,Nuxt有许多开箱即用的模块:Auth 模块、Axios 模块、PWA 模块、API 请求、缓存等)

Nuxt 提供了图层这一强大的共享和复用机制,允许你将应用的不同部分(如核心功能、主题等)拆分到独立的图层中。这种分离使得代码更加模块化和易于维护。同时也支持复用来自 npm 包的第三方图层而在使用中无需手动导入 ,这解决了我在搭建后台管理系统模版时碰到的困惑,即升级或修复后台管理系统时,如何同步已使用这个后台管理系统的更新。

渲染 Rendering 多样性

在之前的章节中,我们已经对单页应用(SPA)、服务端渲染(SSR)和 静态站点生成(SSG)的各个特点和应用场景都做了详细的说明。但 Nuxt 能为我们做的却远不止如此,它还提供了混合渲染 (为不同的路由做适配),为复杂的业务场景做适配。

export default defineNuxtConfig({
  routeRules: {
    // 在构建时预渲染
    '/': { prerender: true },
    
    // 仅在客户端渲染呈现
    '/spa/**': { ssr: false },
    
    // 在 10 秒内重用这个缓存的响应
    '/ten/**': { cache: { maxAge: 10 } },
    
    // 在客户端不在发起远程 script 脚本的请求,仅 script 内联脚本可执行
    '/no-js/**': { experimentalNoScripts: true },
    
    // 页面按需生成,在后台重新验证,缓存直至 API 响应发生变化
    '/products': { swr: true },
    
    // 页面按需生成,在后台重新验证,缓存 1 小时(3600 秒)
    '/products/**': { swr: 3600 },
    
    // 页面按需生成,在后台重新验证,在 CDN 上缓存 1 小时(3600 秒),功能与 swr 类似
    '/blog': { isr: 3600 },
    
    // 页面在下次部署之前按需生成一次,缓存在 CDN 上
    '/blog/**': { isr: true },
    
    // 启用跨源资源共享(CORS)功能
    '/api/**': { cors: true },
    
    // 重定向处理
    '/old-page': { redirect: '/new-page' },
    
    // 返回响应时添加自定义 HTTP 头部 `x-magic-of`,其值为 `nuxt and vercel`
    '/headers': { headers: { 'x-magic-of': 'nuxt and vercel' } },
  }
})

SWR(Stale-While-Revalidate)

概念:是一种缓存策略,主要用于客户端数据获取和更新。它允许在数据过期(stale)后立即返回缓存的数据,同时在后台重新验证并获取新的数据。当新的数据被获取并更新缓存后,客户端会重新渲染组件。

缓存优先:后台重新验证:ISR(Incremental Static Regeneration)

概念:是一种用于静态网站生成的技术,可以在不重新构建整个网站的情况下,逐步更新静态页面。ISR 允许在构建后对特定页面进行增量更新,而不是重新生成整个站点。

服务器端缓存:ISR 主要用于服务器端缓存和更新。定期更新:在一定时间间隔重新生成静态页面。生成后的内容会被缓存,直到下一个更新周期。依赖平台:与 SWR 不同的是,它往往依赖平台(像 Vercel、Netlify),提供平台级性能。性能优化:提高站点性能,减少重新生成整个站点的需要。

官方文档:

推荐视频:…

Demo 演示:/danielroe/n…

部署 Environment 灵活性

Nuxt 在部署环境和运行环境变量配置上非常灵活,在之前的章节中也有说明。

前端 Engineering 安全性

虽然前端应用的安全性在很大程度上依赖于良好的实践和策略 (如 内容安全策略CSP、Vue 模版转义、CSRF 防护),但 Nuxt 提供了一些强大的功能来进一步确保前端应用的安全性。

相关参考官方文档

/

nitro.unjs.io/guide

视频系列

@TheAlexLic…


上一条 查看详情 +没有了
下一条 查看详情 +没有了