Skip to content

Commit

Permalink
✨ IE compatible (#296)
Browse files Browse the repository at this point in the history
  • Loading branch information
Deturium authored and kuitos committed Mar 26, 2020
1 parent 5a0f997 commit 4918ffa
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 199 deletions.
6 changes: 4 additions & 2 deletions src/effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
import { getMountedApps, navigateToUrl } from 'single-spa';

const firstMountLogLabel = '[qiankun]: first app mounted';
const firstMountLogLabel = '[qiankun] first app mounted';
if (process.env.NODE_ENV === 'development') {
console.time(firstMountLogLabel);
}
Expand All @@ -22,7 +22,9 @@ export function setDefaultMountApp(defaultAppLink: string) {
}

export function runDefaultMountEffects(defaultAppLink: string) {
console.warn('runDefaultMountEffects will be removed in next version, please use setDefaultMountApp instead!');
console.warn(
'[qiankun] runDefaultMountEffects will be removed in next version, please use setDefaultMountApp instead',
);
setDefaultMountApp(defaultAppLink);
}

Expand Down
2 changes: 1 addition & 1 deletion src/hijackers/dynamicHeadAppend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function setCachedRules(element: HTMLStyleElement, cssRules: CSSRuleList) {
}

function assertElementExist(appName: string, element: Element | null) {
if (!element) throw new Error(`[qiankun]: ${appName} wrapper with id ${getWrapperId(appName)} not ready!`);
if (!element) throw new Error(`[qiankun] ${appName} wrapper with id ${getWrapperId(appName)} not ready!`);
}

function getWrapperElement(appName: string) {
Expand Down
13 changes: 13 additions & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,16 @@ export type StartOpts = {

export type Rebuilder = () => void;
export type Freer = () => Rebuilder;

export interface SandBox {
/** 沙箱的名字 */
name: string;
/** 沙箱导出的代理实体 */
proxy: WindowProxy;
/** 沙箱是否在运行中 */
sandboxRunning: boolean;
/** 启动沙箱 */
active(): void;
/** 关闭沙箱 */
inactive(): void;
}
4 changes: 2 additions & 2 deletions src/prefetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function prefetchAfterFirstMounted(apps: RegistrableApp[], opts?: ImportE
const notMountedApps = apps.filter(app => mountedApps.indexOf(app.name) === -1);

if (process.env.NODE_ENV === 'development') {
console.log(`prefetch starting after ${mountedApps} mounted...`, notMountedApps);
console.log(`[qiankun] prefetch starting after ${mountedApps} mounted...`, notMountedApps);
}

notMountedApps.forEach(({ name, entry }) => prefetch(name, entry, opts));
Expand All @@ -101,7 +101,7 @@ export function prefetchAll(apps: RegistrableApp[], opts?: ImportEntryOpts): voi
'single-spa:no-app-change',
() => {
if (process.env.NODE_ENV === 'development') {
console.log('prefetch starting for all assets...', apps);
console.log('[qiankun] prefetch starting for all assets...', apps);
}
apps.forEach(({ name, entry }) => prefetch(name, entry, opts));
},
Expand Down
18 changes: 12 additions & 6 deletions src/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export function registerMicroApps<T extends object = {}>(
if (!isFunction(bootstrapApp) || !isFunction(mount) || !isFunction(unmount)) {
if (process.env.NODE_ENV === 'development') {
console.warn(
`LifeCycles are not found from ${appName} entry exports, fallback to get them from window['${appName}'] `,
`[qiankun] lifecycle not found from ${appName} entry exports, fallback to get from window['${appName}']`,
);
}

Expand All @@ -142,7 +142,7 @@ export function registerMicroApps<T extends object = {}>(
// eslint-disable-next-line prefer-destructuring
unmount = globalVariableExports.unmount;
if (!isFunction(bootstrapApp) || !isFunction(mount) || !isFunction(unmount)) {
throw new Error(`You need to export the functional lifecycles in ${appName} entry`);
throw new Error(`[qiankun] You need to export lifecycle functions in ${appName} entry`);
}
}

Expand Down Expand Up @@ -223,14 +223,20 @@ export function start(opts: StartOpts = {}) {

doPrefetch(prefetch, importEntryOpts);

if (jsSandbox) {
useJsSandbox = jsSandbox;
}

if (singular) {
singularMode = singular;
}

if (jsSandbox) {
// 快照沙箱不支持非 singular 模式
if (!window.Proxy && !singular) {
console.error('[qiankun] singular is forced to be true when jsSandbox enable but proxySandbox unavailable');
singularMode = true;
}

useJsSandbox = jsSandbox;
}

startSingleSpa();

frameworkStartedDefer.resolve();
Expand Down
188 changes: 0 additions & 188 deletions src/sandbox.ts

This file was deleted.

88 changes: 88 additions & 0 deletions src/sandbox/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* @author Kuitos
* @since 2019-04-11
*/
import { hijackAtBootstrapping, hijackAtMounting } from '../hijackers';
import { Freer, Rebuilder, SandBox } from '../interfaces';
import ProxySandbox from './proxySandbox';
import SnapshotSandbox from './snapshotSandbox';

/**
* 生成应用运行时沙箱
*
* 沙箱分两个类型:
* 1. app 环境沙箱
* app 环境沙箱是指应用初始化过之后,应用会在什么样的上下文环境运行。每个应用的环境沙箱只会初始化一次,因为子应用只会触发一次 bootstrap 。
* 子应用在切换时,实际上切换的是 app 环境沙箱。
* 2. render 沙箱
* 子应用在 app mount 开始前生成好的的沙箱。每次子应用切换过后,render 沙箱都会重现初始化。
*
* 这么设计的目的是为了保证每个子应用切换回来之后,还能运行在应用 bootstrap 之后的环境下。
*
* @param appName
*/
export function genSandbox(appName: string) {
// mounting freers are one-off and should be re-init at every mounting time
let mountingFreers: Freer[] = [];

let sideEffectsRebuilders: Rebuilder[] = [];

let sandbox: SandBox;
if (window.Proxy) {
sandbox = new ProxySandbox(appName);
} else {
console.warn('[qiankun] Miss window.Proxy, proxySandbox degenerate into snapshotSandbox');
sandbox = new SnapshotSandbox(appName);
}

// some side effect could be be invoked while bootstrapping, such as dynamic stylesheet injection with style-loader, especially during the development phase
const bootstrappingFreers = hijackAtBootstrapping(appName, sandbox.proxy);

return {
sandbox: sandbox.proxy,

/**
* 沙箱被 mount
* 可能是从 bootstrap 状态进入的 mount
* 也可能是从 unmount 之后再次唤醒进入 mount
*/
async mount() {
const sideEffectsRebuildersAtBootstrapping = sideEffectsRebuilders.slice(0, bootstrappingFreers.length);
const sideEffectsRebuildersAtMounting = sideEffectsRebuilders.slice(bootstrappingFreers.length);

// must rebuild the side effects which added at bootstrapping firstly to recovery to nature state
if (sideEffectsRebuildersAtBootstrapping.length) {
sideEffectsRebuildersAtBootstrapping.forEach(rebuild => rebuild());
}

/* ------------------------------------------ 因为有上下文依赖(window),以下代码执行顺序不能变 ------------------------------------------ */

/* ------------------------------------------ 1. 启动/恢复 沙箱------------------------------------------ */
sandbox.active();

/* ------------------------------------------ 2. 开启全局变量补丁 ------------------------------------------*/
// render 沙箱启动时开始劫持各类全局监听,尽量不要在应用初始化阶段有 事件监听/定时器 等副作用
mountingFreers = hijackAtMounting(appName, sandbox.proxy);

/* ------------------------------------------ 3. 重置一些初始化时的副作用 ------------------------------------------*/
// 存在 rebuilder 则表明有些副作用需要重建
if (sideEffectsRebuildersAtMounting.length) {
sideEffectsRebuildersAtMounting.forEach(rebuild => rebuild());
}

// clean up rebuilders
sideEffectsRebuilders = [];
},

/**
* 恢复 global 状态,使其能回到应用加载之前的状态
*/
async unmount() {
// record the rebuilders of window side effects (event listeners or timers)
// note that the frees of mounting phase are one-off as it will be re-init at next mounting
sideEffectsRebuilders = [...bootstrappingFreers, ...mountingFreers].map(free => free());

sandbox.inactive();
},
};
}
Loading

0 comments on commit 4918ffa

Please sign in to comment.