溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

@angular前端項目代碼優(yōu)化之構(gòu)建Api Tree的方法

發(fā)布時間:2020-09-14 06:56:01 來源:腳本之家 閱讀:243 作者:SimpleXD 欄目:web開發(fā)

前顏(yan)

在前端項目的開發(fā)過程中,往往后端會給到一份數(shù)據(jù)接口(本文簡稱api),為了減少后期的維護以及出錯成本,我的考慮是希望能夠找到這么一種方法,可以將所有的api以某種方式統(tǒng)一的管理起來,并且很方便的進行維護,比如當(dāng)后端修改了api名,我可以很快的定位到該api進行修改,或者當(dāng)后端添加了新的api,我可以很快的知道具體是一個api寫漏了。

于是,我有了構(gòu)建Api Tree的想法。

一、前后端分離(Resful api)

在前后端分離的開發(fā)模式中,前后端的交互點主要在于各個數(shù)據(jù)接口,也就是說后端把每個功能封裝成了api,供前端調(diào)用。

舉個例子,假設(shè)后端提供了關(guān)于user的以下3個api:

http(s)://www.xxx.com/api/v1/user/{ id }
http(s)://www.xxx.com/api/v1/user/getByName/{ name }
http(s)://www.xxx.com/api/v1/user/getByAge/{ age }

對應(yīng)的api描述如下(為了方便理解,這里只考慮get請求):

1 獲取用戶id的用戶數(shù)據(jù)
2 獲取用戶名為name的用戶信息    
3 獲取年齡為age的用戶列表

二、在Component中調(diào)用api接口獲取數(shù)據(jù)

目前各大前端框架比如angular、vue以及react等,都有提供相關(guān)HttpClient,用來發(fā)起http請求,比如get、post、put、delete等,由于本人比較熟悉angular,下面代碼以angular進行舉例(其他框架做法類似),代碼統(tǒng)一使用typescript語法。

在app.component.ts中調(diào)用api:

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.scss']
})
export class AppComponent {

 userInfo;

 constructor(private http: HttpClient) {
  this.getUserById(1);
 }

 async getUserById(userId) {
  const url = `https://www.xxx.com/api/v1/user/${userId}`;
  this.userInfo = await this.http.get(url).toPromise();
 }
}

三、封裝UserHttpService

在項目中,由于多個頁面可能需要調(diào)用同一個api,為了減少代碼的冗余以及方便維護,比較好的方式是將所有的api封裝到一個Service中,然后將這個Service實例化成單例模式,為所有的頁面提供http服務(wù)。

angular提供了依賴注入的功能,可以將Service注入到Module中,并且在Module中的各個Component共享同一個Service,因此不需要手動去實現(xiàn)Service的單例模式。

代碼如下:

user.http.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

const HOST_URL = `https://www.xxx.com/api/v1`;

@Injectable()
export class UserHttpService {

 constructor(private http: HttpClient) { }

 async getUserById(userId) {
  const url = `${HOST_URL}/user/${userId}`;
  return this.http.get(url).toPromise();
 }

 async getUserByName(name) {
  const url = `${HOST_URL}/user/getByName/${name}`;
  return this.http.get(url).toPromise();
 }

 async getUserByAge(age) {
  const url = `${HOST_URL}/user/getByAge/${age}`;
  return this.http.get(url).toPromise();
 }

}

app.component.ts

import { Component } from '@angular/core';
import { UserHttpService } from './user.http.service';
@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.scss']
})
export class AppComponent {

 constructor(private userHttpService: UserHttpService) {
  this.getUserById(1);
 }

 async getUserById(userId) {
  const userInfo = await this.userHttpService.getUserById(userId);
  console.log(userInfo);
 }

 async getUserByName(name) {
  const userInfo = await this.userHttpService.getUserByName(name);
  console.log(userInfo);
 }

 async getUserByAge(age) {
  const userInfoList = await this.userHttpService.getUserByAge(age);
  console.log(userInfoList);
 }

}

這樣的好處在于:

1、團隊合作:

可以將前端項目分為HttpService層和Component層,由不同的人進行分開維護

2、減少代碼的冗余:

在多個Component中調(diào)用同一個api時,不需要寫多份代碼

3、降低維護和擴展成本:

當(dāng)后端增加或修改接口時,由于所有的user api都在UserHttpService里,所以能夠很容易的進行接口調(diào)整,并且不影響Component層的代碼

但以上方案還存在一個缺點,即url使用字符串拼接的形式:

const url = `${HOST_URL}/user/getByName/${name}`;

這樣容易出現(xiàn)以下問題:

1、接口名拼接出錯,并且由于是字符串拼接,不會有語法提示(ts)

2、沒有一份完整的映射后端的api表,出現(xiàn)問題時,不容易排查 因此,接下來進入本文的主題:構(gòu)建Api Tree。

四、手動構(gòu)建Api Tree

什么是Api Tree呢,我把它定義為將所有的api以節(jié)點的形式掛在一個樹上,最后形成了一棵包含所有api的樹形結(jié)構(gòu)。

對api tree的構(gòu)建初步想法(手動構(gòu)建)如下:

/**
 * 手動構(gòu)建 api tree 
 */
const APITREE = {
 domain1: {
  api: {
   v1: {
    user: {
     getByName: 'https://www.xxx.com/api/v1/user/getByName',
     getByAge: 'https://www.xxx.com/api/v1/user/getByAge'
    },
    animal: {
     getByType: 'https://www.xxx.com/api/v1/animal/getByType',
     getByAge: 'https://www.xxx.com/api/v1/animal/getByAge'
    }
   }
  }
 },
 domain2: {
  api: {
   car: {
    api1: 'https://xxx.xxx.cn/api/car/api1',
    api2: 'https://xxx.xxx.cn/api/car/api2'
   }
  }
 },
 domain3: {}
};
export { APITREE };

有了api tree,我們就可以采用如下方式來從api樹上摘取各個api節(jié)點的url,代碼如下:

// 獲取url:https://www.xxx.com/api/v1/user/getByName
const getByNameUrl = APITREE.domain1.api.v1.user.getByName;

// 獲取url:https://xxx.xxx.cn/api/car/api1
const carApi1Url = APITREE.domain2.api.car.api1;

但是以上構(gòu)建api tree的方式存在兩個缺點:

1、需要在各個節(jié)點手動拼接全路徑

2、只能摘取子節(jié)點的url:getByName和getByAge,無法摘取父節(jié)點的url,比如我想獲取 https://www.xxx.com/api/v1/user ,無法通過 APITREE.domain1.api.v1.user 獲取

const APITREE = {
 domain1: {
  api: {
   v1: {
    // user為父節(jié)點
    // 缺點一:無法通過APITREE.domain1.api.v1.user獲取
    //    https://www.xxx.com/api/v1/user
    user: {
     // 缺點二:在getByName和getByAge節(jié)點中手動寫入全路徑拼接
     getByName: 'https://www.xxx.com/api/v1/user/getByName',
     getByAge: 'https://www.xxx.com/api/v1/user/getByAge'
    }
   }
  }
 }
};

五、Api Tree生成器(ApiTreeGenerator)

針對手動構(gòu)建Api Tree的問題,我引入了兩個概念:apiTreeConfig(基本配置)和apiTreeGenerator(生成器)。

通過apiTreeGenerator對apiTreeConfig進行處理,最終生成真正的apiTree。

1、apiTreeConfig我把它稱之為基本配置,apiTreeConfig具有一定的配置規(guī)則,要求每個節(jié)點名(除了域名)必須與api url中的每一節(jié)點名一致,因為apiTreeGenerator是根據(jù)apiTreeConfig的各個節(jié)點名進行生成, api tree config配置如下:

/**
 * api tree config
 * _this可以省略不寫,但是不寫的話,在ts就沒有語法提示
 * 子節(jié)點getByName,getByAge以及_this可以為任意值,因為將會被apiTreeGenerator重新賦值
 */
const APITREECONFIG = {
 api: {
  v1: {
   user: {
    getByName: '',
    getByAge: '',
    _this: ''
   }
  },
  _this: ''
 }
 };

export { APITREECONFIG };

2、apiTreeGenerator我把它稱之為生成器,具有如下功能:

1) 遍歷apiTreeConfig,處理apiTreeConfig的所有子節(jié)點,并根據(jù)該節(jié)點的所有父節(jié)點鏈生成完整的url,并且作為該節(jié)點的value,比如: APITREECONFIG.api.v1.user.getByName -> https://www.xxx.com/api/v1/user/getByName

2) 遍歷apiTreeConfig,處理apiTreeConfig的所有父節(jié)點,在每個父節(jié)點中添加_this子節(jié)點指向父節(jié)點的完整url。

apiTreeGenerator(生成器)的代碼如下:

(由于項目中只用到一個后端的數(shù)據(jù),這里只實現(xiàn)了單域名的apiTreeGenerator,關(guān)于多域名的apiTreeGenerator,大家可以自行修改實現(xiàn)。)

import { APITREECONFIG } from './api-tree.config';

const APITREE = APITREECONFIG;
const HOST_URL = `https://www.xxx.com`;

/**
 * 為api node chain添加HOST_URL前綴
 */

const addHost = (apiNodeChain: string) => {
 return apiNodeChain ? `${HOST_URL}/${apiNodeChain.replace(/^\//, '')}` : HOST_URL;
};

/**
 * 根據(jù)api tree config 生成 api tree:
 * @param apiTreeConfig api tree config
 * @param parentApiNodeChain parentApiNode1/parentApiNode2/parentApiNode3
 */
const apiTreeGenerator = (apiTreeConfig: string | object, parentApiNodeChain?: string) => {
 for (const key of Object.keys(apiTreeConfig)) {
  const apiNode = key;
  const prefixChain = parentApiNodeChain ? `${parentApiNodeChain}/` : '';
  if (Object.prototype.toString.call(apiTreeConfig[key]) === '[object Object]') {
   apiTreeGenerator(apiTreeConfig[key], prefixChain + apiNode);
  } else {
   apiTreeConfig[key] = parentApiNodeChain
    ? addHost(prefixChain + apiTreeConfig[key])
    : addHost(apiTreeConfig[key]);
  }
 }
 // 創(chuàng)建_this節(jié)點 (這里需要放在上面的for之后)
 apiTreeConfig['_this'] = parentApiNodeChain
  ? addHost(`${parentApiNodeChain}`)
  : addHost('');
};

apiTreeGenerator(APITREECONFIG);

export { APITREE };

結(jié)果:

@angular前端項目代碼優(yōu)化之構(gòu)建Api Tree的方法

優(yōu)化后的UserHttpService代碼如下: user.http.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { APITREE } from './api-tree';

@Injectable()
export class UserHttpService {

 constructor(private http: HttpClient) { }

 async getUserById(userId) {
  const url = APITREE.api.v1.user._this + '/' + userId;
  return this.http.get(url).toPromise();
 }

 async getUserByName(name) {
  const url = APITREE.api.v1.user.getByName + '/' + name;
  return this.http.get(url).toPromise();
 }

 async getUserByAge(age) {
  const url = APITREE.api.v1.user.getByAge + '/' + age;
  return this.http.get(url).toPromise();
 }

}

六、總結(jié)

通過api tree,能帶來如下好處:

1、能夠通過樹的形式來獲取api,關(guān)鍵是有語法提示
APITREE.api.v1.user.getByName

2、apiTreeConfig配置文件與后端的api接口一 一對應(yīng),方便維護

3、當(dāng)后端修改api名時,apiTreeConfig可以很方便的進行調(diào)整

七、demo

github代碼:https://github.com/SimpleCodeCX/myCode/tree/master/angular/api-tree

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI