Nuxt3 基础总结

前言

Nuxt3 的对比之前的2 和 1 ,只能感叹前端发展的越来越快了,不学无术

(PS :好好学,我还年轻 我还可以 每天进步一小点)

开发更快
打包更小
支持 vite
支持 vue3
支持自动引入
支持文件路由
支持布局系统
支持多种渲染模式
支持 typescript
支持 composition-api

 

安装NUXT3 需要node 大于16的版本

brew 更新 node 

brew update

brew upgrade node

还有就是 node 安装

 

npm install -g

npm install -g

sudo n stable

最后查看node版本

然后 安装官网的来

npx nuxi@latest init <project-name>

因为家里的网 没有那么快,需要在hosts 配置

 sudo vi /etc/hosts

 

 把上面的代码输入上去 按 : wq 退出保存

 

然后在运行

 最后 安装 cd my-nuxt.  npm run dev

打开目录 vscode

 

vetur  把插件卸载 然后装上 Volar.  vue3 的插件 这个是有好

在开发项目中我们经常会使用到一些第三方库/插件、组件来丰富项目,这里列举一些开发后台项目用到的第三方库/插件、组件。

naive-ui

安装 naive-ui@css-render/vue3-ssr

yarn add -D naive-ui @css-render/vue3-ssr

成功之后再配置

在 nuxt.config.ts 增添下列配置

import { defineNuxtConfig } from 'nuxt'

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  build: {
    transpile:
      process.env.NODE_ENV === 'production'
        ? [
            'naive-ui',
            'vueuc',
            '@css-render/vue3-ssr',
            '@juggle/resize-observer'
          ]
        : ['@juggle/resize-observer']
  },
  vite: {
    optimizeDeps: {
      include:
        process.env.NODE_ENV === 'development'
          ? ['naive-ui', 'vueuc', 'date-fns-tz/formatInTimeZone']
          : []
    }
  }
})

然后再添加一个插件

 

新建目录: plugins   - naive-ui.ts

 官网的内容 Ui内容
import { setup } from '@css-render/vue3-ssr'
import { defineNuxtPlugin } from '#app'

export default defineNuxtPlugin((nuxtApp) => {
  if (process.server) {
    const { collect } = setup(nuxtApp.vueApp)
    const originalRenderMeta = nuxtApp.ssrContext?.renderMeta
    nuxtApp.ssrContext = nuxtApp.ssrContext || {}
    nuxtApp.ssrContext.renderMeta = () => {
      if (!originalRenderMeta) {
        return {
          headTags: collect()
        }
      }
      const originalMeta = originalRenderMeta()
      if ('then' in originalMeta) {
        return originalMeta.then((resolvedOriginalMeta) => {
          return {
            ...resolvedOriginalMeta,
            headTags: resolvedOriginalMeta['headTags'] + collect()
          }
        })
      } else {
        return {
          ...originalMeta,
          headTags: originalMeta['headTags'] + collect()
        }
      }
    }
  }
})

自定义错误页面

根目录下新建 error.vue

<template>
  <n-result status="500" title="错误提示" :description="error.message">
    <template #footer>
      <n-button @click="handleError">回到首页</n-button>
    </template>
  </n-result>
</template>
<script setup>
import {
  NResult,
  NButton
} from "naive-ui"

const prop = defineProps({
  error : Object
})
const handleError = () => clearError({ redirect: '/' })
</script>
<style lang="">
  
</style>

 

在app.vue 

<script setup>
import {
  NButton
} from "naive-ui"

throwError('故意报错')
</script>

就看到了这个页面

 

自定义全局loading 实现

 上面那个不适合服务端渲染

然后要用到其他的UI的其他

 

然后再。plugins 下新建一个  globalloading.ts 文件

import {
  createDiscreteApi,
  ConfigProviderProps,
  // darkTheme,
  // lightTheme
} from 'naive-ui'
export default defineNuxtPlugin((nuxtApp) => {
  const bar = ref(null);
  nuxtApp.hook("app:mounted", (e) => {
    if (!bar.value) {
      const { loadingBar } = createDiscreteApi(['loadingBar'])
      bar.value = loadingBar;
    }
    console.log('app:mounted')
  })
  nuxtApp.hook("page:start", (e) => {
    bar.value?.start()
    //bar.value ? bar.value.start() : '';
    console.log('age:start')
  })
  nuxtApp.hook("page:finish", (e) => {
    console.log('page:finish')
    // bar.value?.finish()
    setTimeout(() => {
      bar.value?.finish()
    }, 1000)
  })
  nuxtApp.hook("app:error", (e) => {
    console.log('app;error')
  })

})

 

最后就看到了加载的页面进度条

 

安装  windicss

yarn add nuxt-windicss -D

 

关于NUXT目录的

Nuxt 使用.nuxt/目录在开发中生成您的Vue应用程序。

你不应该碰里面的任何文件,因为整个目录将在运行 nuxt dev 时重新创建。

就是最上面的nuxt 目录

components 组件

组件名策略

默认情况下,Nuxt自动导入components目录中的任何组件,组件名将基于它的路径、目录和文件名。

惰性加载组件

要动态导入一个组件(也称为惰性加载组件),则在组件名称前添加 Lazy 前缀。通过使用 Lazy 前缀,你可以延迟加载组件代码,直到合适的时刻

 
<template>
   <div>
      <MyImg />
      <LazyMyImg />
   </div>
</template>

composables 

composables 目录下的内容也将自动将  Vue 组合导入到应用中,Nuxt 只扫描 composables/ 目录的顶层文件。Composables 的主要作用是将常用逻辑和逻辑相关的代码抽象出来,以提高代码可复用性和可维护性,如:跨组件创建响应性的、对ssr友好的共享状态—— useState

 
/**
* composables/counter.ts 内容
**/
export const userCounter = () => {
   return useState('counter', () => 0);
};

 

/**
* 业务组件
**/
<script setup>
  import Count from "~/components/business/Count.vue";
  const counter = userCounter();
</script>
<template>
  <div>
    业务组件内容: {{ counter }}
    <a-button type="primary" @click="counter--"> - </a-button>
    <a-button type="primary" @click="counter++"> + </a-button>
  </div>
</template>

 

/**
* business/Count 组件内容
**/
<template>
  <div>Count组件内容:{{ counter }}</div>
</template>
<script setup lang="ts">
  const counter = userCounter();
</script>

layouts

Nuxt提供了一个可定制的布局框架,可以在整个应用程序中使用,非常适合将常见的UI或代码模式提取到可重用的布局组件中。布局放在layouts/目录中,使用时将通过异步导入自动加载。

默认布局

在layouts目录下添加default.vue 布局文件。

不像其他组件,布局组件必须有一个根元素,以允许 Nuxt 在布局变化之间应用过渡-这个根元素不能是<slot />。如果你的应用只有一个布局,建议使用app.vue。

在布局文件中,布局的内容将加载在<slot />中,~/layouts/default.vue:

 
<template>
   <div>
      <slot />
   </div>
</template>

 

如果你使用app.vue你还需要添加 :


<template>
   <NuxtLayout>
      // 在app.vue中没有NuxtLayout组件,内容将会不显示
   </NuxtLayout>
</template>

 

配置布局

-| layouts/
---| default.vue
---| custom.vue

 

可以在 NuxtLayout 中添加 name 属性来覆盖默认布局:

<template>
   <NuxtLayout :name="layout">
      <NuxtPage />
   </NuxtLayout>
</template>
<script setup>
   // 您可以根据API调用或登录状态来选择此选项
   const layout = "custom";
</script>

 

也可以通过 definePageMeta 设置

<template>
  <NuxtLayout>
    2023,12.23
  </NuxtLayout>
</template>
<script setup>
  definePageMeta({
    layout: "custom",
  });
</script>

如果业务组件不使用 <NuxtLayout> 组件包裹,配置布局是不会生效的

方法

 

pages 页面

页面目录。Nuxt 提供了一个基于文件的路由,使用 Vue Router 在底层创建路由。pages/index.vue 文件将被映射到应用程序 / 路由。

如果你正在使用app.vue,确保在 app.vue 使用 <NuxtPage/> 组件来显示当前页面。

动态路由

建立页面文件时,如果命名时将任何内容放在方括号内,它将被转换为路由参数。在文件名或目录中混合和匹配多个参数。

 
-| pages/
---| index.vue
---| users-[group]/
-----| [id].vue

 

会生成路由:

{
   "routes": [
     {
        "name": "users-group-id",
        "path": "/users-:group()/:id()",
        "component": "~/pages/users-[group]/[id].vue"
     },
   ]
}

根据上面的例子,你可以通过 $route 对象中的 params 访问组件中的 group & idx

<template>
   <ul class="text-base text-gray-600">
     <li>
        获取到的 group 是 <span class="text-green-500 text-xl">{{ group }}</span>
     </li>
     <li>
        获取到的 id 是 <span class="text-green-500 text-xl">{{ id }}</span>
     </li>
   </ul>
</template>

 

nuxt3  中内置了四种请求的方法


useFetch
useLazyFetch
useAsyncData
useLazyAsyncData

推荐的话还是 useFetch

const param1 = ref('value1')
const { data, pending, error, refresh } = await useFetch('/api/modules', {
  query: { param1, param2: 'value2' }
})

 

 

Request 的封装与使用

 没有用 nuxt3 自带的接口返回格式才进行这样的简易封装;

  • ~enums/interface.ts  // 定义接口返回 code 值的枚举
 
export enum ResultEnum {
  SUCCESS = 0,
  TOKEN_OVERDUE = 404, // 用户登录失败
  INTERNAL_SERVER_ERROR = 500, // 服务异常
}
  • ~/composables/useDefaultRequest.ts  // 定义接口统一拦截函数
import { UseFetchOptions } from "nuxt/app";
import { RouteLocationRaw } from "vue-router";
import { ResultEnum } from "~/enums/interface";

interface DefaultResult<T = any> {
  code: number;
  data: T;
  msg: string;
  success: boolean;
}

type UrlType = string | Request | Ref<string | Request> | (() => string | Request);

type HttpOption<T> = UseFetchOptions<DefaultResult<T>>;

interface RequestConfig<T = any> extends HttpOption<T> {
  // 忽略拦截,不走拦截,拥有 responseData,且 code !== 0 的情况下,直接返回 responseData,
  // 但是若无 responseData, 不设置 ignoreGlobalErrorMessage 也会报错
  ignoreCatch?: boolean;

  // 忽略全局错误提示,走拦截,但是任何情况下都不会提示错误信息
  ignoreGlobalErrorMessage?: boolean;
}

const request = async <T>(
  url: UrlType,
  params: any,
  options: RequestConfig<T>
): Promise<DefaultResult<T> | T> => {
  const headers = useRequestHeaders(["cookie"]);
  const method = ((options?.method || "GET") as string).toUpperCase();

  const runtimeConfig = useRuntimeConfig();
  const nuxtApp = useNuxtApp();
  const { $message, $login } = nuxtApp;
  const { apiBaseUrl } = runtimeConfig.public;
  const baseURL = `${apiBaseUrl}/mall/api`;

  // 处理用户信息过期
  const hanlerTokenOverdue = async () => {
    const { _route } = nuxtApp;
    await $login(_route?.fullPath);
  };

  // 处理报错异常
  const handlerError = (msg = "服务异常") => {
    if (process.server) {
      showError({ message: msg, statusCode: 500 });
    } else {
      $message.error(msg);
    }
  };

  const { data, error } = await useFetch(url, {
    baseURL,
    headers,
    credentials: "include",
    params: method === "GET" ? params : undefined,
    body: method === "POST" ? JSON.stringify(params) : undefined,
    ...options,
  });

  const responseData = data.value as DefaultResult<T>;
  const { ignoreCatch, ignoreGlobalErrorMessage } = options; // 忽略全局

  if (error.value || !responseData) {
    if (!ignoreGlobalErrorMessage) handlerError();
    return Promise.reject(error.value || "服务响应失败,请稍后重试");
  } else {
    const { code, data: result, msg } = responseData;
    // 接口请求成功,直接返回结果
    if (code === ResultEnum.SUCCESS || !code) {
      return result;
    }
    if (!ignoreCatch) {
      // 接口请求错误,统一处理
      switch (code) {
        case ResultEnum.TOKEN_OVERDUE: // 登录信息过期,去登录
          // 用户信息过期
          await hanlerTokenOverdue();
        default:
          if (!ignoreGlobalErrorMessage) handlerError(msg);
          return Promise.reject(msg || "服务响应失败,请稍后重试");
      }
    }
  }

  return responseData;
};

// 自动导出
export const useDefaultRequest = {
  get: <T>(url: UrlType, params?: any, option?: RequestConfig<T>) => {
    return request<T>(url, params, { method: "get", ...option });
  },
  post: <T>(url: UrlType, params?: any, option?: RequestConfig<T>) => {
    return request<T>(url, params, { method: "post", ...option });
  },
};

 

useFetch() 在 onMounted 执行返回值为 null?

 解决
     onMounted(async () => {
        console.log('onMounted');
        await nextTick();
        testFetch1();
      });
 
原因:只有客户度执行 
服务端不执行onMounted内容;
如果onMounted内需执行useFetch,应该放在await nextTick之后,否则会返回null;
在setup内(onMounted外)执行useFetch,服务端和客户端都执行,
为了避免渲染时useFetch未执行完毕,尽量使用await而不是then方法;

 
 
先更新到这里先把
 

 

posted @ 2023-12-23 16:56  -鹿-  阅读(637)  评论(0编辑  收藏  举报