bg

一、项目安装

  • 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 学习笔记