Skip to content

Commit

Permalink
feat: 添加默认loading的能力和api (#121)
Browse files Browse the repository at this point in the history
close #111 #120
  • Loading branch information
yiludege authored Sep 1, 2022
1 parent 3d4232d commit 841385f
Show file tree
Hide file tree
Showing 15 changed files with 140 additions and 13 deletions.
2 changes: 2 additions & 0 deletions docs/api/setupApp.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type cacheOptions {
exec?: boolean;
/** 渲染的容器 */
el?: HTMLElement | string;
/** 子应用加载时loading元素 */
loading?: HTMLElement;
/** 路由同步开关 */
sync?: boolean;
/** 子应用短路径替换,路由同步时生效 */
Expand Down
8 changes: 8 additions & 0 deletions docs/api/startApp.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type startOption {
url: string;
/** 渲染的容器 */
el: HTMLElement | string;
/** 子应用加载时loading元素 */
loading?: HTMLElement;
/** 路由同步开关, false刷新无效,但是前进后退依然有效 */
sync?: boolean;
/** 子应用短路径替换,路由同步时生效 */
Expand Down Expand Up @@ -83,6 +85,12 @@ type startOption {

- **详情:** 子应用渲染容器,子应用渲染容器的最好设置好宽高防止渲染问题,在`webcomponent`元素上无界还设置了`wujie_iframe``class`方便用户自定义样式

## loading

- **类型:** `HTMLElement`

- **详情:** 自定义的`loading`元素,如果不想出现默认加载,可以赋值一个空元素:`document.createElement('span')`

## sync

- **默认值:** `false`
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Techniques, strategies and recipes for building a modern web app with multiple t

## 无界方案

在乾坤的`issue`中一个[议题](https://github.com/umijs/qiankun/issues/286)非常有意思,有个开发者提出能否利用`iframe`来实现`js`沙箱能力,这个`ideal`启发了无界方案,下面详细介绍
在乾坤的`issue`中一个[议题](https://github.com/umijs/qiankun/issues/286)非常有意思,有个开发者提出能否利用`iframe`来实现`js`沙箱能力,这个`idea`启发了无界方案,下面详细介绍

### 应用加载机制和 js 沙箱机制

Expand Down
2 changes: 2 additions & 0 deletions docs/pack/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const wujieVueOptions = {
width: { type: String, default: "" },
height: { type: String, default: "" },
name: { type: String, default: "" },
loading: { type: HTMLElement, default: undefined },
url: { type: String, default: "" },
sync: { type: Boolean, default: false },
prefix: { type: Object, default: undefined },
Expand Down Expand Up @@ -124,6 +125,7 @@ const wujieVueOptions = {
name: this.name,
url: this.url,
el: this.$refs.wujie,
loading: this.loading,
alive: this.alive,
fetch: this.fetch,
props: this.props,
Expand Down
3 changes: 3 additions & 0 deletions docs/pack/react.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export default class WujieReact extends React.PureComponent {
height: PropTypes.string,
width: PropTypes.string,
name: PropTypes.string,
loading: PropTypes.element;
url: PropTypes.string,
alive: PropTypes.bool,
fetch: PropTypes.func,
Expand Down Expand Up @@ -104,6 +105,7 @@ export default class WujieReact extends React.PureComponent {
const {
name,
url,
loading,
alive,
fetch,
props,
Expand All @@ -128,6 +130,7 @@ export default class WujieReact extends React.PureComponent {
name,
url,
el: this.state.myRef.current,
loading,
alive,
fetch,
props,
Expand Down
4 changes: 2 additions & 2 deletions examples/main-react/src/App.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HashRouter as Router, Route, Routes, NavLink, useLocation } from "react-router-dom";
import { HashRouter as Router, Route, Routes, NavLink, Navigate, useLocation } from "react-router-dom";
import React, { useState } from "react";
import Home from "./pages/Home";
import React16 from "./pages/React16";
Expand Down Expand Up @@ -154,7 +154,7 @@ class App extends React.PureComponent {
<Route exact path="/vite-sub/:path" element={<Vite />} />
<Route exact path="/angular12" element={<Angular12 />} />
<Route exact path="/all" element={<All />} />
{/* <Route path="*" element={<Navigate to="/home" replace />} /> */}
<Route path="*" element={<Navigate to="/home" replace />} />
</Routes>
</div>
</Router>
Expand Down
20 changes: 20 additions & 0 deletions packages/wujie-core/src/constant.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
// 私有元素属性
export const WUJIE_DATA_ID = "data-wujie-id";
export const WUJIE_DATA_FLAG = "data-wujie-Flag";
export const CONTAINER_POSITION_DATA_FLAG = "data-container-position-flag";
export const CONTAINER_OVERFLOW_DATA_FLAG = "data-container-overflow-flag";
export const LOADING_DATA_FLAG = "data-loading-flag";
export const WUJIE_DATA_ATTACH_CSS_FLAG = "data-wujie-attach-css-flag";

// 需要使用的某些固定变量
export const WUJIE_IFRAME_CLASS = "wujie_iframe";
export const WUJIE_ALL_EVENT = "_wujie_all_event";
export const WUJIE_SHADE_STYLE =
"position: fixed; z-index: 2147483647; visibility: hidden; inset: 0px; backface-visibility: hidden;";
export const WUJIE_LOADING_STYLE =
"position: absolute; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; z-index:1;";

export const WUJIE_LOADING_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="30px" viewBox="0 0 24 30">
<rect x="0" y="13" width="4" height="5" fill="#909090">
<animate attributeName="height" attributeType="XML" values="5;21;5" begin="0s" dur="0.6s" repeatCount="indefinite"></animate>
<animate attributeName="y" attributeType="XML" values="13; 5; 13" begin="0s" dur="0.6s" repeatCount="indefinite"></animate>
</rect>
<rect x="10" y="13" width="4" height="5" fill="#909090">
<animate attributeName="height" attributeType="XML" values="5;21;5" begin="0.15s" dur="0.6s" repeatCount="indefinite"></animate>
<animate attributeName="y" attributeType="XML" values="13; 5; 13" begin="0.15s" dur="0.6s" repeatCount="indefinite"></animate>
</rect>
<rect x="20" y="13" width="4" height="5" fill="#909090">
<animate attributeName="height" attributeType="XML" values="5;21;5" begin="0.3s" dur="0.6s" repeatCount="indefinite"></animate>
<animate attributeName="y" attributeType="XML" values="13; 5; 13" begin="0.3s" dur="0.6s" repeatCount="indefinite"></animate>
</rect>
</svg>`;

// 提醒类
export const WUJIE_TIPS_NO_URL = "url参数为空";
Expand Down
27 changes: 24 additions & 3 deletions packages/wujie-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import importHTML, { processCssLoader } from "./entry";
import { StyleObject } from "./template";
import WuJie, { lifecycle } from "./sandbox";
import { defineWujieWebComponent } from "./shadow";
import { defineWujieWebComponent, addLoading } from "./shadow";
import { processAppForHrefJump } from "./sync";
import { getPlugins } from "./plugin";
import { wujieSupport, mergeOptions, isFunction, requestIdleCallback, isMatchSyncQueryById, warn } from "./utils";
Expand Down Expand Up @@ -119,6 +119,8 @@ export type startOptions = baseOptions & {
sync?: boolean;
/** 子应用短路径替换,路由同步时生效 */
prefix?: { [key: string]: string };
/** 子应用加载时loading元素 */
loading?: HTMLElement;
};

export type cacheOptions = baseOptions & {
Expand All @@ -130,6 +132,8 @@ export type cacheOptions = baseOptions & {
sync?: boolean;
/** 子应用短路径替换,路由同步时生效 */
prefix?: { [key: string]: string };
/** 子应用加载时loading元素 */
loading?: HTMLElement;
};

/**
Expand Down Expand Up @@ -167,8 +171,23 @@ export async function startApp(startOptions: startOptions): Promise<Function | v
const cacheOptions = getOptionsById(startOptions.name);
// 合并缓存配置
const options = mergeOptions(startOptions, cacheOptions);
const { name, url, replace, fetch, props, attrs, fiber, alive, degrade, sync, prefix, el, plugins, lifecycles } =
options;
const {
name,
url,
replace,
fetch,
props,
attrs,
fiber,
alive,
degrade,
sync,
prefix,
el,
loading,
plugins,
lifecycles,
} = options;
// 已经初始化过的应用,快速渲染
if (sandbox) {
sandbox.plugins = getPlugins(plugins);
Expand Down Expand Up @@ -212,6 +231,8 @@ export async function startApp(startOptions: startOptions): Promise<Function | v
}
}

// 设置loading
addLoading(el, loading);
const newSandbox = new WuJie({ name, url, attrs, fiber, degrade, plugins, lifecycles });
newSandbox.lifecycles?.beforeLoad?.(newSandbox.iframe.contentWindow);
const { template, getExternalScripts, getExternalStyleSheets } = await importHTML(url, {
Expand Down
4 changes: 4 additions & 0 deletions packages/wujie-core/src/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
renderTemplateToShadowRoot,
createIframeContainer,
renderTemplateToIframe,
removeLoading,
} from "./shadow";
import { proxyGenerator, localGenerator } from "./proxy";
import { ScriptResultList } from "./entry";
Expand Down Expand Up @@ -320,6 +321,8 @@ export default class Wujie {
this.execQueue.shift()?.();
};
this.execQueue.push(this.fiber ? () => requestIdleCallback(domLoadedTrigger) : domLoadedTrigger);
// 由于没有办法准确定位是哪个代码做了mount,保活、重建模式提前关闭loading
if (this.alive || !isFunction(this.iframe.contentWindow.__WUJIE_UNMOUNT)) removeLoading(this.el);
this.execQueue.shift()();

// 所有的execQueue队列执行完毕,start才算结束,保证串行的执行子应用
Expand All @@ -340,6 +343,7 @@ export default class Wujie {
public mount(): void {
if (this.mountFlag) return;
if (isFunction(this.iframe.contentWindow.__WUJIE_MOUNT)) {
removeLoading(this.el);
this.lifecycles?.beforeMount?.(this.iframe.contentWindow);
this.iframe.contentWindow.__WUJIE_MOUNT();
this.lifecycles?.afterMount?.(this.iframe.contentWindow);
Expand Down
64 changes: 61 additions & 3 deletions packages/wujie-core/src/shadow.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { WUJIE_DATA_ID, WUJIE_IFRAME_CLASS, WUJIE_SHADE_STYLE } from "./constant";
import {
WUJIE_DATA_ID,
WUJIE_IFRAME_CLASS,
WUJIE_SHADE_STYLE,
CONTAINER_POSITION_DATA_FLAG,
CONTAINER_OVERFLOW_DATA_FLAG,
LOADING_DATA_FLAG,
WUJIE_LOADING_STYLE,
WUJIE_LOADING_SVG,
} from "./constant";
import { getWujieById, rawElementAppendChild, rawElementRemoveChild, relativeElementTagAttrMap } from "./common";
import { getExternalStyleSheets } from "./entry";
import Wujie from "./sandbox";
Expand Down Expand Up @@ -58,8 +67,11 @@ export function createWujieWebComponent(id: string): HTMLElement {
export function renderElementToContainer(element: Element, selectorOrElement: string | HTMLElement): HTMLElement {
const container = getContainer(selectorOrElement);
if (container && !container.contains(element)) {
// 清除内容
clearChild(container);
// 有 loading 无需清理,已经清理过了
if (!container.querySelector(`div[${LOADING_DATA_FLAG}]`)) {
// 清除内容
clearChild(container);
}
// 插入元素
if (element) {
rawElementAppendChild.call(container, element);
Expand Down Expand Up @@ -242,6 +254,52 @@ export function clearChild(root: ShadowRoot | Node): void {
}
}

/**
* 给容器添加loading
*/
export function addLoading(el: string | HTMLElement, loading: HTMLElement): void {
const container = getContainer(el);
clearChild(container);
// 给容器设置一些样式,防止 loading 抖动
const containerStyles = window.getComputedStyle(container);
if (containerStyles.position === "static") {
container.setAttribute(CONTAINER_POSITION_DATA_FLAG, containerStyles.position);
container.setAttribute(
CONTAINER_OVERFLOW_DATA_FLAG,
containerStyles.overflow === "visible" ? "" : containerStyles.overflow
);
container.style.setProperty("position", "relative");
container.style.setProperty("overflow", "hidden");
} else if (["relative", "sticky"].includes(containerStyles.position)) {
container.setAttribute(
CONTAINER_OVERFLOW_DATA_FLAG,
containerStyles.overflow === "visible" ? "" : containerStyles.overflow
);
container.style.setProperty("overflow", "hidden");
}
const loadingContainer = document.createElement("div");
loadingContainer.setAttribute(LOADING_DATA_FLAG, "");
loadingContainer.setAttribute("style", WUJIE_LOADING_STYLE);
if (loading) loadingContainer.appendChild(loading);
else loadingContainer.innerHTML = WUJIE_LOADING_SVG;
container.appendChild(loadingContainer);
}
/**
* 移除loading
*/
export function removeLoading(el: HTMLElement): void {
// 去除容器设置的样式
const positionFlag = el.getAttribute(CONTAINER_POSITION_DATA_FLAG);
const overflowFlag = el.getAttribute(CONTAINER_OVERFLOW_DATA_FLAG);
if (positionFlag) el.style.removeProperty("position");
if (overflowFlag !== null) {
overflowFlag ? el.style.setProperty("overflow", overflowFlag) : el.style.removeProperty("overflow");
}
el.removeAttribute(CONTAINER_POSITION_DATA_FLAG);
el.removeAttribute(CONTAINER_OVERFLOW_DATA_FLAG);
const loadingContainer = el.querySelector(`div[${LOADING_DATA_FLAG}]`);
loadingContainer && el.removeChild(loadingContainer);
}
/**
* 获取修复好的样式元素
* 主要是针对对root样式和font-face样式
Expand Down
1 change: 1 addition & 0 deletions packages/wujie-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ export function mergeOptions(options: cacheOptions, cacheOptions: cacheOptions)
props: options.props || cacheOptions?.props,
sync: options.sync !== undefined ? options.sync : cacheOptions?.sync,
prefix: options.prefix || cacheOptions?.prefix,
loading: options.loading || cacheOptions?.loading,
// 默认 {}
attrs: options.attrs !== undefined ? options.attrs : cacheOptions?.attrs || {},
// 默认 true
Expand Down
7 changes: 4 additions & 3 deletions packages/wujie-react/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default class WujieReact extends React.PureComponent {
height: typeof PropTypes.string;
width: typeof PropTypes.string;
name: typeof PropTypes.string;
loading: typeof PropTypes.element;
url: typeof PropTypes.string;
alive: typeof PropTypes.bool;
fetch: typeof PropTypes.func;
Expand All @@ -22,9 +23,9 @@ export default class WujieReact extends React.PureComponent {
afterMount: typeof PropTypes.func;
beforeUnmount: typeof PropTypes.func;
afterUnmount: typeof PropTypes.func;
activated: typeof PropTypes.func,
deactivated: typeof PropTypes.func,
loadError: typeof PropTypes.func,
activated: typeof PropTypes.func;
deactivated: typeof PropTypes.func;
loadError: typeof PropTypes.func;
};
static bus: typeof bus;
static setupApp: typeof setupApp;
Expand Down
3 changes: 3 additions & 0 deletions packages/wujie-react/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default class WujieReact extends React.PureComponent {
height: PropTypes.string,
width: PropTypes.string,
name: PropTypes.string,
loading: PropTypes.element,
url: PropTypes.string,
alive: PropTypes.bool,
fetch: PropTypes.func,
Expand Down Expand Up @@ -44,6 +45,7 @@ export default class WujieReact extends React.PureComponent {
const {
name,
url,
loading,
alive,
fetch,
props,
Expand All @@ -69,6 +71,7 @@ export default class WujieReact extends React.PureComponent {
name,
url,
el: this.state.myRef.current,
loading,
alive,
fetch,
props,
Expand Down
2 changes: 2 additions & 0 deletions packages/wujie-vue2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const wujieVueOptions = {
width: { type: String, default: "" },
height: { type: String, default: "" },
name: { type: String, default: "" },
loading: { type: HTMLElement, default: undefined },
url: { type: String, default: "" },
sync: { type: Boolean, default: undefined },
prefix: { type: Object, default: undefined },
Expand Down Expand Up @@ -52,6 +53,7 @@ const wujieVueOptions = {
name: this.name,
url: this.url,
el: this.$refs.wujie,
loading: this.loading,
alive: this.alive,
fetch: this.fetch,
props: this.props,
Expand Down
4 changes: 3 additions & 1 deletion packages/wujie-vue3/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ const wujieVueOptions = {
width: { type: String, default: "" },
height: { type: String, default: "" },
name: { type: String, default: "" },
loading: { type: HTMLElement, default: undefined },
url: { type: String, default: "" },
sync: { type: Boolean, default: undefined },
prefix: { type: Object, default: undefined },
alive: { type: Boolean, default: undefined },
props: { type: Object, default: undefined },
attrs: {type: Object, default: undefined},
attrs: { type: Object, default: undefined },
replace: { type: Function, default: undefined },
fetch: { type: Function, default: undefined },
fiber: { type: Boolean, default: undefined },
Expand Down Expand Up @@ -52,6 +53,7 @@ const wujieVueOptions = {
name: this.name,
url: this.url,
el: this.$refs.wujie,
loading: this.loading,
alive: this.alive,
fetch: this.fetch,
props: this.props,
Expand Down

0 comments on commit 841385f

Please sign in to comment.