フロントエンドフレームワークでのAOP設計
フロントエンドフレームワークでのAOP設計
AOPとは
AOPは、ビジネスプロセスの側面を対象としたプログラミングパラダイムです。AOPを使用するというアイデアは、各ビジネスロジックモジュールを分離し、各モジュール間の結合を減らし、再利用性を高めることができます。AOPは、そのプロトコルの特定の実装を指定していません。自分のニーズ。
たとえば、一般的なフロントエンドグローバルフィルター、リクエストバックエンドインターフェイス、グローバルデータマッピング、中間状態処理などは、線形ビジネスロジックの特定の段階を独立したモジュールとして抽象化し、必要に応じてより多くのコールチェーンに分割します。道
フロントエンドフレームワークでAOP思考を実現する方法
例としてhttpリクエストを取り上げます。たとえば、次のコードには、リクエストヘッダーの処理、データの変換、リクエストされたドメイン名の追加、およびレスポンスの処理が含まれています。
async function requestAPI(url: string, init: RequestInit) {
init.headers = init.headers ?? {
}
init.headers = {
'contentType': 'application/x-www-form-urlencoded; charset=UTF-8', ...init.headers }
if (init.body === 'object' && !Array.isArray(init.body)) {
init.body = JSON.stringify(init.body ?? {
})
}
const regex = new RegExp('(^https:)|(^http:)\/\/([^\/\?]+)', 'g')
if (!regex.test(url)) {
url = 'https:xxx.com' + url
}
try {
const response = await fetch(url, init)
const res = await response.json()
if (res.code === 200) {
return res.data
} else {
throw new ApiException({
code: res.code,
msg: res.msg,
url
})
}
} catch (error) {
if (error instanceof ApiException) {
throw error
}
// ...处理异常
}
}
このように、リクエストヘッダーを調整する必要があり、リクエストパラメータやレスポンスの処理が必要な場合は、新しいリクエストメソッドを構築するか、元のメソッドに変更を加える必要があります。また、スロットルやアンチシェイクも必要です。この関数がここで変更またはカプセル化されている場合、AOPのアイデアでどのように実装する必要がありますか?
interface HttpOption extends RequestInit {
url: string
body?: any
}
interface HttpResponse<T> {
code: number
data: T
msg: string
}
function DomainDecorators() {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const fn = descriptor.value as (options: HttpOption) => Promise<Response>
descriptor.value = (options: {
domain: string
} & HttpOption) => {
if (options?.domain) {
const regex = new RegExp('(^https:)|(^http:)\/\/([^\/\?]+)', 'g')
if (!regex.test(options.url)) {
options.url = options.domain + options.url
}
}
return fn.call(target, options)
}
}
}
function OptionsDecorators() {
return function (target: HttpClient, propertyKey: string, descriptor: PropertyDescriptor) {
const fn = descriptor.value as (options: HttpOption) => Promise<Response>
descriptor.value = (options: HttpOption) => {
options.headers = {
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', ...options.headers }
if (options.body === 'object' && !Array.isArray(options.body)) {
options.body = JSON.stringify(options.body ?? {
})
}
return fn.call(target, options)
}
}
}
function ExceptionDecorators() {
return function (target: HttpClient, propertyKey: string, descriptor: PropertyDescriptor) {
const fn = descriptor.value as (options: HttpOption) => Promise<HttpResponse<any>>
descriptor.value = async (options: HttpOption) => {
const res = await fn.call(target, options)
if (res.code === 200) {
return res.data
} else {
throw new ApiException({
code: res.code,
msg: res.msg,
url: options.url
})
}
}
}
}
abstract class HttpBase {
get options(): {
domain?: string
} & RequestInit {
return {
mode: 'cors',
credentials: 'include',
cache: 'no-cache',
redirect: 'follow'
}
}
post<T>(options: HttpOption): Promise<HttpResponse<T>> {
options.method = 'POST'
return fetch(options.url, options).then((res) => res.json())
}
get<T>(options: HttpOption): Promise<HttpResponse<T>> {
options.method = 'GET'
return fetch(options.url, options).then((res) => res.json())
}
}
class HttpClient extends HttpBase {
@DomainDecorators()
@OptionsDecorators()
@ExceptionDecorators()
post<T>(options: HttpOption): Promise<HttpResponse<T>> {
return super.post(options)
}
}
ここでは、実装されたリクエストクラスにアスペクトが織り込まれ、パラメータ処理、ドメイン名の追加、応答の例外を分離し、必要に応じてそれらをアセンブルするときに、リクエストヘッダー処理部分と対応する部分を抽象化できます。各モジュールはリクエストを処理します。 、ただし、装飾されたオブジェクトのメソッドシグネチャが同じである限り、以前は関連付けや依存関係はありませんでした。これらの関数は再利用されます〜