关于Nuxt3+Vue3的基础使用

nuxt.config.ts的配置

1. 配置应用标题和图标

arduino 复制代码
export default ({
  app: {
    head: {
      title: '我是nuxt应用', // 应用的默认标题
      charset: "utf-8", // 字符集
      htmlAttrs: {
        lang: "zh-CN", // HTML的语言属性为"zh-CN"(中文)
      },
      // 修改页签图标
      link: [
        {
          rel: 'icon',
          type: 'image/x-icon',
          href: '/favicon.png', // 该图片文件的放置在根目录下的public下
        },
      ],
    },
  },
  ...
});

应用的头部配置需要放置在app:{}中,

2. 配置渲染方式

服务端渲染
arduino 复制代码
export default ({
 ssr: true
  ...
});
客户端渲染
arduino 复制代码
export default ({
 ssr: false
  ...
});
混合渲染
arduino 复制代码
export default ({
 // ssr: false 这里可以不用写
   routeRules: {
    '/': { ssr: true }, // 开启服务端渲染
    '/route1': { ssr: false }, // 仅客户端渲染
    '/route2': { ssr: false }, // 仅客户端渲染
    '/route1/*': { ssr: false },// 仅客户端渲染
    '/route2/*': { ssr: false },// 仅客户端渲染
  },
  ...
});

3. 配置端口

yaml 复制代码
export default ({
 devServer: {
  port: 8080, //配置端口为8080
},
 server: {
  port: 3000,
  host: '0.0.0.0', //指定应用在生产环境中可被访问的主机地址。设置为 '0.0.0.0' 表示应用将接受来自任何 IP 地址的请求。
  timing: false, //用于启用或禁用服务器端请求计时,这对于调试和性能监控可能有用。
},
...
});

server 配置是针对生产环境的,而 devServer 配置是针对开发环境的。

4. 配置开发代理--网关配置

nuxt3的代理需要在nitro里面进行配置

arduino 复制代码
export default ({
  nitro: {
    devProxy: {
      "/api": {
        target: "http://xxx.xxx.x.xxx:xxxx", // 后端的接口地址
        changeOrigin: true,
        prependPath: true,
      },
    },
    routeRules: {
      '/api/**': {
        proxy: 'http://xxx.xxx.x.xxx:xxxx/**'
      }
    }
  },
  ...
});

注意routeRules后面还有/**,这是必不可少的,devProxy 是客户端渲染的,而 routeRules 是针对的服务端渲染的。

5. 多环境开发配置

假设我们有多个环境,如dev、sit环境,这里采用的是通过命令行传参,定义构建的是哪个环境,并在项目里接收参数设置环境变量。

在根目录下新建一个文件,这里叫作build.js,进行参数的接收

javascript 复制代码
// build.js

// 解析命令行参数

//这里是获取构建命令 --env=环境,注意,我们通过process.env.npm_config_argv获取的对象是字符串的形式,所以需要通过JSON.parse进行转换
const envArg = JSON.parse(process.env.npm_config_argv)?.original[1];  

//sit
const env = envArg ? envArg.split('=')[1] : 'dev';  //

// 设置环境变量
process.env.NUXT_ENV_MODE = env;

// 接下来,调用 Nuxt 构建命令或其他需要的脚本
console.log(`正在以 ${env} 模式构建...`);

在nuxt.config.ts文件引用,并且修改nitro routeRules的proxy

javascript 复制代码
import './build.js';
...

function getEnv(env: any) {
  let api = '';
    switch (env) {
    case 'dev':
      api = 'http://dev-api/**';
      break;

    case sit:
      api = 'http://sit-api/**';
      break;
   
  }
  
  return api;
}
...

export default ({
  nitro: {
    routeRules: {
      '/api/**': {
        proxy: getEnv(process.env.NUXT_ENV_MODE)
      }
    }
  },
  ...
});

规定构建命令传参方式("--env=环境变量"是我们的参数):

ini 复制代码
//构建命令 终端执行
yarn build --env=环境变量

//例子------构建sit环境
yarn build --env=sit  

这时候执行构建命令,便可以构建出相应的环境了

更多关于nuxt.config.ts的配置可参考:ezdoc.cn/docs/nuxtjs...

插件的安装并全局引入

ant-design-vue

除了使用yarnnpm等命令安装ant-design之外,需要在根目录创建一个plugins文件夹(有就不用创建了)创建一个文件(这里用的是ts)antd.ts

javascript 复制代码
// plugins/antd.ts
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.less';
import { defineNuxtPlugin } from 'nuxt/app';

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(Antd);
});

nuxt.config.ts配置

arduino 复制代码
export default ({
  plugins: [
    '@/plugins/antd.ts',
    '@/plugins/echarts.ts',
    { src: '@/plugins/pdf.ts', ssr: false, mode: client }
  ],
  ...
});

类似这样的依赖库需要在plugins文件夹下创建文件全局引入,并在nuxt.config.ts,但类似less、typescript等插件或扩展类的直接使用命令安装相应的版本就可以了。

API的解释

useAsyncData、useFetch、useLazyAsyncData和useLazyFetch的区别

Nuxt 3 提供了几种不同的函数来处理异步数据和 API 请求,包括 useAsyncData, useFetch, useLazyAsyncData, 和 useLazyFetch。这些函数都是为了在 Nuxt 应用中更方便地处理异步操作,但它们各自有不同的使用场景和行为。下面是每个函数的概述及其之间的主要区别:

1. useAsyncData

  • 用途:用于在服务器端或客户端获取异步数据,并将数据响应式地绑定到组件上。这个函数特别适合用于获取初始渲染页面时需要的数据。

  • 特点:它会在服务器端进行数据的获取(如果是首次页面加载的情况),之后的数据更新会在客户端进行。这有助于实现服务端渲染(SSR)和静态站点生成(SSG)。

  • 场景:适合需要预先获取数据以便在页面加载时立即可用的场景。

2. useFetch

  • 用途 :与 useAsyncData 类似,也是用于获取异步数据并响应式地绑定到组件。它提供了更多的配置选项,比如可以设置请求的方式(GET、POST 等)。

  • 特点useFetch 也支持 SSR 和客户端数据获取,但提供了更丰富的配置选项,例如可以设置请求头、方法等。

  • 场景:当你需要更细粒度的控制请求配置时使用。

3. useLazyAsyncData

  • 用途:这个函数用于懒加载异步数据,即数据的获取将推迟到明确需要时再进行。

  • 特点:它不会在服务器端自动执行,数据的获取会延迟到客户端,并且通常是在用户进行某些操作(如点击按钮)时触发。

  • 场景:适合那些不需要在页面初次加载时立即可用,或者对页面首屏加载性能有严格要求的场景。

4. useLazyFetch

  • 用途 :与 useLazyAsyncData 类似,用于在客户端懒加载异步数据。它也提供了配置选项来控制请求的行为。

  • 特点 :这个函数提供了类似于 useFetch 的配置能力,但数据获取的触发是懒惰的,需要在特定时机手动触发。

  • 场景:当你需要在客户端懒加载数据,并且需要详细配置请求时使用。

总结

  • 立即获取数据 :如果你需要在页面加载时立即获取数据,并可能涉及服务端渲染,使用 useAsyncDatauseFetch

  • 懒加载数据 :如果数据不需要立即加载,或者你想在用户执行某些操作后才获取数据,以优化性能或用户体验,使用 useLazyAsyncDatauseLazyFetch

  • 配置请求 :如果需要对 HTTP 请求进行详细配置,比如设置请求头或请求方法,倾向于使用 useFetchuseLazyFetch

这些函数提供了灵活的数据获取方式,可以根据你的具体需求和应用场景选择最合适的方法。

useHydration

useHydration 是 Nuxt 3 中的一个组合式 API(Composition API),设计用来处理客户端渲染(Client-Side Rendering, CSR)和服务端渲染(Server-Side Rendering, SSR)之间的状态同步问题。在 SSR 应用中,首次页面加载时的 HTML 是在服务器上生成的,然后发送给客户端。客户端接着"激活"这些静态标记,使其变成动态的应用。这个激活过程通常被称为"hydration"。useHydration 用于在这个过程中,特别是在客户端上,控制和管理特定的状态或逻辑,以确保客户端和服务端的同步。

使用场景

  • 条件渲染: 在服务端生成一部分标记,但你希望在客户端根据用户的交互或设备能力(例如,检测是否支持 JavaScript)来决定是否显示这部分内容。

  • 客户端特有的操作: 某些操作只能或应该在客户端进行,如访问本地存储(localStorage)、使用 windownavigator 对象等。

基本使用

基本上,useHydration 允许你检测组件是否正在进行 hydration 过程。这可以帮助你决定何时执行客户端特定的逻辑。

xml 复制代码
<script setup>
import { useHydration } from '#app'

// 检测是否在进行客户端的 hydration 过程
const isHydrating = useHydration()

// 你可以基于 isHydrating 的值来决定是否执行某些操作
</script>

示例

假设你有一个组件,它在客户端上需要根据用户的交互来显示不同的内容,但这个交互不会影响到服务端渲染的结果。你可以利用 useHydration 来控制这部分逻辑的执行时机。

xml 复制代码
<script setup>
import { useHydration, onMounted } from 'vue'
import { someClientSideOnlyFunction } from 'path/to/your/functions'

const isHydrating = useHydration()

onMounted(() => {
  if (!isHydrating.value) {
    // 仅在客户端且非 hydration 过程中执行
    someClientSideOnlyFunction()
  }
})
</script>

在这个示例中,someClientSideOnlyFunction 只会在客户端渲染完成后执行,避免了在服务端渲染过程中执行客户端特定的逻辑。

注意事项

  • 使用 useHydration 需要对 SSR 和 CSR 有一定的理解,特别是它们在应用初始化时的行为差异。

  • 适当使用 useHydration 可以提升应用性能和用户体验,尤其是在涉及到复杂客户端交互的场景中。

  • 记得检测 isHydrating.value 的值来确保代码只在适当的时机执行,避免不必要的性能开销或潜在的错误。

通过合理利用 useHydration,你可以更精细地控制应用在不同渲染环境下的行为,从而提高应用的灵活性和用户体验。

接口的封装

在nuxt中,我们可以使用useAsyncData或useFetch进行接口的调用,在这里我采用了二者的接口,使用useFetch进行接口的调用,useAsyncData对useFetch获取的数据进行处理。

1. useFetch的封装

typescript 复制代码
import { type UseFetchOptions } from "nuxt/app";

export interface IResultData {
  data?: any;
  code?: number;
  message?: string;
  success?: boolean;
}

// 封装请求函数
async function request(
  url: string,
  option?: UseFetchOptions<IResultData>,
): Promise<Partial<IResultData>> {
  if (process.client) {
    await nextTick(); //解决刷新页面useFetch无返回
  }

  const { data } = await useFetch<IResultData>(url, {
    onRequest({ options }) {
      // 设置请求头
      options.headers = {....};
      return options;
    },
    // 请求拦截error
    onRequestError({ response }) {
      throw new Error(response);
    },

    // 响应拦截
    onResponse({ response }) {
      const { code, message } = response._data;
      if (code !== SUCCESS_CODE) {
        throw new Error(message);
      }
      return response._data;

    },
    ...option
  }

  );
  return data.value || {};
}


// 封装常用方法
export function get(url: string, params?: any, options?: UseFetchOptions<IResultData>) {
  return request(url, { method: "GET", params, ...options });
}

export function post(url: string, params: any, options?: UseFetchOptions<IResultData>) {
  return request(url, { method: "POST", body: params, ...options });
}

export function put(url: string, params: any, options?: UseFetchOptions<IResultData>) {
  return request(url, { method: "PUT", body: params, ...options });
}

export function del(url: string, params: any, options?: UseFetchOptions<IResultData>) {
  return request(url, { method: "DELETE", body: params, ...options });
}

2. useAsyncData的使用

javascript 复制代码
useAsyncData('get-data', async () => {
    const results = await Promise.allSettled([api1(), api1()]);
    results?.forEach((result, index) => {
        if (result.status === 'fulfilled') {
            switch (index) {
                case 0:
                   const data1 = result.value.data;
                   break;
                case 1:
                   const data2 = result.value.data;
                   break;
            }
        } else {
            console.error(`Request ${index} failed:`, result.reason);
        }
    });
});

当需要执行并行和独立的异步操作并收集所有结果时,Promise.allSettled() 就是不错的选择,即使一些异步操作可能失败。

如果不需要执行多个接口:

javascript 复制代码
useAsyncData('get-data-2', async () => {
    const res = await api()
    console.log(res)
});

注意:如果在同一个页面中使用useAsyncData执行多个相同的接口(但参数不一样)会导致最终的数据都会被最后调用的那个接口的数据覆盖。解决办法,不使用useAsyncData执行,采用常见的项目的接口调用方法。

路由生成

nuxt的项目会对在pages目录下的文件自动生成路由,不需要自行配置,除components文件夹外;components文件夹下的文件不会被nuxt处理。

Nuxt.js 依据 pages 目录结构自动生成 vue-router 模块的路由配置。假设 pages 的目录结构如下:

diff 复制代码
pages/
--| custompage/
-----| index.vue
--| index.vue

那么,Nuxt.js 自动生成的路由配置如下:

css 复制代码
router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'custompage',
      path: '/custompage',
      component: 'pages/custompage/index.vue'
    }
  ]
}

layout布局

在根目录下的layouts文件夹下的存储的是页面的布局,命名为default的文件会成为每一个页面级的默认布局。例子:

xml 复制代码
<template>
  <CHeader />
    <div>
      <slot></slot>
    </div>
  <CFooter />
</template>

<script lang="ts">
import CHeader from '@/components/common/CHeader.vue';
import CFooter from '@/components/common/CFooter.vue';
export default defineComponent({
        name: 'layout',
        components: { CHeader, CFooter},
});
</script>

在app.vue使用

xml 复制代码
<template>
    <div id="app">
        <NuxtLayout>
            <NuxtPage></NuxtPage>
        </NuxtLayout>
    </div>
</template>

如果某个页面不需要使用这个布局

xml 复制代码
<script lang="ts">
export default defineComponent({
    name: 'xxxx',
    setup() {
        //ui布局
        definePageMeta({
            layout: false,
        });
    },
});
</script>

使用其他布局则把layouts文件夹下的文件名赋值给definePageMeta的的layout属性就好:

xml 复制代码
<script lang="ts">
export default defineComponent({
    name: 'xxxx',
    setup() {
        //ui布局
        definePageMeta({
            layout: '其他布局的文件名',
        });
    },
});
</script>

服务端部署

nginx配置:

ini 复制代码
server {
    listen      80;
    server_name  localhost;
    gzip_static  on;
    gzip_proxied expired no-cache no-store private auth;
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.0;
    gzip_comp_level 9;
    gzip_types text/plain application/javascript text/css application/xml;
    gzip_vary on;
    # 将客户端访问代理到node服务的3000端口
    location / {
        index  index.html index.htm index.jsp;
        proxy_pass         http://web-official-website-node-node.dev-cicd.svc.cluster.local:3000/;  #web-official-website-node-node nuxt内网地址
        proxy_redirect     off;
        proxy_set_header   Host             $host;        # 传递域名
        proxy_set_header   X-Real-IP        $remote_addr; # 传递ip
        proxy_set_header   X-Scheme         $scheme;      # 传递协议
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    }

    location /api/ {
        index  index.html index.htm index.jsp;
        proxy_pass         http://yunshang-portal-gateway-node.dev-cicd.svc.cluster.local:49010/;  #yunshang-portal-gateway 网关集群内网连接
        proxy_redirect     off;
        proxy_set_header   Host             $host;        # 传递域名
        proxy_set_header   X-Real-IP        $remote_addr; # 传递ip
        proxy_set_header   X-Scheme         $scheme;      # 传递协议
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    }
}

构建执行 "yarn build" 命令之后,将编译出的以下文件上传到服务器:

lua 复制代码
ecosystem.config.cjs 
nuxt.config.ts 
package.json 
.nuxt 
.output

通过Docker镜像安装pm2启动nuxt服务,以下是Dockerfile配置:

bash 复制代码
# node版本最好和开发环境的版本保持一致
FROM node:hydrogen-alpine3.19

# 容器内创建目录/nuxt3
RUN mkdir -p /nuxt3

# 设置工作目录
WORKDIR /nuxt3/

# 复制当前的内容到容器内容部目录/nuxt3
COPY ./web-official-website-node/ /nuxt3/

RUN cd /nuxt3/

# 设置文件权限
RUN chmod -R 777 * 

# 禁用SSL检查
RUN npm config set strict-ssl false

# 安装pm2
RUN npm i pm2 -g --registry=http://registry.npm.taobao.org

# 容器对外暴露的端口号,要和node项目配置的端口号一致
EXPOSE 3000

# 运行
ENTRYPOINT ["pm2-runtime", "start", "ecosystem.config.cjs"]

编码常见

1. 标签

在服务端渲染的页面中,如果存在部分组件需要进行客户端渲染可使用该标签进行包裹,如:

xml 复制代码
<ClientOnly>
    <我是客户端渲染的组件/>
</ClientOnly>
2. 只能在客户端渲染代码

在Nuxt 3中,有些行为或代码只适用于客户端(浏览器环境),在服务端渲染(SSR)环境中执行时会报错。这通常是因为某些对象或功能在Node.js环境中不存在,但在浏览器中是可用的。以下是一些在服务端渲染中可能导致错误的常见情况:

  1. 访问浏览器特定的全局对象 :例如 windowdocumentlocalStoragesessionStorage。这些对象只在浏览器环境中存在,在服务端渲染中使用它们会导致 ReferenceError

  2. 直接操作DOM :任何直接操作DOM的代码,如 document.getElementById 或使用 querySelector,在服务端都会失败,因为在Node.js环境中没有DOM。

  3. 依赖于浏览器API的功能 :例如 FileReaderWebSocketCanvas API,这些只在浏览器中定义。

  4. 客户端特定的事件处理:比如监听滚动事件、点击事件等。这些在服务端没有相应的环境来触发它们。

  5. 使用浏览器导航和位置接口 :如使用 window.locationhistory API来处理URL和导航。

如果需要进行上述行为,我们可以采取以下措施

  • 使用 process.clientprocess.server 来判断当前代码是在客户端还是服务端执行,并据此调整行为。
  • 将依赖于客户端API的代码放在Vue的生命周期钩子 mounted 中,因为 mounted 只在客户端调用。
  • 对于客户端专用的功能或组件,可以使用动态导入(dynamic import)并结合 ssr: false 来避免在服务端加载它们。
  • 使用标签对使用上述代码的组件进行包裹。
相关推荐
程序员爱钓鱼7 天前
在 Nuxt 3 中实现和使用 SEO 数据:通过 useState 管理全局 SEO 信息
vue.js·后端·nuxt.js
仿生狮子11 天前
Reka UI 是个啥?
vue.js·nuxt.js·ui kit
MurphyChen12 天前
🧭 React 组件通信指南:父传子、子传父、任意组件通信
前端·react.js·nuxt.js
四木呀16 天前
Nuxt3 实现接口域名动态化
前端·nuxt.js
朝阳391 个月前
Nuxt.js 3【详解】服务器 Server
服务器·javascript·nuxt.js
wxz9992 个月前
nuxt2项目
javascript·nuxt.js
vortesnail3 个月前
Nuxt3 切换主题色且不闪屏该怎么做?
前端·vue.js·nuxt.js
陶然同学3 个月前
【学生管理系统】权限管理之角色管理
java·elementui·nuxt.js·学生管理系统
陶然同学3 个月前
【学生管理系统】权限管理之用户管理
java·nuxt.js·学生管理系统