diff --git a/.changeset/wise-eagles-tease.md b/.changeset/wise-eagles-tease.md new file mode 100644 index 000000000..abfea4e03 --- /dev/null +++ b/.changeset/wise-eagles-tease.md @@ -0,0 +1,5 @@ +--- +"@qiankunjs/sandbox": patch +--- + +fix(sandbox): compatible with dynamically appending scripts to detached containers diff --git a/packages/sandbox/src/patchers/dynamicAppend/common.ts b/packages/sandbox/src/patchers/dynamicAppend/common.ts index 569474a24..243717363 100644 --- a/packages/sandbox/src/patchers/dynamicAppend/common.ts +++ b/packages/sandbox/src/patchers/dynamicAppend/common.ts @@ -4,7 +4,7 @@ import type { AssetsTranspilerOpts, ScriptTranspilerOpts } from '@qiankunjs/shar * @author Kuitos * @since 2019-10-21 */ -import { prepareDeferredQueue } from '@qiankunjs/shared'; +import { prepareDeferredQueue, warn } from '@qiankunjs/shared'; import { qiankunHeadTagName } from '../../consts'; import type { SandboxConfig } from './types'; @@ -201,7 +201,26 @@ export function getOverwrittenAppendChildOrInsertBefore( const transpiledScriptElement = nodeTransformer(scriptElement, transformerOpts); - const result = appendChild.call(this, transpiledScriptElement, refChild) as T; + /* + The target container of script element might be removed from current document. + For example, the main application clears the DOM first during route switching, and then trigger unmount, + at this time, the micro app may still be processing the logic of the route switching, and try to add nodes to the detached container, + in this scenario, we have to append the script to global document head to trigger script evaluation + */ + const scriptTargetDetached = !document.contains(this); + if (scriptTargetDetached) { + warn( + `Trying to append script element ${ + transpiledScriptElement.src || transpiledScriptElement.dataset.src + } to a detached container which may cause unexpected behaviors!`, + ); + } + /* + FIXME we have to set the target container to global document head to trigger script evaluation while the real container was detached, + as dynamic append script element to detached element will not trigger script evaluation automatically + */ + const targetContainerDOM = scriptTargetDetached ? document.head : this; + const result = appendChild.call(targetContainerDOM, transpiledScriptElement, refChild) as T; // the script have no src attribute after transpile, indicating that the script needs to wait for the src to be filled if (externalSyncMode && !transpiledScriptElement.hasAttribute('src')) {