Skip to content

Commit 2ead742

Browse files
committed
Merge branch 'dev'
2 parents d8957b2 + 76e192f commit 2ead742

File tree

7 files changed

+63
-56
lines changed

7 files changed

+63
-56
lines changed

docs/zh-cn/native-mode.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
它拥有更好的用户体验,但也更容易导致主应用和子应用的路由冲突,所以需要更加复杂的路由配置,要对主应用和子应用路由进行一些改造。
44

5-
实际上主应用和子应用的路由即同时基于浏览器地址进行渲染,又相互独立,我们通过路由配置让两个独立的路由系统实现共存,具体原理参考[关于native模式的原理解析](/zh-cn/native-mode?id=关于native模式的原理解析)
5+
实际上主应用和子应用的路由即同时基于浏览器地址进行渲染,又相互独立,我们通过路由配置让两个独立的路由系统实现共存,具体原理参考[关于native模式的原理解析](/zh-cn/native-mode?id=principle)
66

77
### 基础路径 :id=base
88
基础路径即vue-router的[base](https://router.vuejs.org/zh/api/interfaces/RouterHistory.html#Properties-base)、react-router的[basename](https://reactrouter.com/en/main/router-components/browser-router#basename),通常与应用托管在服务器的文件夹地址一致,但在微前端下子应用基础路径的设置有所不同,需要根据主应用的地址动态设置。

src/libs/utils.ts

+3
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ export function isVideoElement(target: unknown): target is HTMLVideoElement {
144144
export function isLinkElement(target: unknown): target is HTMLLinkElement {
145145
return toTypeString(target) === '[object HTMLLinkElement]'
146146
}
147+
export function isBodyElement(target: unknown): target is HTMLBodyElement {
148+
return toTypeString(target) === '[object HTMLBodyElement]'
149+
}
147150

148151
export function isStyleElement(target: unknown): target is HTMLStyleElement {
149152
return toTypeString(target) === '[object HTMLStyleElement]'

src/sandbox/iframe/document.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ function patchDocumentProperty (
306306
get: () => {
307307
throttleDeferForIframeAppName(appName)
308308
if (tagName === 'body') {
309-
return sandbox.options.container?.querySelector('micro-app-body')
309+
return sandbox.options.container?.querySelector('micro-app-body') || rawDocument[tagName]
310310
}
311311
return rawDocument[tagName]
312312
},

src/sandbox/scoped_css.ts

+6-9
Original file line numberDiff line numberDiff line change
@@ -510,22 +510,19 @@ export default function scopedCSS (
510510

511511
if (!parser) parser = new CSSParser()
512512

513+
const escapeRegExp = (regStr: string) => regStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
514+
513515
if (styleElement.textContent) {
514-
commonAction(
515-
styleElement,
516-
app.name,
517-
prefix,
518-
app.url,
519-
linkPath,
520-
)
516+
commonAction(styleElement, app.name, prefix, app.url, linkPath)
517+
521518
const observer = new MutationObserver(() => {
522-
const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
519+
const escapedPrefix = escapeRegExp(prefix)
523520
const isPrefixed = styleElement.textContent && new RegExp(escapedPrefix).test(styleElement.textContent)
524521
observer.disconnect()
525522
if (!isPrefixed) {
526523
styleElement.__MICRO_APP_HAS_SCOPED__ = false
524+
scopedCSS(styleElement, app, linkPath)
527525
}
528-
scopedCSS(styleElement, app, linkPath)
529526
})
530527
observer.observe(styleElement, { childList: true, characterData: true })
531528
} else {

src/source/index.ts

+42-43
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ import {
44
CompletionPath,
55
injectFiberTask,
66
serialExecFiberTasks,
7-
isLinkElement,
8-
isScriptElement,
9-
isStyleElement,
10-
isImageElement,
7+
isBodyElement,
118
} from '../libs/utils'
129
import {
1310
extractLinkFromHtml,
@@ -24,62 +21,64 @@ import globalEnv from '../libs/global_env'
2421

2522
/**
2623
* Recursively process each child element
27-
* @param parent parent element
24+
* @param body body element
2825
* @param app app
2926
* @param microAppHead micro-app-head element
3027
*/
31-
function flatChildren (
32-
parent: HTMLElement,
28+
function flatBodyChildren(
29+
body: HTMLElement,
3330
app: AppInterface,
34-
microAppHead: Element,
3531
fiberStyleTasks: fiberTasks,
3632
): void {
37-
const children = Array.from(parent.children)
33+
if (!body || !isBodyElement(body)) {
34+
return
35+
}
36+
const links = Array.from(body.getElementsByTagName('link'))
37+
38+
links.map((dom) => {
39+
if (dom.hasAttribute('exclude') || checkExcludeUrl(dom.getAttribute('href'), app.name)) {
40+
dom.parentElement!.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom)
41+
} else if (!(dom.hasAttribute('ignore') || checkIgnoreUrl(dom.getAttribute('href'), app.name))) {
42+
extractLinkFromHtml(dom, dom.parentElement, app)
43+
} else if (dom.hasAttribute('href')) {
44+
globalEnv.rawSetAttribute.call(dom, 'href', CompletionPath(dom.getAttribute('href')!, app.url))
45+
}
46+
return dom
47+
})
48+
49+
const styles = Array.from(body.getElementsByTagName('style'))
3850

39-
children.length && children.forEach((child) => {
40-
flatChildren(child as HTMLElement, app, microAppHead, fiberStyleTasks)
51+
styles.map((dom) => {
52+
if (dom.hasAttribute('exclude')) {
53+
dom.parentElement!.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom)
54+
} else if (app.scopecss && !dom.hasAttribute('ignore')) {
55+
injectFiberTask(fiberStyleTasks, () => scopedCSS(dom, app))
56+
}
57+
return dom
4158
})
4259

43-
for (const dom of children) {
44-
if (isLinkElement(dom)) {
45-
if (dom.hasAttribute('exclude') || checkExcludeUrl(dom.getAttribute('href'), app.name)) {
46-
parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom)
47-
} else if (!(dom.hasAttribute('ignore') || checkIgnoreUrl(dom.getAttribute('href'), app.name))) {
48-
extractLinkFromHtml(dom, parent, app)
49-
} else if (dom.hasAttribute('href')) {
50-
globalEnv.rawSetAttribute.call(dom, 'href', CompletionPath(dom.getAttribute('href')!, app.url))
51-
}
52-
} else if (isStyleElement(dom)) {
53-
if (dom.hasAttribute('exclude')) {
54-
parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom)
55-
} else if (app.scopecss && !dom.hasAttribute('ignore')) {
56-
injectFiberTask(fiberStyleTasks, () => scopedCSS(dom, app))
57-
}
58-
} else if (isScriptElement(dom)) {
59-
extractScriptElement(dom, parent, app)
60-
} else if (isImageElement(dom) && dom.hasAttribute('src')) {
60+
const scripts = Array.from(body.getElementsByTagName('script'))
61+
62+
scripts.map((dom) => {
63+
extractScriptElement(dom, dom.parentElement, app)
64+
return dom
65+
})
66+
const images = Array.from(body.getElementsByTagName('img'))
67+
68+
images.map((dom) => {
69+
if (dom.hasAttribute('src')) {
6170
globalEnv.rawSetAttribute.call(dom, 'src', CompletionPath(dom.getAttribute('src')!, app.url))
6271
}
63-
/**
64-
* Don't remove meta and title, they have some special scenes
65-
* e.g.
66-
* document.querySelector('meta[name="viewport"]') // for flexible
67-
* document.querySelector('meta[name="baseurl"]').baseurl // for api request
68-
*
69-
* Title point to main app title, child app title used to be compatible with some special scenes
70-
*/
71-
// else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
72-
// parent.removeChild(dom)
73-
// }
74-
}
72+
return dom
73+
})
7574
}
7675

7776
/**
7877
* Extract link and script, bind style scope
7978
* @param htmlStr html string
8079
* @param app app
8180
*/
82-
export function extractSourceDom (htmlStr: string, app: AppInterface): void {
81+
export function extractSourceDom(htmlStr: string, app: AppInterface): void {
8382
const wrapElement = app.parseHtmlString(htmlStr)
8483
const microAppHead = globalEnv.rawElementQuerySelector.call(wrapElement, 'micro-app-head')
8584
const microAppBody = globalEnv.rawElementQuerySelector.call(wrapElement, 'micro-app-body')
@@ -92,7 +91,7 @@ export function extractSourceDom (htmlStr: string, app: AppInterface): void {
9291

9392
const fiberStyleTasks: fiberTasks = app.isPrefetch || app.fiber ? [] : null
9493

95-
flatChildren(wrapElement, app, microAppHead, fiberStyleTasks)
94+
flatBodyChildren(wrapElement, app, fiberStyleTasks)
9695

9796
/**
9897
* Style and link are parallel, as it takes a lot of time for link to request resources. During this period, style processing can be performed to improve efficiency.

src/source/scripts.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -531,9 +531,16 @@ export function runScript (
531531
runParsedFunction(app, scriptInfo)
532532
}
533533
} catch (e) {
534-
console.error(`[micro-app from ${replaceElement ? 'runDynamicScript' : 'runScript'}] app ${app.name}: `, e, address)
534+
console.warn(`[micro-app from ${replaceElement ? 'runDynamicScript' : 'runScript'}] app ${app.name}: `, e, address)
535535
// throw error in with sandbox to parent app
536-
throw e
536+
const error = e as Error
537+
let throwError = true
538+
if (typeof microApp?.options?.excludeRunScriptFilter === 'function') {
539+
throwError = microApp.options.excludeRunScriptFilter(address, error, app.name, app.url) !== true
540+
}
541+
if (throwError) {
542+
throw e
543+
}
537544
}
538545
}
539546

typings/global.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ declare module '@micro-app/types' {
363363
fetch?: fetchType
364364
globalAssets?: globalAssetsType,
365365
excludeAssetFilter?: (assetUrl: string) => boolean
366+
excludeRunScriptFilter?: (scriptUrl: string, error: Error, appName: string, appUrl: string) => boolean
366367
/* image video audio 是否设置 crossOrigin = 'anonymous' */
367368
includeCrossOrigin?: (assetUrl: string) => boolean
368369
getRootElementParentNode?: (node: Node, appName: AppName) => void

0 commit comments

Comments
 (0)