diff --git a/README.md b/README.md index baa408eb5..1a847d113 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,6 @@ license - - PRs-welcome - gitter @@ -38,7 +35,7 @@ It is the lowest cost framework for accessing micro front-end, and provides a se The micro-app has nothing to do with the technology stack, nor is it tied to the business, and can be used in any front-end framework. # Getting Start -The micro front-end is divided into base application and sub application. We list the modifications required for base application and sub application respectively, and introduce the use of micro-app in detail. +The micro front-end is divided into base application and micro application. We list the modifications required for base application and micro application respectively, and introduce the use of micro-app in detail. ## base application > The base application takes the vue framework as an example @@ -56,7 +53,7 @@ import microApp from '@micro-zoe/micro-app' microApp.start() ``` -3、Assign a route to the sub application +3、Assign a route to the micro application ```js // router.js import Vue from 'vue' @@ -82,19 +79,19 @@ export default routes ``` -> Please refer to [Routing Chapter](https://zeroing.jd.com/micro-app/docs.html#/zh-cn/route) for the relationship between url and sub application routing +> Please refer to [Routing Chapter](https://zeroing.jd.com/micro-app/docs.html#/zh-cn/route) for the relationship between url and micro application routing -## sub application -> The sub application takes the react framework as an example +## micro application +> The micro application takes the react framework as an example -1、Add basename for route +1、Add basename for route(If the base application is history route and the micro application is hash route, it is not necessary to set the baseurl, this step can be skipped) ```js // router.js @@ -102,7 +99,7 @@ import { BrowserRouter, Switch, Route } from 'react-router-dom' export default function AppRoute () { return ( - // 👇 the sub application can get the baseurl issued by the base application through window.__MICRO_APP_BASE_URL__ + // 👇 the micro application can get the baseurl issued by the base application through window.__MICRO_APP_BASE_URL__ ... @@ -121,11 +118,11 @@ devServer: { }, ``` -Then micro front-end can be rendered normally, and the react sub application is embedded in the vue base application. The effect is as follows: +Then micro front-end can be rendered normally, and the react micro application is embedded in the vue base application. The effect is as follows: ![image](https://img10.360buyimg.com/imagetools/jfs/t1/188373/14/17696/41854/6111f4a0E532736ba/4b86f4f8e2044519.png) -The above lists the usage of react and Vue framework. They can be combined freely. For example, the base application is react, the sub application is Vue, or the base application is Vue, the sub application is react, or both the base application and the sub application are react and Vue. The micro-app has no restrictions on the front-end framework, and any framework can be used as a base application to embed any type of sub application of the framework. +The above lists the usage of react and Vue framework. They can be combined freely. For example, the base application is react, the micro application is Vue, or the base application is Vue, the micro application is react, or both the base application and the micro application are react and Vue. The micro-app has no restrictions on the front-end framework, and any framework can be used as a base application to embed any type of micro application of the framework. More detailed configuration can be viewed [Official website document](https://zeroing.jd.com/micro-app/docs.html#/zh-cn/start). @@ -173,7 +170,7 @@ The react base application is started by default. If you want to start the vue b
- Must sub applications support cross-domain? + Must micro applications support cross-domain? yes! If it is a development environment, you can set headers in webpack-dev-server to support cross-domain. diff --git a/README.zh-cn.md b/README.zh-cn.md index c2e0c4e92..efa46c8b6 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -14,9 +14,6 @@ license - - PRs-welcome - gitter @@ -95,7 +92,7 @@ export default routes ## 子应用 > 子应用以react框架为例 -1、添加路由前缀 +1、添加路由前缀(如果基座应用是history路由,子应用是hash路由,不需要设置路由前缀,这一步可以省略) ```js // router.js diff --git a/docs/home/assets/github-logo.png b/docs/home/assets/github-logo.png new file mode 100644 index 000000000..60a3b3b37 Binary files /dev/null and b/docs/home/assets/github-logo.png differ diff --git a/docs/index.html b/docs/index.html index c00fce331..7827914fa 100644 --- a/docs/index.html +++ b/docs/index.html @@ -25,7 +25,7 @@
diff --git a/docs/zh-cn/prefetch.md b/docs/zh-cn/prefetch.md index be69df118..c3c7b2ab8 100644 --- a/docs/zh-cn/prefetch.md +++ b/docs/zh-cn/prefetch.md @@ -12,7 +12,6 @@ app: { disableScopecss?: boolean // 是否关闭样式隔离,非必传 disableSandbox?: boolean // 是否关闭沙盒,非必传 macro?: boolean // 是否以宏任务方式绑定元素作用域,非必传 - shadowDOM?: boolean // 是否开启shadowDOM,非必传 } ``` @@ -42,5 +41,6 @@ microApp.start({ ``` > [!NOTE] -> 预加载入参:`disableScopecss`、`disableSandbox`、`macro`、`shadowDOM` 必须和 ``[配置项](/zh-cn/configure)保持一致。如果产生冲突,以先执行的一方为准。 +> 1、预加载入参:`disableScopecss`、`disableSandbox`、`macro` 必须和 ``[配置项](/zh-cn/configure)保持一致。如果产生冲突,以先执行的一方为准。 > +> 2、如果子应用开启了shadowDOM,则预加载中的`disableScopecss`需要设置为true diff --git a/docs/zh-cn/route.md b/docs/zh-cn/route.md index 6b03d1cfc..05e33e9ce 100644 --- a/docs/zh-cn/route.md +++ b/docs/zh-cn/route.md @@ -3,7 +3,7 @@ ### url属性和子应用路由的关系 答:没有关系! -micro-app的url属性指向html的地址,它只是用来获取html,不会对子应用产生影响。 +micro-app的url属性指向html的地址,它只是用来获取html。 基座应用和子应用本质是在同一个页面渲染,所以影响到子应用路由的是浏览器地址。 @@ -13,7 +13,7 @@ micro-app的url属性指向html的地址,它只是用来获取html,不会对 基座应用会匹配`page1`并渲染对应的组件,子应用也是一样,浏览器地址会同时影响到基座应用和子应用,因为每个应用都有一套自己的路由系统,它们是可以共存的,不会冲突。 -此时我们要渲染子应用`http://www.xxx.com/`的`page1`页面,那么url属性填写的不是`http://www.xxx.com/page1/`,而是`http://www.xxx.com/`。 +此时我们要渲染子应用`http://www.xxx.com/`的`page1`页面,那么url属性填写的是`http://www.xxx.com/`,而不是`http://www.xxx.com/page1/`。 ```html // http://www.xxx.com/ 会兜底到 http://www.xxx.com/index.html @@ -23,15 +23,27 @@ micro-app的url属性指向html的地址,它只是用来获取html,不会对 ### 路由配置 -如果子应用是单页面应用,那么不需要关心路由的问题。 - -如果是子应用多页面,需要正确配置路由,否则容易出错,以下是需要注意的点: +路由配置非常容易出问题,下面列出了一些注意点: +**路由类型** - 1、基座是hash路由,子应用也必须是hash路由 - 2、基座是history路由,子应用可以是hash或history路由 -- 3、基座路由匹配的path不能使用严格匹配 -- 4、子应用根据基座路由分配的path添加路由前缀 -- 5、如果基座是history路由,子应用是hash路由,不需要设置路由前缀 + +**路由前缀(baseurl)** +- 1、如果基座是history路由,子应用是hash路由,不需要设置路由前缀 +- 2、vue-router在hash模式下不支持置base添加路由前缀,需要创建一个空的路由页面,将其它路由作为它的children + +```js +const routes = [ + { + path: window.__MICRO_APP_BASE_URL__ || '/', + component: Home, + children: [ + // 其他的路由都写到这里 + ], + }, +] +``` **示例** diff --git a/docs/zh-cn/start.md b/docs/zh-cn/start.md index b56de6bca..9139c84f7 100755 --- a/docs/zh-cn/start.md +++ b/docs/zh-cn/start.md @@ -96,7 +96,7 @@ export function MyPage () { ### 子应用 -1、添加路由前缀 +1、添加路由前缀(如果基座应用是history路由,子应用是hash路由,不需要设置路由前缀,这一步可以省略) diff --git a/examples/children/react16/src/router.js b/examples/children/react16/src/router.js index 92118a6f9..840487d43 100644 --- a/examples/children/react16/src/router.js +++ b/examples/children/react16/src/router.js @@ -1,5 +1,5 @@ import React, { lazy, Suspense } from 'react' -import { BrowserRouter, Switch, Route, Redirect, Link } from 'react-router-dom' +import { BrowserRouter, Switch, Route, Redirect, Link, HashRouter } from 'react-router-dom' import Page1 from './pages/page1/page1' import { Menu } from 'antd'; import { MailOutlined, AppstoreOutlined } from '@ant-design/icons'; diff --git a/examples/children/vite/vite.config.js b/examples/children/vite/vite.config.js index a2f086529..cef4d30f9 100644 --- a/examples/children/vite/vite.config.js +++ b/examples/children/vite/vite.config.js @@ -35,5 +35,5 @@ export default defineConfig({ outDir: 'vite', }, clearScreen: false, - base: `${process.env.NODE_ENV === 'production' ? 'https://cangdu.org' : ''}/micro-app/vite/`, + base: `${process.env.NODE_ENV === 'production' ? 'https://zeroing.jd.com' : ''}/micro-app/vite/`, }) diff --git a/examples/children/vue2/src/main.js b/examples/children/vue2/src/main.js index 4dd3f78b3..0e82f4239 100644 --- a/examples/children/vue2/src/main.js +++ b/examples/children/vue2/src/main.js @@ -11,16 +11,10 @@ import App from './App.vue' Vue.config.productionTip = false Vue.use(ElementUI) - -function getBaseName () { - // 基座是history路由,子应用是hash路由,不需要设置路由前缀 - return '/' -} - const router = new VueRouter({ - options: { - base: getBaseName(), - }, + // vue-router在hash模式下不支持base,可以用一个根页面进行包裹 + // base: window.__MICRO_APP_BASE_URL__ || '/', + // mode: 'history', routes, }) diff --git a/examples/children/vue2/src/pages/root.vue b/examples/children/vue2/src/pages/root.vue new file mode 100644 index 000000000..6fef8edbe --- /dev/null +++ b/examples/children/vue2/src/pages/root.vue @@ -0,0 +1,12 @@ + + + diff --git a/examples/children/vue2/src/router.js b/examples/children/vue2/src/router.js index 4f4081157..d7e817114 100644 --- a/examples/children/vue2/src/router.js +++ b/examples/children/vue2/src/router.js @@ -1,19 +1,29 @@ import Vue from 'vue'; import VueRouter from 'vue-router'; +import Root from './pages/root.vue'; import Home from './pages/page1.vue'; Vue.use(VueRouter); const routes = [ { + // 因为vue-router在hash模式下无法设置base,如果基座和子应用都是hash路由,需要创建一个空的路由页面作为根页面,用于设置路由前缀 + // 如果基座应用是history模式则不需要使用root组件包裹 + // path: window.__MICRO_APP_BASE_URL__ || '/', path: '/', - name: 'home', - component: Home, - }, - { - path: '/page2', - name: 'page2', - component: () => import(/* webpackChunkName: "page2" */ './pages/page2.vue'), + component: Root, + children: [ + { + path: '/', + name: 'home', + component: Home, + }, + { + path: '/page2', + name: 'page2', + component: () => import(/* webpackChunkName: "page2" */ './pages/page2.vue'), + }, + ], }, ]; diff --git a/examples/children/vue3/src/router.js b/examples/children/vue3/src/router.js index c46844ca4..fda3c7152 100644 --- a/examples/children/vue3/src/router.js +++ b/examples/children/vue3/src/router.js @@ -1,4 +1,4 @@ -import { createRouter, createWebHistory } from 'vue-router' +import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router' import Home from './pages/page1.vue'; const routes = createRouter({ diff --git a/examples/main-vue2/src/pages/vue2.vue b/examples/main-vue2/src/pages/vue2.vue index 2e7ff08b9..9c442dea1 100644 --- a/examples/main-vue2/src/pages/vue2.vue +++ b/examples/main-vue2/src/pages/vue2.vue @@ -2,12 +2,12 @@
发送数据 - - + name='vue2' + url='http://localhost:4001/micro-app/vue2' + :data='data' + > + +
diff --git a/src/constants.ts b/src/constants.ts index 7b283d456..d71014fc6 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,7 +3,7 @@ export enum ObservedAttrName { URL = 'url', } -// 应用状态 +// app status export enum appStatus { NOT_LOADED = 'NOT_LOADED', LOADING_SOURCE_CODE = 'LOADING_SOURCE_CODE', @@ -14,7 +14,7 @@ export enum appStatus { UNMOUNT = 'UNMOUNT', } -// 生命周期 +// lifecycles export enum lifeCycles { CREATED = 'created', BEFOREMOUNT = 'beforemount', diff --git a/src/create_app.ts b/src/create_app.ts index e78068eac..eab49cfe6 100644 --- a/src/create_app.ts +++ b/src/create_app.ts @@ -12,10 +12,10 @@ import SandBox from './sandbox' import { defer } from './libs/utils' import dispatchLifecyclesEvent, { dispatchUnmountToMicroApp } from './interact/lifecycles_event' -// 微应用实例 +// micro app instances export const appInstanceMap = new Map() -// CreateApp构造函数入参 +// params of CreateApp export interface CreateAppParam { name: string url: string @@ -29,7 +29,7 @@ export interface CreateAppParam { export default class CreateApp implements AppInterface { private status: string = appStatus.NOT_LOADED - private loadSourceLevel: -1|0|1|2 = 0 // level为2,资源加载完成 + private loadSourceLevel: -1|0|1|2 = 0 isPrefetch = false name: string url: string @@ -46,7 +46,7 @@ export default class CreateApp implements AppInterface { this.container = container ?? null this.inline = inline ?? false this.baseurl = baseurl ?? '' - // 初始化时非必传👆 + // optional during init👆 this.name = name this.url = url this.useSandbox = useSandbox @@ -62,14 +62,14 @@ export default class CreateApp implements AppInterface { } } - // 加载资源 + // Load resources loadSourceCode (): void { this.status = appStatus.LOADING_SOURCE_CODE extractHtml(this) } /** - * 资源加载完成,非预加载和卸载时执行mount操作 + * When resource is loaded, mount app if it is not prefetch or unmount */ onLoad (html: HTMLElement): void { if (++this.loadSourceLevel === 2) { @@ -84,7 +84,7 @@ export default class CreateApp implements AppInterface { } /** - * 加载html资源出错 + * Error loading HTML * @param e Error */ onLoadError (e: Error): void { @@ -96,10 +96,10 @@ export default class CreateApp implements AppInterface { } /** - * 初始化资源完成后进行渲染 - * @param container 容器 - * @param inline 是否使用内联模式 - * @param baseurl 路由前缀,每个应用的前缀都是不同的,兜底为空字符串 + * mount app + * @param container app container + * @param inline js runs in inline mode + * @param baseurl route prefix, default is '' */ mount ( container?: HTMLElement | ShadowRoot, @@ -155,8 +155,8 @@ export default class CreateApp implements AppInterface { } /** - * 应用卸载 - * @param destory 是否完全销毁,删除缓存资源 + * unmount app + * @param destory completely destroyed, delete cache resources */ unmount (destory: boolean): void { if (this.status === appStatus.LOAD_SOURCE_ERROR) { @@ -168,7 +168,7 @@ export default class CreateApp implements AppInterface { this.name, lifeCycles.UNMOUNT, ) - // 向微应用发送卸载事件,在沙盒清空之前&声明周期执行之后触发 + // Send an unmount event to the micro application, which is triggered before the sandbox is cleared & after the unmount lifecycle is executed dispatchUnmountToMicroApp(this.name) this.sandBox?.stop() this.container = null @@ -178,7 +178,7 @@ export default class CreateApp implements AppInterface { } /** - * 阻断应用正常渲染的错误钩子 + * app rendering error * @param e Error */ onerror (e: Error): void { @@ -190,7 +190,7 @@ export default class CreateApp implements AppInterface { ) } - // 获取应用状态 + // get app status getAppStatus (): string { return this.status } diff --git a/src/interact/event_center.ts b/src/interact/event_center.ts index 16baf49c1..f95ea3114 100755 --- a/src/interact/event_center.ts +++ b/src/interact/event_center.ts @@ -8,7 +8,7 @@ type eventInfo = { export default class EventCenter { eventList = new Map() - // 判断名称是否正确 + // whether the name is legal isLegalName (name: string): boolean { if (!name) { console.error( @@ -21,10 +21,10 @@ export default class EventCenter { } /** - * 绑定监听函数 - * @param name 事件名称 - * @param f 绑定函数 - * @param autoTrigger 在初次绑定监听函数时有缓存数据,是否需要主动触发一次,默认为false + * add listener + * @param name event name + * @param f listener + * @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false */ on (name: string, f: CallableFunction, autoTrigger = false): void { if (this.isLegalName(name)) { @@ -42,7 +42,7 @@ export default class EventCenter { } this.eventList.set(name, eventInfo) } else if (autoTrigger && Object.getOwnPropertyNames(eventInfo.data).length) { - // 如果数据池中有数据,绑定时主动触发一次 + // auto trigger when data not null f(eventInfo.data) } @@ -50,7 +50,7 @@ export default class EventCenter { } } - // 解除绑定,但数据不清空 + // remove listener, but the data is not cleared off (name: string, f?: CallableFunction): void { if (this.isLegalName(name)) { const eventInfo = this.eventList.get(name) @@ -64,7 +64,7 @@ export default class EventCenter { } } - // 发送数据 + // dispatch data dispatch (name: string, data: Record): void { if (this.isLegalName(name)) { if (toString.call(data) !== '[object Object]') { @@ -74,7 +74,7 @@ export default class EventCenter { } let eventInfo = this.eventList.get(name) if (eventInfo) { - // 当数据不相等时才更新 + // Update when the data is not equal if (eventInfo.data !== data) { eventInfo.data = data for (const f of eventInfo.callbacks) { @@ -91,7 +91,7 @@ export default class EventCenter { } } - // 获取数据 + // get data getData (name: string): Record | null { const eventInfo = this.eventList.get(name) return eventInfo?.data ?? null diff --git a/src/interact/index.ts b/src/interact/index.ts index b1a7b37a1..d786fec27 100644 --- a/src/interact/index.ts +++ b/src/interact/index.ts @@ -5,29 +5,29 @@ import { removeDomScope } from '../libs/utils' const eventCenter = new EventCenter() /** - * 格式化事件名称 - * @param appName 应用名称 - * @param fromBaseApp 是否从基座应用发送数据 + * Format event name + * @param appName app.name + * @param fromBaseApp is from base app */ function formatEventName (appName: string, fromBaseApp: boolean): string { if (typeof appName !== 'string' || !appName) return '' return fromBaseApp ? `__from_base_app_${appName}__` : `__from_micro_app_${appName}__` } -// 全局数据通信 +// Global data class EventCenterForGlobal { /** - * 添加全局数据监听 - * @param cb 绑定函数 - * @param autoTrigger 在初次绑定监听函数时有缓存数据,是否需要主动触发一次,默认为false + * add listener of global data + * @param cb listener + * @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false */ addGlobalDataListener (cb: CallableFunction, autoTrigger?: boolean): void { eventCenter.on('global', cb, autoTrigger) } /** - * 解除全局数据监听函数 - * @param cb 绑定函数 + * remove listener of global data + * @param cb listener */ removeGlobalDataListener (cb: CallableFunction): void { if (typeof cb === 'function') { @@ -36,37 +36,37 @@ class EventCenterForGlobal { } /** - * 发送数据 - * @param data 对象数据 + * dispatch global data + * @param data data */ setGlobalData (data: Record): void { eventCenter.dispatch('global', data) } /** - * 清空所有全局数据绑定函数 + * clear all listener of global data */ clearGlobalDataListener (): void { eventCenter.off('global') } } -// 基座应用的数据通信方法集合 +// Event center for base app export class EventCenterForBaseApp extends EventCenterForGlobal { /** - * 添加数据监听 - * @param appName 子应用名称 - * @param cb 绑定函数 - * @param autoTrigger 在初次绑定监听函数时有缓存数据,是否需要主动触发一次,默认为false + * add listener + * @param appName app.name + * @param cb listener + * @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false */ addDataListener (appName: string, cb: CallableFunction, autoTrigger?: boolean): void { eventCenter.on(formatEventName(appName, false), cb, autoTrigger) } /** - * 解除监听函数 - * @param appName 子应用名称 - * @param cb 绑定函数 + * remove listener + * @param appName app.name + * @param cb listener */ removeDataListener (appName: string, cb: CallableFunction): void { if (typeof cb === 'function') { @@ -75,33 +75,33 @@ export class EventCenterForBaseApp extends EventCenterForGlobal { } /** - * 主动获取子应用或基座传递的数据 - * @param appName 子应用名称 - * @param fromBaseApp 是否获取基座应用发送给子应用的数据,默认false + * get data from micro app or base app + * @param appName app.name + * @param fromBaseApp whether get data from base app, default is false */ getData (appName: string, fromBaseApp = false): Record | null { return eventCenter.getData(formatEventName(appName, fromBaseApp)) } /** - * 向指定子应用发送数据 - * @param appName 子应用名称 - * @param data 对象数据 + * Dispatch data to the specified micro app + * @param appName app.name + * @param data data */ setData (appName: string, data: Record): void { eventCenter.dispatch(formatEventName(appName, true), data) } /** - * 清空某个应用的监听函数 - * @param appName 子应用名称 + * clear all listener for specified micro app + * @param appName app.name */ clearDataListener (appName: string): void { eventCenter.off(formatEventName(appName, false)) } } -// 子应用的数据通信方法集合 +// Event center for micro app export class EventCenterForMicroApp extends EventCenterForGlobal { appName: string constructor (appName: string) { @@ -110,17 +110,17 @@ export class EventCenterForMicroApp extends EventCenterForGlobal { } /** - * 监听基座应用发送的数据 - * @param cb 绑定函数 - * @param autoTrigger 在初次绑定监听函数时有缓存数据,是否需要主动触发一次,默认为false + * add listener, monitor the data sent by the base app + * @param cb listener + * @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false */ addDataListener (cb: CallableFunction, autoTrigger?: boolean): void { eventCenter.on(formatEventName(this.appName, true), cb, autoTrigger) } /** - * 解除监听函数 - * @param cb 绑定函数 + * remove listener + * @param cb listener */ removeDataListener (cb: CallableFunction): void { if (typeof cb === 'function') { @@ -129,15 +129,15 @@ export class EventCenterForMicroApp extends EventCenterForGlobal { } /** - * 主动获取来自基座的数据 + * get data from base app */ getData (): Record | null { return eventCenter.getData(formatEventName(this.appName, true)) } /** - * 向基座应用发送数据 - * @param data 对象数据 + * dispatch data to base app + * @param data data */ dispatch (data: Record): void { removeDomScope() @@ -161,7 +161,7 @@ export class EventCenterForMicroApp extends EventCenterForGlobal { } /** - * 清空当前子应用绑定的所有监听函数 + * clear all listeners */ clearDataListener (): void { eventCenter.off(formatEventName(this.appName, true)) diff --git a/src/interact/lifecycles_event.ts b/src/interact/lifecycles_event.ts index 4af01b82b..0811b098b 100644 --- a/src/interact/lifecycles_event.ts +++ b/src/interact/lifecycles_event.ts @@ -17,11 +17,11 @@ function eventHandler (event: CustomEvent, element: HTMLElement): void { } /** - * 发送生命周期事件 - * @param element 容器元素 - * @param appName 应用名称 - * @param lifecycleName 生命周期名称 - * @param error 错误钩子的参数 + * dispatch lifeCycles event + * @param element container + * @param appName app.name + * @param lifecycleName lifeCycle name + * @param error param from error hook */ export default function dispatchLifecyclesEvent ( element: HTMLElement, @@ -49,7 +49,7 @@ export default function dispatchLifecyclesEvent ( }) eventHandler(event, element) - // 全局钩子 + // global hooks // @ts-ignore if (typeof microApp.lifeCycles?.[lifecycleName] === 'function') { // @ts-ignore @@ -60,8 +60,8 @@ export default function dispatchLifecyclesEvent ( } /** - * 向微应用发送卸载事件 - * @param appName 应用名称 + * Dispatch unmount event to micro app + * @param appName app.name */ export function dispatchUnmountToMicroApp (appName: string): void { const event = new CustomEvent(`unmount-${appName}`) diff --git a/src/libs/additional.ts b/src/libs/additional.ts index e9de1c585..d92367b8a 100644 --- a/src/libs/additional.ts +++ b/src/libs/additional.ts @@ -14,14 +14,14 @@ function unmountAppInline (): void { appInstanceMap.clear() } -// 循环内嵌时子应用卸载后辈应用 +// if micro-app run in micro application, delete all next generation application when unmount event received export function listenUmountAppInline (): void { if (window.__MICRO_APP_ENVIRONMENT__) { window.addEventListener('unmount', unmountAppInline, false) } } -// 解除监听 +// release listener export function replaseUnmountAppInline (): void { if (window.__MICRO_APP_ENVIRONMENT__) { window.removeEventListener('unmount', unmountAppInline, false) diff --git a/src/libs/utils.ts b/src/libs/utils.ts index e826b0a7f..ea04dc15b 100644 --- a/src/libs/utils.ts +++ b/src/libs/utils.ts @@ -27,8 +27,8 @@ export const rawDocument = new Function('return document')() export const version = '__VERSION__' /** - * 格式化log信息 - * @param msg log信息 + * Format log msg + * @param msg log msg */ export function formatLogMessage (msg: string): string { if (typeof msg === 'string') { @@ -39,32 +39,32 @@ export function formatLogMessage (msg: string): string { } /** - * 延迟执行 - * @param fn 回调函数 - * @param args 入参 + * async execution + * @param fn callback + * @param args params */ export function defer (fn: Func, ...args: any[]): void { Promise.resolve().then(fn.bind(null, ...args)) } /** - * 添加地址协议 - * @param url 地址 + * Add address protocol + * @param url address */ export function addProtocol (url: string): string { return url.startsWith('//') ? `${location.protocol}${url}` : url } /** - * 格式化URL地址 - * @param url 地址 + * Format URL address + * @param url address */ export function formatURL (url: string | null): string { if (typeof url !== 'string' || !url) return '' try { const { origin, pathname } = new URL(addProtocol(url)) - // 如果以.html结尾,则不需要补全 / + // If it ends with .html, don’t need to add / if (/\.html$/.test(pathname)) { return `${origin}${pathname}` } @@ -77,7 +77,7 @@ export function formatURL (url: string | null): string { } /** - * 获取的地址的有效域名,如 https://xxx/xx/xx.html 格式化为 https://xxx/xx/ + * Get valid address, such as https://xxx/xx/xx.html to https://xxx/xx/ * @param url app.url */ export function getEffectivePath (url: string): string { @@ -91,9 +91,9 @@ export function getEffectivePath (url: string): string { } /** - * 补全静态资源相对地址 - * @param path 静态资源地址 - * @param baseURI 基础地址 -- app.url + * Complete address + * @param path address + * @param baseURI base url(app.url) */ export function CompletionPath (path: string, baseURI: string): string { if (/^((((ht|f)tps?)|file):)?\/\//.test(path)) return path @@ -104,8 +104,9 @@ export function CompletionPath (path: string, baseURI: string): string { } /** - * 获取link资源所在文件夹,用于补全css中的相对地址 - * @param linkpath link地址 + * Get the folder where the link resource is located, + * which is used to complete the relative address in the css + * @param linkpath full link address */ export function getLinkFileDir (linkpath: string): string { const pathArr = linkpath.split('/') @@ -114,11 +115,11 @@ export function getLinkFileDir (linkpath: string): string { } /** - * promise流 - * @param promiseList promise数组,必传 - * @param successsCb 成功回调,必传 - * @param errorCb 失败回调,必传 - * @param finallyCb 结束回调,必传 + * promise stream + * @param promiseList promise list + * @param successsCb success callback + * @param errorCb failed callback + * @param finallyCb finally callback */ export function promiseStream ( promiseList: Array | T>, @@ -157,18 +158,18 @@ export function promiseStream ( }) } -// 检测浏览器是否支持module script +// Check whether the browser supports module script export function isSupportModuleScript (): boolean { const s = document.createElement('script') return 'noModule' in s } -// 创建随机symbol字符串 +// Create a random symbol string export function createNonceStr (): string { return Math.random().toString(36).substr(2, 15) } -// 数组去重 +// Array deduplication export function unique (array: any[]): any[] { return array.filter(function (this: Record, item) { return item in this ? false : (this[item] = true) @@ -190,35 +191,35 @@ export const requestIdleCallback = window.requestIdleCallback || } /** - * 记录当前正在运行的appName + * Record the currently running app.name */ let currentMicroAppName: string | null = null export function setCurrentAppName (appName: string | null): void { currentMicroAppName = appName } -// 获取当前运行的应用名称 +// get the currently running app.name export function getCurrentAppName (): string | null { return currentMicroAppName } -// 清除appName绑定 +// Clear appName export function removeDomScope (): void { setCurrentAppName(null) } -// 是否是safari浏览器 +// is safari browser export function isSafari (): boolean { return /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) } -// 是否是函数类型 +// is function export function isFunction (target: unknown): boolean { return typeof target === 'function' } /** - * 创建纯净的无绑定的元素 + * Create pure elements */ export function pureCreateElement (tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K] { const element = rawDocument.createElement(tagName, options) diff --git a/src/micro_app_element.ts b/src/micro_app_element.ts index 33e8c7661..f8e59d876 100644 --- a/src/micro_app_element.ts +++ b/src/micro_app_element.ts @@ -23,14 +23,14 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem isWating = false cacheData: Record | null = null - // 👇可配置项 - // shadowDom 开启shadowDOM,默认为false - // destory 卸载时是否强制删除缓存资源,默认为false - // inline js以内联script方式运行,默认为false - // disableScopecss 禁用css隔离,默认为false - // disableSandbox 停用js沙盒,默认为false - // macro 用于解决vue3的异步渲染问题,和预加载的入参保持一致,默认为false - // baseUrl 路由前缀,默认为 '' + // 👇Configuration + // shadowDom: use shadowDOM, default is false + // destory: whether delete cache resources when unmount, default is false + // inline: whether js runs in inline script mode, default is false + // disableScopecss: whether disable css scoped, default is false + // disableSandbox: whether disable sandbox, default is false + // macro: used to solve the async render problem of vue3, default is false + // baseUrl: route prefix, default is '' connectedCallback (): void { if (++MicroAppElement.microAppCount === 1) { @@ -108,8 +108,7 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem } /** - * 处理初始化后name或url发生变化 - * 只要name或url发生变化,则将旧应用完全卸载,并渲染新的应用 + * handle for change of name an url after element inited */ handleAttributeUpdate = (): void => { this.isWating = false @@ -118,7 +117,7 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem if (this.legalAttribute('name', attrName) && this.legalAttribute('url', attrUrl)) { const existApp = appInstanceMap.get(attrName!) if (attrName !== this.name && existApp) { - // 处理已缓存的非预加载app + // handling of cached and non-prefetch apps if (existApp.getAppStatus() !== appStatus.UNMOUNT && !existApp.isPrefetch) { this.setAttribute('name', this.name) return console.error( @@ -133,12 +132,12 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem this.url = attrUrl ;(this.shadowRoot ?? this).innerHTML = '' /** - * existApp存在 - * 如果attrName和this.name相等,则existApp已经被卸载 - * 如果attrName和this.name不相等,则existApp为预加载或已卸载 + * when existApp not undefined + * if attrName and this.name are equal, existApp has been unmounted + * if attrName and this.name are not equal, existApp is prefetch or unmounted */ if (existApp && existApp.url === attrUrl) { - // app直接挂载 + // mount app this.handleAppMount(existApp) } else { this.handleCreate() @@ -150,9 +149,9 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem } /** - * 判断元素属性是否符合条件 - * @param name 属性名称 - * @param val 属性值 + * judge the attribute is correct + * @param name attribute name + * @param val attribute value */ legalAttribute (name: string, val: AttrType): boolean { if (typeof val !== 'string' || !val) { @@ -166,7 +165,7 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem return true } - // 挂载应用 + // mount app handleAppMount (app: AppInterface): void { app.isPrefetch = false defer(() => app.mount( @@ -176,7 +175,7 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem )) } - // 创建应用 + // create app instance handleCreate (): void { const instance: AppInterface = new CreateApp({ name: this.name!, @@ -193,8 +192,8 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem } /** - * 卸载应用 - * @param destory 是否完全销毁 + * unmount app + * @param destory delete cache resources when unmount */ handleUnmount (destory: boolean): void { const app = appInstanceMap.get(this.name!) @@ -202,9 +201,9 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem } /** - * 获取配置结果 - * 全局的优先级最低 - * @param name 名称 + * Get configuration results + * Global setting is lowest priority + * @param name Configuration item name */ getDisposeResult (name: string): boolean { // @ts-ignore @@ -212,7 +211,7 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem } /** - * 基座应用传入的数据 + * Data from the base application */ set data (value: Record | null) { if (this.name) { @@ -223,7 +222,7 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem } /** - * data取值只在jsx-custom-event中使用一次 + * get data only used in jsx-custom-event once */ get data (): Record | null { if (this.name) { @@ -236,8 +235,8 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem } /** - * 定义元素 - * @param tagName 元素名称 + * define element + * @param tagName element name */ export function defineElement (tagName: string): boolean { if (window.customElements.get(tagName)) { diff --git a/src/polyfill/jsx-custom-event.ts b/src/polyfill/jsx-custom-event.ts index e488b7535..d004d8c8a 100644 --- a/src/polyfill/jsx-custom-event.ts +++ b/src/polyfill/jsx-custom-event.ts @@ -3,7 +3,7 @@ import React from 'react' type MicroElementType = HTMLElement & Record -// 生命周期事件 +// lifecycles const eventLifeCycles = ['oncreated', 'onbeforemount', 'onmounted', 'onunmount', 'onerror', 'ondatachange'] export default function jsxCustomEvent ( @@ -14,7 +14,7 @@ export default function jsxCustomEvent ( const newProps = Object.assign({}, props) if (/^micro-app(-\S+)?/.test(type as string) && props) { - // 初始化和卸载、更新时都会执行 + // ref will call when create, update, unmount newProps.ref = (element: MicroElementType | null) => { if (typeof props.ref === 'function') { props.ref(element) @@ -22,9 +22,9 @@ export default function jsxCustomEvent ( (props.ref as any).current = element } - // 卸载和更新时,element为null + // when unmount and update the element is null if (element) { - // 前后数据不同时才更新数据,保持和其它框架(如vue)一致 + // Update data when the prev and next data are different if (toString.call(props.data) === '[object Object]' && element.data !== props.data) { element.data = props.data } diff --git a/src/prefetch.ts b/src/prefetch.ts index d50590135..9d9c4cd0f 100644 --- a/src/prefetch.ts +++ b/src/prefetch.ts @@ -24,22 +24,20 @@ function filterPreFetchTarget (apps: T[]): T[] { } /** - * 预加载 * preFetch([ * { * name: string, * url: string, - * disableScopecss: boolean, - * disableSandbox: boolean, - * macro: boolean, - * shadowDOM: boolean, + * disableScopecss?: boolean, + * disableSandbox?: boolean, + * macro?: boolean, * }, * ... * ]) - * 注意: - * 1、预加载是异步的,在浏览器空闲时才会执行 - * 2、预加载的 disableScopecss、disableSandbox、macro、shadowDOM 和micro-app组件要保持一致,如果冲突,谁先执行则以谁为准 - * @param apps 应用列表 + * Note: + * 1: preFetch is asynchronous and is performed only when the browser is idle + * 2: disableScopecss, disableSandbox, macro must be same with micro-app element, if conflict, the one who executes first shall prevail + * @param apps micro apps */ export default function preFetch (apps: prefetchParamList): void { requestIdleCallback(() => { @@ -49,10 +47,7 @@ export default function preFetch (apps: prefetchParamList): void { const app = new CreateApp({ name: item.name, url: item.url, - scopecss: !( - (item.disableScopecss ?? microApp.disableScopecss) || - (item.shadowDOM ?? microApp.shadowDOM) - ), + scopecss: !(item.disableScopecss ?? microApp.disableScopecss), useSandbox: !(item.disableSandbox ?? microApp.disableSandbox), macro: item.macro ?? microApp.macro, }) diff --git a/src/sandbox/effect.ts b/src/sandbox/effect.ts index 131c641e1..a5e8eb429 100644 --- a/src/sandbox/effect.ts +++ b/src/sandbox/effect.ts @@ -1,6 +1,7 @@ import type { microWindowType } from '@micro-app/types' import { getCurrentAppName, formatLogMessage } from '../libs/utils' +// save raw methods const rawWindowAddEventListener = window.addEventListener const rawWindowRemoveEventListener = window.removeEventListener const rawSetInterval = window.setInterval @@ -11,11 +12,11 @@ const rawClearTimeout = window.clearTimeout const rawDocumentAddEventListener = document.addEventListener const rawDocumentRemoveEventListener = document.removeEventListener -// document.onclick绑定列表,每个应用的绑定函数是唯一的 +// document.onclick binding list, the binding function of each application is unique const documentClickListMap = new Map() let hasRewriteDocumentOnClick = false /** - * 重写document.onclick,只执行一次 + * Rewrite document.onclick and execute it only once */ function overwriteDocumentOnClick (): void { hasRewriteDocumentOnClick = true @@ -63,7 +64,7 @@ function overwriteDocumentOnClick (): void { } /** - * document 的事件是全局共享的,在子应用卸载时我们需要清空这些副作用事件绑定 + * The document event is globally, we need to clear these event bindings when micro application unmounted */ const documentEventListenerMap = new Map>>() export function effectDocumentEvent (): void { @@ -111,17 +112,16 @@ export function effectDocumentEvent (): void { } } -// 清空document事件代理 +// Clear the document event agent export function releaseEffectDocumentEvent (): void { document.addEventListener = rawDocumentAddEventListener document.removeEventListener = rawDocumentRemoveEventListener } /** - * 格式化特定事件名称 - * @param type 事件名称 - * @param microWindow 原型对象 - * @returns string + * Format event name + * @param type event name + * @param microWindow micro window */ function formatEventType (type: string, microWindow: microWindowType): string { if (type === 'unmount') { @@ -131,8 +131,8 @@ function formatEventType (type: string, microWindow: microWindowType): string { } /** - * 注册和监听副作用事件 - * @param microWindow 原型对象 + * Rewrite side-effect events + * @param microWindow micro window */ export default function effect (microWindow: microWindowType): CallableFunction { const eventListenerMap = new Map>() @@ -198,7 +198,7 @@ export default function effect (microWindow: microWindowType): CallableFunction } return () => { - // 清空window绑定事件 + // Clear window binding events if (eventListenerMap.size) { eventListenerMap.forEach((listenerList, type) => { if (listenerList.size) { @@ -210,7 +210,7 @@ export default function effect (microWindow: microWindowType): CallableFunction eventListenerMap.clear() } - // 清空定时器 + // Clear timers if (intervalIdList.size) { intervalIdList.forEach((intervalId: number) => { rawClearInterval(intervalId) @@ -227,10 +227,10 @@ export default function effect (microWindow: microWindowType): CallableFunction const appName = microWindow.__MICRO_APP_NAME__ - // 清空当前子应用通过document.onclick绑定的函数 + // Clear the function bound by micro application through document.onclick documentClickListMap.delete(appName) - // 清空document绑定事件 + // Clear document binding event const documentAppListenersMap = documentEventListenerMap.get(appName) if (documentAppListenersMap) { documentAppListenersMap.forEach((listenerList, type) => { diff --git a/src/sandbox/index.ts b/src/sandbox/index.ts index 2a01c17a6..aae834423 100644 --- a/src/sandbox/index.ts +++ b/src/sandbox/index.ts @@ -23,14 +23,14 @@ type injectDataType = { rawDocument: Document } -// 可以逃逸到外层window的变量 +// Variables that can escape to rawWindow const staticEscapeProperties: PropertyKey[] = [ 'System', '__cjsWrapper', '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__', ] -// 一些只能赋值到原window上的变量 +// Variables that can only assigned to rawWindow const escapeSetterKeyList: PropertyKey[] = [ 'location', ] @@ -49,7 +49,7 @@ const unscopables = { } /** - * 宏任务延迟执行,解决vue3的部分渲染问题 + * macro task to solve the rendering problem of vue3 */ let macroTimer: number function macroTask (fn: TimerHandler): void { @@ -58,26 +58,26 @@ function macroTask (fn: TimerHandler): void { } export default class SandBox implements SandBoxInterface { - static activeCount = 0 // 正在运行的沙盒数量 - active = false // 当前沙盒运行状态 + static activeCount = 0 // number of active sandbox + active = false // sandbox state proxyWindow: WindowProxy & injectDataType releaseEffect: CallableFunction - // 强隔离的全局变量(只能在沙箱中获取和设置的属性,不会兜底到外层window) + // Scoped global Properties(Properties that can only get and set in microWindow, will not escape to rawWindow) scopeProperties: PropertyKey[] = ['webpackJsonp'] - // 可以泄漏到外部window的全局变量 + // Properties that can be escape to rawWindow escapeProperties: PropertyKey[] = [] - microWindow = {} as Window & injectDataType // 代理原型 - injectedKeys: Set = new Set() // proxyWindow新添加的属性 - escapeKeys: Set = new Set() // 泄漏到外部window的变量,卸载时清除 + microWindow = {} as Window & injectDataType // Proxy target + injectedKeys: Set = new Set() // Properties newly added to microWindow + escapeKeys: Set = new Set() // Properties escape to rawWindow, cleared when unmount constructor (appName: string, url: string, macro: boolean) { const descriptorTargetMap = new Map() const hasOwnProperty = (key: PropertyKey) => this.microWindow.hasOwnProperty(key) || rawWindow.hasOwnProperty(key) - // 通过插件系统获取隔离属性和可逃逸属性 + // get scopeProperties and escapeProperties from plugins this.getScopeProperties(appName) - // 注入全局变量 + // inject global properties this.inject(this.microWindow, appName, url) - // 重写全局事件监听&定时器 + // Rewrite global event listener & timeout this.releaseEffect = effect(this.microWindow) this.proxyWindow = new Proxy(this.microWindow, { @@ -89,7 +89,7 @@ export default class SandBox implements SandBoxInterface { } if (key === 'top' || key === 'parent') { - if (rawWindow === rawWindow.parent) { // 不在iframe + if (rawWindow === rawWindow.parent) { // not in iframe return this.proxyWindow } return Reflect.get(rawWindow, key) // iframe @@ -236,8 +236,8 @@ export default class SandBox implements SandBoxInterface { } /** - * 通过插件系统获取隔离属性和可逃逸属性 - * @param appName 应用名称 + * get scopeProperties and escapeProperties from plugins + * @param appName app name */ getScopeProperties (appName: string): void { if (typeof microApp.plugins !== 'object') return @@ -270,9 +270,9 @@ export default class SandBox implements SandBoxInterface { } /** - * 向原型window注入全局变量 - * @param microWindow 原型window - * @param appName 应用名称 + * nject global properties to microWindow + * @param microWindow micro window + * @param appName app name * @param url app url */ inject (microWindow: microWindowType, appName: string, url: string): void { diff --git a/src/source/fetch.ts b/src/source/fetch.ts index 952b3e09a..1939af184 100644 --- a/src/source/fetch.ts +++ b/src/source/fetch.ts @@ -2,10 +2,10 @@ import { isFunction } from '../libs/utils' import microApp from '../micro_app' /** - * 获取静态资源 - * @param url 静态资源地址 - * @param appName 应用名称 - * @param config 配置项 + * fetch source of html, js, css + * @param url source path + * @param appName app name + * @param config config of fetch */ export function fetchSource (url: string, appName: string, options = {}): Promise { if (isFunction(microApp.fetch)) { diff --git a/src/source/index.ts b/src/source/index.ts index 0eca40568..53580c525 100644 --- a/src/source/index.ts +++ b/src/source/index.ts @@ -6,7 +6,7 @@ import { extractScriptElement, fetchScriptsFromHtml } from './scripts' import scopedCSS from './scoped_css' /** - * 将html字符串转换为dom + * transform html string to dom * @param str string dom */ function getWrapElement (str: string): HTMLElement { @@ -18,10 +18,10 @@ function getWrapElement (str: string): HTMLElement { } /** - * 递归处理每一个子元素 - * @param parent 父元素 - * @param app 应用实例 - * @param microAppHead micro-app-head标签 + * Recursively process each child element + * @param parent parent element + * @param app app + * @param microAppHead micro-app-head element */ function flatChildren ( parent: HTMLElement, @@ -62,9 +62,9 @@ function flatChildren ( } /** - * 提取link和script,绑定style作用域 - * @param htmlStr html字符串 - * @param app 应用实例 + * Extract link and script, bind style scope + * @param htmlStr html string + * @param app app */ function extractSourceDom (htmlStr: string, app: AppInterface) { const wrapElement = getWrapElement(htmlStr) @@ -95,8 +95,8 @@ function extractSourceDom (htmlStr: string, app: AppInterface) { } /** - * 提取并格式化html - * @param app 应用实例 + * Get and format html + * @param app app */ export default function extractHtml (app: AppInterface): void { fetchSource(app.url, app.name, { cache: 'no-cache' }).then((htmlStr: string) => { diff --git a/src/source/links.ts b/src/source/links.ts index 7eb6a879f..9f54c3d59 100644 --- a/src/source/links.ts +++ b/src/source/links.ts @@ -15,16 +15,16 @@ import { dispatchOnErrorEvent, } from './load_event' -// 全局link,跨应用复用 +// Global links, reuse across apps export const globalLinks = new Map() /** - * 提取link标签 - * @param link link标签 - * @param parent link父级容器 - * @param app 实例 - * @param microAppHead micro-app-head标签,初始化时必传 - * @param isDynamic 是否是动态插入 + * Extract link elements + * @param link link element + * @param parent parent element of link + * @param app app + * @param microAppHead micro-app-head element + * @param isDynamic dynamic insert */ export function extractLinkFromHtml ( link: HTMLLinkElement, @@ -41,7 +41,7 @@ export function extractLinkFromHtml ( if (!isDynamic) { replaceComment = document.createComment(`the link with href=${href} move to micro-app-head as style element`) const placeholderComment = document.createComment(`placeholder for link with href=${href}`) - // style标签统一放入microAppHead + // all style elements insert into microAppHead microAppHead!.appendChild(placeholderComment) app.source.links.set(href, { code: '', @@ -70,9 +70,9 @@ export function extractLinkFromHtml ( } /** - * 获取link远程资源 - * @param wrapElement 容器 - * @param app 应用实例 + * Get link remote resources + * @param wrapElement htmlDom + * @param app app * @param microAppHead micro-app-head */ export function fetchLinksFromHtml ( @@ -103,12 +103,12 @@ export function fetchLinksFromHtml ( } /** - * 请求link资源成功,将placeholder替换为style标签 - * @param url 资源地址 - * @param info 资源详情 - * @param data 资源内容 + * fetch link succeeded, replace placeholder with style tag + * @param url resource address + * @param info resource link info + * @param data code * @param microAppHead micro-app-head - * @param app 应用实例 + * @param app app */ export function fetchLinkSuccess ( url: string, @@ -132,12 +132,12 @@ export function fetchLinkSuccess ( } /** - * 获取动态创建的link资源 - * @param url link地址 + * get css from dynamic link + * @param url link address * @param info info - * @param app 应用实例 - * @param originLink 原link标签 - * @param replaceStyle style映射 + * @param app app + * @param originLink origin link element + * @param replaceStyle style element which replaced origin link */ export function foramtDynamicLink ( url: string, diff --git a/src/source/patch.ts b/src/source/patch.ts index 17759dd0b..138005b5b 100644 --- a/src/source/patch.ts +++ b/src/source/patch.ts @@ -26,6 +26,7 @@ declare global { } } +// save raw methods const rawSetAttribute = Element.prototype.setAttribute const rawAppendChild = Node.prototype.appendChild const rawInsertBefore = Node.prototype.insertBefore @@ -44,14 +45,14 @@ const rawGetElementsByClassName = Document.prototype.getElementsByClassName const rawGetElementsByTagName = Document.prototype.getElementsByTagName const rawGetElementsByName = Document.prototype.getElementsByName -// 记录元素与映射元素 +// Record element and map element const dynamicElementInMicroAppMap = new WeakMap() /** - * 处理新建的node,格式化style、link、script标签 - * @param parent 父元素 - * @param child 新增的元素 - * @param app 应用实例 + * Process the new node and format the style, link and script element + * @param parent parent node + * @param child new node + * @param app app */ function handleNewNode (parent: Node, child: Node, app: AppInterface): Node { if (child instanceof HTMLStyleElement) { @@ -101,11 +102,11 @@ function handleNewNode (parent: Node, child: Node, app: AppInterface): Node { ) if (url && info) { - if (info.code) { // 内联script + if (info.code) { // inline script const replaceElement = runScript(url, info.code, app, info.module, true) dynamicElementInMicroAppMap.set(child, replaceElement) return replaceElement - } else { // 外部script + } else { // remote script const replaceElement = runDynamicScript(url, info, app, child) dynamicElementInMicroAppMap.set(child, replaceElement) return replaceElement @@ -120,12 +121,12 @@ function handleNewNode (parent: Node, child: Node, app: AppInterface): Node { } /** - * 针对插入head和body的元素进行处理,其它情况正常执行 - * @param app 实例 - * @param method 原方法 - * @param parent 父元素 - * @param targetChild 经过格式化的目标元素 - * @param passiveChild insertBefore replaceChild的第二个参数 + * Handle the elements inserted into head and body, and execute normally in other cases + * @param app app + * @param method raw method + * @param parent parent node + * @param targetChild target node + * @param passiveChild second param of insertBefore and replaceChild */ function invokePrototypeMethod ( app: AppInterface, @@ -135,14 +136,14 @@ function invokePrototypeMethod ( passiveChild?: Node | null, ): any { /** - * 如果passiveChild不是子元素,则 insertBefore replaceChild 会有问题,此时降级处理为 appendchild - * 类似:document.head.insertBefore(targetChild, document.head.childNodes[0]) + * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild + * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0]) */ if (parent instanceof HTMLHeadElement) { const microAppHead = app.container!.querySelector('micro-app-head')! /** - * 1、passiveChild 存在,则必然为 insertBefore 或 replaceChild - * 2、removeChild时,targetChild不一定在microAppHead或者head中 + * 1. If passivechild exists, it must be insertBefore or replacechild + * 2. When removeChild, targetChild may not be in microAppHead or head */ if (passiveChild && !microAppHead.contains(passiveChild)) { return rawAppendChild.call(microAppHead, targetChild) @@ -175,17 +176,17 @@ function invokePrototypeMethod ( return rawMethod.call(parent, targetChild, passiveChild) } -// 获取映射元素 +// Get the map element function getMappingNode (node: Node): Node { return dynamicElementInMicroAppMap.get(node) ?? node } /** - * 新增元素通用处理方法 - * @param parent 父元素 - * @param newChild 新增元素 - * @param passiveChild 可能存在的passive元素 - * @param rawMethod 原方法 + * method of handle new node + * @param parent parent node + * @param newChild new node + * @param passiveChild passive node + * @param rawMethod raw method */ function commonElementHander ( parent: Node, @@ -226,12 +227,12 @@ function commonElementHander ( } /** - * 重写元素原型链方法 + * Rewrite element prototype method */ export function patchElementPrototypeMethods (): void { patchDocument() - // 重写setAttribute + // Rewrite setAttribute Element.prototype.setAttribute = function setAttribute (key: string, value: string): void { if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') { if (toString.call(value) === '[object Object]') { @@ -263,7 +264,7 @@ export function patchElementPrototypeMethods (): void { } } - // 添加元素👇 + // prototype methods of add element👇 Node.prototype.appendChild = function appendChild (newChild: T): T { return commonElementHander(this, newChild, null, rawAppendChild) } @@ -293,7 +294,7 @@ export function patchElementPrototypeMethods (): void { } } - // 删除元素👇 + // prototype methods of delete element👇 Node.prototype.removeChild = function removeChild (oldChild: T): T { if (oldChild?.__MICRO_APP_NAME__) { const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__) @@ -313,8 +314,8 @@ export function patchElementPrototypeMethods (): void { } /** - * 将微应用中新建的元素打标 - * @param element 新建的元素 + * Mark the newly created element in the micro application + * @param element new element */ function markElement (element: T): T { const appName = getCurrentAppName() @@ -324,9 +325,9 @@ function markElement (element: T): T return element } -// document相关方法 +// methods of document function patchDocument () { - // 创建元素👇 + // create element 👇 Document.prototype.createElement = function createElement ( tagName: string, options?: ElementCreationOptions, @@ -349,7 +350,7 @@ function patchDocument () { return markElement(element) } - // 查询元素👇 + // query element👇 function querySelector (selectors: string): any { const appName = getCurrentAppName() if (!appName || selectors === 'head' || selectors === 'body') { @@ -369,7 +370,7 @@ function patchDocument () { Document.prototype.querySelector = querySelector Document.prototype.querySelectorAll = querySelectorAll - // querySelector 不支持数字开头 + // querySelector does not support the beginning of a number Document.prototype.getElementById = function getElementById (key: string): HTMLElement | null { const appName = getCurrentAppName() if (!appName || /^\d/.test(key)) { @@ -420,7 +421,7 @@ function releasePatchDocument (): void { Document.prototype.getElementsByName = rawGetElementsByName } -// 解除绑定 +// release patch export function releasePatches (): void { setCurrentAppName(null) releasePatchDocument() @@ -433,7 +434,7 @@ export function releasePatches (): void { Element.prototype.prepend = rawPrepend } -// 设置micro-app、micro-app-body的样式 +// Set the style of micro-app-head and micro-app-body let hasRejectMicroAppStyle = false export function rejectMicroAppStyle (): void { if (!hasRejectMicroAppStyle) { diff --git a/src/source/scoped_css.ts b/src/source/scoped_css.ts index 455b6d69c..983b6c938 100644 --- a/src/source/scoped_css.ts +++ b/src/source/scoped_css.ts @@ -10,8 +10,8 @@ enum CSSRuleType { } /** - * 绑定css作用域 - * 特殊情况: + * Bind css scope + * Special case: * 1. html-abc | abc-html * 2. html body.abc * 3. abchtml | htmlabc | abcbody | bodyabc @@ -37,7 +37,7 @@ function scopedStyleRule (rule: CSSStyleRule, prefix: string): string { return cssText.replace(/^[\s\S]+{/, (selectors) => { return selectors.replace(/(^|,)([^,]+)/g, (all, $1, $2) => { if (builtInRootSelectorRE.test($2)) { - // body[name=xx]|body.xx|body#xx 等都不需要转换 + // body[name=xx]|body.xx|body#xx etc. do not need to handle return all.replace(builtInRootSelectorRE, prefix) } return `${$1} ${prefix} ${$2.replace(/^\s*/, '')}` @@ -46,11 +46,11 @@ function scopedStyleRule (rule: CSSStyleRule, prefix: string): string { } /** - * 补全静态资源地址 - * @param cssText css内容 - * @param baseURI 域名 - * @param textContent 原始内容 - * @param linkpath link资源地址,如果是link转换为的style,会带有linkpath + * Complete static resource address + * @param cssText css content + * @param baseURI domain name + * @param textContent origin content + * @param linkpath link resource address, if it is the style converted from link, it will have linkpath */ function scopedHost ( cssText: string, @@ -83,7 +83,7 @@ function scopedHost ( }) } -// 处理media 和 supports +// handle media and supports function scopedPackRule ( rule: CSSMediaRule | CSSSupportsRule, prefix: string, @@ -94,9 +94,9 @@ function scopedPackRule ( } /** - * 依次处理每个cssRule + * Process each cssrule * @param rules cssRule - * @param prefix 前缀 + * @param prefix prefix as micro-app[name=xxx] */ function scopedRule (rules: CSSRule[], prefix: string): string { let result = '' @@ -121,7 +121,7 @@ function scopedRule (rules: CSSRule[], prefix: string): string { } /** - * 绑定css通用方法 + * common method of bind CSS */ function commonAction ( templateStyle: HTMLStyleElement, @@ -139,9 +139,9 @@ function commonAction ( linkpath, ) /** - * 解决部分safari浏览器下content引号丢失的问题 - * 参考文档 https://developer.mozilla.org/zh-CN/docs/Web/CSS/content - * 如果依然有问题,推荐使用attr()方案降级处理 + * Solve the problem of missing content quotes in some Safari browsers + * docs: https://developer.mozilla.org/zh-CN/docs/Web/CSS/content + * If there are still problems, it is recommended to use the attr() */ if (isSafari()) { result = result.replace(/([;{]\s*content:\s*)([^\s"][^";}]*)/gm, (all, $1, $2) => { @@ -160,9 +160,9 @@ function commonAction ( let templateStyle: HTMLStyleElement = rawDocument.body.querySelector('#micro-app-template-style') /** - * 绑定css作用域 - * @param styleElement 目标style元素 - * @param appName 应用名称 + * scopedCSS + * @param styleElement target style element + * @param appName app name */ export default function scopedCSS (styleElement: HTMLStyleElement, appName: string): HTMLStyleElement { const app = appInstanceMap.get(appName) @@ -182,7 +182,7 @@ export default function scopedCSS (styleElement: HTMLStyleElement, appName: stri } else { const observer = new MutationObserver(function () { observer.disconnect() - // styled-component 暂时不处理 + // styled-component will not be processed temporarily if ( (!styleElement.textContent && styleElement.sheet?.cssRules?.length) || styleElement.hasAttribute('data-styled') diff --git a/src/source/scripts.ts b/src/source/scripts.ts index 29398349c..04328bc41 100644 --- a/src/source/scripts.ts +++ b/src/source/scripts.ts @@ -19,16 +19,16 @@ import { } from './load_event' import microApp from '../micro_app' -// 全局script,跨应用复用 +// Global scripts, reuse across apps export const globalScripts = new Map() const supportModuleScript = isSupportModuleScript() /** - * 提取script标签 - * @param script script标签 - * @param parent 父级容器 - * @param app 实例 - * @param isDynamic 是否动态插入 + * Extract script elements + * @param script script element + * @param parent parent element of script + * @param app app + * @param isDynamic dynamic insert */ export function extractScriptElement ( script: HTMLScriptElement, @@ -45,7 +45,7 @@ export function extractScriptElement ( (!supportModuleScript && script.type === 'module') ) { replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`) - } else if (src) { // 远程script + } else if (src) { // remote script src = CompletionPath(src, app.url) const info = { code: '', @@ -62,7 +62,7 @@ export function extractScriptElement ( } else { return { url: src, info } } - } else if (script.textContent) { // 内联script + } else if (script.textContent) { // inline script const nonceStr: string = createNonceStr() const info = { code: script.textContent, @@ -90,9 +90,9 @@ export function extractScriptElement ( } /** - * 获取script远程资源 - * @param wrapElement 容器 - * @param app 实例 + * Get remote resources of script + * @param wrapElement htmlDom + * @param app app */ export function fetchScriptsFromHtml ( wrapElement: HTMLElement, @@ -131,10 +131,10 @@ export function fetchScriptsFromHtml ( } /** - * 请求js成功,记录code值 - * @param url script地址 - * @param info 详情 - * @param data 资源内容 + * fetch js succeeded, record the code value + * @param url script address + * @param info resource script info + * @param data code */ export function fetchScriptSuccess ( url: string, @@ -149,9 +149,9 @@ export function fetchScriptSuccess ( } /** - * mount生命周期中执行js - * @param scriptList html中的script列表 - * @param app 应用实例 + * Execute js in the mount lifecycle + * @param scriptList script list + * @param app app */ export function execScripts (scriptList: Map, app: AppInterface): void { const scriptListEntries: Array<[string, sourceScriptInfo]> = Array.from(scriptList.entries()) @@ -184,11 +184,11 @@ export function execScripts (scriptList: Map, app: App } /** - * 获取动态创建的远程js - * @param url js地址 + * Get dynamically created remote script + * @param url script address * @param info info - * @param app 应用 - * @param originScript 原script标签 + * @param app app + * @param originScript origin script element */ export function runDynamicScript ( url: string, @@ -242,12 +242,12 @@ export function runDynamicScript ( } /** - * 运行代码 - * @param url 文件地址 - * @param code js代码 - * @param app 应用实例 - * @param module 是否是module标签 - * @param isDynamic 动态创建的script标签 + * run code + * @param url script address + * @param code js code + * @param app app + * @param module type='module' of script + * @param isDynamic dynamically created script */ export function runScript ( url: string, @@ -274,11 +274,10 @@ export function runScript ( } /** - * 绑定js作用域 - * @param url js地址 - * @param code 代码内容 - * @param app 应用实例 - * @returns string + * bind js scope + * @param url script address + * @param code code + * @param app app */ function bindScope ( url: string, @@ -296,12 +295,11 @@ function bindScope ( } /** - * 调用插件处理文件 - * @param url js地址 - * @param code 代码 - * @param appName 应用名称 - * @param plugins 插件列表 - * @returns string + * Call the plugin to process the file + * @param url script address + * @param code code + * @param appName app name + * @param plugins plugin list */ function usePlugins (url: string, code: string, appName: string, plugins: plugins): string { if (toString.call(plugins.global) === '[object Array]') {