abp最方便的莫过于它提供了一套完整的脚手架,给新手以方向,给老手以时间。在众多方便的功能中莫过于生成angular的proxy代理(js代理无类型提示),如何在vue3中使用typescript的proxy呢。
本文主要设计三个知识点。
符号链接
核心替换
首先符号链接功能可参考pnpm的实现方式,源文件在其他项目中,并不在根目录中,我们可通过符号链接,让编译器认为在根目录中。通过angular生成的proxy在angular目录中(必须在angular根目录里面才能生成typescrpt代理),以下为生成代理及符号链接的命令。
abp generate-proxy -t ng -s dcp --target dcp --source dcp
mklink /d mini-app\\src\\proxy ..\\..\\angular\\src\\app\\proxy 此时,proxy文件夹已经在vue3根目录下,可以通过tsc的路径引用添加快捷路径,由于proxy文件夹里面包含了@abp/ng.core和@angular/core的引用,所以需要通过tsc和webpack进行替换。
tsconfig.json
"paths": {
"@abp/ng.core": ["src/replace/mockAbp/index.ts"],
"@angular/core": ["src/replace/mockNg/index.ts"],
"@proxy": ["src/proxy/index.ts"],
"@proxy/*": ["src/proxy/*"],
........ package.json
"browser": {
"@abp/ng.core": "./src/replace/mockAbp",
"@angular/core": "./src/replace/mockNg"
} 代码中出现的mockAbp和mockNg是@abp/ng.core和@angular/core的替换实现。
├───replace
│ ├───mockAbp
│ │ │ index.ts
│ │ │ rest.service.ts
│ │ │
│ │ └───mock
│ │ auth.config.ts
│ │ common.ts
│ │ dto.ts
│ │ environment.ts
│ │ index.ts
│ │ mapEnumToOptions.ts
│ │
│ └───mockNg
│ index.ts
│ injectable.ts
│
├───services
│ authService.ts
│ configService.ts 这里主要说两个文件rest.service.ts和injectable.ts文件,其他文件可以从源码直接复制。
rest.service.ts主要实现了http请求,代码如下。主要进行了实现替换,注意angular版本返回Observable,这里返回Promise,也可修改为返回Observable,在vue3中类型提示为Promise,同时下面代码接入了部分angular的interceptor功能,依实际情况调整。
import axios, {
AxiosHeaders,
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
} from "axios";
import config from "../../app.consts";
import { useTokenStore } from "../../stores/token";
const tokenStore = useTokenStore();
export class RestService {
http: AxiosInstance;
constructor() {
this.http = axios.create({
baseURL: config.account.url,
headers: new AxiosHeaders({
Authorization: `Bearer ${tokenStore.token}`,
}),
});
}
async request<T = any, R = any, D = any>(
config: HttpRequest<D>,
_option: Record<string, string>
): Promise<R> {
config.body && (config.data = config.body);
let res = await this.http.request<T, AxiosResponse<R>>(config);
if (res.data?.toString()?.includes("<!DOCTYPE html>")) {
const token = await tokenStore.login();
this.http.defaults.headers.Authorization = `Bearer ${token}`;
res = await this.http.request<T, AxiosResponse<R>>(config);
}
return res.data;
}
}
export interface HttpRequest<D> extends AxiosRequestConfig<D> {
body?: D;
}
injectable.ts主要实现了vue3的依赖注入功能,需要获取vue3的app全局实例,这样才能实现全局注入,这里需要更改服务类名称,因为在打包的时候会丢失这些信息。
import Taro from "@tarojs/taro";
import { inject } from "vue";
import { RestService } from "../mockAbp/rest.service";
let serviceIndex: number = 0;
export function Injectable(_option: Record<string, any>) {
return function (_ctor: new (...args: readonly any[]) => unknown) {
const app = Taro.getApp();
if (app?.instance) {
Reflect.defineProperty(_ctor, "name", {
value: `restService(${_ctor.name})-${serviceIndex++}`,
writable: false,
});
const ctor = Reflect.construct(_ctor, [new RestService()]);
app.instance.provide(_ctor.name, ctor);
}
};
}
以上步骤完成后,在vue文件中即可注入以上服务,如下所示。
const projectService = inject<ProjectService>(ProjectService.name)!;