Nuxt3基础10分钟快餐

420 阅读6分钟

文档地址: nuxt.com/
中文文档: nuxtjs.org.cn/

一、项目搭建

项目说明

nuxt 是服务端 + 客户端 同时存在的项目, 大部分客户端的无需交互就可以展示的内容由服务端直接渲染来展示,因此来通过 seo 的支持

初始化项目

nuxt.com/docs/gettin…
codeload.github.com/nuxt/starte…

依赖版本

npm list

项目目录结构

nuxt.com/docs/guide

启动端口配置

devServer: {
    host: "0.0.0.0",
    port: 3001,
},

引入 element-plus

nuxt.com/modules?q=e…

二、路由

路由自动识别

nuxt.com/docs/api/co…
nuxt.com/docs/gettin…

1、根据文件路径/ 名称自动识别路由名称

2、动态路由自动识别 [xxx].vue

该方式可以处理用 .html 的情况

3、 嵌套路由自动识别

[xxx]/test.vue

路由重定义和别名

definePageMeta({
    path: '/index.html',
    alias: '/',
})

其他相关

手动编写路由: nuxt.com/docs/guide/…
编程式跳转: nuxt.com/docs/api/ut…
其他路由操作: nuxt.com/docs/api/co…

三、自动注册

在 composables 和 utils 下的方法会自动注册,其他地方可以直接调用

composables: nuxt.com/docs/guide/…
utils: nuxt.com/docs/guide/…

composables 下创建: test.js

export const helloTest = async (params) => {
    return "hello" + params;
}

任意 .vue 文件下使用

helloTest(" 666")

四、全局参数

定义

  // 全局参数
    runtimeConfig: {
        // 只在服务端可获取
        isServer: "true",
        public: {
            // 在服务端 + 客户端都能获取
            baseUrl: "192.168.1.231:10008"
        }
    }

使用

const config  =useRuntimeConfig()
config.xxx

五、判断是服务端还是用户端

A
image.png
B
image.png
C
image.png

import.meta 判断

if(import.meta.server){
    // 服务端
}

if(import.meta.client){
    // 客户端
}

全局参数 判断

我们之前定义了一个全局参数 isServer
1、如果当前代码中能获取到 isServer 的值,就说明是服务端
2、如果当前代码中不能获取到 isServer 的值,就说明是客户端

const config  =useRuntimeConfig();
config.isServer

六、SSR 共享状态 useState

nuxt.com/docs/api/co…

在服务端 + 用户端可以共享数据
注意: 数据只在当前页面有效,刷新页面会丢失数据 (如: a 链接跳转地址)

定义

let res = useState("count',()=>{
    return 0;
}

// 读取+操作数据, 可以使用 callOnce 调用接口来异步设置数据 (详见文档)
res.value++

注意: 这里服务端会执行一次 res.value++, 客户端也会执行一次 res.value++

七、客户端全局状态 pinia

pinia= 客户端全局参数
pinia文档: pinia.vuejs.org/core-concep…

1、安装插件
安装1: nuxt.com/modules?q=p…
安装2: pinia.vuejs.org/ssr/nuxt.ht…

2、添加
composables/useStore.js

import { defineStore } from 'pinia'

export const useStore = defineStore('storeId1', {
    state: () => ({
        count: 0,
    }),
    actions: {
        increment() {
            this.count++
        },
        decrement() {
            this.count--
        },
    },
    getters: {
        doubleCount: (state) => state.count * 2,
    },
})

3、使用

直接使用

// 直接获取数据并操作
let count = store.count;
store.count++;

// 调用 actions 方法
useStore().increment();
useStore().decrement();

// 调用 getters 方法
useStore().getters();

注意: pinia 的数据是存储在内存中的,浏览器刷新,关闭会丢失数据, 可以在 useStore 下方法中添加把数据更新到 localStorage 或 cookie 中

pinia 插件: pinia-plugin-persistedstate

nuxt.com/modules?q=p…

多页面自动更新数据

initStorageListener() {
   // 监听 localStorage 变化
   if(import.meta.client){
       window.addEventListener('storage', (event) => {
           if (event.key === 'storeId1') {  // storeId1是你的store标识符
               const newValue = JSON.parse(event.newValue)
               if (newValue && newValue.count !== undefined) {
                   this.count = newValue.count
               }
           }
       })
   }
},

需要更新的页面中

useStore().initStorageListener();

八、插件

nuxt.com/docs/guide/…

  1. 自定义应用行为 (执行生命周期的钩子,如SSR 渲染完成后执行某些操作,或者在客户端挂载前执行某些操作)
  2. 添加全局方法和属性
  3. 处理全局事件 (路由变化、错误处理)
  4. 集成第三方库
  5. 初始化和配置 (设置全局样式、初始化第三方服务等)
  6. 注册全局组件和指令 (有点多余, 已经存在自动注册的组解目录)

九、生命周期钩子

nuxt.com/docs/api/ad…

服务端渲染html完成

插件中使用

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('app:rendered', (renderContext) => {
    // 在 SSR 完成后执行某些操作
    console.log('服务端渲染html完成');
  });
});

客户端页面加载完成

插件中使用

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('app:mounted', () => {
    // 在客户端应用挂载后执行某些操作
    console.log('客户端页面加载完成');
  });
});

或使用 Composition API, 当前 vue 文件中

import { onMounted } from 'vue'; // 已经全局存在了, 可以省略
onMounted(() => {
    console.log("客户端页面加载完成")         
})

十、全局路由守卫

nuxt.com/docs/guide/…

在 middleware 目录下创建 xxx.global.ts 会自动注册,访问路由时自动触发调用

export default defineNuxtRouteMiddleware((to, from) => {
  // 后续处理页面的登录拦截
    console.log("route: " + to.path)

    // 判断是否是客户端
    if (import.meta.client) {
        // 判断是否是不需要登录的 url
        let noTokenUrls = ['/login']
        if (noTokenUrls.includes(to.path)) {
            return;
        }
        if (localStorage.getItem("token")) {
            // 未登录
            return navigateTo('/login.html')
        }
    }
})

十一、网络请求

fetch 请求: nuxt.com/docs/api/ut…
useFetch 请求: nuxt.com/docs/api/co…

composables/crud.js

/**
 *  说明 useFetch 直接访问默认访问页面只在服务端请求一次,客户端端不会重复发起请求,后续客户端可以通过事件进行触发
 */

/**
 * http get 请求
 * @param url
 * @returns {Promise<void>}
 */
export const httpGet = async (url) => {
    return await httpRequest(url, {method: 'GET'})
}

/**
 * 统一请求方法
 * @param url
 * @param options
 * @returns {*}
 */
export const httpRequest = async (url, options) => {
    const config = useRuntimeConfig();
    // console.log("请求地址:" + config.public.baseUrl)
    return await useFetch(url, {
        baseURL: config.public.baseUrl,
        onRequest({options}) {
            // 全局请求拦截 (需要注意是服务端请求还是客户端请求,需要单独处理相关逻辑)
            options.headers = {
                "TOKEN": "123456",
                ...options.headers
            }
        },
        onResponse({response}) {
            // 全局响应拦截, 判断响应码等处理
            // console.log("返回数据" + response._data.value)
            return response._data;
        },
    })
}

十二、全局错误处理

增加全局错误

<!-- 错误页. 不管是 404 还是 500 还是其他错误都会自动到这个页面来 -->
<template>
    <div>
        <!--  服务端跳转这里可以根据 "statusCode": 404 来判断是404还是500 来进行展示  -->
        <!--  客户端错误过来的,是没有code的, 数据直接就是 Error: xxx错误信息,需要根据自定义的错误内容来判断  -->
        <h2> 错误页 </h2>
        <pre>
            { { error } }
       </pre>
    </div>

</template>

<script setup lang="ts">
defineProps({
    error: Object
})
</script>

<style scoped>
</style>

主动跳转错误

nuxt.com/docs/gettin…
服务端 + 客户端 (注意看当前的 nuxt的版本号是否支持)

throw useError({statusCode: 404, message: "找不到页面"})

服务端(部分) + 客户端

throw createError({statusCode: 404, message: "找不到页面", fatal: true})

客户端 + 中间件/插件

throw showError({statusCode: 404, message: "找不到页面数据"})

十三、layouts

nuxt.com/docs/guide/…

抽象默认的通用布局

创建默认通用布局 layouts/default.vue
<slot /> 展示具体页面内容

<div>
    <p>通用层代码</p>
    <slot />
  </div>

app.vue 指定使用 layouts

<NuxtLayout>
   <NuxtPage />
</NuxtLayout>

单独布局

不需要使用通用布局的页面添加 layout: false,

definePageMeta({
   layout: false,
})

十四、环境配置

nuxt.com/docs/guide/…

创建文件

  • .env.dev 本地环境
  • .env.pro 生产环境

可以在不同的环境中定义不同的值,如: 接口地址, 语言等

启动时指定配置

启动时指定配置: package.json

"dev": "nuxt dev --dotenv .env.dev",
"pro": "nuxt dev --dotenv .env.pro",

读取配置

.env.dev / .env.pro 中配置

NUXT_API_SECRET=api_secret_token
NUXT_PUBLIC_API_BASE=https://nuxtjs.org

nuxt.config.ts

export default defineNuxtConfig({
  runtimeConfig: {
    apiSecret: '', // can be overridden by NUXT_API_SECRET environment variable
    public: {
      apiBase: '', // can be overridden by NUXT_PUBLIC_API_BASE environment variable
    }
  },
})

根据命名规则,自动读取 .env.环境中的值到 runtimeConfig 运行时参数中

十五、SEO 配置

nuxt.com/docs/gettin…

十六、加载外部 js/css

nuxt.com/docs/gettin…

十七、 cdn 代理静态资源

提示

静态部署+ 动态部署都可以使用 cdn 代理静态资源

使 cdn 代理静态资源, 使用相对路径或绝对路径

// nuxt.config.ts 中, 仅打包时指定, 在本地无需指定
app: {
    cdnURL: '/nuxt3/'
}

cdn 地址为相对路径时可使用 nginx 代理

# 静态资源转发到具体的静态资源地址
location  ^~/nuxt3/ {
   proxy_pass http://172.17.0.1:3401/;
}

十八、docker生产部署

看我之前文章 第一次用docker快速部署pm2和nuxt前端项目