Bootstrap

Nuxt3重构问题总结

一、项目安装

  • 1、检查node版本,建议使用Node.js - v18+
  • 2、使用nvm管理不同版本的node
  • 3、安装nvm - https://github.com/coreybutler/nvm-windows/releases
  • 4、安装指定版本的 node nvm install 18.19.0
  • 5、查看已安装 node nvm ls
  • 6、使用指定版本的 node nvm use 18.19.0
  • 7、创建新的nuxt3项目 npx nuxi@latest init nuxt-demo
  • 8、安装依赖 npm install
  • 9、启动服务 npm run dev
  • 10、配合element-plus、vuex、vue-router使用的完整依赖如下所示:
    {
      "name": "nuxt-demo",
      "private": true,
      "type": "module",
      "scripts": {
        "build": "nuxt build",
        "dev": "nuxt dev",
        "generate": "nuxt generate",
        "preview": "nuxt preview",
        "postinstall": "nuxt prepare"
      },
      "dependencies": {
        "@element-plus/icons-vue": "^2.0.10",
        "axios": "^0.21.1",
        "element-plus": "^2.2.27",
        "gsap": "^3.8.0",
        "prismjs": "^1.25.0",
        "vue": "3.2.45",
        "umob": "^0.2.5"
      },
      "devDependencies": {
        "@element-plus/nuxt": "^1.0.6",
        "@nuxt/devtools": "latest",
        "nuxt": "^3.8.0",
        "vite-plugin-prismjs": "^0.0.8",
        "vue": "^3.3.6",
        "vue-router": "^4.2.5",
        "vuex": "^4.0.2"
      }
    }
    

二、Vue-router使用的注意事项

  • 1、无需createRouter和路由配置,直接使用 import { useRoute } from 'vue-router'const route = useRoute() 即可
  • 2、store内部禁止使用 const route = useRoute(), 不会响应式更新,需放在setup内部。
  • 3、路由全局守卫, middleware 目录下创建 base.global.ts, 内容如下:
export default defineNuxtRouteMiddleware((to, from) => {
	const { name: toName } = to
	const { name: fromName } = from
	console.log(toName, fromName)
})

三、数据获取

  • 1、本地请求代理配置,nuxt.config.ts中的vite部分配置如下所示:
vite: {
    server: { // 本地请求服务器代理
        proxy: {
            '/api': {
                target: 'https://xxxx.com/api',
                changeOrigin: true,
            },
        },
    },
}
  • 2、服务器请求代理配置,nuxt.config.ts中的nitro部分配置如下所示:
nitro: {
    routeRules: { // 服务器请求代理
        '/api/**': {
            proxy: 'https://xxxx.com/api/**',
        },
    },
}
  • 3、数据获取必须在setup内部调用(禁止在store内调用),useFetch会自动避免客户端重复发起请求,这是nuxt实现ssr的重要方式,具体请求如下:
await Promise.all([
    useFetch('/a/b', {
        query: { id: '01' },
        method: 'get',
    }),
    useFetch('/a/c', {
        query: { id: '02' },
        method: 'get',
    })
])

请求完成之后的数据处理与正常请求一致。若出现 Hydration children mismatch in <div>: server rendered element contains more child nodes than client vdom 警告,这可能是初始化时存在某个判断条件,该条件在服务端渲染和客户端时存在不一样的表现,进而导致服务端水合结果与客户端不一致。如:localStorage的判断、onMounted内的数值初始化等

四、如何在head内注入js代码片段?

nuxt.config.ts中的app部分配置如下所示:

head: {
    script: [{
        children: `window.addEventListener('load', function() {
            if ('serviceWorker' in navigator) {
                navigator.serviceWorker.register('/sw.js')
            }
        })`,
    }],
}

五、路由跳转短暂白屏处理

默认情况下,useFetch会在异步函数解析完成之前使用Vue的Suspense进行页面导航,而Suspense的#fallback默认为空,从而造成白屏现象

  • 1、使用 useLazyFetch 可以忽略此功能在客户端导航时的使用。在这种情况下,需要手动处理加载状态。首先,修改请求方法:
const fetchHandle = async (id) => {
    const { data: response, pending } = await useLazyFetch('/a/b/getById', {
        query: { id },
        method: 'get',
        key: 'getById:' + id,
    })
    return {
        response,
        pending,
    }
}
  • 2、在composables目录下新建 index.ts
export const useLazyFetchHandle = (data: any, res: any, key: string) => {
    const { response, pending } = data
    const { data: prevData } = useNuxtData(key)
    if (response.value) {
        // 服务端渲染的数据处理
        res.value = response.value.data
    }

    const isLoading = computed(() => { // 加载中
        if (prevData.value) {
            return !prevData.value.success && pending.value
        }
        return pending.value
    })
    
    watch(response, (newRes) => {
        // 监听路由跳转请求的数据处理
        res.value = newRes.data
    })

    return isLoading
}
  • 3、组件内部调用
const result = ref({})
const isLoading = useLazyFetchHandle(
    await fetchHandle(id),
    result,
    'getById:' + id,
)

六、部署

  • 1、检查node版本, 若出现报错 [nuxt] [request error] [unhandled] [500] _fetch is not a function 则需升级node

可安装NVM升级node,参考链接 https://blog.csdn.net/xhp312098226/article/details/131247719

  • 2、Ubuntu 18.04 出现GLIBC_2.28 not found,则需升级Ubuntu系统版本
sudo apt update
sudo apt upgrade
sudo apt full-upgrade
sudo apt autoremove
sudo systemctl reboot
sudo apt install update-manager-core
sudo do-release-upgrade -m desktop -d

升级完成后,mysql也将会被升级至mysql 8,需要重新设置root用户密码和权限

  • 3、执行打包命令 npm run build,并生成 .output文件夹
  • 4、使用pm2管理node服务,PM2 是维持一个 Process 执行的管理器,們可以藉由 PM2 來啟動我們的 Nitro Server,当服务崩溃时能自动的重新启动,以维持服务的正常运作,除此之外 PM2 可以启用集群 (Cluster) 的功能结合请求的负载均衡,来让多核心的机器提升资源的利用率和效能

安装pm2 npm install -g pm2

  • 5、在 Nuxt 目录下放入.output文件夹,并创建 ecosystem.config.js 文件,內容如下:
module.exports = {
    apps: [
        {
            name: 'nuxt-demo',
            exec_mode: 'cluster',
            instances: 'max',
            script: './.output/server/index.mjs',
            env: {
                PORT: 3000,
                HOST: '0.0.0.0',
            }
        }
    ]
}
  • 6、启动服务 pm2 start ecosystem.config.js
  • 7、配置nginx
location / {
    proxy_pass http://127.0.0.1:3000/;
    proxy_set_header HOST $host;
}

七、静态资源404

经排除,问题为以下location导致

location ~ .*.(ttf|woff|webp|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$ {
    expires max;
    if_modified_since off;
    add_header Last-Modified "";
    etag off;
}

调整location匹配规则,过滤_nuxt路径下的静态资源

location ~* ^(?!/(_nuxt|favicon))(.+).(ttf|woff|webp|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$ {
    expires max;
    if_modified_since off;
    add_header Last-Modified "";
    etag off;
}

References

[1] Nuxt3文档

[2] https://github.com/element-plus/element-plus-nuxt-starter

[3] Nuxt 3 学习笔记

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;