Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/api/preloadApp.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ type preOptions {
deactivated?: lifecycle;
/** 子应用资源加载失败后调用 */
loadError?: loadErrorHandler
/** 超时取消开关 */
cancelRequest?: boolean;
/** 超时等待时间 */
timeout?: number;
};

```
Expand Down
4 changes: 4 additions & 0 deletions docs/api/setupApp.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ type baseOptions = {
activated?: lifecycle;
deactivated?: lifecycle;
loadError?: loadErrorHandler;
/** 超时取消开关 */
cancelRequest?: boolean;
/** 超时等待时间 */
timeout?: number;
};

type preOptions = baseOptions & {
Expand Down
6 changes: 5 additions & 1 deletion docs/api/startApp.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ type startOption {
activated?: lifecycle;
deactivated?: lifecycle;
/** 子应用资源加载失败后调用 */
loadError?: loadErrorHandler
loadError?: loadErrorHandler;
/** 超时取消开关 */
cancelRequest?: boolean;
/** 超时等待时间 */
timeout?: number;
};
```

Expand Down
62 changes: 38 additions & 24 deletions packages/wujie-core/src/effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,18 @@ function rewriteAppendOrInsertChild(opts: {
const { rawDOMAppendOrInsertBefore, wujieId } = opts;
const sandbox = getWujieById(wujieId);

const { styleSheetElements, replace, fetch, plugins, iframe, lifecycles, proxyLocation, fiber } = sandbox;
const {
styleSheetElements,
replace,
fetch,
plugins,
iframe,
lifecycles,
proxyLocation,
fiber,
cancelRequest,
timeout,
} = sandbox;

if (!isHijackingTag(element.tagName) || !wujieId) {
const res = rawDOMAppendOrInsertBefore.call(this, element, refChild) as T;
Expand Down Expand Up @@ -288,29 +299,32 @@ function rewriteAppendOrInsertChild(opts: {
ignore: isMatchUrl(src, getEffectLoaders("jsIgnores", plugins)),
attrs: parseTagAttributes(element.outerHTML),
} as ScriptObject;
getExternalScripts([scriptOptions], fetch, lifecycles.loadError, fiber).forEach((scriptResult) => {
dynamicScriptExecStack = dynamicScriptExecStack.then(() =>
scriptResult.contentPromise.then(
(content) => {
if (sandbox.execQueue === null) return warn(WUJIE_TIPS_REPEAT_RENDER);
const execQueueLength = sandbox.execQueue?.length;
sandbox.execQueue.push(() =>
fiber
? sandbox.requestIdleCallback(() => {
execScript({ ...scriptResult, content });
})
: execScript({ ...scriptResult, content })
);
// 同步脚本如果都执行完了,需要手动触发执行
if (!execQueueLength) sandbox.execQueue.shift()();
},
() => {
manualInvokeElementEvent(element, "error");
element = null;
}
)
);
});
getExternalScripts([scriptOptions], fetch, lifecycles.loadError, fiber, cancelRequest, timeout).forEach(
(scriptResult) => {
dynamicScriptExecStack = dynamicScriptExecStack.then(() =>
// fetch请求超时会影响后续其它任务通过then链式调用的方式推入execQueue当中
scriptResult.contentPromise.then(
(content) => {
if (sandbox.execQueue === null) return warn(WUJIE_TIPS_REPEAT_RENDER);
const execQueueLength = sandbox.execQueue?.length;
sandbox.execQueue.push(() =>
fiber
? sandbox.requestIdleCallback(() => {
execScript({ ...scriptResult, content });
})
: execScript({ ...scriptResult, content })
);
// 同步脚本如果都执行完了,需要手动触发执行
if (!execQueueLength) sandbox.execQueue.shift()();
},
() => {
manualInvokeElementEvent(element, "error");
element = null;
}
)
);
}
);
} else {
const execQueueLength = sandbox.execQueue?.length;
sandbox.execQueue.push(() =>
Expand Down
48 changes: 36 additions & 12 deletions packages/wujie-core/src/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ import processTpl, {
ScriptBaseObject,
StyleObject,
} from "./template";
import { defaultGetPublicPath, getInlineCode, requestIdleCallback, error, compose, getCurUrl } from "./utils";
import {
defaultGetPublicPath,
getInlineCode,
requestIdleCallback,
error,
compose,
getCurUrl,
fetchWithTimeOut,
} from "./utils";
import {
WUJIE_TIPS_NO_FETCH,
WUJIE_TIPS_SCRIPT_ERROR_REQUESTED,
Expand Down Expand Up @@ -34,6 +42,8 @@ type ImportEntryOpts = {
fiber?: boolean;
plugins?: Array<plugin>;
loadError?: loadErrorHandler;
cancelRequest?: boolean;
timeout?: number;
};

const styleCache = {};
Expand Down Expand Up @@ -105,10 +115,12 @@ const fetchAssets = (
cache: Object,
fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response>,
cssFlag?: boolean,
loadError?: loadErrorHandler
loadError?: loadErrorHandler,
cancelRequest?: boolean,
timeout?: number
) =>
cache[src] ||
(cache[src] = fetch(src)
(cache[src] = fetchWithTimeOut(src, fetch, cancelRequest, timeout)
.then((response) => {
// usually browser treats 4xx and 5xx response of script loading as an error and will fire a script error event
// https://stackoverflow.com/questions/5625420/what-http-headers-responses-trigger-the-onerror-handler-on-a-script-tag/5625603
Expand Down Expand Up @@ -143,7 +155,9 @@ const fetchAssets = (
export function getExternalStyleSheets(
styles: StyleObject[],
fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response> = defaultFetch,
loadError: loadErrorHandler
loadError: loadErrorHandler,
cancelRequest?: boolean,
timeout?: number
): StyleResultList {
return styles.map(({ src, content, ignore }) => {
// 内联
Expand All @@ -157,7 +171,9 @@ export function getExternalStyleSheets(
return {
src,
ignore,
contentPromise: ignore ? Promise.resolve("") : fetchAssets(src, styleCache, fetch, true, loadError),
contentPromise: ignore
? Promise.resolve("")
: fetchAssets(src, styleCache, fetch, true, loadError, cancelRequest, timeout),
};
}
});
Expand All @@ -168,7 +184,9 @@ export function getExternalScripts(
scripts: ScriptObject[],
fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response> = defaultFetch,
loadError: loadErrorHandler,
fiber: boolean
fiber: boolean,
cancelRequest?: boolean,
timeout?: number
): ScriptResultList {
// module should be requested in iframe
return scripts.map((script) => {
Expand All @@ -178,8 +196,10 @@ export function getExternalScripts(
if ((async || defer) && src && !module) {
contentPromise = new Promise((resolve, reject) =>
fiber
? requestIdleCallback(() => fetchAssets(src, scriptCache, fetch, false, loadError).then(resolve, reject))
: fetchAssets(src, scriptCache, fetch, false, loadError).then(resolve, reject)
? requestIdleCallback(() =>
fetchAssets(src, scriptCache, fetch, false, loadError, cancelRequest, timeout).then(resolve, reject)
)
: fetchAssets(src, scriptCache, fetch, false, loadError, cancelRequest, timeout).then(resolve, reject)
);
// module || ignore
} else if ((module && src) || ignore) {
Expand All @@ -189,7 +209,7 @@ export function getExternalScripts(
contentPromise = Promise.resolve(script.content);
// outline
} else {
contentPromise = fetchAssets(src, scriptCache, fetch, false, loadError);
contentPromise = fetchAssets(src, scriptCache, fetch, false, loadError, cancelRequest, timeout);
}
// refer https://html.spec.whatwg.org/multipage/scripting.html#attr-script-defer
if (module && !async) script.defer = true;
Expand All @@ -205,7 +225,7 @@ export default function importHTML(params: {
const { url, opts, html } = params;
const fetch = opts.fetch ?? defaultFetch;
const fiber = opts.fiber ?? true;
const { plugins, loadError } = opts;
const { plugins, loadError, cancelRequest, timeout } = opts;
const htmlLoader = plugins ? compose(plugins.map((plugin) => plugin.htmlLoader)) : defaultGetTemplate;
const jsExcludes = getEffectLoaders("jsExcludes", plugins);
const cssExcludes = getEffectLoaders("cssExcludes", plugins);
Expand Down Expand Up @@ -243,15 +263,19 @@ export default function importHTML(params: {
.map((script) => ({ ...script, ignore: script.src && isMatchUrl(script.src, jsIgnores) })),
fetch,
loadError,
fiber
fiber,
cancelRequest,
timeout
),
getExternalStyleSheets: () =>
getExternalStyleSheets(
styles
.filter((style) => !style.src || !isMatchUrl(style.src, cssExcludes))
.map((style) => ({ ...style, ignore: style.src && isMatchUrl(style.src, cssIgnores) })),
fetch,
loadError
loadError,
cancelRequest,
timeout
),
};
});
Expand Down
14 changes: 14 additions & 0 deletions packages/wujie-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ type baseOptions = {
iframeAddEventListeners?: Array<string>;
/** 子应用iframe on事件 */
iframeOnEvents?: Array<string>;
/** 是否取消请求 */
cancelRequest?: boolean;
/** 请求超时时间 */
timeout?: number;
/** 子应用生命周期 */
beforeLoad?: lifecycle;
beforeMount?: lifecycle;
Expand Down Expand Up @@ -212,6 +216,8 @@ export async function startApp(startOptions: startOptions): Promise<Function | v
lifecycles,
iframeAddEventListeners,
iframeOnEvents,
cancelRequest,
timeout,
} = options;
// 已经初始化过的应用,快速渲染
if (sandbox) {
Expand All @@ -235,6 +241,8 @@ export async function startApp(startOptions: startOptions): Promise<Function | v
plugins: sandbox.plugins,
loadError: sandbox.lifecycles.loadError,
fiber,
cancelRequest,
timeout,
},
});
await sandbox.start(getExternalScripts);
Expand Down Expand Up @@ -285,6 +293,8 @@ export async function startApp(startOptions: startOptions): Promise<Function | v
plugins: newSandbox.plugins,
loadError: newSandbox.lifecycles.loadError,
fiber,
cancelRequest,
timeout,
},
});

Expand Down Expand Up @@ -325,6 +335,8 @@ export function preloadApp(preOptions: preOptions): void {
lifecycles,
iframeAddEventListeners,
iframeOnEvents,
cancelRequest,
timeout,
} = options;

const sandbox = new WuJie({
Expand All @@ -350,6 +362,8 @@ export function preloadApp(preOptions: preOptions): void {
plugins: sandbox.plugins,
loadError: sandbox.lifecycles.loadError,
fiber,
cancelRequest,
timeout,
},
});
const processedHtml = await processCssLoader(sandbox, template, getExternalStyleSheets);
Expand Down
13 changes: 12 additions & 1 deletion packages/wujie-core/src/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ export default class Wujie {
mainHostPath: string;
};

/** 是否取消超时请求 */
public cancelRequest: boolean;

/** 超时时间 */
public timeout: number;

/** 激活子应用
* 1、同步路由
* 2、动态修改iframe的fetch
Expand Down Expand Up @@ -423,6 +429,8 @@ export default class Wujie {
this.prefix = null;
this.iframeAddEventListeners = null;
this.iframeOnEvents = null;
this.cancelRequest = null;
this.timeout = null;
// 清除 dom
if (this.el) {
clearChild(this.el);
Expand Down Expand Up @@ -491,6 +499,8 @@ export default class Wujie {
lifecycles: lifecycles;
iframeAddEventListeners?: Array<string>;
iframeOnEvents?: Array<string>;
cancelRequest?: boolean;
timeout?: number;
}) {
// 传递inject给嵌套子应用
if (window.__POWERED_BY_WUJIE__) this.inject = window.__WUJIE.inject;
Expand All @@ -515,7 +525,8 @@ export default class Wujie {
this.plugins = getPlugins(plugins);
this.iframeAddEventListeners = options.iframeAddEventListeners;
this.iframeOnEvents = options.iframeOnEvents;

this.cancelRequest = options.cancelRequest;
this.timeout = options.timeout;
// 创建目标地址的解析
const { urlElement, appHostPath, appRoutePath } = appRouteParse(url);
const { mainHostPath } = this.inject;
Expand Down
6 changes: 3 additions & 3 deletions packages/wujie-core/src/shadow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,15 @@ export function initRenderIframeAndContainer(
*/
async function processCssLoaderForTemplate(sandbox: Wujie, html: HTMLHtmlElement): Promise<HTMLHtmlElement> {
const document = sandbox.iframe.contentDocument;
const { plugins, replace, proxyLocation } = sandbox;
const { plugins, replace, proxyLocation, cancelRequest, timeout } = sandbox;
const cssLoader = getCssLoader({ plugins, replace });
const cssBeforeLoaders = getPresetLoaders("cssBeforeLoaders", plugins);
const cssAfterLoaders = getPresetLoaders("cssAfterLoaders", plugins);
const curUrl = getCurUrl(proxyLocation);

return await Promise.all([
Promise.all(
getExternalStyleSheets(cssBeforeLoaders, sandbox.fetch, sandbox.lifecycles.loadError).map(
getExternalStyleSheets(cssBeforeLoaders, sandbox.fetch, sandbox.lifecycles.loadError, cancelRequest, timeout).map(
({ src, contentPromise }) => contentPromise.then((content) => ({ src, content }))
)
).then((contentList) => {
Expand All @@ -131,7 +131,7 @@ async function processCssLoaderForTemplate(sandbox: Wujie, html: HTMLHtmlElement
});
}),
Promise.all(
getExternalStyleSheets(cssAfterLoaders, sandbox.fetch, sandbox.lifecycles.loadError).map(
getExternalStyleSheets(cssAfterLoaders, sandbox.fetch, sandbox.lifecycles.loadError, cancelRequest, timeout).map(
({ src, contentPromise }) => contentPromise.then((content) => ({ src, content }))
)
).then((contentList) => {
Expand Down
19 changes: 19 additions & 0 deletions packages/wujie-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ export function mergeOptions(options: cacheOptions, cacheOptions: cacheOptions)
deactivated: options.deactivated || cacheOptions?.deactivated,
loadError: options.loadError || cacheOptions?.loadError,
},
cancelRequest: options.cancelRequest || cacheOptions?.cancelRequest,
timeout: options.timeout || cacheOptions?.timeout,
};
}

Expand All @@ -376,3 +378,20 @@ export function stopMainAppRun() {
warn(WUJIE_TIPS_STOP_APP_DETAIL);
throw new Error(WUJIE_TIPS_STOP_APP);
}

export function fetchWithTimeOut(
url: string,
fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response>,
cancelRequest?: boolean,
timeout: number = 5000
) {
if (cancelRequest) {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
return fetch(url, {
signal: controller.signal,
}).finally(() => clearTimeout(id));
} else {
return fetch(url);
}
}
2 changes: 2 additions & 0 deletions packages/wujie-react/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,6 @@ const propTypes = {
style: PropTypes.object,
iframeAddEventListeners: PropTypes.arrayOf(PropTypes.string),
iframeOnEvents: PropTypes.arrayOf(PropTypes.string),
cancelRequest: PropTypes.bool,
timeout: PropTypes.number,
};
Loading