Nuxt项目使用pm2配置文件启动不同的环境

926 阅读3分钟

起因

公司因为是做电商平台的,有部分交易平台的项目是使用Nuxtjs2开发的,然后再服务器上使用PM2部署。

在我刚接手的时候被告知,每次部署项目之前第一次Jenkins部署失败后需要先到服务器上项目目录下执行命令 pm2 start npm --name xxx -- run start:test (测试环境),然后再次Jenkins编译就可以了。

这个确实很令人奇怪,于是就去Jenkins看了一下项目的打包配置,如下

图1 Jenkins项目打包配置

这才发现很容易理解为什么需要这样子的操作了。这个配置不难理解就是编译,部署。关键就在于最后一句pm2 restart的命令,是重启进程而不是启动,所以第一次部署的时候编译没有问题只是在最后执行的时候报错,因为需要restart的进程是不存在的,所以要去服务器上先创建。

pm2是node进程管理工具,之前没有接触过,那么我的目标就是将项目部署改成通过配置文件启动。

研究

pm2的配置文件

使用pm2管理多个应用程序时,可以通过JS配置文件来组织管理。那么单个的同样可以,针对于我们项目的改造,就是在项目根目录下创建ecosystem.config.js文件,在Jenkins的部署命令上改造一下。之前package.json中的启动命令区分开发了环境、测试环境和生产环境,注入不同的环境变量,使用配置文件的时候就同样需要支持。

在 PM2 的配置文件 ecosystem.config.js 中,可以通过配置 env_* 来注入环境变量,并使用 pm2 start 命令指定环境 。

pm2 start ecosystem.config.js --env development
pm2 start ecosystem.config.js --env test
pm2 start ecosystem.config.js --env production

所以改造之后的Jenkins打包配置(测试环境)如下

image.png

那么就希望改造成使用.env文件进行加载,pm2配置文件改造如下:

const path = require('path')
module.exports = {
  apps: [
    {
      ...,
      env_development: {
        NODE_ENV: 'development',
        APP_ENV: 'development',
        DOTENV_CONFIG_PATH: path.join(__dirname, '.env.development') // 开发环境
      },
      env_test: {
        NODE_ENV: 'production',
        APP_ENV: 'test',
        DOTENV_CONFIG_PATH: path.join(__dirname, '.env.test') // 测试环境
      },
      env_production: {
        NODE_ENV: 'production',
        APP_ENV: 'production',
        DOTENV_CONFIG_PATH: path.join(__dirname, '.env.production') // 生产环境
      }
    }
  ]
};

接下来就是改造nuxt.config.js中的环境变量注入。

nuxt中的环境变量

在nuxt项目中,可以通过nuxt配置文件注入环境变量。

export default {
  ...,
  // 配置环境变量(会打包进前端代码,客户端和服务端共享)
  env: {
    apiPrefix: process.env.API_PREFIX || '/api',
    baseUrl:process.env.BASE_URL || 'https://example.com'
  },
  // Runtime Configuration(推荐方式,适合动态设置环境变量)
  publicRuntimeConfig: { // (可以在客户端和服务端访问)
    apiPrefix: process.env.API_PREFIX || '/api',
    baseUrl:process.env.BASE_URL || 'https://example.com'
  },
  privateRuntimeConfig: { // (仅在服务端访问,客户端无法读取)
    apiSecret: process.env.API_SECRET || '',
  },
};

env配置的环境变量有两种使用方式:

  • 通过process.env.baseUrl访问,比如使用在axios的配置中
import axios from 'axios'


const request= axios.create({
  baseURL: process.env.baseUrl
})

export default request;
  • 通过context.apiPrefix访问
<template></template>
<script>
/*******
asyncData是nuxt提供的在渲染组件之前异步获取数据的方法。asyncData方法会在组件(限于页面组件)
每次加载之前被调用。它可以在服务端或路由更新之前被调用。
******/
export default {
  ...,
  asyncData(context){
    console.log('接口的公共前缀',context.apiPrefix)
    return {}
  }
}
</script>

publicRuntimeConfig和privateRuntimeConfig是nuxt2.13+之后支持的可以添加运行时环境变量的两个配置项,配置之后可以通过this.config进行访问。config进行访问。config可以在任意上下文中使用(pages、store和plugins)

export default {
  asyncData({ $config: { baseURL } }) {
    fetch(`${baseURL}/test`)
  },
  mounted() {
    console.log(this.$config.apiSecret)
  }
}

如果需要根据不同的环境设置变量不同的值,可以使用nuxt推荐的runtimeConfig来进行配置,在package.json中启动环境的时候指定环境变量。

{
  ...
  "scripts":{
    "build:dev": "cross-env NODE_ENV=development APP_ENV=development API_PREFIX=/api BASE_URL=https:example.dev.com nuxt build",
    "build:test": "cross-env NODE_ENV=production APP_ENV=test API_PREFIX=/api BASE_URL=https:example.back.com nuxt build",
    "build:prod": "cross-env NODE_ENV=production APP_ENV=production API_PREFIX=/api BASE_URL=https:example.com nuxt build",
  }
}

nuxt对于.env文件的支持

如果项目根目录下有.env文件,那么.env文件会被nuxt使用dotenv自动加载。但这仅限于.env文件本身,并不会自动区分和加载不同环境的配置文件(.env.[development|test|production])。如果有区分环境加载配置文件的需求,则依然需要使用dotenv进行手动配置。

dotenv是一个零依赖模块,作用就是将环境变量从.env文件加载到process.env中,使得环境变量可以通过process.env进行访问。

通过安装dotenv之后,在nuxt.config.js中加载.env.*文件

API_PREFIX=/api
NODE_ENV=development
APP_ENV=development
ENV_API=https://example.com
NODE_ENV=production
APP_ENV=test
ENV_API=https://example.back.com
NODE_ENV=production
APP_ENV=production
ENV_API=https://example.back.com

import dotenv from 'dotenv';

const envConfigFile=`.env.${process.env.APP_ENV || 'production'}`;
console.log('加载的环境文件路径:', envConfigFile)
const result = dotenv.config({ path: envConfigFile })
if (result.error) {
  console.error('dotenv 加载失败:', result.error)
} else {
  console.log('dotenv 加载成功:', result.parsed)
}

export default {
  ...,
  proxy: {
    [process.env.API_PREFIX]: {
      target: process.env.ENV_API,
      changeOrigin: true
    }
  },
}

至此,简单的改造全部完成,后续如果有进一步的优化需求再做其他修改吧。