本方法已棄用椭懊,請參考新方法 ionic 3 開發(fā)環(huán)境切換
ionic 優(yōu)雅的解決開發(fā)跨域及后臺環(huán)境切換
之前主要接觸的是Android
和J2EE
之類的開發(fā),不是一個正統(tǒng)前端狗,所以很多前端的特性和騷操作都沒有深入了解過。去年部門開始要我?guī)ь^入坑ionic2
,于是我從一個連JavaScript
的Hello World
都沒寫過的人雾消,轉(zhuǎn)型為一個Angualr 2
+ionic 2
的偽前端狗灾搏。由于沒人指導(dǎo)挫望,期間踩坑無數(shù),的感覺頭發(fā)日漸稀少狂窑,炎熱的夏天媳板,頭頂總是有絲絲涼意。所以現(xiàn)在閑了下來(更應(yīng)該說是頓悟后打雞血般的興奮感以及女朋友的支持……)泉哈,決定把一些遇到的坑寫出來蛉幸。
-1
我是前端入門小白,所以可能這條路是錯的丛晦,不喜勿噴奕纫。
0x00 開始
后面都圍繞著Gank.io這個網(wǎng)站提供的API
測試。
ionic
其實就是一個基于Angular
前端項目烫沙。所以在前端開發(fā)的過程中匹层,必須要解決CORS
跨域問題。解決方法主要有兩個:
-
Chrome
瀏覽器中安裝Access-Control-Allow-OriginChrome
拓展 修改后臺Service
,允許跨域訪問(非常極其以及十分的不安全锌蓄,但我 TMD 前面兩個項目就是這樣做的升筏,而且因為項目問題,現(xiàn)在不能隨便更改項目配置瘸爽,只能眼睜睜開著項目裸奔著您访,這或許是我這輩子最大的敗筆吧)- 使用
ionic CLI代理服務(wù)器
為何不用第一種方法?
裝完Chrome
拓展后剪决,開啟拓展后灵汪,默認(rèn)所有網(wǎng)站都允許跨域,這就很不安全了 柑潦。
這方法還存在一個坑享言,就是開啟拓展后,上
Gayhub
Star 一些屌炸天的項目的時候妒茬,居然會報錯担锤,一兩次沒注意,以為是梯子不穩(wěn)了乍钻,一直錯誤肛循,才感覺好像并沒有那么簡單。經(jīng)過一番推理银择,發(fā)現(xiàn)就是因為開啟了跨域拓展多糠,Gayhub
做了保護,拒絕了請求浩考,導(dǎo)致報錯
當(dāng)然夹孔,一定要用這方法也不是不行,就要花點心思配置了,一個項目還好搭伤,多個項目就麻煩了只怎,而且這拓展有時候還會莫名的失效,需要開關(guān)拓展刷新頁面才恢復(fù)正常怜俐,這就很不優(yōu)雅了身堡。
為何不用第二種方法?
f**k
為何用第三種方法拍鲤?
因為這合理切符合我們對優(yōu)雅的追求贴谎。
0x01 設(shè)置ionic CLI
代理服務(wù)器
在ionic 2
及ionic 3
項目根目錄中,都有ionic.config.json
這個文件季稳,我們就是通過這個配置文件實現(xiàn)的擅这。
實現(xiàn)代理
現(xiàn)在我們要調(diào)用API
接口http://www.gank.io/api/day/history,獲取所有歷史記錄景鼠。我們來拆解下這個URL
:
通過API說明頁:
- 這個網(wǎng)站的主
URL
是http://gank.io
- 所有接口都是
/api
這個路由下的
專業(yè)一點的說法就是仲翎,這個項目的endpoint
是http://www.gank.io/api
,path
是/api
。
說點人話就是铛漓,如果我是http://www.gank.io
網(wǎng)頁中包含的一個ajax
請求谭确,當(dāng)我請求/api/history
這個url
的時候,瀏覽器最終會把你的請求地址補全票渠,補全為http://www.gank.io/api/day/history
,然后你就可以得到數(shù)據(jù)了芬迄。你要實現(xiàn)這自動補全(瞎稱呼的问顷,為了方便理解)URL
的功能,必須要以/api
開頭禀梳,當(dāng)瀏覽器發(fā)現(xiàn)是/api
開頭的時候杜窄,就會自動補全,如果不是算途,就直接請求你輸入的URL
了塞耕。可以把這理解為規(guī)定嘴瓤,沒有為什么扫外。
結(jié)論:
-
proxyUrl
=>http://gank.io/api
-
path
=>/api
修改ionic.config.json
文件:
{
"name": "gank-me",
"app_id": "",
"type": "ionic-angular",
"integrations": {},
"proxies": [
{
"path": "/api",
"proxyUrl": "http://gank.io/api"
}
]
}
測試
import { Http } from '@angular/http';
//***
this.http.get("/api/day/history")
.toPromise()
.then(res=>{debugger});
結(jié)果:
就是成功的返回數(shù)據(jù)嘛
0x02優(yōu)雅的切換服務(wù)器環(huán)境
一個稍微正經(jīng)點的商業(yè)項目,總有至少3個環(huán)境情況廓脆,開發(fā)筛谚、測試、生產(chǎn)(正式)環(huán)境,即Dev
,UAT
,Prod
停忿。在不同環(huán)境下驾讲,我們有不一樣的配置,如數(shù)據(jù)庫的文件名不同,某些服務(wù)用到的key
不同………這就需要在各個環(huán)境之間進行切換吮铭。切換的方式有兩種:
- 在一個
*.ts
文件中时迫,定義好URL
,Key
等信息,測試谓晌、打包時掠拳,手動切換 - 通過
Webpack
腳本,通過參數(shù)的方式切換
為何放棄方法一扎谎?
放棄主要是誤操作概率太高碳想,且難以接入Jenkins
自動構(gòu)建打包犁柜。如果沒有一個人專門負(fù)責(zé)打包铐尚,且這個人還有自己的一個打包TODO list
,很容易想象這樣一個場景较沪,經(jīng)過漫長的打包過程后预吆,安裝到手機時龙填,才發(fā)現(xiàn)環(huán)境錯了,這就很僵硬了拐叉;當(dāng)然還有比這更糟糕的岩遗,就是在生產(chǎn)環(huán)境一頓猛如虎的操作后才發(fā)現(xiàn),丫喲凤瘦,環(huán)境錯了K藿浮!
方法二蔬芥?
如果是通過參數(shù)傳入的方式更改環(huán)境梆靖,或許會簡單準(zhǔn)確很多吧,至少在Android
開發(fā)時笔诵,通過多渠道打包的方式打包返吻,沒有出現(xiàn)過環(huán)境搞錯的問題。所以這方法二就顯得很正確乎婿,很優(yōu)雅了测僵。那么如何實現(xiàn)呢?
開始
預(yù)配置環(huán)境參數(shù)
創(chuàng)建三個環(huán)境對應(yīng)的ts
文件:
environment.dev.ts
environment.prod.ts
environment.uat.ts
結(jié)構(gòu):
.
├── src
│ ├── environments
│ │ ├── environment.dev.ts
│ │ ├── environment.prod.ts
│ │ └── environment.uat.ts
以environment.prod.ts
為例谢翎,內(nèi)容如下:
export const ENV = {
"mode": "Prod",
"database": "data.db",
"cordova": {
"id": "io.ionic.starter",
"version": "0.0.1",
"ios": {
"CodePushServerUrl": "prod",
"CodePushDeploymentKey": "1234567890"
},
"android": {
"CodePushServerUrl": "prod",
"CodePushDeploymentKey": "0987654321"
}
}
}
然后在項目根目錄中創(chuàng)建文件夾config
捍靠,里面創(chuàng)建文件webpack.config.js
.
├── config
│ └── webpack.config.js
內(nèi)容如下:
var chalk = require("chalk");
var fs = require('fs');
var path = require('path');
var useDefaultConfig = require('@ionic/app-scripts/config/webpack.config.js');
var env = process.env.NODE_ENV || 'dev';
var IONIC_ENV = process.env.IONIC_ENV
console.log('NODE_ENV:' + env);
console.log('IONIC_ENV:' + IONIC_ENV);
if (env === 'dev') {
if (IONIC_ENV == 'dev') {
useDefaultConfig.dev.resolve.alias = {
"@env/environment": path.resolve(environmentPath('dev'))
};
};
if (IONIC_ENV == 'prod') {
useDefaultConfig.prod.resolve.alias = {
"@env/environment": path.resolve(environmentPath('dev'))
};
};
}
if (env === 'uat') {
if (IONIC_ENV == 'dev') {
useDefaultConfig.dev.resolve.alias = {
"@env/environment": path.resolve(environmentPath('uat'))
};
};
if (IONIC_ENV == 'prod') {
useDefaultConfig.prod.resolve.alias = {
"@env/environment": path.resolve(environmentPath('uat'))
};
};
}
if (env === 'prod') {
if (IONIC_ENV == 'dev') {
useDefaultConfig.dev.resolve.alias = {
"@env/environment": path.resolve(environmentPath('prod'))
};
};
if (IONIC_ENV == 'prod') {
useDefaultConfig.prod.resolve.alias = {
"@env/environment": path.resolve(environmentPath('prod'))
};
};
}
function environmentPath(env) {
var filePath = 'src/environments/environment.' + env + '.ts';
console.log("use env file:" + filePath);
if (!fs.existsSync(filePath)) {
console.log(chalk.red('\n' + filePath + ' does not exist!'));
} else {
return filePath;
}
}
module.exports = function () {
return useDefaultConfig;
};
修改項目的package.json
文件,添加:
"config": {
"ionic_webpack": "./config/webpack.config.js"
}
修改tsconfig.json
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@env/environment": [
"environments/environment.prod"
]
}
}
}
實現(xiàn)動態(tài)切換環(huán)境URL
前面我們說了設(shè)置ionic CLI
代理岳服,這時候當(dāng)然也要加入剂公,實現(xiàn)一個命令,切換環(huán)境及參數(shù)吊宋,才是我們的目標(biāo)纲辽。下面我們開始實現(xiàn).
- 項目根目錄創(chuàng)建文件
enviroment.json
:
{
"devUrl": "http://localhost:8080/api",
"devPath": "/api",
"uatUrl": "http://r.example.com/api",
"uatPath": "/api",
"prodUrl": "http://gank.io/api",
"prodPath": "/api"
}
dev和uat我瞎寫的颜武,不要在意,只是為了實現(xiàn)效果而已啦拖吼。
- 然后鳞上,我們修改剛剛的
webpack.config.js
文件,添加一個方法setProxy()
及調(diào)用者方法的一句代碼吊档,完整的webpack.config.js
:
var chalk = require("chalk");
var fs = require('fs');
var path = require('path');
var useDefaultConfig = require('@ionic/app-scripts/config/webpack.config.js');
var env = process.env.NODE_ENV || 'dev';
var IONIC_ENV = process.env.IONIC_ENV
console.log('NODE_ENV:' + env);
console.log('IONIC_ENV:' + IONIC_ENV);
if (env === 'dev') {
if (IONIC_ENV == 'dev') {
useDefaultConfig.dev.resolve.alias = {
"@env/environment": path.resolve(environmentPath('dev'))
};
};
if (IONIC_ENV == 'prod') {
useDefaultConfig.prod.resolve.alias = {
"@env/environment": path.resolve(environmentPath('dev'))
};
};
}
if (env === 'uat') {
if (IONIC_ENV == 'dev') {
useDefaultConfig.dev.resolve.alias = {
"@env/environment": path.resolve(environmentPath('uat'))
};
};
if (IONIC_ENV == 'prod') {
useDefaultConfig.prod.resolve.alias = {
"@env/environment": path.resolve(environmentPath('uat'))
};
};
}
if (env === 'prod') {
if (IONIC_ENV == 'dev') {
useDefaultConfig.dev.resolve.alias = {
"@env/environment": path.resolve(environmentPath('prod'))
};
};
if (IONIC_ENV == 'prod') {
useDefaultConfig.prod.resolve.alias = {
"@env/environment": path.resolve(environmentPath('prod'))
};
};
}
function environmentPath(env) {
var filePath = 'src/environments/environment.' + env + '.ts';
console.log("use env file:" + filePath);
setProxy(env);
if (!fs.existsSync(filePath)) {
console.log(chalk.red('\n' + filePath + ' does not exist!'));
} else {
return filePath;
}
}
function setProxy(env) {
console.info("Set proxy");
var configJson = require("../enviroment.json");
fs = require('fs');
var path = require('path');
var filePath = path.resolve(__dirname, '../ionic.config.json');
var m = JSON.parse(fs.readFileSync(filePath).toString());
var serverUrl;
var urlPath;
switch (env) {
case "prod":
serverUrl = configJson.prodUrl;
urlPath = configJson.prodPath;
break;
case "dev":
serverUrl = configJson.devUrl;
serverUrl = configJson.devPath;
break;
default:
serverUrl = configJson.devUrl;
serverPath = configJson.devPath;
break;
}
m.proxies[0].proxyUrl = serverUrl;
m.proxies[0].path = urlPath;
fs.writeFileSync(filePath, JSON.stringify(m));
}
module.exports = function () {
return useDefaultConfig;
};
添加啟動Script
現(xiàn)在其實已經(jīng)完成了配置文件的編寫篙议,下面就是解決如何使用的問題,我這里舉個不完整例子
-
修改
package.json
添加兩個環(huán)境,Dev
和Prod
:{ "scripts": { "clean": "ionic-app-scripts clean", "build": "ionic-app-scripts build", "lint": "ionic-app-scripts lint", "ionic:build": "ionic-app-scripts build", "ionic:serve": "ionic-app-scripts serve", "serve:uat": "NODE_ENV=uat ionic serve", "serve:prod": "NODE_ENV=prod ionic serve" } }
2. [例]在`home.ts`中調(diào)用配置的參數(shù)
```typescript
import { Http } from '@angular/http';
import { ENV } from '@env/environment'
@IonicPage()
@Component({
selector: 'page-home',
templateUrl: 'home.html',
})
export class HomePage {
constructor(private http: Http) {
}
ionViewDidLoad() {
console.log(ENV.mode); //打印環(huán)境
debugger; //加個斷點
}
}
- 切換環(huán)境并運行
假設(shè)需要切換到prod
環(huán)境怠硼,那么終端執(zhí)行$ npm run serve:prod
鬼贱,如果成功,會自動打開網(wǎng)頁
我們看到香璃,參數(shù)都在ENV
這個對象中这难。
ionic CLI
代理就不測了,反正都可以用就對了葡秒。
如果要切換uat
那就$ npm run serve:uat
唄姻乓,這些命令都是定義在package.json
中scripts
里面,大家可以依葫蘆畫瓢整起來 - 完成
項目
Gank-me
參考allnew_package
分支