-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
818 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# http://editorconfig.org | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 2 | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[*.md] | ||
trim_trailing_whitespace = false | ||
|
||
[Makefile] | ||
indent_style = tab |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
pids | ||
logs | ||
node_modules | ||
npm-debug.log | ||
coverage/ | ||
run | ||
dist | ||
public | ||
.DS_Store | ||
.nyc_output | ||
.basement | ||
config.local.js | ||
.umi | ||
.umi-production | ||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
"name": "qiankun", | ||
"version": "0.0.1", | ||
"description": "An completed implementation of Micro Frontends", | ||
"main": "index.js", | ||
"scripts": { | ||
"build": "npm run build:esm && npm run build:cjs", | ||
"build:esm": "rm -fr esm && tsc", | ||
"build:cjs": "rm -fr lib && tsc -p tsconfig.cjs.json", | ||
"prepush": "tslint ", | ||
"lint": "tslint 'src/**/*.ts'", | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/kuitos/qiankun.git" | ||
}, | ||
"files": [ | ||
"esm", | ||
"lib" | ||
], | ||
"author": "", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/kuitos/qiankun/issues" | ||
}, | ||
"homepage": "https://github.com/kuitos/qiankun#readme", | ||
"dependencies": { | ||
"import-html-entry": "^0.4.11", | ||
"lodash": "^4.17.11", | ||
"single-spa": "^4.3.3", | ||
"tslib": "^1.9.3" | ||
}, | ||
"devDependencies": { | ||
"@types/lodash": "^4.14.129", | ||
"@types/node": "^12.0.2", | ||
"husky": "^2.3.0", | ||
"tslint": "^5.16.0", | ||
"tslint-eslint-rules": "^5.4.0", | ||
"typescript": "^3.4.5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/** | ||
* @author Kuitos | ||
* @since 2019-02-19 | ||
*/ | ||
import { getMountedApps, navigateToUrl } from 'single-spa'; | ||
|
||
export function runDefaultMountEffects(defaultAppLink: string) { | ||
|
||
window.addEventListener('single-spa:no-app-change', () => { | ||
const mountedApps = getMountedApps(); | ||
if (!mountedApps.length) { | ||
navigateToUrl(defaultAppLink); | ||
} | ||
}, { once: true }); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/** | ||
* @author Kuitos | ||
* @since 2019-04-11 | ||
*/ | ||
|
||
import { isFunction, noop } from 'lodash'; | ||
|
||
export default function hijack() { | ||
|
||
// FIXME umi unmount feature request | ||
// @see http://gitlab.alipay-inc.com/bigfish/bigfish/issues/1154 | ||
let rawHistoryListen = (..._: any[]) => noop; | ||
const historyListeners: Array<typeof noop> = []; | ||
const historyUnListens: Array<typeof noop> = []; | ||
|
||
if ((window as any).g_history && isFunction((window as any).g_history.listen)) { | ||
|
||
rawHistoryListen = (window as any).g_history.listen.bind((window as any).g_history); | ||
|
||
(window as any).g_history.listen = (listener: typeof noop) => { | ||
|
||
historyListeners.push(listener); | ||
|
||
const unListen = rawHistoryListen(listener); | ||
historyUnListens.push(unListen); | ||
|
||
return () => { | ||
unListen(); | ||
historyUnListens.splice(historyUnListens.indexOf(unListen), 1); | ||
historyListeners.splice(historyListeners.indexOf(listener), 1); | ||
}; | ||
}; | ||
} | ||
|
||
return function free() { | ||
|
||
let rebuild = noop; | ||
|
||
/* | ||
还存在余量 listener 表明未被卸载,存在两种情况 | ||
1. 应用在 unmout 时未正确卸载 listener | ||
2. listener 是应用 mount 之前绑定的, | ||
第二种情况下应用在下次 mount 之前需重新绑定该 listener | ||
*/ | ||
if (historyListeners.length) { | ||
rebuild = () => { | ||
// 必须使用 window.g_history.listen 的方式重新绑定 listener,从而能保证 rebuild 这部分也能被捕获到,否则在应用卸载后无法正确的移除这部分副作用 | ||
historyListeners.forEach(listener => (window as any).g_history.listen(listener)); | ||
}; | ||
} | ||
|
||
// 卸载余下的 listener | ||
historyUnListens.forEach(unListen => unListen()); | ||
|
||
// restore | ||
if ((window as any).g_history && isFunction((window as any).g_history.listen)) { | ||
(window as any).g_history.listen = rawHistoryListen; | ||
} | ||
|
||
return rebuild; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* @author Kuitos | ||
* @since 2019-04-11 | ||
*/ | ||
|
||
import hijackHistoryListener from './historyListener'; | ||
import hijackTimer from './timer'; | ||
import hijackWindowListener from './windowListener'; | ||
|
||
export function hijack() { | ||
|
||
return [ | ||
hijackTimer(), | ||
hijackWindowListener(), | ||
hijackHistoryListener(), | ||
]; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/** | ||
* @author Kuitos | ||
* @since 2019-04-11 | ||
*/ | ||
|
||
import { noop } from 'lodash'; | ||
import { sleep } from '../utils'; | ||
|
||
export default function hijack() { | ||
|
||
const rawWindowInterval = window.setInterval.bind(window); | ||
const rawWindowTimeout = window.setTimeout.bind(window); | ||
const timerIds: number[] = []; | ||
const intervalIds: number[] = []; | ||
|
||
window.setInterval = (...args: any[]) => { | ||
// @ts-ignore | ||
const intervalId = rawWindowInterval(...args); | ||
intervalIds.push(intervalId); | ||
return intervalId; | ||
}; | ||
|
||
window.setTimeout = (...args: any[]) => { | ||
// @ts-ignore | ||
const timerId = rawWindowTimeout(...args); | ||
timerIds.push(timerId); | ||
return timerId; | ||
}; | ||
|
||
return function free() { | ||
window.setInterval = rawWindowInterval; | ||
window.setTimeout = rawWindowTimeout; | ||
|
||
timerIds.forEach(async id => { | ||
// 延迟 timeout 的清理,因为可能会有动画还没完成 | ||
await sleep(500); | ||
window.clearTimeout(id); | ||
}); | ||
intervalIds.forEach(id => { | ||
window.clearInterval(id); | ||
}); | ||
|
||
return noop; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/** | ||
* @author Kuitos | ||
* @since 2019-04-11 | ||
*/ | ||
|
||
import { noop } from 'lodash'; | ||
|
||
export default function hijack() { | ||
|
||
const listenerMap = new Map<string, EventListenerOrEventListenerObject[]>(); | ||
const rawAddEventListener = window.addEventListener; | ||
const rawRemoveEventListener = window.removeEventListener; | ||
|
||
window.addEventListener = | ||
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) => { | ||
const listeners = listenerMap.get(type) || []; | ||
listenerMap.set(type, [...listeners, listener]); | ||
return rawAddEventListener.call(window, type, listener, options); | ||
}; | ||
|
||
window.removeEventListener = | ||
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) => { | ||
const storedTypeListeners = listenerMap.get(type); | ||
if (storedTypeListeners && storedTypeListeners.length && storedTypeListeners.indexOf(listener) !== -1) { | ||
storedTypeListeners.splice(storedTypeListeners.indexOf(listener), 1); | ||
} | ||
return rawRemoveEventListener.call(window, type, listener, options); | ||
}; | ||
|
||
return function free() { | ||
|
||
listenerMap.forEach((listeners, type) => listeners.forEach(listener => window.removeEventListener(type, listener))); | ||
window.addEventListener = rawAddEventListener.bind(window); | ||
window.removeEventListener = rawRemoveEventListener.bind(window); | ||
|
||
return noop; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/** | ||
* @author Kuitos | ||
* @since 2019-04-25 | ||
*/ | ||
|
||
import importHTML from 'import-html-entry'; | ||
import { isFunction } from 'lodash'; | ||
import { registerApplication, start as startSpa } from 'single-spa'; | ||
import prefetch from './prefetch'; | ||
import { genSandbox } from './sandbox'; | ||
|
||
import { sleep } from './utils'; | ||
|
||
export type RenderFunction = (props?: { appContent: string, loading: boolean }) => any; | ||
export type ActiveRule = (app: RegistrableApp) => boolean; | ||
export type RegistrableApp = { | ||
appName: string; // 应用名(ID),也可当做应用的产品码来用 | ||
entryHTML: string; // 应用入口 HTML | ||
routerPrefix: string; // 应用路由前缀 | ||
props?: object; | ||
}; | ||
|
||
interface Options { | ||
renderFunction: RenderFunction; | ||
activeRule: ActiveRule; | ||
} | ||
|
||
export function registerMicroApps(apps: RegistrableApp[], options: Options) { | ||
|
||
const { renderFunction, activeRule } = options; | ||
|
||
apps.forEach(app => { | ||
|
||
const { appName, entryHTML, props = {} } = app; | ||
|
||
// TODO 优化:将 prefetch 移到第一个应用 mount 之后 | ||
prefetch(entryHTML); | ||
|
||
registerApplication(appName, | ||
|
||
async () => { | ||
|
||
// 获取入口 html 模板及脚本加载器 | ||
const { template: appContent, execScripts } = await importHTML(entryHTML); | ||
// 第一次加载设置应用可见区域 dom 结构 | ||
// 确保每次应用加载前容器 dom 结构已经设置完毕 | ||
renderFunction({ appContent, loading: true }); | ||
|
||
const { sandbox, mount: mountSandbox, unmount: unmountSandbox } = genSandbox(appName); | ||
// 等待 300 ms,确保菜单切换动画完成 | ||
await sleep(300); | ||
// 获取 模块/应用 导出的 lifecycle hooks | ||
const { bootstrap: exportedBootstrap, mount, unmount } = await execScripts(sandbox); | ||
if (!isFunction(exportedBootstrap) || !isFunction(mount) || !isFunction(unmount)) { | ||
throw new Error(`You need to export the functional lifecycles in ${appName} entry`); | ||
} | ||
|
||
return { | ||
bootstrap: [exportedBootstrap], | ||
mount: [ | ||
mountSandbox, | ||
// 添加 mount hook, 确保每次应用加载前容器 dom 结构已经设置完毕 | ||
async () => renderFunction({ appContent, loading: false }), | ||
mount, | ||
], | ||
unmount: [ | ||
unmount, | ||
unmountSandbox, | ||
], | ||
}; | ||
}, | ||
|
||
() => activeRule(app), | ||
props, | ||
); | ||
}); | ||
} | ||
|
||
export function start() { | ||
startSpa(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/** | ||
* @author Kuitos | ||
* @since 2019-02-26 | ||
*/ | ||
|
||
import importHTML from 'import-html-entry'; | ||
import { noop } from 'lodash'; | ||
|
||
/** | ||
* 预加载静态资源,不兼容 requestIdleCallback 的浏览器不做任何动作 | ||
* @param entryHTML | ||
*/ | ||
export default function prefetch(entryHTML: string) { | ||
|
||
const requestIdleCallback = window.requestIdleCallback || noop; | ||
|
||
requestIdleCallback(async () => { | ||
const { getExternalScripts, getExternalStyleSheets } = await importHTML(entryHTML); | ||
requestIdleCallback(getExternalStyleSheets); | ||
requestIdleCallback(getExternalScripts); | ||
}); | ||
|
||
} |
Oops, something went wrong.