小程序统一 API 层
Q1: 什么是小程序的统一 API 层?为什么需要这种设计?
小程序的统一 API 层是对底层各平台原生能力的封装与抽象,提供一致的接口供开发者使用。需要这种设计的原因有:
- 跨平台一致性:抹平不同平台(iOS/Android/Web)间的实现差异,提供统一接口
- 向下兼容:处理不同版本客户端的能力差异,实现版本适配
- 错误处理:统一的错误处理和参数校验机制
- 扩展性:便于添加新功能而不破坏已有接口
- 安全性:对原生能力进行安全控制和权限管理
Q2: 小程序 API 封装与传统 Web API 封装有何异同?
相同点:
- 都提供对底层能力的封装和抽象
- 都关注开发体验和易用性
- 都需要考虑兼容性和异常处理
不同点:
- 运行环境:Web API 运行在浏览器环境,小程序 API 运行在定制化容器中
- 通信机制:小程序 API 需要通过 JSBridge 与原生通信,而不是直接调用
- 跨线程特性:小程序双线程模型使 API 需要考虑跨线程通信
- 生命周期:小程序 API 需要适配特定的应用和页面生命周期
- 能力范围:小程序可以调用更多设备原生能力,超出传统 Web 范围
Q3: 如何设计一个可扩展的小程序 API 系统?请描述你的架构设计。
一个良好的小程序 API 系统应具备以下架构特点:
- 分层设计:
- 接口层:提供开发者直接调用的 API
- 适配层:处理平台差异性的逻辑
- 通信层:负责与原生层通信
- 核心层:处理公共逻辑和基础能力
- 插件化架构:
- 核心 API 与扩展 API 分离
- 扩展 API 通过插件方式加载
- 支持按需引入和懒加载
- 统一接口规范:
- 一致的参数结构(options 对象)
- 统一的回调模式(success/fail/complete)
- Promise 化支持
- 版本控制:
- API 支持版本标记
- 降级策略和兼容性处理
- 能力检测机制
- 错误处理:
- 统一的错误码系统
- 全局错误拦截和处理
- 调试和日志机制
Q4: 请描述小程序框架中的能力扩展体系是如何实现的?
小程序框架的能力扩展体系通常通过以下方式实现:
注入式扩展:
typescript
// 将扩展API注入到全局对象
function injectExtendApis(globalObject) {
// 云能力API注入
globalObject.cloud = createCloudAPI();
// 支付API注入
globalObject.payment = createPaymentAPI();
}
插件式扩展:
typescript
class ApiExtension {
constructor(name, apis) {
this.name = name;
this.apis = apis;
}
register(apiHost) {
Object.keys(this.apis).forEach((key) => {
apiHost[key] = this.apis[key];
});
}
}
// 注册扩展
apiManager.registerExtension(new ApiExtension("cloud", cloudApis));
代理拦截模式:
typescript
const apiProxy = new Proxy(baseApis, {
get(target, property) {
// 检查扩展API中是否存在
if (extendedApis[property]) {
return extendedApis[property];
}
return target[property];
},
});
动态加载机制:
typescript
async function loadApiExtension(name) {
const extension = await import(`./extensions/${name}.js`);
registerApi(extension.default);
}
Q5: 在小程序 API 层中,如何处理异步 API 的 Promise 化?
小程序 API 的 Promise 化处理通常有以下几种方式:
包装器模式:
typescript
function promisify(api) {
return function (options = {}) {
return new Promise((resolve, reject) => {
api({
...options,
success: (res) => {
resolve(res);
options.success && options.success(res);
},
fail: (err) => {
reject(err);
options.fail && options.fail(err);
},
});
});
};
}
// 使用方式
const request = promisify(ty.request);
全局转换:
typescript
function promisifyAll(apis) {
const promisifiedApis = {};
Object.keys(apis).forEach((key) => {
if (typeof apis[key] === "function") {
promisifiedApis[key] = promisify(apis[key]);
}
});
return promisifiedApis;
}
API 标记和选择性处理
typescript
const asyncApiList = ["request", "login", "uploadFile"];
function createPromisifiedApis(rawApis) {
const apis = { ...rawApis };
asyncApiList.forEach((name) => {
if (apis[name]) {
apis[name + "Async"] = promisify(apis[name]);
}
});
return apis;
}
Q6: 如何在小程序 API 层实现向下兼容?如何处理不同平台间的 API 差异?
实现向下兼容和处理平台差异的方法:
能力检测模式:
typescript
function callAPI(name, options) {
// 检查该API是否存在
if (!nativeAPI[name]) {
// 降级处理
return fallbackImplementation(name, options);
}
// 正常调用
return nativeAPI[name](options);
}
版本比较处理:
typescript
function callVersionedAPI(name, options) {
const currentVersion = getContainerVersion();
const requiredVersion = API_VERSION_MAP[name];
if (semverCompare(currentVersion, requiredVersion) < 0) {
// 版本过低,执行降级逻辑
return polyfill(name, options);
}
return nativeAPI[name](options);
}
平台适配策略:
typescript
const platformImplementations = {
ios: {
scanCode: iosScanCodeImpl,
},
android: {
scanCode: androidScanCodeImpl,
},
web: {
scanCode: webScanCodePolyfill,
},
};
function getPlatformAPI(name) {
const platform = getPlatformType();
return platformImplementations[platform][name] || defaultImpl[name];
}
参数转换:
typescript
function callCrossplatformAPI(name, options) {
const platform = getPlatformType();
// 转换参数格式以适应不同平台
const adaptedOptions = parameterAdapters[platform](options);
return nativeAPI[name](adaptedOptions);
}
Q7: 在小程序 API 层面,有哪些性能优化技术?如何减少调用开销?
小程序 API 层面的性能优化技术:
缓存机制:
typescript
// 实现 API 结果缓存
const apiCache = new Map();
function callWithCache(name, options, cacheTime = 5000) {
const cacheKey = `${name}_${JSON.stringify(options)}`;
const cached = apiCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < cacheTime) {
return Promise.resolve(cached.data);
}
return originalAPI[name](options).then((res) => {
apiCache.set(cacheKey, {
data: res,
timestamp: Date.now(),
});
return res;
});
}
批量处理:
typescript
// 收集短时间内的多次请求,合并处理
class BatchProcessor {
constructor(api, delay = 50) {
this.queue = [];
this.timer = null;
this.api = api;
this.delay = delay;
}
add(params, resolve, reject) {
this.queue.push({ params, resolve, reject });
if (!this.timer) {
this.timer = setTimeout(() => {
this.process();
}, this.delay);
}
}
process() {
const batch = this.queue.slice();
this.queue = [];
this.timer = null;
// 批量处理逻辑
this.api
.batchProcess(batch.map((item) => item.params))
.then((results) => {
results.forEach((result, index) => {
batch[index].resolve(result);
});
})
.catch((error) => {
batch.forEach((item) => item.reject(error));
});
}
}
延迟加载:
typescript
// 非核心 API 延迟加载
const lazyAPIs = {};
function getLazyAPI(name) {
if (!lazyAPIs[name]) {
lazyAPIs[name] = new Proxy({}, {
get(\_, property) {
// 首次调用时加载
if (!lazyAPIs[name].loaded) {
lazyAPIs[name].loaded = true;
lazyLoadAPI(name);
}
return lazyAPIs[name][property];
}
});
}
return lazyAPIs[name];
}
通信优化:
typescript
// 优化序列化/反序列化过程
function optimizedCall(name, data) {
// 避免不必要的深拷贝
const serializedData = fastSerialize(data);
return bridge.call(name, serializedData).then((result) => {
return fastDeserialize(result);
});
}
Q8: 如何设计一个高效的小程序 API 权限控制系统?
高效的 API 权限控制系统设计:
分层权限模型:
typescript
const permissionLevels = {
PUBLIC: 0, // 无需授权
USER: 1, // 需要用户授权
SENSITIVE: 2, // 敏感权限,需要额外确认
SYSTEM: 3, // 系统级权限,限制使用场景
};
const apiPermissions = {
getLocation: permissionLevels.USER,
getPhoneNumber: permissionLevels.SENSITIVE,
getFileSystem: permissionLevels.SYSTEM,
};
权限检查代理:
typescript
function createPermissionProxy(apis) {
return new Proxy(apis, {
get(target, property) {
const api = target[property];
if (typeof api !== "function") return api;
return function (options) {
const permission = apiPermissions[property];
// 检查权限
return checkPermission(property, permission).then((granted) => {
if (granted) {
return api(options);
} else {
throw new Error(`Permission denied: ${property}`);
}
});
};
},
});
}
权限缓存机制:
typescript
const permissionCache = new Map();
function checkPermission(name, level) {
// 检查缓存
if (permissionCache.has(name)) {
const cached = permissionCache.get(name);
if (Date.now() - cached.timestamp < PERMISSION_CACHE_TIME) {
return Promise.resolve(cached.granted);
}
}
// 实际权限检查
return performPermissionCheck(name, level).then((granted) => {
permissionCache.set(name, {
granted,
timestamp: Date.now(),
});
return granted;
});
}
Q9: 如何实现一个跨平台的文件系统 API?请详细说明设计思路。
跨平台文件系统 API 设计思路:
统一接口定义:
typescript
interface FileSystemAPI {
// 读取文件内容
readFile(options: {
filePath: string;
encoding?: string;
success?: (res: { data: string | ArrayBuffer }) => void;
fail?: (err: Error) => void;
complete?: () => void;
}): Promise<{ data: string | ArrayBuffer }>;
// 写入文件
writeFile(options: {
filePath: string;
data: string | ArrayBuffer;
encoding?: string;
success?: (res: { errMsg: string }) => void;
fail?: (err: Error) => void;
complete?: () => void;
}): Promise<{ errMsg: string }>;
// 其他方法:mkdir, rmdir, unlink 等
}
平台特定实现:
typescript
// iOS 实现
const iOSFileSystem: FileSystemAPI = {
readFile(options) {
return bridge.invokeNative({
namespace: "fileSystem",
method: "readFile",
args: formatOptions(options),
});
},
// 其他方法实现...
};
// Android 实现
const androidFileSystem: FileSystemAPI = {
readFile(options) {
// Android 特有的参数处理
const androidOptions = {
...options,
androidSpecific: getAndroidPath(options.filePath),
};
return bridge.invokeNative({
namespace: "fileSystem",
method: "readFile",
args: formatOptions(androidOptions),
});
},
// 其他方法实现...
};
// Web 实现 (使用 FileSystem API 或降级处理)
const webFileSystem: FileSystemAPI = {
readFile(options) {
if (window.FileReader) {
// 使用 Web API 实现
return webFileReadImplementation(options);
} else {
// 降级到服务器端实现
return serverSideFileRead(options);
}
},
// 其他方法实现...
};
自动平台检测:
typescript
function createFileSystemAPI(): FileSystemAPI {
const platform = detectPlatform();
switch (platform) {
case "ios":
return iOSFileSystem;
case "android":
return androidFileSystem;
case "web":
return webFileSystem;
default:
// 默认实现或错误处理
throw new Error(`Unsupported platform: ${platform}`);
}
}
// 导出统一 API
export const fileSystem = createFileSystemAPI();
路径标准化:
typescript
function normalizePath(path: string, platform: string): string {
// 处理不同平台的路径格式
if (platform === "android") {
// Android 路径处理
return path.replace(/^\//, "");
} else if (platform === "ios") {
// iOS 路径处理
return path.startsWith("/") ? path : `/${path}`;
} else {
// Web 路径处理
return path;
}
}
错误码统一:
typescript
const errorCodeMap = {
// 原生错误码到统一错误码的映射
ios: {
"10001": "FILE_NOT_FOUND",
"10002": "NO_PERMISSION",
},
android: {
"2001": "FILE_NOT_FOUND",
"2002": "NO_PERMISSION",
},
};
function normalizeError(nativeError, platform) {
const code = nativeError.code;
const standardCode = errorCodeMap[platform][code] || "UNKNOWN_ERROR";
return {
code: standardCode,
message: getErrorMessage(standardCode),
originalError: nativeError,
};
}
Q10: 你如何设计小程序的网络请求 API,以处理不同的认证方式、拦截器和缓存策略?
小程序网络请求 API 的设计:
基础接口设计:
typescript
interface RequestOptions {
url: string;
method?: "GET" | "POST" | "PUT" | "DELETE";
data?: object | string | ArrayBuffer;
header?: Record<string, string>;
timeout?: number;
dataType?: "json" | "text" | "base64";
success?: (res: {
data: any;
statusCode: number;
header: Record<string, string>;
}) => void;
fail?: (err: Error) => void;
complete?: () => void;
}
function request(options: RequestOptions): Promise<any> {
// 实现逻辑
}
拦截器链:
typescript
interface Interceptor {
request?: (
config: RequestOptions
) => RequestOptions | Promise<RequestOptions>;
response?: (response: any) => any | Promise<any>;
error?: (error: Error) => Error | Promise<Error>;
}
class HttpClient {
private interceptors: Interceptor[] = [];
addInterceptor(interceptor: Interceptor) {
this.interceptors.push(interceptor);
return this;
}
async request(options: RequestOptions) {
let config = { ...options };
// 请求拦截
for (const interceptor of this.interceptors) {
if (interceptor.request) {
config = await interceptor.request(config);
}
}
try {
// 发起请求
let response = await nativeRequest(config);
// 响应拦截
for (const interceptor of this.interceptors) {
if (interceptor.response) {
response = await interceptor.response(response);
}
}
return response;
} catch (error) {
// 错误拦截
let processedError = error;
for (const interceptor of this.interceptors) {
if (interceptor.error) {
processedError = await interceptor.error(processedError);
}
}
throw processedError;
}
}
}
认证机制:
typescript
// 认证拦截器
const authInterceptor = {
request: async (config) => {
const token = await getAuthToken();
if (token) {
// 设置认证头
config.header = config.header || {};
config.header["Authorization"] = `Bearer ${token}`;
}
return config;
},
response: async (response) => {
// 处理 401 错误,尝试刷新 token
if (response.statusCode === 401) {
const newToken = await refreshToken();
if (newToken) {
// 使用新 token 重试请求
return httpClient.request({
...response.config,
header: {
...response.config.header,
Authorization: `Bearer ${newToken}`,
},
});
}
}
return response;
},
};
// 添加到 HTTP 客户端
httpClient.addInterceptor(authInterceptor);
缓存策略:
typescript
// 缓存拦截器
const cacheInterceptor = {
request: async (config) => {
// 只缓存 GET 请求
if (config.method !== "GET" || config.noCache) {
return config;
}
const cacheKey = generateCacheKey(config);
const cachedResponse = await getCachedResponse(cacheKey);
if (cachedResponse && !isCacheExpired(cachedResponse, config.cacheTime)) {
// 返回缓存的响应
return Promise.reject({
__CACHE__: true,
data: cachedResponse,
});
}
// 将缓存键附加到配置
config.__cacheKey = cacheKey;
return config;
},
response: async (response) => {
// 缓存响应
if (response.config.__cacheKey && response.statusCode === 200) {
await cacheResponse(response.config.__cacheKey, response.data);
}
return response;
},
error: async (error) => {
// 如果是缓存命中,返回缓存的响应
if (error.__CACHE__) {
return error.data;
}
throw error;
},
};
// 添加到 HTTP 客户端
httpClient.addInterceptor(cacheInterceptor);
重试机制:
typescript
// 重试拦截器
const retryInterceptor = {
error: async (error) => {
const config = error.config;
// 检查是否应该重试
if (!config || !config.retry || config.__retryCount >= config.retry) {
throw error;
}
// 增加重试计数
config.__retryCount = (config.__retryCount || 0) + 1;
// 延迟重试
await new Promise((resolve) =>
setTimeout(resolve, config.retryDelay || 1000)
);
// 重试请求
return httpClient.request(config);
},
};
// 添加到 HTTP 客户端
httpClient.addInterceptor(retryInterceptor);