在小程序中实现多环境切换

简介

在前端开发中,我们的工程化体系中都是有多套服务环境的。比如本地开发、测试、生产(线上)这几种环境,这对应的是不同的服务地址。也许在以前我们会手动更改我们的服务地址,比如接口域名的更改。但是这样的人工操作不仅麻烦,而且还极容易出现错误。所以 web 项目中都有 script 脚本来更改环境配置,这里就让小程序也拥有配置多环境的能力。

配置文件目录结构

1
2
3
4
5
6
7
8
9
10
.
├── config.js // 项目配置文件
├── configs // 环境配置文件夹
│ ├── common.config.js // 公共基础配置
│ ├── dev.config.js // 开发环境配置
│ ├── prd.config.js // 生产环境配置
│ ├── script.js // 切换环境配置脚本文件
│ └── test.config.js // 测试环境配置
└── wxs // wxs配置文件夹
└── config.wxs // wxs配置文件

切换环境原理

首先要使用脚本切换配置,就需要安装 node 环境。这点做过前端的开发就不用多说了。这里也是通过 script 脚本进行不同环境和文件配置的切换。在执行node ./script脚本的时候,通过改写项目的配置文件config.js来达到切换的效果。因为项目中使用的配置都是从config.jswxs/config.wxs文件中导入的。

文件基本配置代码展示

  • config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*eslint-disable*/
/**
* @desc 项目配置文件
*/

// 接口base路径
export const API_URL = 'https://www.baidu.com'
// 运行环境
export const RUN_ENV = 'test-release'

// 静态图片地址
export const IMAGE_URL = 'https://www.baidu.com/static'
// 高德定位接口
export const LOCATION_API = 'https://www.amap.com/'
// 高德sdk秘钥
export const AMAP_KEY = '12324324jjdjfojoejgj2e232323'

// 当前版本(预发版本)
export const APP_VERSION = 'TEST'

这个文件是项目的配置文件,提供对整体项目的配置。配置一些我们小程序需要使用的公共部分,我们在切换环境的时候也是切换这个配置文件。我们在使用的过程中可以根据自己的项目进行添加更改以达到自己项目的需求。

  • configs/script.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// 引入命令行颜色包
require('colors')
// node文件处理模块
const fs = require('fs')
// node读取文件
const readline = require('readline')
// node环境获取
const path = require('path')
// 各环境配置文件的获取
const common = require('./common.config')
const dev = require('./dev.config')
const prd = require('./prd.config')
const pre = require('./test.config')

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
// 项目配置config.js文件路径
const OUTPUT = path.resolve(__dirname, '../config.js')
// 使用的wxs文件的配置路径
const OUTPUT_WXS = path.resolve(__dirname, '../wxs/config.wxs')
// 用require方式生成导出文件
const writeRequire = (path, obj) => {
fs.writeFileSync(path, '/*eslint-disable*/\n/**\n * @desc 项目配置文件 \n */ \n')
fs.appendFileSync(path, `\nmodule.exports = {\n`)
Object.keys(obj).forEach(item => {
const conf = obj[item]
fs.appendFileSync(path, ` // ${conf.title}\n`)
if (typeof conf.value === 'string') {
fs.appendFileSync(path, ` ${item}: '${conf.value}',\n`)
} else if (typeof conf.value === 'object') {
if (conf.value instanceof Array) {
fs.appendFileSync(path, ` ${item}: ${JSON.stringify(conf.value)},\n`)
} else {
fs.appendFileSync(path, ` ${item}: {\n`)
Object.keys(conf.value).forEach(key => {
fs.appendFileSync(path, ` '${key}': '${conf.value[key]}',\n`)
})
fs.appendFileSync(path, ` },\n`)
}
} else {
fs.appendFileSync(path, ` ${item}: ${conf.value},\n`)
}
})
fs.appendFileSync(path, `}\n`)
}
// 用es6的模块进行导出文件 @isOverflow是否覆盖原文件
const writeExport = (path, obj, isOverflow = false) => {
isOverflow && fs.writeFileSync(path, '/*eslint-disable*/\n/**\n * @desc 项目配置文件 \n */ \n')
fs.appendFileSync(path, `\n`)
Object.keys(obj).forEach(item => {
const conf = obj[item]
fs.appendFileSync(path, `// ${conf.title}\n`)
if (typeof conf.value === 'string') {
fs.appendFileSync(path, `export const ${item} = '${conf.value}'\n`)
} else if (typeof conf.value === 'object') {
if (conf.value instanceof Array) {
fs.appendFileSync(path, `export const ${item} = ${JSON.stringify(conf.value)}\n`)
} else {
fs.appendFileSync(path, `export const ${item} = {\n`)
Object.keys(conf.value).forEach(key => {
fs.appendFileSync(path, ` '${key}': '${conf.value[key]}',\n`)
})
fs.appendFileSync(path, `}\n`)
}
} else {
fs.appendFileSync(path, `export const ${item} = ${conf.value}\n`)
}
})
}
// 写入配置文件到项目文件夹
const write = (obj, isOverflow = false) => {
writeExport(OUTPUT, obj, isOverflow)
writeRequire(OUTPUT_WXS, obj)
}
// 渲染一个命令行文本
function heredoc(fn) {
return (
fn
.toString()
.split('\n')
.slice(1, -1)
.join('\n') + '\n'
)
}
const tmpl = heredoc(() => {
/*

███████╗██╗ ██╗██╗████████╗ ██████╗██╗ ██╗ ██████╗ ██████╗ ███╗ ██╗███████╗██╗ ██████╗ ███████╗
██╔════╝██║ ██║██║╚══██╔══╝██╔════╝██║ ██║ ██╔════╝██╔═══██╗████╗ ██║██╔════╝██║██╔════╝ ██╔════╝
███████╗██║ █╗ ██║██║ ██║ ██║ ███████║ ██║ ██║ ██║██╔██╗ ██║█████╗ ██║██║ ███╗███████╗
╚════██║██║███╗██║██║ ██║ ██║ ██╔══██║ ██║ ██║ ██║██║╚██╗██║██╔══╝ ██║██║ ██║╚════██║
███████║╚███╔███╔╝██║ ██║ ╚██████╗██║ ██║ ╚██████╗╚██████╔╝██║ ╚████║██║ ██║╚██████╔╝███████║
╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝

+----------------------------------------------------+
| |
| 切 换 环 境 |
| |
| 小 心 驶 得 万 年 船 |
| |
| 马 虎 一 时 爽 生 死 两 茫 茫 (╥╯^╰╥) |
| |
+----------------------------------------------------+

*/
})

// const setLanguage = (path) => {
// fs.appendFileSync(path, '\n\n// 语言设置 目前支持:zh_CN (简体中文)、en_US(英文版)')
// fs.appendFileSync(path, '\nconst systemLanguage = wx.getSystemInfoSync().language || "zh_CN"')
// fs.appendFileSync(path, '\nexport const APP_LANGUAGE = systemLanguage.indexOf("zh") !== -1 ? "zh_CN" : "en_US"')
// }

// 返回不同环境的命令行颜色
const titleInfo = () => {
var data = fs.readFileSync(OUTPUT, 'utf-8')
var index = data.indexOf('export const RUN_ENV')
var enviroment = data.slice(index + 24, data.indexOf('\n', index) - 1)
var info = heredoc(tmpl) + ' 当前开发环境为:' + enviroment + '\n\n\n\n\n'
if (enviroment === 'release') {
return info.blue
} else if (enviroment === 'test-release') {
return info.green
} else if (enviroment === 'develop') {
return info.yellow
}
}

// 命令行让用户自行选择环境
rl.question(
titleInfo() +
'请输入需要切换的开发环境~ \n\n' +
'-------------------------------------------------------------------------------------------- \n|' +
' prd'.blue +
' 生产 | ' +
'dev'.yellow +
' 开发 | ' +
'test'.green +
' 测试 |\n ' +
'-------------------------------------------------------------------------------------------- \n\nEnter: ',
answer => {
switch (answer) {
case 'prd':
rl.question('\n请输入要发布的版本号,格式类似于' + 'x.x.x'.green + ':\n\n', version => {
var reg = new RegExp('[1-9]+.[0-9]+.[0-9]*[0-9]$')
if (reg.test(version)) {
write(prd, true)
write(common, false)
fs.appendFileSync(OUTPUT, '\n// 当前版本')
fs.appendFileSync(OUTPUT, `\nexport const APP_VERSION = "${version}"`)
// setLanguage(OUTPUT)
console.log('\n ~> 🙂 ' + '生产环境 '.green + '配置完成 ')
} else {
console.log('\n ~> 😈 ' + `切换失败,版本号(${version})输入有误`.red)
}
rl.close()
})
break
case 'dev':
write(dev, true)
write(common, false)
fs.appendFileSync(OUTPUT, '\n// 当前版本(开发版本)')
fs.appendFileSync(OUTPUT, `\nexport const APP_VERSION = "DEV"`)
// setLanguage(OUTPUT)
console.log('\n ~> 🙂 ' + '开发环境 '.green + '配置完成(真机演示请打开调试)')
rl.close()
break
case 'test':
write(pre, true)
write(common, false)
fs.appendFileSync(OUTPUT, '\n// 当前版本(预发版本)')
fs.appendFileSync(OUTPUT, `\nexport const APP_VERSION = "TEST"`)
// setLanguage(OUTPUT)
console.log('\n ~> 🙂 ' + '测试环境 '.green + '配置完成 ')
rl.close()
break
default:
console.log('\n ~> 🙄 ' + '输入错误'.red)
rl.close()
break
}
}
)

这个脚本文件就是对配置文件根据用户的命令行输入条件进行输入对应的配置。

  • configs/common.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
IMAGE_URL: {
title: '静态图片地址',
value: 'https://www.baidu.com',
},
LOCATION_API: {
title: '高德定位接口',
value: 'https://www.amap.com',
},

AMAP_KEY: {
title: '高德sdk秘钥',
value: '12324324jjdjfojoejgj2e232323',
},
}

configs/common.config.js文件是多环境中的公共配置,就是多环境用的都是一样的东西就放在了这个文件中

  • configs/dev.config.js
1
2
3
4
5
6
7
8
9
10
module.exports = {
API_URL: {
title: '接口base路径',
value: 'http://dev.baidu.com',
},
RUN_ENV: {
title: '运行环境',
value: 'develop',
},
}

configs/dev.config.js文件是开发环境下不一样的部分,比如接口的服务器域名

  • configs/test.config.js
1
2
3
4
5
6
7
8
9
10
11
module.exports = {
API_URL: {
title: '接口base路径',
value: 'http://test.baidu.com',
},

RUN_ENV: {
title: '运行环境',
value: 'test-release',
},
}

configs/test.config.js文件是测试环境下不一样的部分,比如接口的服务器域名

  • configs/prd.config.js
1
2
3
4
5
6
7
8
9
10
module.exports = {
API_URL: {
title: '接口base路径',
value: 'http://prod.baidu.com',
},
RUN_ENV: {
title: '运行环境',
value: 'release',
},
}

configs/prd.config.js文件是正式环境下不一样的部分,比如接口的服务器域名

  • wxs/config.wxs
1
2
3
4
5
6
7
8
9
10
11
12
13
/*eslint-disable*/
/**
* @desc 项目配置文件
*/

module.exports = {
// 静态图片地址
IMAGE_URL: 'https://www.baidu.com',
// 高德定位接口
LOCATION_API: 'https://www.amap.com',
// 高德sdk秘钥
AMAP_KEY: '12324324jjdjfojoejgj2e232323',
}

wxs/config.wxs文件是wxs的配置,主要是用于在wxml中使用wxs中的基础配置,比如在wxml中直接获取图片

使用脚本命令快速执行脚本

在项目包管理文件package.json中配置 script 脚本命令简化操作

1
2
3
"scripts": {
"config": "node ./configs/script.js",
}

使用如下命令进行运行环境的选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
npm run config

> node ./configs/script.js

███████╗██╗ ██╗██╗████████╗ ██████╗██╗ ██╗ ██████╗ ██████╗ ███╗ ██╗███████╗██╗ ██████╗ ███████╗
██╔════╝██║ ██║██║╚══██╔══╝██╔════╝██║ ██║ ██╔════╝██╔═══██╗████╗ ██║██╔════╝██║██╔════╝ ██╔════╝
███████╗██║ █╗ ██║██║ ██║ ██║ ███████║ ██║ ██║ ██║██╔██╗ ██║█████╗ ██║██║ ███╗███████╗
╚════██║██║███╗██║██║ ██║ ██║ ██╔══██║ ██║ ██║ ██║██║╚██╗██║██╔══╝ ██║██║ ██║╚════██║
███████║╚███╔███╔╝██║ ██║ ╚██████╗██║ ██║ ╚██████╗╚██████╔╝██║ ╚████║██║ ██║╚██████╔╝███████║
╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝

+----------------------------------------------------+
| |
| 切 换 环 境 |
| |
| 小 心 驶 得 万 年 船 |
| |
| 马 虎 一 时 爽 生 死 两 茫 茫 (╥╯^╰╥) |
| |
+----------------------------------------------------+

当前开发环境为:release




请输入需要切换的开发环境~

-------------------------------
| prd 生产 | dev 开发 | test 测试
-------------------------------

Enter: test

~> 🙂 测试环境 配置完成

总结

以上就是多环境切换的简单使用,使用脚本化配置,最简单的好处就是不会忘记如何配置,直接执行命令就配置完成了。简化了手动操作,减少出错的几率,增加了开发效率。

-------------本文结束感谢您的阅读-------------

本文标题:在小程序中实现多环境切换

文章作者:Water

发布时间:2020年08月24日 - 11:08

最后更新:2023年08月01日 - 06:08

原始链接:https://water.buging.cn/2020/08/24/在小程序中实现多环境切换/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!