摘要:面對如何在現有的低版本的框架服務上,運行新版本的前端服務問題,華爲雲前端推出了一種融合方案,該方案能讓獨立的Angular項目總體運行在低版本的框架服務上,經過各類適配手段,讓Angular項目也能獲取到外層框架服務的資源。
華爲雲前端服務前期採用AngularJs做爲框架技術棧,技術較爲老舊,性能較差,在華爲雲快速發展的今天,顯然不能知足要求。所以咱們必需要升級前端技術棧,使用Angular2+來承載咱們的前端服務。GeminiDB做爲新服務,也是數據庫乃至華爲雲將來的重點服務,做爲前端部分,必須在技術上使用最前沿的框架,以最大地提升用戶體驗。javascript
可是技術棧的升級不是一蹴而就的,尤爲是在華爲雲,全部的雲服務必須在框架服務的底座上運行,而框架服務承載了全部的雲服務,若是要進行技術棧升級,必然是一個緩慢的過程。GeminiDB做爲華爲雲服務裏的一員,也不可能脫離框架服務而存在。所以存在一個問題,就是如何在現有的低版本的框架服務上,運行新版本的前端服務。css
爲了解決以上問題,華爲雲前端推出了一種融合方案,該方案能讓獨立的Angular項目總體運行在低版本的框架服務上,經過各類適配手段,讓Angular項目也能獲取到外層框架服務的資源。html
底層項目使用webpack打包,打包後經過在index.html裏引入businessAll.js文件,以該文件爲入口啓動整個框架服務。前端
<script type="text/javascript" src="businessAll.js"></script>複製代碼
在底層框架服務啓動後,再渲染出具體雲服務內容。java
<div class="service-content-view" ui-view ng-animate="{enter:'fade-enter'}"></div>複製代碼
Angular項目支持獨立運行,有單獨的index.html,也有單獨的main.ts入口。可是若是但願Angular項目運行在底層框架服務上,就必須把Angular項目看做是一個獨立的模塊,把項目總體引入到底層項目中。所以,咱們能夠預先把Angular項目編譯好,放到底層項目的一個目錄下。在運行底層項目時,在index.html裏將Angular項目引進來,獨立運行。webpack
<link rel="stylesheet" type="text/css" href="{底層項目中Angular項目的路徑}/styles.css" />
<script type="text/javascript" src="{底層項目中Angular項目的路徑}/runtime.js"></script>
<script type="text/javascript" src="{底層項目中Angular項目的路徑}/polyfills.js"></script>
<script type="text/javascript" src="{底層項目中Angular項目的路徑}/main.js"></script>複製代碼
底層項目和Angular項目均能獨立,可是要讓二者融合起來,會遇到如下幾個問題:web
1.底層項目中如何渲染出Angular項目。數據庫
2.Angular項目依賴底層項目的資源,如何保證Angular項目在底層項目運行起來後再運行。bootstrap
3.如何解決底層項目和Angular項目的路由衝突問題。bash
底層項目分爲兩部分,一部分是底層框架服務,另外一部分是具體雲服務。如今咱們要作的是把老的雲服務項目替換成新的Angular項目,所以咱們能夠直接在渲染老的雲服務的地方替換成新的Angular項目的渲染容器。
<div class="service-content-view" ui-view ng-animate="{enter:'fade-enter'}"></div>
<app-root></app-root>複製代碼
底層框架服務對頁面渲染上作了一些體驗上的優化,所以必須保留原模板中的ui-view,使底層項目正常運行起來,實際上老的雲服務項目的渲染內容已經轉發到新的Angular項目上面。
底層框架服務給雲服務提供了不少公共變量與服務,這些變量和服務是各個雲服務必需要使用的,不然雲服務將不能正常運做。
對於Angular項目來講,要使用底層框架服務提供的內容,首先要求Angular項目在底層項目運行起來以後再運行。這裏採用Augular中的APP_INITIALIZER令牌來解決這個問題。APP_INITIALIZER是一個函數,在程序初始化的時候被調用。這裏在根模塊的providers中以factory的形式來配置。
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { AppInitService } from './services/app-init.service';
import { AppComponent } from "./app.component";
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [
AppInitService,
{
provide: APP_INITIALIZER,
useFactory: initializeApp,
deps: [AppInitService],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
export function initializeApp(appInitService: AppInitService) {
return (): Promise<any> => {
return appInitService.Init();
};
}複製代碼
在appInitService裏,先獲取到底層框架的資源,再進行Angular項目的初始化。
import { Injectable } from '@angular/core';
@Injectable()
export class AppInitService {
constructor() {}
Init() {
return new Promise<void>((resolve, reject) => {
// 獲取到底層框架服務的資源
resolve();
});
}
}複製代碼
底層項目使用的是AngularJs,Angular項目獲取底層框架服務提供的資源不能經過Angular的方式引入,所以須要藉助AngularJS的注入器獲取在底層框架中註冊的服務組件:
static get(inject: string): any {
return (window as any).angular.element('html').injector().get(inject);}
如,要獲取 $rootScope:
rootScope = (window as any).angular.element('html').injector().get(‘$rootScope’);複製代碼
Angular項目自己有本身的路由,可是Angular項目是運行在底層框架之上的,Angular項目的路由將會被底層框架所攔截。所以,咱們也須要在底層框架的項目中配置相同的路由,以避免Angular項目中的有效路由被底層框架識導向爲404。
Angular項目路由:
{
path: '',
redirectTo: 'ng2app1',
pathMatch: 'full'
},
{
path: 'ng2app1',
loadChildren: './ng2app1/ng2app1.module#Ng2app1Module',
},
{
path: 'ng2app2',
loadChildren: './ng2app2/ng2app2.module#Ng2app2Module',
}
底層框架路由:
var configArr = [
{
name: 'ng2app1',
url: '/ng2app1'
},
{
name: 'ng2app2',
url: '/ng2app2'
}
];複製代碼
另外,因爲底層項目使用的是hash路由,Angular項目中也要作相應的配置,默認是使用的是PathLocationStrategy,須要切換到hash模式。
import { LocationStrategy, HashLocationStrategy } from '@angular/common';
...
providers: [
{
provide: LocationStrategy,
useClass: HashLocationStrategy
}
]複製代碼
以上方案是在底層框架升級週期長的前提下的一個臨時方案,實際上仍是存在着很多的問題。好比底層框架對於老的雲服務容器是有統一管理的,老的雲服務容器會針對不一樣的場景可以自適應,而融合方案中的Angular項目則不能;每次啓動整個項目時,必需要預先編譯好裏面的Angular項目,再去啓動外層的底層框架,開發效率比較低。所以,後續GeminiDB服務應該在底層框架升級後,儘快適應到新的底層框架體系中,提升服務的可用性和穩定性。