refactor: Optimize data and cache request logic

This commit is contained in:
Gabe
2025-08-31 23:37:29 +08:00
parent 4a5e6c2a23
commit c4fba1c905
10 changed files with 443 additions and 425 deletions

View File

@@ -1,6 +1,6 @@
import { getMsauth, setMsauth } from "./storage";
import { URL_MICROSOFT_AUTH } from "../config";
import { fetchHandle } from "./fetch";
import { fetchData } from "./fetch";
import { kissLog } from "./log";
const parseMSToken = (token) => {
@@ -35,7 +35,7 @@ const _msAuth = () => {
}
// 缓存没有或失效,查询接口
token = await fetchHandle({ input: URL_MICROSOFT_AUTH });
token = await fetchData(URL_MICROSOFT_AUTH);
exp = parseMSToken(token);
await setMsauth({ token, exp });
return [token, exp];

137
src/libs/cache.js Normal file
View File

@@ -0,0 +1,137 @@
import {
CACHE_NAME,
DEFAULT_CACHE_TIMEOUT,
MSG_GET_HTTPCACHE,
MSG_PUT_HTTPCACHE,
} from "../config";
import { kissLog } from "./log";
import { isExt } from "./client";
import { isBg } from "./browser";
import { sendBgMsg } from "./msg";
import { blobToBase64 } from "./utils";
/**
* 构造缓存 request
* @param {*} input
* @param {*} init
* @returns
*/
const newCacheReq = async (input, init) => {
let request = new Request(input, init);
if (request.method !== "GET") {
const body = await request.text();
const cacheUrl = new URL(request.url);
cacheUrl.pathname += body;
request = new Request(cacheUrl.toString(), { method: "GET" });
}
return request;
};
/**
* 查询 caches
* @param {*} input
* @param {*} init
* @returns
*/
export const getHttpCache = async (input, init) => {
try {
const req = await newCacheReq(input, init);
const cache = await caches.open(CACHE_NAME);
const res = await cache.match(req);
if (res) {
return await parseResponse(res);
}
} catch (err) {
kissLog(err, "get cache");
}
return null;
};
/**
* 插入 caches
* @param {*} input
* @param {*} init
* @param {*} data
*/
export const putHttpCache = async (input, init, data) => {
try {
const req = await newCacheReq(input, init);
const cache = await caches.open(CACHE_NAME);
const res = new Response(JSON.stringify(data), {
status: 200,
headers: {
"Content-Type": "application/json",
"Cache-Control": `max-age=${DEFAULT_CACHE_TIMEOUT}`,
},
});
// res.headers.set("Cache-Control", `max-age=${DEFAULT_CACHE_TIMEOUT}`);
await cache.put(req, res);
} catch (err) {
kissLog(err, "put cache");
}
};
/**
* 解析 response
* @param {*} res
* @returns
*/
export const parseResponse = async (res) => {
if (!res) {
throw new Error("Response object does not exist");
}
if (!res.ok) {
const msg = {
url: res.url,
status: res.status,
};
if (res.headers.get("Content-Type")?.includes("json")) {
msg.response = await res.json();
}
throw new Error(JSON.stringify(msg));
}
const contentType = res.headers.get("Content-Type");
if (contentType?.includes("json")) {
return res.json();
} else if (contentType?.includes("audio")) {
const blob = await res.blob();
return blobToBase64(blob);
}
return res.text();
};
/**
* getHttpCache 兼容性封装
* @param {*} input
* @param {*} init
* @returns
*/
export const getHttpCachePolyfill = (input, init) => {
// 插件
if (isExt && !isBg()) {
return sendBgMsg(MSG_GET_HTTPCACHE, { input, init });
}
// 油猴/网页/BackgroundPage
return getHttpCache(input, init);
};
/**
* putHttpCache 兼容性封装
* @param {*} input
* @param {*} init
* @param {*} data
* @returns
*/
export const putHttpCachePolyfill = (input, init, data) => {
// 插件
if (isExt && !isBg()) {
return sendBgMsg(MSG_PUT_HTTPCACHE, { input, init, data });
}
// 油猴/网页/BackgroundPage
return putHttpCache(input, init, data);
};

View File

@@ -1,38 +1,11 @@
import { isExt, isGm } from "./client";
import { sendBgMsg } from "./msg";
import { taskPool } from "./pool";
import { getSettingWithDefault } from "./storage";
import {
MSG_FETCH,
MSG_GET_HTTPCACHE,
CACHE_NAME,
DEFAULT_FETCH_INTERVAL,
DEFAULT_FETCH_LIMIT,
DEFAULT_HTTP_TIMEOUT,
} from "../config";
import { MSG_FETCH, DEFAULT_HTTP_TIMEOUT } from "../config";
import { isBg } from "./browser";
import { genTransReq } from "../apis/trans";
import { kissLog } from "./log";
import { blobToBase64 } from "./utils";
/**
* 构造缓存 request
* @param {*} input
* @param {*} init
* @returns
*/
const newCacheReq = async (input, init) => {
let request = new Request(input, init);
if (request.method !== "GET") {
const body = await request.text();
const cacheUrl = new URL(request.url);
cacheUrl.pathname += body;
request = new Request(cacheUrl.toString(), { method: "GET" });
}
return request;
};
import { getFetchPool } from "./pool";
import { getHttpCachePolyfill, parseResponse } from "./cache";
/**
* 油猴脚本的请求封装
@@ -73,56 +46,25 @@ export const fetchGM = async (
/**
* 发起请求
* @param {*} param0
* @param {*} input
* @param {*} init
* @param {*} opts
* @returns
*/
export const fetchPatcher = async (input, init, transOpts, apiSetting) => {
if (transOpts?.translator) {
[input, init] = await genTransReq(transOpts, apiSetting);
}
if (!input) {
throw new Error("url is empty");
}
let timeout = apiSetting?.httpTimeout || DEFAULT_HTTP_TIMEOUT;
if (!apiSetting) {
export const fetchPatcher = async (input, init = {}, opts) => {
let timeout = opts?.httpTimeout;
if (!timeout) {
try {
timeout = (await getSettingWithDefault()).httpTimeout;
} catch (err) {
//
kissLog(err, "getSettingWithDefault");
}
}
if (!timeout) {
timeout = DEFAULT_HTTP_TIMEOUT;
}
if (isGm) {
// let info;
// if (window.KISS_GM) {
// info = await window.KISS_GM.getInfo();
// } else {
// info = GM.info;
// }
// Tampermonkey --> .connects
// Violentmonkey --> .connect
// const connects = info?.script?.connects || info?.script?.connect || [];
// const url = new URL(input);
// const isSafe = connects.find((item) => url.hostname.endsWith(item));
// if (isSafe) {
// // todo: 自定义接口 init 可能包含了 signal
// Object.assign(init, { timeout });
// const { body, headers, status, statusText } = window.KISS_GM
// ? await window.KISS_GM.fetch(input, init)
// : await fetchGM(input, init);
// return new Response(body, {
// headers: new Headers(headers),
// status,
// statusText,
// });
// }
// todo: 自定义接口 init 可能包含了 signal
Object.assign(init, { timeout });
@@ -144,92 +86,13 @@ export const fetchPatcher = async (input, init, transOpts, apiSetting) => {
return fetch(input, init);
};
/**
* 解析 response
* @param {*} res
* @returns
*/
const parseResponse = async (res) => {
if (!res) {
return null;
}
const contentType = res.headers.get("Content-Type");
if (contentType?.includes("json")) {
return await res.json();
} else if (contentType?.includes("audio")) {
const blob = await res.blob();
return await blobToBase64(blob);
}
return await res.text();
};
/**
* 查询 caches
* @param {*} input
* @param {*} param1
* @returns
*/
export const getHttpCache = async (input, { method, headers, body }) => {
try {
const req = await newCacheReq(input, { method, headers, body });
const cache = await caches.open(CACHE_NAME);
const res = await cache.match(req);
return parseResponse(res);
} catch (err) {
kissLog(err, "get cache");
}
return null;
};
/**
* 插入 caches
* @param {*} input
* @param {*} param1
* @param {*} res
*/
export const putHttpCache = async (input, { method, headers, body }, res) => {
try {
const req = await newCacheReq(input, { method, headers, body });
const cache = await caches.open(CACHE_NAME);
await cache.put(req, res);
} catch (err) {
kissLog(err, "put cache");
}
};
/**
* 处理请求
* @param {*} param0
* @returns
*/
export const fetchHandle = async ({
input,
useCache,
transOpts,
apiSetting,
...init
}) => {
// 发送请求
const res = await fetchPatcher(input, init, transOpts, apiSetting);
if (!res) {
throw new Error("Unknow error");
} else if (!res.ok) {
const msg = {
url: res.url,
status: res.status,
};
if (res.headers.get("Content-Type")?.includes("json")) {
msg.response = await res.json();
}
throw new Error(JSON.stringify(msg));
}
// 插入缓存
if (useCache) {
await putHttpCache(input, init, res.clone());
}
export const fetchHandle = async ({ input, init, opts }) => {
const res = await fetchPatcher(input, init, opts);
return parseResponse(res);
};
@@ -238,7 +101,7 @@ export const fetchHandle = async ({
* @param {*} args
* @returns
*/
export const fetchPolyfill = (args) => {
const fetchPolyfill = (args) => {
// 插件
if (isExt && !isBg()) {
return sendBgMsg(MSG_FETCH, args);
@@ -248,72 +111,36 @@ export const fetchPolyfill = (args) => {
return fetchHandle(args);
};
/**
* getHttpCache 兼容性封装
* @param {*} input
* @param {*} init
* @returns
*/
export const getHttpCachePolyfill = (input, init) => {
// 插件
if (isExt && !isBg()) {
return sendBgMsg(MSG_GET_HTTPCACHE, { input, init });
}
// 油猴/网页/BackgroundPage
return getHttpCache(input, init);
};
/**
* 请求池实例
*/
export const fetchPool = taskPool(
fetchPolyfill,
null,
DEFAULT_FETCH_INTERVAL,
DEFAULT_FETCH_LIMIT
);
/**
* 数据请求
* @param {*} input
* @param {*} init
* @param {*} param1
* @returns
*/
export const fetchData = async (input, { useCache, usePool, ...args } = {}) => {
export const fetchData = async (
input,
init,
{ useCache, usePool, fetchInterval, fetchLimit, ...opts } = {}
) => {
if (!input?.trim()) {
throw new Error("URL is empty");
}
// 查询缓存
// 使用缓存数据
if (useCache) {
const cache = await getHttpCachePolyfill(input, args);
if (cache) {
return cache;
const resCache = await getHttpCachePolyfill(input, init);
if (resCache) {
return resCache;
}
}
// 通过任务池发送请求
if (usePool) {
return fetchPool.push({ input, useCache, ...args });
const fetchPool = getFetchPool(fetchInterval, fetchLimit);
return fetchPool.push(fetchPolyfill, { input, init, opts });
}
// 直接请求
return fetchPolyfill({ input, useCache, ...args });
};
/**
* 更新 fetch pool 参数
* @param {*} interval
* @param {*} limit
*/
export const updateFetchPool = (interval, limit) => {
fetchPool.update(interval, limit);
};
/**
* 清空任务池
*/
export const clearFetchPool = () => {
fetchPool.clear();
return fetchPolyfill({ input, init, opts });
};

View File

@@ -1,3 +1,4 @@
import { DEFAULT_FETCH_INTERVAL, DEFAULT_FETCH_LIMIT } from "../config";
import { kissLog } from "./log";
/**
@@ -8,13 +9,7 @@ import { kissLog } from "./log";
* @param {*} _limit
* @returns
*/
export const taskPool = (
fn,
preFn,
_interval = 100,
_limit = 100,
_retryInteral = 1000
) => {
const taskPool = (_interval = 100, _limit = 100, _retryInteral = 1000) => {
const pool = [];
const maxRetry = 2; // 最大重试次数
let maxCount = _limit; // 最大数量
@@ -31,10 +26,9 @@ export const taskPool = (
const item = pool.shift();
if (item) {
curCount++;
const { args, resolve, reject, retry } = item;
const { fn, args, resolve, reject, retry } = item;
try {
const preArgs = preFn ? await preFn(item.args) : {};
const res = await fn({ ...args, ...preArgs });
const res = await fn(args);
resolve(res);
} catch (err) {
kissLog(err, "task");
@@ -54,12 +48,12 @@ export const taskPool = (
};
return {
push: async (args) => {
push: async (fn, args) => {
if (!timer) {
run();
}
return new Promise((resolve, reject) => {
pool.push({ args, resolve, reject, retry: 0 });
pool.push({ fn, args, resolve, reject, retry: 0 });
});
},
update: (_interval = 100, _limit = 100) => {
@@ -78,3 +72,40 @@ export const taskPool = (
},
};
};
/**
* 请求池实例
*/
let fetchPool;
/**
* 获取请求池实例
*/
export const getFetchPool = (interval, limit) => {
if (!fetchPool) {
fetchPool = taskPool(
interval ?? DEFAULT_FETCH_INTERVAL,
limit ?? DEFAULT_FETCH_LIMIT
);
} else if (interval && limit) {
updateFetchPool(interval, limit);
}
return fetchPool;
};
/**
* 更新请求池参数
* @param {*} interval
* @param {*} limit
*/
export const updateFetchPool = (interval, limit) => {
fetchPool && fetchPool.update(interval, limit);
};
/**
* 清空请求池
*/
export const clearFetchPool = () => {
fetchPool && fetchPool.clear();
};

View File

@@ -13,11 +13,9 @@ import {
OPT_TIMING_PAGEOPEN,
OPT_TIMING_MOUSEOVER,
DEFAULT_TRANS_APIS,
DEFAULT_FETCH_LIMIT,
DEFAULT_FETCH_INTERVAL,
} from "../config";
import Content from "../views/Content";
import { updateFetchPool, clearFetchPool } from "./fetch";
import { clearFetchPool } from "./pool";
import { debounce, genEventName, getHtmlText } from "./utils";
import { runFixer } from "./webfix";
import { apiTranslate } from "../apis";
@@ -107,18 +105,6 @@ export class Translator {
};
};
_updatePool(translator) {
if (!translator) {
return;
}
const {
fetchInterval = DEFAULT_FETCH_INTERVAL,
fetchLimit = DEFAULT_FETCH_LIMIT,
} = this._setting.transApis[translator] || {};
updateFetchPool(fetchInterval, fetchLimit);
}
constructor(rule, setting) {
this._overrideAttachShadow();
@@ -131,8 +117,6 @@ export class Translator {
.map((item) => item.split(",").map((item) => item.trim()))
.filter(([term]) => Boolean(term));
this._updatePool(rule.translator);
if (rule.transOpen === "true") {
this._register();
}
@@ -169,7 +153,6 @@ export class Translator {
updateRule = (obj) => {
this.rule = { ...this.rule, ...obj };
this._updatePool(obj.translator);
};
toggle = () => {