返回

初步接触nuxt3

Nuxt 使用通用渲染来提供更好的用户体验、性能并优化搜索引擎索引,但您可以在一行配置中切换渲染模式。

关键概念

导入功能的提升

Components 文件夹组件自动导入
  • 在 Nuxt 中,你可以在 components/ 目录中创建这些组件,它们将自动在整个应用程序中可用,无需显式地导入。
Auto-imports 自动导入(响应式 API、生命周期)
1
2
3
4
5
<script setup lang="ts">
/* ref() and computed() are auto-imported */
const count = ref(1)
const double = computed(() => count.value * 2)
</script>
Built-in Auto-imports 内置自动导入
1
2
3
<script setup lang="ts">
  /* useFetch() is auto-imported */ const {(data, refresh, status)} = await useFetch('/api/hello')
</script>

Vue.js 路由

1. 通过pages/每个组件生成路由
1
2
3
4
5
-| pages/
---| about.vue
---| index.vue
---| posts/
-----| [id].vue
2. <NuxtLink>标签进行路由跳转
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<template>
  <header>
    <nav>
      <ul>
        <li><NuxtLink to="/about">About</NuxtLink></li>
        <li><NuxtLink to="/posts/1">Post 1</NuxtLink></li>
        <li><NuxtLink to="/posts/2">Post 2</NuxtLink></li>
      </ul>
    </nav>
  </header>
</template>
3. Route Parameters 路由参数
1
2
3
4
<script setup lang="ts">
  const route = useRoute() // When accessing /posts/1, route.params.id will be 1
  console.log(route.params.id)
</script>
4. Route Middleware 路由中间件
  • 导航到特定路由之前提取要运行的代码
  • 路由中间件在 Nuxt 应用程序的 Vue 部分运行。尽管名称相似,但它们与服务器中间件完全不同,后者在应用程序的 Nitro 服务器部分运行。
1
2
3
4
5
6
export default defineNuxtRouteMiddleware((to, from) => {
  // isAuthenticated() is an example method verifying if a user is authenticated
  if (isAuthenticated() === false) {
    return navigateTo("/login");
  }
});
1
2
3
4
5
6
7
8
9
<script setup lang="ts">
definePageMeta({
  middleware: "auth",
});
</script>

<template>
  <h1>Welcome to your dashboard</h1>
</template>
5. Route Validation 路由验证
  • Nuxt 通过你想要验证的每个页面中 definePageMeta()中的 validate 属性提供路由验证。
  • validate 属性接受路由作为参数。您可以返回一个布尔值,以确定这是否是要使用此页面呈现的有效路由。如果返回 false,但找不到其他匹配项,则会导致 404 错误。您也可以直接返回带有 statusCode/statusMessage 的对象,以立即响应错误(不会检查其他匹配项)。
1
2
3
4
5
6
7
8
<script setup lang="ts">
definePageMeta({
  validate: async (route) => {
    // Check if the id is made up of digits
    return typeof route.params.id === 'string' && /^\d+$/.test(route.params.id)
  }
})
</script>

Rendering Modes 渲染模式

  • 浏览器和服务器都可以解释 JavaScript 代码,以将 Vue.js 组件转换为 HTML 元素。此步骤称为 渲染
  • 默认情况下,Nuxt 使用通用渲染来提供更好的用户体验、性能并优化搜索引擎索引,但您可以在一行配置中切换渲染模式。

关于服务端渲染和客户端渲染的一个bug,使用setInterval计时器,服务端报错500

1
2
3
setInterval(() => {
  request()
}, 3000);
1. 问题分析

当你在 Nuxt 的组件或页面中直接使用 setInterval 时,这段代码会在服务端渲染阶段被执行。然而,setInterval 是浏览器环境中的 API,在服务端(Node.js)环境中会报错,提示类似以下内容:

1
ReferenceError: setInterval is not defined
2. 解决方案
  1. 使用 process.client 判断是否在客户端运行

    nuxt 提供了 process.client 变量,用于判断当前代码是否在浏览器环境中运行。你可以通过这个变量确保 setInterval 只在客户端执行。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    // 确保 setInterval 只在客户端运行
    if (process.client) {
      const intervalId = setInterval(() => {
        loadData()
      }, 3000)
    
      // 组件销毁时清除计时器,防止内存泄漏
      onUnmounted(() => {
        clearInterval(intervalId)
      })
    }
    
  2. 将计时器逻辑放在 mounted 生命周期钩子中

    在 Vue.js 中,mounted 钩子只会在客户端渲染完成后执行,因此可以将计时器逻辑放在这里。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    export default {
      mounted() {
        this.interval = setInterval(() => {
          this.loadData();
        }, 3000);
      },
      beforeDestroy() {
        // 清除计时器,防止内存泄漏
        clearInterval(this.interval);
      },
      methods: {
        loadData() {
          console.log('加载数据...');
        }
      }
    };
    

什么是服务器渲染的,什么是客户端渲染的?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<script setup lang="ts">
const counter = ref(0); // executes in server and client environments

const handleClick = () => {
  counter.value++; // executes only in a client environment
};
</script>

<template>
  <div>
    <p>Count: {{ counter }}</p>
    <button @click="handleClick">Increment</button>
  </div>
</template>

在初始请求中,计数器 ref 在服务器中初始化,因为它呈现在 <p>标记内。handleClick 的内容在此处永远不会执行。在浏览器中激活期间,counter ref 会重新初始化。handleClick 最终将自身绑定到按钮;因此,可以合理地推断 handleClick 的主体将始终在浏览器环境中运行。

Universal Rendering 通用渲染

原理
  1. 当浏览器请求启用了通用渲染的 URL 时,Nuxt 会在服务器环境中运行 JavaScript (Vue.js) 代码,并将完全渲染的 HTML 页面返回给浏览器。如果页面是提前生成的,Nuxt 也可以从缓存中返回完全渲染的 HTML 页面。用户会立即获得应用程序的全部初始内容,这与客户端渲染相反。
  2. 下载 HTML 文档后,浏览器会解释此内容,Vue.js 将控制该文档。曾经在服务器上运行的相同 JavaScript 代码再次在客户端(浏览器)上运行,现在通过将其侦听器绑定到 HTML 来启用交互性(因此是通用渲染)。
效果
  1. 通用渲染允许 Nuxt 应用程序提供快速的页面加载时间,同时保留客户端渲染的优势。此外,由于内容已经存在于 HTML 文档中,因此爬网程序可以毫无开销地为其编制索引。
  2. 通用渲染用途广泛,几乎可以适应任何用例,特别适用于任何面向内容的网站:博客、营销网站、投资组合、电子商务网站和市场。
1. 服务器端渲染(SSR)
  • 概念:

    • 在 SSR 模式下,页面的 HTML 内容是在服务器端动态生成的,然后发送到客户端。客户端接收到 HTML 后,再通过 JavaScript 进行进一步的交互。
    • SSR 是 Nuxt 3 的默认渲染模式,适合需要 SEO 优化或需要快速首屏加载的应用。
  • 好处

    • 性能:用户可以立即访问页面的内容,因为浏览器显示静态内容的速度比 JavaScript 生成的内容快得多。同时,Nuxt 在水合过程中保留了 Web 应用程序的交互性。
    • 搜索引擎优化:通用渲染将页面的整个 HTML 内容作为经典服务器应用程序交付给浏览器。Web 爬虫可以直接为页面的内容编制索引,这使得 Universal rendering 成为您想要快速编制索引的任何内容的绝佳选择。
  • 缺点

    • 开发约束:服务器和浏览器环境不提供相同的 API,因此编写可以在两端无缝运行的代码可能很棘手。幸运的是,Nuxt 提供了指南和特定变量来帮助您确定一段代码的执行位置。
    • 成本:需要运行服务器才能动态呈现页面。这会增加每月成本,就像任何传统服务器一样。但是,由于浏览器接管了客户端导航的通用渲染,服务器调用大大减少了。利用 edge-side 渲染可以降低成本。
2. 静态站点生成(Static Site Generation, SSG)
  • 在 SSG 模式下,页面的 HTML 是在构建时预先生成的,并作为静态文件部署到服务器。这些静态页面可以在没有服务器端逻辑的情况下直接被客户端加载。
  • SSG 适合内容不经常更新的网站,如博客或文档网站。
3. 单页应用(Single Page Application, SPA)
  • 在 SPA 模式下,页面的 HTML 是在客户端动态生成的,服务器只提供一个初始的 HTML 模板和必要的 JavaScript 文件。
  • SPA 适合对交互性要求较高的应用,但可能对 SEO 不太友好。

Client-Side Rendering 客户端渲染

相较于通用渲染相反,不做过多叙述

  • 好处
    • 开发速度:不考虑服务器的兼容性
    • 便宜: 不考虑服务器基础设施成本
    • 离线: 在 Internet 不可用时很好地保持工作
  • 缺点
    • 性能: 需等待浏览器下载、解析、运行js,响应长,影响用户体验
    • 搜索引擎优化: 索引和更新通过客户端渲染交付的内容需要更多时间。不利于引擎爬虫。
  1. 您可以在 nuxt.config.ts中使用 Nuxt 启用仅客户端渲染:
1
2
3
export default defineNuxtConfig({
  ssr: false
})

:warning: 如果你确实使用 ssr: false,你还应该放置一个 HTML 文件, ~/app/spa-loading-template.html 其中包含一些你想用来渲染加载屏幕的 HTML,该屏幕将一直呈现,直到你的应用程序被激活。

Deploying a Static Client-Rendered App部署静态客户端呈现的应用程序

Hybrid Rendering 混合渲染

Route Rules 路由规则

Edge-Side Rendering 边侧渲染

Nuxt的Server层服务

通过defineEventHandler()定义接口

server文件夹下定义接 名字channel.get.ts,默认get接口

1
2
3
4
5
6
7
//  server
//  - /api
// 	  - channel.get.ts
import channel from "~/database/channel" //channel 是database定义的默认数据
export default defineEventHandler(()=>{
    return channel
})

使用useFetch()调用接口

1
2
3
// 无需导入
// 解构-重命名
const {data:channelList} =await useFetch('/api/channel')

我写过的项目的写法的记录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { API_HOT_CATEGORY_LIST } from '~/api/category'
import { nodeFetch } from '~/server/utils/handle'

export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  const data = await nodeFetch(API_HOT_CATEGORY_LIST, { body, nitroEvent: event })

  return {
    data,
    status: 0,
    info: 'ok',
  }
})
404_@林达所作
使用 Hugo 构建
主题 StackJimmy 设计