Write the Code. Change the World.

6月 27

配置、打包、部署

因为想部署到非根目录下。所以需要明确配置该非根目录的目录名。

修改 nuxt.config.js

import postcss from './postcss.config'

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  devtools: { enabled: false },
  css: [
    '@/assets/css/main.scss'
  ],
  postcss,
  app: {
    baseURL: '/nuxt/'
  }
})

其中, app: { baseURL: ''} 就是配置目录的。

打包

# 打包
pnpm build

# 输出结果
✔ Server built in 4067ms                                                                                                                            11:57:02
✔ Generated public .output/public                                                                                                             nitro 11:57:02
ℹ Building Nitro Server (preset: node-server)                                                                                                 nitro 11:57:02
✔ Nitro server built                                                                                                                          nitro 11:57:08
  ├─ .output/server/chunks/app/_nuxt/about-cdbe941d.mjs (8.56 kB) (2.16 kB gzip)
  ├─ .output/server/chunks/app/_nuxt/about-cdbe941d.mjs.map (2.27 kB) (569 B gzip)
  ├─ .output/server/chunks/app/_nuxt/base-9144d31d.mjs (6.82 kB) (2.1 kB gzip)
  ├─ .output/server/chunks/app/_nuxt/base-9144d31d.mjs.map (3.99 kB) (677 B gzip)
  ├─ .output/server/chunks/app/_nuxt/error-404-8552a549.mjs (9.95 kB) (3.18 kB gzip)
  ├─ .output/server/chunks/app/_nuxt/error-404-8552a549.mjs.map (8.04 kB) (1.98 kB gzip)
  ├─ .output/server/chunks/app/_nuxt/error-404-styles.0e2b8478.mjs (3.92 kB) (1.25 kB gzip)
  ├─ .output/server/chunks/app/_nuxt/error-404-styles.0e2b8478.mjs.map (392 B) (234 B gzip)
  ├─ .output/server/chunks/app/_nuxt/error-500-cbeabbfb.mjs (3.29 kB) (1.44 kB gzip)
  ├─ .output/server/chunks/app/_nuxt/error-500-cbeabbfb.mjs.map (1.71 kB) (625 B gzip)
  ├─ .output/server/chunks/app/_nuxt/error-500-styles.b0a119b9.mjs (2.22 kB) (865 B gzip)
  ├─ .output/server/chunks/app/_nuxt/error-500-styles.b0a119b9.mjs.map (392 B) (232 B gzip)
  ├─ .output/server/chunks/app/_nuxt/error-component-22adb31f.mjs (2.13 kB) (939 B gzip)
  ├─ .output/server/chunks/app/_nuxt/error-component-22adb31f.mjs.map (2.42 kB) (825 B gzip)
  ├─ .output/server/chunks/app/_nuxt/index-7ec6b3b3.mjs (6.92 kB) (2.64 kB gzip)
  ├─ .output/server/chunks/app/_nuxt/index-7ec6b3b3.mjs.map (2.98 kB) (623 B gzip)
  ├─ .output/server/chunks/app/_nuxt/island-renderer-c6ceb0d6.mjs (1.35 kB) (612 B gzip)
  ├─ .output/server/chunks/app/_nuxt/island-renderer-c6ceb0d6.mjs.map (946 B) (455 B gzip)
  ├─ .output/server/chunks/app/client.manifest.mjs (6.81 kB) (952 B gzip)
  ├─ .output/server/chunks/app/client.manifest.mjs.map (4.51 kB) (765 B gzip)
  ├─ .output/server/chunks/app/server.mjs (32.5 kB) (8.65 kB gzip)
  ├─ .output/server/chunks/app/server.mjs.map (38.3 kB) (6.31 kB gzip)
  ├─ .output/server/chunks/app/styles.mjs (508 B) (261 B gzip)
  ├─ .output/server/chunks/app/styles.mjs.map (396 B) (229 B gzip)
  ├─ .output/server/chunks/error-500.mjs (4.26 kB) (1.82 kB gzip)
  ├─ .output/server/chunks/error-500.mjs.map (1.14 kB) (432 B gzip)
  ├─ .output/server/chunks/handlers/renderer.mjs (9.65 kB) (3.06 kB gzip)
  ├─ .output/server/chunks/handlers/renderer.mjs.map (11.3 kB) (2.34 kB gzip)
  ├─ .output/server/chunks/nitro/node-server.mjs (26.7 kB) (7.8 kB gzip)
  ├─ .output/server/chunks/nitro/node-server.mjs.map (87.5 kB) (6.32 kB gzip)
  ├─ .output/server/chunks/rollup/_virtual_head-static.mjs (301 B) (217 B gzip)
  ├─ .output/server/chunks/rollup/_virtual_head-static.mjs.map (111 B) (112 B gzip)
  ├─ .output/server/index.mjs (506 B) (267 B gzip)
  └─ .output/server/package.json (1.24 kB) (477 B gzip)
Σ Total size: 2.94 MB (698 kB gzip)
✔ You can preview this build using node .output/server/index.mjs

打包 .output 文件夹里的所有内容。上传到服务器。因为我想直接用 https://zeipan.com/nuxt 来访问刚才打包的内容。所以我将刚才打包的文件复制到 https://zeipan.com 站点的根目录下,然后解压。重命名为 nuxt。这个 nuxt是在打包前配置的哈。

服务端准备使用 pm2 来启动和守护该项目。

安装 pm2

npm install -g pm2

在服务端 nuxt 下新建 ecosystem.config.js 文件。

module.exports = {
  apps: [
    {
      name: 'xiangrongNuxt',  // 设置启动项目名称
      port: 3000,
      exec_mode: 'cluster',
      instances: 'max',
      script: './server/index.mjs',
      args: "", // 传递给脚本的参数
      watch: true, // 开启监听文件变动重启
      ignore_watch: ["node_modules", "public", "logs"], // 不用监听的文件
      exec_mode: "fork",// 自家主机window cluster_mode 模式下启动失败
      instances: "2", // max表示最大的 应用启动实例个数,仅在 cluster 模式有效 默认为 fork
      autorestart: true, // 默认为 true, 发生异常的情况下自动重启
      max_memory_restart: "1G",
       merge_logs: true, // 设置追加日志而不是新建日志
       log_date_format: "YYYY-MM-DD HH:mm:ss", // 指定日志文件的时间格式
       min_uptime: "60s", // 应用运行少于时间被认为是异常启动
       max_restarts: 30, // 最大异常重启次数
       restart_delay: 60, // 异常重启情况下,延时重启时间
       error_file: './logs/app-err.log', // 错误日志文件
       out_file: './logs/app-out.log', // 正常日志文件
    }
  ]
}

启动和查看。

pm2 start ecosystem.config.js

pm2 list

输入信息如图所示。

然后配置 nginx,我这里是放在本来就配置好的 zeipan.com 域名下的,现在去修改下,做下代理就好了。

# zeipan.com.conf 编辑配置文件,增加代理
 location /nuxt/ {
      proxy_pass http://localhost:3030;
 }

 # 重启 nginx
 nginx -s reload

然后打开 https://www.zeipan.com/nuxt 看看效果。

看动图。表面上这里至少有两个很严重的 bug 。一是 favicon 不对,还有就是点击导航按钮跳转到不对。其实,还有更不对的地方,站点访问的静态资源是放在站点根目录的 public/_nuxt 下的。但是引用少了 public 这一层。所以静态资源都是 404. 我手动将 _nuxt 目录上移了一层,才能正确显示。

先解决导航栏按钮跳转错误的问题吧。

之前的导航栏,是直接使用的 a 标签。那么现在,使用 nuxt 的组件 NuxtLink 来实现。所以,修改 layouts/base/header.vue

<template>
    <header class="w-full shadow-sm relative z-50">
        <!-- 大点屏幕布局 -->
        <div class="container md:flex hidden items-center h-[72px]">
            <img class="w-[72px] h-[72px] mr-10" src="../../assets/image/base/logo.png" alt="首页" />

            <NuxtLink v-for="(item, index) in links" :key="index" :to="item.value" class="flex items-center relative group cursor-pointer h-full mr-5">
                <div class="text-black text-[15px] font-medium">{{ item.label }}</div>
                <div :class="lineClass(item)"></div>
            </NuxtLink>

            <a class="text-gray-600 text-[13px] cursor-pointer ml-auto hover:text-black" href="#">登录</a>
            <a class="text-gray-600 text-[13px] cursor-pointer hover:text-black ml-3" href="#">注册</a>
        </div>

        <!-- 小屏幕下的布局 -->
        <div class="container md:hidden h-[60px] flex items-center">
            <img class="w-[60px] h-[60px] mr-10" src="../../assets/image/base/logo.png" alt="首页" />

            <span class="ml-auto w-8 h-8 text-gray-900">
                <svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
                    <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 5.25h16.5m-16.5 4.5h16.5m-16.5 4.5h16.5m-16.5 4.5h16.5"></path>
                </svg>
            </span>

            <div class="w-screen h-screen bg-black/40 fixed left-0 top-0">
                <div class="w-60 h-screen bg-white flex flex-col p-4 absolute top-0 right-0 rounded-s-sm">
                    <NuxtLink v-for="(item, index) in links" :key="index" :to="item.value" class="flex items-center relative group cursor-pointer w-full h-12 mr-5">
                        <div class="text-black text-[15px] font-medium">{{ item.label }}</div>
                        <div :class="lineClass(item)"></div>
                    </NuxtLink>

                    <div class="mt-5">
                        <a class="text-gray-600 text-[13px] cursor-pointer hover:text-black" href="#">登录</a>
                        <a class="text-gray-600 text-[13px] cursor-pointer hover:text-black ml-3" href="#">注册</a>
                    </div>
                </div>
            </div>
        </div>
    </header>

</template>

<script setup lang="ts">

import { computed } from 'vue'

import { useRoute } from 'nuxt/app';

interface Item {
    label: string,
    value: string,
    active: boolean
}

const links:Array<Item> = [ 
    {label: '产品', value: '/', active: false},
    {label: 'Web3D',value: '/web3D', active: false},
    {label: '关于我们',value: '/about', active: false},
    {label: '技术博客',value: 'https://blog.vini123.com', active: false},
];

const lineClass = computed(() => {
    return (item:Item) => {
        if (item.active) {
            return 'absolute bottom-0 w-full h-[3px] bg-lime-300'
        }
        return 'absolute bottom-0 w-full h-[3px] scale-0 transition-all group-hover:scale-100 bg-lime-300'
    }
})

const route = useRoute();

for(let item of links) {
    if (item.value == route.path) {
        item.active = true
    }
}
</script>

pm2 启动的时候,会有一个配置文件。还有对 pm2 命令的管理,我们使用脚本来控制。这里添加 start、stop、restart 三个脚本。这里,我建立一个文件夹 build,将这四个文件放在这里。打包好项目后,将这四个文件移动到打包好的项目下,一起上传到服务端进行部署。

下边是四个文件。

# ecosystem.config.js
module.exports = {
    apps: [
      {
        name: 'xiangrongNuxt',  // 设置启动项目名称
        port: 3030,
        exec_mode: 'cluster', // 开启集群,多线程模式
        enstances: 'max', //集群实例数
        script: './server/index.mjs',
        args: "", // 传递给脚本的参数
        watch: true, // 开启监听文件变动重启
        ignore_watch: ["node_modules", "public", "logs"], // 不用监听的文件
        exec_mode: "fork",// 自家主机window cluster_mode 模式下启动失败
        instances: "2", // max表示最大的 应用启动实例个数,仅在 cluster 模式有效 默认为 fork
        autorestart: true, // 默认为 true, 发生异常的情况下自动重启
        max_memory_restart: "1G",
        merge_logs: true, // 设置追加日志而不是新建日志
        log_date_format: "YYYY-MM-DD HH:mm:ss", // 指定日志文件的时间格式
        min_uptime: "60s", // 应用运行少于时间被认为是异常启动
        max_restarts: 30, // 最大异常重启次数
        restart_delay: 60, // 异常重启情况下,延时重启时间
        error_file: './logs/app-err.log', // 错误日志文件
        out_file: './logs/app-out.log', // 正常日志文件
      }
    ]
  }

# nuxt-restart.sh
 echo 'begin restart'

rm -rf ./logs

pm2 restart xiangrongNuxt

echo 'restarted'

# nuxt-start.sh
echo "pm2 starting"

rm -rf ./logs

pm2 start ecosystem.config.js

echo "pm2 started"

# nuxt-stop.sh
echo 'begin stop'

pm2 stop xiangrongNuxt

pm2 delete xiangrongNuxt

rm -rf ./logs

echo 'stoped'

这里,手动将 public 目录下的 _nuxt 目录上移一层(静态文件目录不对问题,还没解决,先这样处理)。

做好这些,我们再打包上传。

处理 favicon 和 seo 优化

nuxt.config.ts 中,我们在 app 节点写,可以对 head 部分进行配置。不过,这里是静态的。没关系,这里可以配置全局的一些东西。动态的可以通过 useHead 方法,在各自的页面里进行配置。

https://nuxt.com/docs/getting-started/seo-meta

修改 nuxt.config.ts, 增加 favicon 和 description 以及 keywords 的配置。

import postcss from "./postcss.config";

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  devtools: { enabled: false },
  css: ["@/assets/css/main.scss"],
  postcss,
  app: {
    baseURL: "/nuxt/",
    head: {
      title: '上海想容',
      meta: [
        { name: 'description', content: '想容(上海)互联网技术有限公司是一家专注互联网产品服务的公司,致力于改变人们的工作和生活方式。我们认为”美的事物是永恒的喜悦“,将更美产品的体验和功能给到用户就是最美。' },
        { name: 'keywords', content: '上海想容,想容(上海)互联网技术有限公司,上海想容官网,app 开发,小程序开发,公众号开发,官网开发,后台开发,web3d开发,私人定制' },
      ],
      bodyAttrs: {
        class: 'body'
      },
      link: [
        { rel: 'icon', type: 'image/x-icon', href: '/nuxt/favicon.ico' }
      ],
      script: [ { children: 'console.log(\'Welcome xiangrong\')' } ]
    }
  },
});

然后,其他页面,我们可以通过 useHead 函数来动态配置。

比如,在 pages/index.vue 中,加入一下代码。

<script setup lang="ts">

const head = reactive({
    title: '上海想容',
    description: '想容(上海)互联网技术有限公司是一家专注互联网产品服务的公司,致力于改变人们的工作和生活方式。我们认为”美的事物是永恒的喜悦“,将更美产品的体验和功能给到用户就是最美。',
    content: '上海想容,想容(上海)互联网技术有限公司,上海想容官网,app 开发,小程序开发,公众号开发,官网开发,后台开发,web3d开发,私人定制'
})

useHead({
  title: head.title,
  meta: [
    { name: 'description', content: head.content },
    { name: 'keywords', content: head.description },
  ]
})
</script>

我们再打包,再上传到服务端,部署测试看看。发现 favicon 已经好了, head 中, description 和 keywords 也有了。

到此, nuxt 基本的流程算是走完了。但是想一个项目做完整,做好,这些还远远不够的。nuxt 的 api 还有 99% 都没使用到,就可以说明问题了。还有文档的其他部分。本来说使用 daisyui ,可是没用上。也好,至少页面基本布局算是完成了。

添加版本,提交到 github 上

git add .
git commit -m '配置 seo 优化等'
git remote add origin git@github.com:vinistudy/nuxt3.git
git push -u origin main

好了。到此,算是高一段落了。

线上地址: https://www.zeipan.com/nuxt
代码地址: https://github.com/vinistudy/nuxt3

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注