起因
公司因为是做电商平台的,有部分交易平台的项目是使用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打包配置(测试环境)如下
那么就希望改造成使用.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可以在任意上下文中使用(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
}
},
}
至此,简单的改造全部完成,后续如果有进一步的优化需求再做其他修改吧。