From 90a9612d08f1c60d2c75d9352ee1a8ce2ada3ab4 Mon Sep 17 00:00:00 2001 From: bailicangdu <1264889788@qq.com> Date: Wed, 20 Oct 2021 15:59:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BA=86module=20scr?= =?UTF-8?q?ipt=E7=9A=84=E6=89=A7=E8=A1=8C=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/children/react16/src/index.js | 13 ++++- examples/main-react16/src/global.jsx | 15 +++++- examples/main-react16/src/pages/vite/vite.js | 10 +++- src/libs/utils.ts | 4 +- src/source/scripts.ts | 51 ++++++++++++-------- 5 files changed, 66 insertions(+), 27 deletions(-) diff --git a/examples/children/react16/src/index.js b/examples/children/react16/src/index.js index dd93cc38d..8f8f42d2a 100644 --- a/examples/children/react16/src/index.js +++ b/examples/children/react16/src/index.js @@ -100,5 +100,14 @@ if (window.__MICRO_APP_ENVIRONMENT__) { // const dynamicScript1 = document.createElement('script') // dynamicScript1.setAttribute('type', 'module') -// dynamicScript1.textContent = 'console.warn("inline module")' -// document.head.appendChild(dynamicScript1) +// // dynamicScript1.textContent = 'console.warn("inline module")' +// dynamicScript1.setAttribute('src', 'http://127.0.0.1:8080/test.js') +// dynamicScript1.onload = () => { +// console.log('动态module加载完成了') +// } +// document.body.appendChild(dynamicScript1) + +console.log('__micro_app_environment__', window.__micro_app_environment__) +console.log('__micro_app_name__', window.__micro_app_name__) +console.log('__full_public_path__', window.__full_public_path__) +console.log('baseurl', window.baseurl) diff --git a/examples/main-react16/src/global.jsx b/examples/main-react16/src/global.jsx index c2339477d..d70bf79a8 100644 --- a/examples/main-react16/src/global.jsx +++ b/examples/main-react16/src/global.jsx @@ -37,7 +37,20 @@ microApp.start({ // console.log('vue2插件', url, options) return code } - }], + }, + { + loader (code) { + code = ` + window.__micro_app_environment__ = window.__MICRO_APP_ENVIRONMENT__ + window.__micro_app_name__ = window.__MICRO_APP_NAME__ + window.__full_public_path__ = window.__MICRO_APP_PUBLIC_PATH__ + window.baseurl = window.__MICRO_APP_BASE_ROUTE__ + ;${code} + ` + return code + } + } + ], modules: { react16: [{ scopeProperties: ['3', '4'], diff --git a/examples/main-react16/src/pages/vite/vite.js b/examples/main-react16/src/pages/vite/vite.js index c25e9d0ae..6d836bee1 100644 --- a/examples/main-react16/src/pages/vite/vite.js +++ b/examples/main-react16/src/pages/vite/vite.js @@ -12,6 +12,12 @@ const antIcon = function vite () { const [data, changeData] = useState({from: '来自基座的初始化数据'}) const [showLoading, hideLoading] = useState(true) + + function handleMounted () { + hideLoading(false) + console.log('生命周期: vite 渲染完成了') + } + return (
{/*
@@ -32,9 +38,9 @@ function vite () { // url={`http://127.0.0.1:8080/micro-app/vite/`} data={data} // onBeforemount={() => hideLoading(false)} - onMounted={() => hideLoading(false)} + onMounted={handleMounted} // destory - inline + // inline disableSandbox > diff --git a/src/libs/utils.ts b/src/libs/utils.ts index 1ed5cbefb..b44127ccc 100644 --- a/src/libs/utils.ts +++ b/src/libs/utils.ts @@ -170,8 +170,8 @@ export function isSupportModuleScript (): boolean { } // Create a random symbol string -export function createNonceStr (): string { - return Math.random().toString(36).substr(2, 15) +export function createNonceSrc (): string { + return 'inline-' + Math.random().toString(36).substr(2, 15) } // Array deduplication diff --git a/src/source/scripts.ts b/src/source/scripts.ts index 7f3da400b..f519b6ef7 100644 --- a/src/source/scripts.ts +++ b/src/source/scripts.ts @@ -9,7 +9,7 @@ import { fetchSource } from './fetch' import { CompletionPath, promiseStream, - createNonceStr, + createNonceSrc, pureCreateElement, defer, logError, @@ -21,6 +21,8 @@ import { import microApp from '../micro_app' import globalEnv from '../libs/global_env' +type moduleCallBack = Func & { moduleCount?: number } + // Global scripts, reuse across apps export const globalScripts = new Map() @@ -69,7 +71,7 @@ export function extractScriptElement ( return { url: src, info } } } else if (script.textContent) { // inline script - const nonceStr: string = createNonceStr() + const nonceStr: string = createNonceSrc() const info = { code: script.textContent, isExternal: false, @@ -158,12 +160,12 @@ export function fetchScriptSuccess ( * Execute js in the mount lifecycle * @param scriptList script list * @param app app - * @param callback callback for umd mode + * @param initedHook callback for umd mode */ export function execScripts ( scriptList: Map, app: AppInterface, - callback: Func, + initedHook: moduleCallBack, ): void { const scriptListEntries: Array<[string, sourceScriptInfo]> = Array.from(scriptList.entries()) const deferScriptPromise: Array|string> = [] @@ -177,6 +179,8 @@ export function execScripts ( deferScriptPromise.push(info.code) } deferScriptInfo.push([url, info]) + + if (info.module) initedHook.moduleCount = initedHook.moduleCount ? ++initedHook.moduleCount : 1 } else { runScript(url, info.code, app, info.module, false) } @@ -187,15 +191,15 @@ export function execScripts ( Promise.all(deferScriptPromise).then((res: string[]) => { res.forEach((code, index) => { const [url, info] = deferScriptInfo[index] - runScript(url, info.code = info.code || code, app, info.module, false, callback) + runScript(url, info.code = info.code || code, app, info.module, false, initedHook) }) - callback(true) + initedHook(typeof initedHook.moduleCount === 'undefined') }).catch((err) => { logError(err) - callback(true) + initedHook(true) }) } else { - callback(true) + initedHook(true) } } @@ -206,7 +210,7 @@ export function execScripts ( * @param app app * @param module type='module' of script * @param isDynamic dynamically created script - * @param callback callback from execScripts for first exec + * @param callback callback of module script */ export function runScript ( url: string, @@ -214,7 +218,7 @@ export function runScript ( app: AppInterface, module: boolean, isDynamic: boolean, - callback?: Func, + callback?: moduleCallBack, ): any { try { code = bindScope(url, code, app, module) @@ -245,18 +249,20 @@ export function runDynamicRemoteScript ( app: AppInterface, originScript: HTMLScriptElement, ): HTMLScriptElement | Comment { + const dispatchScriptOnLoadEvent = () => dispatchOnLoadEvent(originScript) + if (app.source.scripts.has(url)) { const existInfo: sourceScriptInfo = app.source.scripts.get(url)! - defer(() => dispatchOnLoadEvent(originScript)) - return runScript(url, existInfo.code, app, info.module, true) + if (!info.module) defer(dispatchScriptOnLoadEvent) + return runScript(url, existInfo.code, app, info.module, true, dispatchScriptOnLoadEvent) } if (globalScripts.has(url)) { const code = globalScripts.get(url)! info.code = code app.source.scripts.set(url, info) - defer(() => dispatchOnLoadEvent(originScript)) - return runScript(url, code, app, info.module, true) + if (!info.module) defer(dispatchScriptOnLoadEvent) + return runScript(url, code, app, info.module, true, dispatchScriptOnLoadEvent) } let replaceElement: Comment | HTMLScriptElement @@ -273,14 +279,14 @@ export function runDynamicRemoteScript ( try { code = bindScope(url, code, app, info.module) if (app.inline || info.module) { - setInlinScriptContent(url, code, info.module, replaceElement as HTMLScriptElement) + setInlinScriptContent(url, code, info.module, replaceElement as HTMLScriptElement, dispatchScriptOnLoadEvent) } else { Function(code)() } } catch (e) { console.error('[micro-app from runDynamicScript]', e, url) } - dispatchOnLoadEvent(originScript) + if (!info.module) dispatchOnLoadEvent(originScript) }).catch((err) => { logError(err) dispatchOnErrorEvent(originScript) @@ -295,22 +301,27 @@ export function runDynamicRemoteScript ( * @param code js code * @param module type='module' of script * @param scriptElement target script element - * @param callback callback from execScripts for first exec + * @param callback callback of module script */ function setInlinScriptContent ( url: string, code: string, module: boolean, scriptElement: HTMLScriptElement, - callback?: Func, + callback?: moduleCallBack, ): void { if (module) { // module script is async, transform it to a blob for subsequent operations const blob = new Blob([code], { type: 'text/javascript;charset=utf-8' }) scriptElement.src = URL.createObjectURL(blob) scriptElement.setAttribute('type', 'module') - scriptElement.setAttribute('originSrc', url) - callback && (scriptElement.onload = callback) + if (!url.startsWith('inline-')) { + scriptElement.setAttribute('originSrc', url) + } + if (callback) { + callback.moduleCount && callback.moduleCount-- + scriptElement.onload = callback.bind(scriptElement, callback.moduleCount === 0) + } } else { scriptElement.textContent = code }