uni-app多環(huán)境部署解決方案詳解
前言
最近幾周都在處理公司的移動業(yè)務(wù),而為在后期能統(tǒng)一多端,解放自己,迎合公司的技術(shù)棧;選用了 uni-app 來開發(fā)。開發(fā)前期重新對公司移動業(yè)務(wù)做深入了解,重構(gòu)大部分業(yè)務(wù)邏輯,也抽離出基礎(chǔ)組件;但實(shí)際到部署的時(shí)候,出現(xiàn)來問題;由于現(xiàn)在只考慮 H5 端,部署和測試會出現(xiàn)多環(huán)境配置,但是我使用的 HBuilderX 工具創(chuàng)建的工程,所以只存在開發(fā)環(huán)境:development 和生產(chǎn)環(huán)境:production。
嘗試幾種方式
查詢官網(wǎng)和論壇并沒有很好的配置方案;官網(wǎng)的一些配置可以提供參考。
- package.json
對于根目錄下 package.json 里可以提供對不同平臺的編譯處理,這里指不同平臺并不是不同環(huán)境,環(huán)境還是只有開發(fā)和生產(chǎn)兩種環(huán)境
- vue.config.js
由于沒有使用 VUE3 來開發(fā),所以默認(rèn)的配置項(xiàng)還是基于 webpack。如果項(xiàng)目根目錄沒有該配置項(xiàng),可以自行創(chuàng)建 vue.config.js 文件,但是很遺憾還是沒有可以處理多種環(huán)境下的一個(gè)配置。而且它還存在一些約束:

不過它可以添加一些自定義的變量
const webpack = require('webpack')
module.exports = {
chainWebpack: config => {
config
.plugin('define')
.tap(args => {
args[0]['process.env'].VUE_APP_TEST = '"test"'
return args
})
}
}解決方案
多環(huán)境部署,實(shí)際是需要對應(yīng)自己部署環(huán)境,存在不同配置項(xiàng)。而實(shí)際業(yè)務(wù)中存在的環(huán)境可以是無限的,一般最基礎(chǔ)而言需要三種:
開發(fā)環(huán)境
測試環(huán)境
生產(chǎn)環(huán)境
對于公司現(xiàn)有業(yè)務(wù),只針對 H5 端,需要的配置可能只有:
接口:開發(fā)、測試、生產(chǎn)對應(yīng)的接口配置不同
部署路徑:測試、開發(fā)對應(yīng)的部署路徑不同
如果我們解決這兩個(gè),那部署方案實(shí)現(xiàn)一大半。對于環(huán)境配置,從 vue-cli 中可以看到很多;cli 配置基于 .env.x 不同文件來獲取不同配置項(xiàng),這里我們可以參考它來自己實(shí)現(xiàn)獲取配置項(xiàng)。
部署方式
部署方式,通過命令行來解決;后續(xù)可以更好的對接自動化部署,而項(xiàng)目中通過 HBuilderX 工具創(chuàng)建,需要使用它的 cli 程序,它支持常見的三端打包;H5 端打包命令,在 package.json 設(shè)置:
"build": "cli publish --platform h5 --project 項(xiàng)目名稱",
獲取接口
項(xiàng)目根目錄創(chuàng)建不同環(huán)境下需要的配置:.env.test、.env.prod。
# .env.test NODE_ENV=test BASE_API = '/test' h5.router.base_config = '/h5/' h5.title_config = 'h5'
實(shí)際上我們只需要測試和生產(chǎn)兩種配置,開發(fā)配置項(xiàng)在代碼內(nèi)部修改,這樣可以增加效率,不用每次修改后,再重啟項(xiàng)目。
配置項(xiàng)對應(yīng)不同環(huán)境,而環(huán)境可以通過在 package.json 中設(shè)置不同參數(shù)來區(qū)分;
"scripts": {
"build:config:test": "node ./deploy/index --mode=test",
"build:config:prod": "node ./deploy/index --mode=prod",
},可以獲取 process.argv 變量讀取到設(shè)置的環(huán)境;后續(xù)還想設(shè)置什么變量也可以在后面直接添加,類似:--test=test
// yargs.js
module.exports = function() {
let args = process.argv;
let argv = {};
for (let i = 2; i < args.length; ++i) {
let cur = args[i];
if (/^(--)(\S*)(=)/.test(cur)) {
const keys = cur.split('=')
argv[keys[0].slice(2)] = keys[1];
}
}
return argv
}
/**
* {
* mode: 'test'
* }
*/獲取配置后,需要讀取配置,這里直接使用 node 的 fs 模塊讀取文件內(nèi)容
const fs = require('fs')
const path = require('path')
// 解析函數(shù)
function parse() {
...
}
module.exports = function() {
// 獲取環(huán)境
const config = yargs();
const env = config.mode;
const envPath = path.resolve(__dirname, '../../') + '/.env.' + env
try{
const data = fs.readFileSync(envPath, 'utf8')
// 解析文件
return parse(data)
}catch(e){
console.log('讀取env出錯(cuò):' + JSON.stringify(e));
}
}其中解析函數(shù),由于不想新增依賴增加項(xiàng)目負(fù)擔(dān),直接參考 dotenv 的 parse 函數(shù)解析出文件內(nèi)容;得到類似的對象:
{
NODE_ENV: 'test',
BASE_API: '/test',
// ...
}解析出配置內(nèi)容,接下來動態(tài)配置文件;由于項(xiàng)目存在可能部署在客戶內(nèi)網(wǎng)環(huán)境下,這里采用動態(tài)生產(chǎn)一個(gè)共用的配置文件,在項(xiàng)目獲取該配置項(xiàng);后續(xù)運(yùn)維人員可以想配置什么就配置什么。
// 寫入 /static/global.js
writeGlobalConfig()
function writeGlobalConfig() {
const _global = {}
for (let key in config) {
if (key.includes('_API')) {
_global[key] = config[key]
}
}
const data = `window._GLOBAL__ = ${JSON.stringify(_global)}`
fs.writeFileSync(path.resolve(__dirname, '../static/global.js'), data)
}在項(xiàng)目中生產(chǎn)環(huán)境下取公用配置項(xiàng)
const NODE_ENV = process.env.NODE_ENV;
/// 默認(rèn)根目錄模板 tempalte.h5 模板引入全局接口 static/global.js
const GLOBAL_CONFIG = window._GLOBAL__ || {};
const defaultAPI = {
BASE_API: {
development: ['/test'],
production: GLOBAL_CONFIG.BASE_API,
},
SOCKET_API_BIDDIGN: {
development: ['ws://test'],
production: GLOBAL_CONFIG.SOCKET_API_BIDDIGN,
},
// ...
}部署路徑
PC 端或者是 H5 測試和生產(chǎn)環(huán)境上的部署路徑可能會不同;由于創(chuàng)建項(xiàng)目使用其開發(fā)工具創(chuàng)建,需要統(tǒng)一工具和更方便的更新 uni-app,不考慮使用 vue-cli 方式。只能動態(tài)修改 manifest.json 文件。
uni-app 的部署路徑是修改 manifest.json 中 h5.router.base。對應(yīng) Router 中的 base
// manifest.js
const fs = require('fs')
const path = require('path')
const manifestPath = path.resolve(__dirname, '../../') + '/manifest.json'
let Manifest = fs.readFileSync(manifestPath, { encoding: 'utf-8' })
function replaceManifest(path, value) {
const arr = path.split('.')
const len = arr.length
const lastItem = arr[len - 1]
let i = 0
let ManifestArr = Manifest.split(/\n/)
for (let index = 0; index < ManifestArr.length; index++) {
const item = ManifestArr[index]
if (new RegExp(`"${arr[i]}"`).test(item)) ++i;
if (i === len) {
const hasComma = /,/.test(item)
ManifestArr[index] = item.replace(new RegExp(`"${lastItem}"[\\s\\S]*:[\\s\\S]*`), `"${lastItem}": ${value}${hasComma ? ',' : ''}`)
break;
}
}
Manifest = ManifestArr.join('\n')
}
module.exports = function(options) {
for (let key in options) {
replaceManifest(key, `"${options[key]}"`)
}
fs.writeFileSync(manifestPath, Manifest, {
"flag": "w"
})
}前面我們已經(jīng)獲取到配置文件內(nèi)容,這里可以直接修改文件;這里需要給一個(gè)標(biāo)明這是修改 manifest.json 的后綴。
h5.router.base_config = '/h5/' h5.title_config = 'h5'
這里配置項(xiàng)如果 _confg 結(jié)束的變量就是修改 manifest.json 。
const path = require('path')
const fs = require('fs')
const env = require('./modules/readEnv')
const parseManifest = require('./modules/manifest')
const config = env();
function writeManifest() {
const _global = {}
for (let key in config) {
if (key.includes('_config')) {
const k = key.slice(0, -7)
_global[k] = config[key]
}
}
parseManifest(_global);
}命令行
準(zhǔn)備工作完成后,可以編寫對應(yīng)的命令,來簡化我們的操作;
"scripts": {
"build": "cli publish --platform h5 --project test-h5",
"build:config:test": "node ./deploy/index --mode=test",
"build:config:prod": "node ./deploy/index --mode=prod",
"build:prod": "npm run build:config:prod && npm run build",
"deploy:test": "npm run build:config:test && npm run build && fd-cli"
},&& 符號是串行命令;& 是并行命令,其中 fd-cli 是個(gè)部署命令,也是我以前基于業(yè)務(wù)需要開發(fā)的一個(gè)前端簡易部署命令。
其他
這里只針對 H5 端做了處理,如果需要做多平臺,也可以在命令行后面接不同平臺參數(shù),然后在部署打包前處理好邏輯;比如在打包前根據(jù)自己定義的環(huán)境變量添加參數(shù)
// vue.config.js
const webpack = require('webpack')
// 生成不同環(huán)境不同平臺的配置
const config = require('./config')
module.exports = {
chainWebpack: config => {
config
.plugin('define')
.tap(args => {
// config.VUE_APP_TEST
for(let key in config) {
args[0]['process.env'][key] = `"${config.VUE_APP_TEST}"`
}
return args
})
}
}總結(jié)
在開發(fā)階段完成后,需要特別注意一些重復(fù)的步驟或者是需要手動修改的操作,只要是手動修改,就會增加上線分險(xiǎn),特別是后來者接受項(xiàng)目的時(shí)候。當(dāng)然也要文檔齊全。
相關(guān)源碼:地址
到此這篇關(guān)于uni-app多環(huán)境部署解決的文章就介紹到這了,更多相關(guān)uni-app多環(huán)境部署內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript中mouseenter與mouseover的異同
javascript中mouseover和mouseenter的區(qū)別主要在于監(jiān)聽對象的子元素是否觸發(fā)事件。mouseover:鼠標(biāo)移入監(jiān)聽對象中,或者從監(jiān)聽對象的一個(gè)子元素移入另一個(gè)子元素中時(shí)觸發(fā)該事件。mouseenter:鼠標(biāo)移入監(jiān)聽對象時(shí)觸發(fā),在監(jiān)聽對象內(nèi)移動不會觸發(fā)。2017-06-06
js下通過prototype擴(kuò)展實(shí)現(xiàn)indexOf的代碼
這里使用js prototype擴(kuò)展實(shí)現(xiàn)的indexOf的實(shí)現(xiàn)代碼,跟js自帶的方法,差不多。2010-12-12
javascript隨機(jī)將第一個(gè)dom中的圖片添加到第二個(gè)div中示例
此代碼的是一個(gè)簡單的例子,將第一個(gè)div中的五張圖片中,提取隨機(jī)兩張顯示到第二個(gè)div中,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下2013-10-10
將兩個(gè)div左右并列顯示并實(shí)現(xiàn)點(diǎn)擊標(biāo)題切換內(nèi)容
將兩個(gè)div左右并列顯示并實(shí)現(xiàn)點(diǎn)擊標(biāo)題切換內(nèi)容的效果,接下來為大家詳細(xì)介紹下js中時(shí)如何實(shí)現(xiàn)的,感興趣的朋友不要錯(cuò)過2013-10-10
javascript 中按屬性值查找數(shù)組中的對象多種方法
JavaScript 數(shù)組可以保存混合類型的不同值,例如字符串、空值或布爾值,并且不需要數(shù)組的大小來指定它在哪里自動增長和動態(tài),這篇文章主要介紹了javascript 中按屬性值查找數(shù)組中的對象多種方法,需要的朋友可以參考下2023-06-06

