likes
comments
collection
share

Nuxt3 -实践指南,你不知道的“黑科技“

作者站长头像
站长
· 阅读数 83

一、简介

Nuxt3 是基于 Vite、Vue3 和 Nitro 的 Nuxt 框架的现代重写,具有一流的 Typescript 支持,是两年多研究、社区反馈、创新和实验的结果。为每个人提供了一个愉快的 Vue 全栈开发体验。

优势:

1、更轻量:以现代浏览器为目标的服务器部署和客户端产物最多可缩小 75 倍 2、更快:基于 nitro 提供动态代码分割能力,以优化冷启动性能 3、Hybrid:增量静态生成和其他的高级功能现在都成为可能 4、Suspense:在任意组件和导航前后都可以获取数据 5、Composition API:使用 Composition API 和 Nuxt 3 的 composables 实现真正的代码复用 6、Nuxt CLI:没有任何依赖,帮你轻松搭建项目和集成模块 7、Nuxt Devtools:通过直接在浏览器中查看信息和快速修复实现更快地工作 8、Nuxt Kit:具有 Typescript 和跨版本兼容性的全新模块开发 9、Webpack 5:更快的构建时间和更小的包大小,无需配置 10、Vite:使用 Vite 作为打包工具,体验闪电般快速的 HMR 11、Vue 3:Vue 3 是你下一个 Web 应用程序的坚实基础 12、TypeScript:使用原生 TypeScript 和 ESM 构建,无需额外步骤

了解不同渲染模式:

传统服务端渲染

在过去传统开发中,页面渲染任务是由服务端完成的,服务器负责获取数据,拼装页面,客户端仅负责内容显示,使用这种方式的典型技术有 JSP、PHP、ASP.NET 等等。

Nuxt3 -实践指南,你不知道的“黑科技“

客户端渲染 - CSR

Vue.js、React 这类框架之所以能解决前面提到的问题,主要是因为采用了前后端分离的开发模式,这种方式的特点是页面是由客户端渲染的,应用在客户端首次获取的是空 HTML 文档,浏览器下载并运行 JS,获取数据之后再创建页面,也就是大家熟悉的单页面应用 - SPA

CSR 适合那些不需要 SEO 或者用户访问频率不高的强交互应用,例如:SaaS 系统、后台管理系统、在线文档等。同时,利用浏览器缓存特性,这些应用也可以避免再次下载资源从而提高性能表现。

Nuxt3 -实践指南,你不知道的“黑科技“

全局渲染 - SSR;

当用户请求一个 URL 时,先按照传统模式在服务端渲染完整 HTML 页面给浏览器,这样用户立刻可以获得首屏内容;

与此同时,客户端会加载 JS 代码,使前面加载的静态内容重新变成可交互的 SPA,这样后续的页面切换就不再需要刷新和重新获取页面内容了。

为了保证前端程序员能够使用熟悉的方式编写页面,即“同构开发”,服务端渲染时,Nuxt 实际上是在服务器上执行 Vue,将我们编写的组件渲染为 HTML 并返回客户端。客户端激活时执行的 JS 实际上也是 Vue,它会重新接管文档,恢复数据和状态,使静态页面变得可交互,这一过程称为“注水(hydration)”。

可以看到,SSR 保留 CSR 优点的同时,还给用户提供了快速加载首屏的能力,这同时也解决了 SEO 问题。

但这种方式也并不完美,因为服务器和浏览器环境有差异,它们不能给开发者提供相同 API,例如,组件在服务端创建时就没有 mounted 钩子,因为根本没有挂载这一步,这导致不少组件库不能在服务端环境正常使用。

又比如,有时候我们的代码只想在客户端运行,如果不小心在服务端执行了就会报错。这些都给开发带来了复杂度和约束性。同样的,由于页面渲染操作发生在服务端,所以服务器负载增加了,只是由于后续 hydration 操作,这个负载增加量被大幅减少了。最后,大家还应该注意的是,SSR 还额外增加了 node.js / serverless 这样的运行时需求,这实际上也是一笔费用。

Nuxt3 -实践指南,你不知道的“黑科技“

静态站点生成 - SSG(Static Site Generate);

Nuxt 项目中 packages.json 文件,里面提供了一个 generate 命令。这个命令的作用是将写好的动态页面基于数据生成为静态网站,这一过程称为静态内容生成 - Static Site Generate,即 SSG。

Nuxt3 -实践指南,你不知道的“黑科技“

混合渲染 - hybrid rendering 根据不同路由规则使用不同方式渲染的模式;

Nuxt3 带来一个新的渲染模式,称为混合渲染 - hybrid rendering,顾名思义,这是一种根据不同路由规则使用不同方式渲染的模式。这种方式就可以用来解决前面提出的需求

Nuxt3 -实践指南,你不知道的“黑科技“

边缘渲染 - edge-side rendering。

过去,SSR 只能运行在 Node.js 环境,但是 Nuxt3 提供了跨平台支持,能够同时运行在 Node.js、Deno、Workers 等运行时环境。

需要深度优化应用打开和交互速度,例如:实时游戏,交易系统,就可以尝试边缘渲染模式

Nuxt3 -实践指南,你不知道的“黑科技“

几种渲染方式的区别:

  • 客户端渲染:开发速度快,节约服务器资源;首屏慢,SEO 不友好;
  • 服务端渲染:首屏快,SEO 友好,适应性强;开发约束大,服务器费用高;
  • 静态站点生成:首屏极快,SEO 友好,服务器成本低;适应性弱,可维护性差;
  • 混合渲染:按需渲染,适应性强,可维护性好;稳定性、可用性不好;
  • 边缘渲染:性能好,服务器成本低;稳定性、可用性不好。

演示项目:

SSR 前端技术:Nodejs+Express+Handlebars模板

CSR(单页应用):Vue、React开发的一些项目,管理平台... 、H5等

混合渲染 前端技术:Nuxt3

nuxt3.dishawang.com/

Lighthouse:性能评测工具,SEO评分等

二、基础知识

1、快速创建项目

Nodejs版本要求:Node.js - v16.10.0 or newer
// 初始化:
npx nuxi init <project-name>
//安装依赖:
yarn install
//运行
yarn dev -o 或者 npm run dev

Nuxt3开发精髓之一是约定式开发,通过约定省去了大部分配置,虽然开发灵活性有所影响,但及大提高代码直观可读性和开发效率。

项目目录:

Nuxt3 -实践指南,你不知道的“黑科技“

2、页面创建和约定路由

pages文件夹

项目根目录下app.vue文件中,使用标签,相当于路由出口

代码演示~

pages/index.vue

pages/about.vue 等等

页面中路由跳转:

<NuxtLink to="/">xx页面</NuxtLink>
<li @click="toMy"> 关于我们 </li>

<script setup>
const toMy = () => {
    // 第一种写法:
    // navigateTo("aboutmy");
    // 携带参数写法:编程式路由
    navigateTo({
        path: "/aboutmy",
        query: {
            id: 111
        },
    });
}
</script>

3、动态路由使用

xxx文件夹下,创建[id].vue

参数接受页面中:$route.params.id
 js中:
 const route = useRoute(); 
 const id = ref(route.params.id);

代码演示~

4、嵌套路由:目录和文件名同名,就制作了嵌套路由

比如:

-| pages/
---| parent/
------| child.vue
---| parent.vue

代码演示~

parent文件下 创建child.vue

//pages下创建 parent页面
<template>
    <p>嵌套页面</p>
    <!-- 子页面出口 -->
    <NuxtPage></NuxtPage>
</template>

// child页面
<template>
    <p>child page</p>
</template>

5、布局模板

layouts 下 defalut.vue,有了这个模板后,可以在任何你想要使用的页面中用标签为页面赋予模板中的内容

app.vue是每个页面的出口

代码演示~

// app.vue

<template>
  <el-config-provider :locale="zhCn">
    <NuxtLayout>
      <NuxtPage />
    </NuxtLayout>
  </el-config-provider>
</template>



<script setup>
//中文包
import zhCn from "element-plus/lib/locale/lang/zh-cn";
</script>

// layouts default.vue

<template>
    <div>
        <HeaderPage></HeaderPage>
        <slot />
        <FooterPage></FooterPage>
    </div>
</template>

6、组件编写

文件夹 components 下编写组件

名称约定、多层级组件的引用

多层级组件就是把一个组件放在一个文件夹里。在实际工作中组件会非常多,所以会把组件分门别类的放置。那这种有层级的组件,我们要如何引用那。比如在 components文件夹下面,新建一个 test文件夹,然后在test文件夹下面再创建一个 MyButton.vue文件。

组件的懒加载:在组件名前面加上Lazy前缀,懒加载组件的目的是在项目打包的时候包更小。简单理解可以理解为只有在组件显示在页面上时才进行加载。

组件,Nuxt 提供了 专门用于仅在客户端渲染组件的组件;

要仅在客户端导入组件,请在仅客户端插件中注册该组件。

<template>
  <div>
    <ClientOnly>
      <!-- 此组件仅在客户端显示 -->
      <Comments />
    </ClientOnly>
  </div>
</template>

代码演示~

页面模块拆分:头部导航、底部导航、主体内容等

7、模块化代码 composable文件夹:

通用的业务逻辑代码,需要模块化管理,这时候就可以试用Composable 这个文件夹来编写。比如我们常用的显示当前时间,这种常用的通用代码,就可以编写成一个单独的代码段,然后在每个页面进行使用

composables 文件夹的引入规则是,只有顶层文件会被引入。

stores.js

import { defineStore } from 'pinia'

export const useUserStore = defineStore('userInfo', () => {

    const userInfo = ref({
        userName: 'zhang',
        id: 1,
        sex: '男',
    })
  
    return {
      userInfo,
    }
  })


//页面中使用

const userInfo = useUserStore().userInfo
console.log("userInfo", userInfo)

8、数据请求:

提供4中数据请求:useAsyncData 、useLazyAsyncData、useFetch、useLazyFetch

首先了解$fetch:服务端渲染,不得不考虑接口的调用时机问题:

首屏渲染,调用发生在服务端;

客户端激活之后,调用发生在客户端。

这导致我们首先需要判断代码执行环境,其次发送请求的 API 可能不同,另外如果在客户端发送请求到其他接口服务器还会存在跨域问题,需要给项目配代理,很复杂是吧!这就是 Nuxt3 为什么替我们封装了 fetch∗∗∗∗,fetch** **,fetchfetch( )方法是nuxt3提供的内置方法,我们直接可以使用。它全局可用,可以智能处理调用时机,还能统一 API,避免配置代理

const { data } = await $fetch('/api/hello', { query: { name: 'tom' } })
const { result } = await $fetch('/api/post', { method: 'post', body: newPost })

可以看到,fetch的API和fetch是一样的,实际调用的是unjs/ofetch它的用法符合我们之前的编码习惯,返回Promise,然后用户负责处理后续操作。但如果要加上一些其他loading、error等反馈,我们通常要添加额外组件状态来实现,比较繁琐。fetch 的 API 和 fetch 是一样的,实际调用的是 unjs/ofetch它的用法符合我们之前的编码习惯,返回 Promise,然后用户负责处理后续操作。但如果要加上一些其他 loading、error 等反馈,我们通常要添加额外组件状态来实现,比较繁琐。fetchAPIfetch是一样的,实际调用的是unjs/ofetch它的用法符合我们之前的编码习惯,返回Promise,然后用户负责处理后续操作。但如果要加上一些其他loadingerror等反馈,我们通常要添加额外组件状态来实现,比较繁琐。fetch API

后来到了 hooks 时代,React 社区出现了诸如 ahooks、swr 等库,通过封装请求,暴露出 data、loading、error 等状态,然后可以在组件内直接使用,非常高效。Nuxt3 也为我们提供了四个接口,通过封装 $fetch,给用户提供响应式数据便于直接使用。

与axios的不同

httpServer.js
创建模拟web服务器

const http = require("http")

let n = 0
// 创建web服务器
const server = http.createServer((req, res) => {
    console.log(n++)
    res.setHeader('Content-Type', 'text/html;charset=utf-8')
    res.end('server')
})

server.listen(8082, () => {
    console.log("服务器启动了")
})

//页面中使用axios

// axios.get('http://localhost:8082').then(res => {
//     console.log(res)
// })

结果:刷新页面,axios向服务器发送两次请求

a、useFetch 页面、组件或者插件中可以使用useFetch获取任意 URL 资源。useFetch是对useAsyncData和$fetch的封装,只需传入请求的 URL 或者一个请求函数即可,一些选项会自动填入,用起来最简洁,是最推荐的数据获取方式。可以理解为所有的都选择默认配置的useAsyncData 方法

useFetch方法签名:

function useFetch(
  url: string | Request | Ref<string | Request> | () => string | Request,
  options?: UseFetchOptions<DataT>
): Promise<AsyncData<DataT>>

type AsyncData<DataT> = {
  data: Ref<DataT> // 返回数据
  pending: Ref<boolean> // 加载状态
  refresh: (opts?: { dedupe?: boolean }) => Promise<void> // 刷新数据
  execute: () => Promise<void> // 同 refresh,没有去重选项
  error: Ref<Error | boolean> // 错误信息
}


使用:
<script setup>
  // const posts = await $fetch("/api/posts");
  const { data, pending, error, refresh } = await useFetch('getTenArticleList', {
    method: 'GET',
    baseURL: "http://121.36.81.61:8000/getTenArticleList"
})
datalist.value = toRaw(data.value.data)
console.log("useFetch-data-result", data.value.data)

</script>

//useLazyAsyncData 和useLazyFetch 他们只是把配置选项中的Lazy 设置成了true, 也就是会阻塞页面

//常用lazy,默认是false,设置成true 就是需要数据都返回后,才会显示出来 ,简单说就是会阻塞页面;

//不会阻塞路由导航,这意味着我们需要处理 data 为 null 的情况(或者通过 default 选项给 data 设置一个默认值);

b、 useLazyFetch

该方法等效于useFetch设置了lazy选项为 true,不同之处在于它不会阻塞路由导航,这意味着我们需要处理 data 为 null 的情况(或者通过 default 选项给 data 设置一个默认值)。

网络切换为3G,模拟效果

const { data, pending, error, refresh } = await useLazyFetch('http://121.36.81.61:8000/getTenArticleList')
 console.log("useLazyFetch-data-result", data.value.data)
 datalist.value = data.value.data

 console.log("useLazyAsyncData-data-result", toRaw(data.value.data))
 datalist.value = toRaw(data.value.data)

c、useAsyncData 异步获取数据

该方法和 useFetch 相比功能上是相同的,但是更底层,使用方法类似于 ahooks 库中的 useRequest,我们需要提供一个用于缓存去重的 key 和数据请求的处理函数。也就是说,useFetch 相当于:useAsyncData(key, () => $fetch(url))。

useAsyncData签名如下,因此 useAsyncData 有两种用法:一种传 key,一种不传 key,但是即使你不传,Nuxt 也会帮你生成一个,所以该用哪个不用我说了吧!?

function useAsyncData(
  handler: (nuxtApp?: NuxtApp) => Promise<DataT>,
  options?: AsyncDataOptions<DataT>
): AsyncData<DataT>
function useAsyncData(
  key: string,
  handler: (nuxtApp?: NuxtApp) => Promise<DataT>,
  options?: AsyncDataOptions<DataT>
): Promise<AsyncData<DataT>>

使用:

 const { data } = await useAsyncData("getList", () =>
     $fetch("http://121.36.81.61:8000/getTenArticleList"),{
     pick: ["data"]
 }
 );

d、useLazyAsyncData

该方法等效于useAsyncData,仅仅设置了lazy选项为true,也就是它不会阻塞路由导航,这意味着我们需要处理 data 为 null 的情况,或者通过 default 选项给 data 设置一个默认值。

刷新数据

有时候页面数据是需要刷新的,比如:翻页、条件过滤、搜索等。

我们可以使用useFetch()等 API 返回的refresh()刷新数据。需要注意,如果请求的 key 参数没有发生变化,我们实际上拿到的还是之前缓存的结果。例如下面代码执行 refresh() 并不会得到最新数据:

const { data, refresh } = useFetch('/api/somedata')
// 数据并没有刷新!
refresh()

而想要获取最新数据,就要在 url 中添加一个参数,并作为函数返回值传给useFetch:

// url需要改为由函数返回
const { data, refresh } = useFetch(() => `/api/somedata?page=${page}`)
// 数据刷新!
page++
refresh()

9、middleware路由中间件:

Nuxt3提供了路由中间件的概念,你可以在整个应用使用它,目的是在导航到某一个页面之前,执行一些代码。最常见的路由守卫就可以用这个实现。

基本格式:

middleware/ auth.js

export default defineNuxtRouteMiddleware((to, from) => {
    console.log("要去那个页面:"+to.path)
    console.log("来自那个页面:"+from.path)
  	// 逻辑处理 例如:未登录判断等
	const user = useUser();
  console.log("useUser", user.value)
	// 用户未登录跳转登录页
  // if (!user.value) {
  //   return navigateTo('/login')
  // }
  })

页面中使用:

<script setup>
definePageMeta({
  middleware: ["auth"]
  // or middleware: 'auth'
})
</script>

10、内置状态管理方法useState以及Pinia支持

跨组件创建响应性的、对ssr友好的共享状态—— useState

useState 是服务端友好的 ref 替换。它的值在服务端渲染(客户端注水的过程中)将被保留并通过唯一key在组件间共享。

共享状态: 可以利用nuxt的composables自动导入特性,把它们定义在composables目录中,这样他们将成为全局类型安全的状态。

//用户状态信息模块

// useState<T>(key: string, init?: () => T): Ref<T>

//key:唯一键用于去重
//init:提供初始值的函数

composables/useAuth.js
export const useUser = () => useState("user", () => null);

export async function useRefreshUserInfo() {
  const token = useCookie("token");
  const user = useUser();
  // 用户已登录,直接获取用户信息
  if (token.value) {
    let { data } = await userGetinfo();
    if (data.value) {
      user.value = data.value.data;
    }
  }
}

// 页面中使用

const user = useUser();
console.log("user",user)

Pinia支持nuxt.com/modules

//安装:

yarn add @pinia/nuxt

//配置 nuxt.config.js
// nuxt.config.js
export default defineNuxtConfig({
  // ... 其他配置
  modules: [
    // ...
    '@pinia/nuxt',
  ],
})

//使用:
composables文件下创建stores.js
import { defineStore } from 'pinia'

export const useUserStore = defineStore('userInfo', () => {

    const userInfo = ref({
        userName: 'zhang',
        id: 1,
        sex: '男',
    })
  
    return {
      userInfo,
    }
  })

// 页面应用
const userInfo = useUserStore().userInfo
console.log("userInfo",userInfo)

11、插件扩展

Nuxt自动读取 plugins 目录中的文件,并在创建 Vue 应用程序时加载它们。可以在文件名中使用.server或.client后缀来只在服务器端或客户端加载插件。

只有在plugins/目录的顶层的文件(或任何子目录中的索引文件)才会被注册为插件。

传递给插件的唯一参数是 nuxtApp。

import vueQr from "vue-qr/src/packages/vue-qr.vue";
//使用defineNuxtPlugin()注册一个插件:
//唯一的参数是nuxt实例
// export default defineNuxtPlugin(nuxtApp => {
//  Doing something with nuxtApp
//   })

// 返回一个对象,在里面设置provide key,
export default defineNuxtPlugin(() => {
    return {
        provide: {
            vueqr: vueQr
        }
    }
})

一个常见应用是给NuxtApp实例提供一些额外的帮助方法,我们可以通过编写一个插件,返回一个对象,在里面设置providekey,比如:

import { defineNuxtPlugin } from '#app'

export default defineNuxtPlugin(() => {
  return {
    provide: {
      hello: () => 'world'
    }
  }
})

使用这个helper,index.vue:

// 会自动加上$前缀
const { $hello } = useNuxtApp();
console.log($hello())

其他:

其他插件:比如

vue-qr

ant-design-vue

12、环境变量

方法一、 Nuxt CLI在开发模式下以及运行 nuxi build 和 nuxi generate 时内置了 dotenv 支持。

除了任何进程环境变量外,如果您的项目根目录中有一个.env文件,它将在构建、开发和生成时自动加载,并且在 nuxt.config 文件和模块中设置的任何环境变量都将可访问。

如果您想使用不同的文件 - 例如,使用 .env.local 或 .env.prod - 我们可以在使用 nuxi 时传递--dotenv 标志来实现。例如:

npx nuxi dev --dotenv .env.local

在开发模式下更新 .env 文件时,Nuxt 实例会自动重新启动以将新值应用于 process.env。

runtimeConfig API 向应用程序的其余部分公开了诸如环境变量之类的值。默认情况下,这些键只在服务器端可用。runtimeConfig.public 中的键也可以在客户端使用。这些值应该在 nuxt.config 中定义,并且可以使用环境变量重写。我们还可以在 app.config 定义公开变量。二者区别是:

  • runtimeConfig:需要在使用环境变量构建后指定的私有或公共令牌。
  • app.config:在构建时确定的公共令牌,网站配置,如主题变量,标题和任何不敏感的项目配置。

启动项配置:

"scripts": {
  "build": "nuxi build --dotenv .env.production",
  "test": "nuxi build --dotenv .env.test",
  "dev": "nuxi dev --dotenv .env.development -p 3001",
  "generate": "nuxi generate",
  "preview": "nuxi preview",
  "start": "node .output/server/index.mjs"
},

.env 文件配置:

# api Url
NUXT_PUBLIC_API_BASE_URL = 'https://magiccube-gateway.3weijia.com'

nuxt.config.js 配置:

import { NuxtConfig } from "nuxt/config";

export default defineNuxtConfig({
  runtimeConfig: {
     public: {
        apiBaseUrl: process.env.NUXT_PUBLIC_API_BASE_URL
     },
   }
});

使用:

const runtimeConfig = useRuntimeConfig();
const { loginUrl, locationOriginUrl } = runtimeConfig.public;

方法二、根目录创建env目录,创建环境变量文件

文件命名规则 .env.[环境变量名,如dev]

env.dev文件,其他文件同理

# 请求接口地址
VITE_REQUEST_BASE_URL = '/m-staff-center/api/v1'
VITE_SERVER_NAME = 'https://md.abc.com.cn/'
# VITE开头的变量才会被暴露出去

// package.json
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev --mode dev",
    "test": "nuxt dev --mode test",
    "uat": "nuxt dev --mode uat",
    "pre": "nuxt dev --mode pre",
    "prd": "nuxt dev --mode prd",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  },

//nuxt.config.js

import { loadEnv } from 'vite'
console.log('基础服务路径', loadEnv(process.argv[process.argv.length-1], './env').VITE_SERVER_NAME)

关于配置的官网文档: Runtime Config

用于持续集成的命令: nuxi build

环境变量文件的说明: .env

配置文件说明: Nuxt Config

13、Nuxt 常用的 Hooks

//1、 useAppConfig:访问项目中的 Nuxt 配置
const appConfig = useAppConfig()

console.log(appConfig)

//2、 useAsyncData 页面、组件和插件中,您可以使用 useAsyncData 来访问异步返回的数据;

const { data, pending, error, refresh } = await useAsyncData(
  'mountains',
  () => $fetch('https://api.nuxtjs.dev/mountains')
)

//3、useFetch 顾名思义,这就是一个 fetch 请求;

在页面、组件或者插件中可以使用 useFetch 获取任意URL资源。

useFetch是对useAsyncData包装,自动生成key同时推断响应类型,用起来更简单。

const param1 = ref('value1')
const { data, pending, error, refresh } = await useFetch('https://api.nuxtjs.dev/mountains',{
    query: { param1, param2: 'value2' }
})

//4、useCookie 在页面、组件和插件中,可以使用 useCookie 一个 SSR 友好的 hooks 来读取和写入 cookie。

//5、useHead Nuxt 提供 useHead 可组合项来添加和自定义 Nuxt 应用程序各个页面的头部属性。

//6、useRoute useRoute 是一个在实际项目开发中使用较多的 hooks,主要返回当前路由的一些数据;并且必须在 setup 函数、插件或路由中间件中调用。

<script setup>
const route = useRoute()
const { data: mountain } = await useFetch(`https://api.nuxtjs.dev/mountains/${route.params.slug}`)
</script>

//7、useRouter useRouter 返回路由器的实例,并且必须在设置函数、插件或路由中间件中调用。

const router = useRouter();
router.back();
router.forward();
router.go();
router.push({ path: "/home" });
router.replace({ hash: "#bio" });

14、配置

使用css/scss/less/stylus

css 的集成有多种方式,有使用原生 css 的,也有使用 less 和 scss 的,也有使用 stylus 的;

为了快速上手,这里我就不一一介绍了,下面主要以 less 为例;

// 安装
pnpm install less less-loader --save-dev

//新建 src/assets/styles 目录添加默认 default.less

$bgColor: rgb(125, 159, 172);
$theme: red;

//在 nuxt.config.js 中添加 less 的配置

 vite: {
        css: {
          preprocessorOptions: {
            less: {
              additionalData: '@import "@/assets/default.less";',
            },
          },
        },
        envDir: "~/env", // 指定env文件夹
  }

SEO和Meta配置

// nuxt.config.js

export default defineNuxtConfig({
  builder: "vite", 
  runtimeConfig: {
    // 运行时常量
    public: {
      // 客户端-服务的都能访问
      ...envData,
    },
  },
  app: {
    head: {
      titleTemplate: "%s - 官网",
      title: "xxx",
      charset: "utf-8",
      htmlAttrs: {
        lang: "zh-cn",
      },
      bodyAttrs: { class: 'body-class' },
      meta: [
        { name: "description", content: "xxx" },
        { name: "keywords", content: "xxx" },
      ],
      script: [
        { src: "https://xxx/jquery.js" }
      ],
      link: [
        // { rel:"stylesheet",href:"http://xxx.css" }
      ],
    },
  },
  modules: ["@element-plus/nuxt"],
  vite: {
    css: {
      preprocessorOptions: {
        less: {
          additionalData: '@import "@/assets/default.less";',
        },
      },
    },
    envDir: "~/env", // 指定env文件夹
  },
  elementPlus: {
    /** Options */
  },
  imports: {
    // Auto-import pinia stores defined in `~/stores`
    dirs: ["apis"],
  },
// 跨域处理
  nitro: {
    devProxy: {
      "/api": {
        target: "http://localhost:3001", // 这里是接口地址
        changeOrigin: true,
        prependPath: true,
      },
    },
  },
});

部署方式一:使用 pm2

根目录新建 ecosystem.config.js

module.exports = {
  apps: [
    {
      name: 'nuxt3 demo',
      port: '8080',
      exec_mode: 'cluster',
      instances: '3',
      script: './.output/server/index.mjs'
    }
  ]
}

三、遇到的问题、解决方法

1、环境变量的问题

最初我是通过 process.env.NODE_ENV 来判断当前环境,然后根据环境来配置不同的变量,然而上线之后遇到了一个问题,测试环境和正式环境的 NODE_ENV 都是 production ,这样就导致了无法区分环境,只能通过手动修改 nuxt.config.ts 文件来切换环境,这样的方式肯定是不行的

2、西瓜视频播放器通过插件形式引入,播放不稳定、出现bug

通过cdn方式加载:

app: {
      script: [
        {
          src: "https://unpkg.byted-static.com/xgplayer/2.31.6/browser/index.js",
        },
        {
          src: "https://unpkg.byted-static.com/xgplayer-hls.js/2.2.2/browser/index.js",
        }
      ],
      link: [
        // { rel:"stylesheet",href:"http://xxx.css" }
      ],
    },
  },

3、渲染第三方插件,服务器渲染与客户端渲染不一致Nuxt3 -实践指南,你不知道的“黑科技“

中的子节点不匹配:服务器渲染的元素包含的子节点比客户端的少。

Nuxt3 -实践指南,你不知道的“黑科技“

服务端与客户端渲染的DOM结构不一致;解决办法是在组件最外层加上组件,该组件只是占位且用于仅在客户端渲染

4、nuxt在使用Element 弹框组件,出现默认的白边,如何去掉

先在nuxt.config.js中添加如下配置

export default defineNuxtConfig({
	app: {
    head: {
      bodyAttrs: { class: 'body-class' }
    }
  }
})

// default.less 中添加css样式

.body-class { width:100%;height: 100%; margin:0; padding: 0;} 

四、其他

Nuxt3.x官网nuxt.com/

Nuxt3 issues github.com/nuxt/nuxt/i…

Nuxt3.x中文文档www.nuxt.com.cn/

Nitronitro.unjs.io/

PM2中文网pm2.fenxianglu.cn/

转载自:https://juejin.cn/post/7242623687122354237
评论
请登录