diff --git a/examples/main-react/package.json b/examples/main-react/package.json
index ad868be01..ded9d1d7c 100644
--- a/examples/main-react/package.json
+++ b/examples/main-react/package.json
@@ -62,6 +62,7 @@
},
"scripts": {
"start": "node scripts/start.js",
+ "integration": "node scripts/integration.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js"
},
diff --git a/examples/main-react/scripts/integration.js b/examples/main-react/scripts/integration.js
new file mode 100644
index 000000000..7def77f71
--- /dev/null
+++ b/examples/main-react/scripts/integration.js
@@ -0,0 +1,153 @@
+
+
+// Do this as the first thing so that any code reading it knows the right env.
+process.env.BABEL_ENV = 'development';
+process.env.NODE_ENV = 'development';
+
+// Makes the script crash on unhandled rejections instead of silently
+// ignoring them. In the future, promise rejections that are not handled will
+// terminate the Node.js process with a non-zero exit code.
+process.on('unhandledRejection', err => {
+ throw err;
+});
+
+// Ensure environment variables are read.
+require('../config/env');
+
+const fs = require('fs');
+const chalk = require('react-dev-utils/chalk');
+const webpack = require('webpack');
+const WebpackDevServer = require('webpack-dev-server');
+const clearConsole = require('react-dev-utils/clearConsole');
+const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
+const {
+ choosePort,
+ createCompiler,
+ prepareProxy,
+ prepareUrls,
+} = require('react-dev-utils/WebpackDevServerUtils');
+const openBrowser = require('react-dev-utils/openBrowser');
+const semver = require('semver');
+const paths = require('../config/paths');
+const configFactory = require('../config/webpack.config');
+const createDevServerConfig = require('../config/webpackDevServer.config');
+const getClientEnvironment = require('../config/env');
+const react = require(require.resolve('react', { paths: [paths.appPath] }));
+
+const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
+const useYarn = fs.existsSync(paths.yarnLockFile);
+const isInteractive = process.stdout.isTTY;
+
+// Warn and crash if required files are missing
+if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
+ process.exit(1);
+}
+
+// Tools like Cloud9 rely on this.
+const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
+const HOST = process.env.HOST || '0.0.0.0';
+
+if (process.env.HOST) {
+ console.log(
+ chalk.cyan(
+ `Attempting to bind to HOST environment variable: ${chalk.yellow(
+ chalk.bold(process.env.HOST)
+ )}`
+ )
+ );
+ console.log(
+ `If this was unintentional, check that you haven't mistakenly set it in your shell.`
+ );
+ console.log(
+ `Learn more here: ${chalk.yellow('https://cra.link/advanced-config')}`
+ );
+ console.log();
+}
+
+// We require that you explicitly set browsers and do not fall back to
+// browserslist defaults.
+const { checkBrowsers } = require('react-dev-utils/browsersHelper');
+checkBrowsers(paths.appPath, isInteractive)
+ .then(() => {
+ // We attempt to use the default port but if it is busy, we offer the user to
+ // run on a different port. `choosePort()` Promise resolves to the next free port.
+ return choosePort(HOST, DEFAULT_PORT);
+ })
+ .then(port => {
+ if (port == null) {
+ // We have not found a port.
+ return;
+ }
+
+ const config = configFactory('development');
+ const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
+ const appName = require(paths.appPackageJson).name;
+
+ const useTypeScript = fs.existsSync(paths.appTsConfig);
+ const urls = prepareUrls(
+ protocol,
+ HOST,
+ port,
+ paths.publicUrlOrPath.slice(0, -1)
+ );
+ // Create a webpack compiler that is configured with custom messages.
+ const compiler = createCompiler({
+ appName,
+ config,
+ urls,
+ useYarn,
+ useTypeScript,
+ webpack,
+ });
+ // Load proxy config
+ const proxySetting = require(paths.appPackageJson).proxy;
+ const proxyConfig = prepareProxy(
+ proxySetting,
+ paths.appPublic,
+ paths.publicUrlOrPath
+ );
+ // Serve webpack assets generated by the compiler over a web server.
+ const serverConfig = {
+ ...createDevServerConfig(proxyConfig, urls.lanUrlForConfig),
+ host: HOST,
+ port,
+ };
+ const devServer = new WebpackDevServer(serverConfig, compiler);
+ // Launch WebpackDevServer.
+ devServer.startCallback(() => {
+ if (isInteractive) {
+ clearConsole();
+ }
+
+ if (env.raw.FAST_REFRESH && semver.lt(react.version, '16.10.0')) {
+ console.log(
+ chalk.yellow(
+ `Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`
+ )
+ );
+ }
+
+ console.log(chalk.cyan('Starting the development server...\n'));
+ });
+
+ ['SIGINT', 'SIGTERM'].forEach(function (sig) {
+ process.on(sig, function () {
+ devServer.close();
+ process.exit();
+ });
+ });
+
+ if (process.env.CI !== 'true') {
+ // Gracefully exit when stdin ends
+ process.stdin.on('end', function () {
+ devServer.close();
+ process.exit();
+ });
+ }
+ })
+ .catch(err => {
+ if (err && err.message) {
+ console.log(err.message);
+ }
+ process.exit(1);
+ });
diff --git a/examples/main-react/scripts/start.js b/examples/main-react/scripts/start.js
index 41047988f..3f59d7a8f 100644
--- a/examples/main-react/scripts/start.js
+++ b/examples/main-react/scripts/start.js
@@ -1,4 +1,4 @@
-'use strict';
+
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'development';
diff --git a/examples/main-vue/vue.config.js b/examples/main-vue/vue.config.js
index e1619d310..bb314ab1b 100644
--- a/examples/main-vue/vue.config.js
+++ b/examples/main-vue/vue.config.js
@@ -9,7 +9,7 @@ module.exports = {
headers: {
"Access-Control-Allow-Origin": "*",
},
- open: true,
+ open: process.env.NODE_ENV === "development",
port: "8000",
},
};
diff --git a/examples/react16/package.json b/examples/react16/package.json
index 20762056b..8b5903673 100644
--- a/examples/react16/package.json
+++ b/examples/react16/package.json
@@ -52,6 +52,7 @@
"source-map-loader": "^3.0.0",
"style-loader": "^3.3.1",
"tailwindcss": "^3.0.2",
+ "tdesign-icons-react": "^0.1.5",
"terser-webpack-plugin": "^5.2.5",
"web-vitals": "^2.1.4",
"webpack": "^5.64.4",
diff --git a/examples/react16/src/App.js b/examples/react16/src/App.js
index bc5b29208..58ba1627c 100644
--- a/examples/react16/src/App.js
+++ b/examples/react16/src/App.js
@@ -3,7 +3,8 @@ import { NavLink, Route, Switch, Redirect } from "react-router-dom";
import Dialog from "./Dialog";
import Location from "./Location";
import Communication from "./Communication";
-import React17 from "./nest";
+import React17 from "./nest";
+import Font from "./Font";
import logo from "./logo.svg";
import Tag from "antd/es/tag";
import Button from "antd/es/button";
@@ -42,7 +43,7 @@ export default function App() {
@@ -65,6 +66,9 @@ export default function App() {
+
+
+
diff --git a/examples/react16/src/Font.js b/examples/react16/src/Font.js
new file mode 100644
index 000000000..ef6009feb
--- /dev/null
+++ b/examples/react16/src/Font.js
@@ -0,0 +1,34 @@
+import React from "react";
+import { IconFont } from "tdesign-icons-react";
+
+export default class Font extends React.Component {
+ componentDidMount() {
+ console.log("react16 font mounted")
+ }
+ render() {
+ return (
+
+
字体处理
+
+
背景
+
+ 子应用的 dom 挂载在 Web Component 的 shadowRoot 内,
+
+ shadowRoot 内部的字体文件不会加载
+
+
+
解决
+
+ 框架加载子应用时将自定义字体样式提取到 shadowRoot 的外部,注意主应用和子应用的 @font-face 的 font-family
+ 不要重名,否则会有字体覆盖的问题。
+
+
IconFont 图标示例
+
TDesign icon
+
+
+
+
+
+ );
+ }
+}
diff --git a/packages/wujie-core/__test__/integration/common.ts b/packages/wujie-core/__test__/integration/common.ts
index 7e999ce0c..21b26c4c3 100644
--- a/packages/wujie-core/__test__/integration/common.ts
+++ b/packages/wujie-core/__test__/integration/common.ts
@@ -17,6 +17,8 @@ export const reactMainAppInfoMap = {
routeMountedMessage: "react16 location mounted",
communicationNavSelectorInAll: `document.querySelector("#root > div > div.content > div > div:nth-child(1) > div > wujie-app").shadowRoot.querySelector("#root > div > nav > a:nth-child(4)")`,
nestNavSelectorInAll: `document.querySelector("#root > div > div.content > div > div:nth-child(1) > div > wujie-app").shadowRoot.querySelector("#root > div > nav > a:nth-child(5)")`,
+ fontNavSelector: `document.querySelector("#root > div > div.content > div > wujie-app").shadowRoot.querySelector("#root > div > nav > a:nth-child(6)")`,
+ fontMountedMessage: `react16 font mounted`,
preloadTitleJsSelector: 'window.frames.react16.document.querySelector("#root > div > div:nth-child(3) > h2")',
degradeTitleJsSelector: `window.document.querySelector("iframe[data-wujie-id='react16']").contentDocument.querySelector("#root > div > div:nth-child(3) > h2")`,
titleJsSelector:
@@ -153,6 +155,8 @@ export const vueMainAppInfoMap = {
routeMountedMessage: "react16 location mounted",
communicationNavSelectorInAll: `document.querySelector("#app > div.content > div > div:nth-child(1) > wujie-app").shadowRoot.querySelector("#root > div > nav > a:nth-child(4)")`,
nestNavSelectorInAll: `document.querySelector("#app > div.content > div > div:nth-child(1) > wujie-app").shadowRoot.querySelector("#root > div > nav > a:nth-child(5)")`,
+ fontNavSelector: `document.querySelector("#app > div.content > div > wujie-app").shadowRoot.querySelector("#root > div > nav > a:nth-child(6)")`,
+ fontMountedMessage: `react16 font mounted`,
preloadTitleJsSelector: 'window.frames.react16.document.querySelector("#root > div > div:nth-child(3) > h2")',
degradeTitleJsSelector: `window.document.querySelector("iframe[data-wujie-id='react16']").contentDocument.querySelector("#root > div > div:nth-child(3) > h2")`,
titleJsSelector:
diff --git a/packages/wujie-core/__test__/integration/font.test.ts b/packages/wujie-core/__test__/integration/font.test.ts
new file mode 100644
index 000000000..ce8b3a704
--- /dev/null
+++ b/packages/wujie-core/__test__/integration/font.test.ts
@@ -0,0 +1,57 @@
+import { awaitConsoleLogMessage, triggerClickByJsSelector } from "./utils";
+import { reactMainAppInfoMap, vueMainAppInfoMap } from "./common";
+
+describe("main react startApp", () => {
+ beforeAll(async () => {
+ await page.evaluateOnNewDocument(() => {
+ // 关闭预加载
+ localStorage.clear();
+ localStorage.setItem("preload", "false");
+ localStorage.setItem("degrade", "false");
+ });
+ await page.goto("http://localhost:7700/");
+ });
+ it("check react16 font-face", async () => {
+ const appInfo = reactMainAppInfoMap.react16;
+ const appInfoMountedPromise = awaitConsoleLogMessage(page, appInfo.mountedMessage);
+ expect(await page.evaluate(() => document.fonts.check("12px t", "E07F"))).toBe(false);
+ await page.click(appInfo.linkSelector);
+ await appInfoMountedPromise;
+ const appInfoFontMountedPromise = awaitConsoleLogMessage(page, appInfo.fontMountedMessage);
+ await triggerClickByJsSelector(page, appInfo.fontNavSelector);
+ await appInfoFontMountedPromise;
+ // 等待字体加载
+ await page.waitForResponse((response) => response.url().includes("https://tdesign.gtimg.com/icon/0.1.1/fonts"));
+ // 等待字体装载
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ // 检查字体是否生效
+ expect(await page.evaluate(() => document.fonts.check("12px t", "E07F"))).toBe(true);
+ });
+});
+describe("main vue startApp", () => {
+ beforeAll(async () => {
+ await page.evaluateOnNewDocument(() => {
+ // 关闭预加载
+ localStorage.clear();
+ localStorage.setItem("preload", "false");
+ localStorage.setItem("degrade", "false");
+ });
+ await page.goto("http://localhost:8000/");
+ });
+ it("check react16 font-face", async () => {
+ const appInfo = vueMainAppInfoMap.react16;
+ const appInfoMountedPromise = awaitConsoleLogMessage(page, appInfo.mountedMessage);
+ expect(await page.evaluate(() => document.fonts.check("12px t", "E07F"))).toBe(false);
+ await page.click(appInfo.linkSelector);
+ await appInfoMountedPromise;
+ const appInfoFontMountedPromise = awaitConsoleLogMessage(page, appInfo.fontMountedMessage);
+ await triggerClickByJsSelector(page, appInfo.fontNavSelector);
+ await appInfoFontMountedPromise;
+ // 等待字体加载
+ await page.waitForResponse((response) => response.url().includes("https://tdesign.gtimg.com/icon/0.1.1/fonts"));
+ // 等待字体装载
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ // 检查字体是否生效
+ expect(await page.evaluate(() => document.fonts.check("12px t", "E07F"))).toBe(true);
+ });
+});
diff --git a/packages/wujie-core/jest-puppeteer.config.js b/packages/wujie-core/jest-puppeteer.config.js
index 53529068e..24492ef3e 100644
--- a/packages/wujie-core/jest-puppeteer.config.js
+++ b/packages/wujie-core/jest-puppeteer.config.js
@@ -48,7 +48,7 @@ module.exports = {
port: 7400,
},
{
- command: 'lerna run start --scope main-react',
+ command: 'lerna run integration --scope main-react',
usedPortAction: 'kill',
launchTimeout: 60000,
host: '0.0.0.0',
diff --git a/packages/wujie-core/src/effect.ts b/packages/wujie-core/src/effect.ts
index 8b4118878..13ad1a97a 100644
--- a/packages/wujie-core/src/effect.ts
+++ b/packages/wujie-core/src/effect.ts
@@ -16,10 +16,11 @@ import {
nextTick,
isExcludeUrl,
getExcludes,
+ getCurUrl,
} from "./utils";
import { insertScriptToIframe } from "./iframe";
import Wujie from "./sandbox";
-import { getHostCssRules } from "./shadow";
+import { getPatchStyleElements } from "./shadow";
import { WUJIE_DATA_ID, WUJIE_DATA_FLAG, WUJIE_TIPS_REPEAT_RENDER } from "./constant";
import { ScriptObject } from "./template";
@@ -55,12 +56,19 @@ function manualInvokeElementEvent(element: HTMLLinkElement | HTMLScriptElement,
/**
* 样式元素的css变量处理
*/
-function handleStylesheetElementHost(stylesheetElement: HTMLStyleElement, sandbox: Wujie) {
+function handleStylesheetElementPatch(stylesheetElement: HTMLStyleElement, sandbox: Wujie, baseUrl?: string) {
if (!stylesheetElement.innerHTML || sandbox.degrade) return;
- const hostStyleSheetElement = getHostCssRules([stylesheetElement.sheet]);
+ const curUrl = getCurUrl(sandbox.proxyLocation as Location);
+ const [hostStyleSheetElement, fontStyleSheetElement] = getPatchStyleElements(
+ [stylesheetElement.sheet],
+ baseUrl ? baseUrl : curUrl
+ );
if (hostStyleSheetElement) {
sandbox.shadowRoot.head.appendChild(hostStyleSheetElement);
}
+ if (fontStyleSheetElement) {
+ sandbox.shadowRoot.host.appendChild(fontStyleSheetElement);
+ }
}
/**
@@ -81,7 +89,7 @@ function patchStylesheetElement(
},
set: function (code: string) {
innerHTMLDesc.set.call(stylesheetElement, cssLoader(code));
- nextTick(() => handleStylesheetElementHost(this, sandbox));
+ nextTick(() => handleStylesheetElementPatch(this, sandbox));
},
},
innerText: {
@@ -90,7 +98,7 @@ function patchStylesheetElement(
},
set: function (code: string) {
innerTextDesc.set.call(stylesheetElement, cssLoader(code));
- nextTick(() => handleStylesheetElementHost(this, sandbox));
+ nextTick(() => handleStylesheetElementPatch(this, sandbox));
},
},
textContent: {
@@ -99,12 +107,12 @@ function patchStylesheetElement(
},
set: function (code: string) {
textContentDesc.set.call(stylesheetElement, cssLoader(code));
- nextTick(() => handleStylesheetElementHost(this, sandbox));
+ nextTick(() => handleStylesheetElementPatch(this, sandbox));
},
},
appendChild: {
value: function (node: Node): Node {
- nextTick(() => handleStylesheetElementHost(this, sandbox));
+ nextTick(() => handleStylesheetElementPatch(this, sandbox));
if (node.nodeType === Node.TEXT_NODE) {
return rawAppendChild.call(
stylesheetElement,
@@ -161,9 +169,9 @@ function rewriteAppendOrInsertChild(opts: {
src
);
styleSheetElements.push(stylesheetElement);
- // 处理host的情况
- handleStylesheetElementHost(stylesheetElement, sandbox);
rawDOMAppendOrInsertBefore.call(this, stylesheetElement, refChild);
+ // 处理样式补丁
+ handleStylesheetElementPatch(stylesheetElement, sandbox, href);
manualInvokeElementEvent(element, "load");
element = null;
},
@@ -186,9 +194,10 @@ function rewriteAppendOrInsertChild(opts: {
compose(plugins.map((plugin) => plugin.cssLoader))(replace ? replace(content) : content);
content && (stylesheetElement.innerHTML = cssLoader(content));
patchStylesheetElement(stylesheetElement, cssLoader, sandbox);
- // 处理host的情况
- nextTick(() => handleStylesheetElementHost(stylesheetElement, sandbox));
- return rawDOMAppendOrInsertBefore.call(this, element, refChild);
+ const res = rawDOMAppendOrInsertBefore.call(this, element, refChild);
+ // 处理样式补丁
+ handleStylesheetElementPatch(stylesheetElement, sandbox);
+ return res;
}
case "SCRIPT": {
const { src, text, type, crossOrigin } = element as HTMLScriptElement;
diff --git a/packages/wujie-core/src/sandbox.ts b/packages/wujie-core/src/sandbox.ts
index b68285914..caaa78a02 100644
--- a/packages/wujie-core/src/sandbox.ts
+++ b/packages/wujie-core/src/sandbox.ts
@@ -9,7 +9,7 @@ import { syncUrlToWindow, syncUrlToIframe, clearInactiveAppUrl } from "./sync";
import {
createWujieWebComponent,
clearChild,
- getHostCssRules,
+ getPatchStyleElements,
renderElementToContainer,
renderTemplateToShadowRoot,
createIframeContainer,
@@ -25,7 +25,7 @@ import {
rawDocumentQuerySelector,
} from "./common";
import { EventBus, appEventObjMap, EventObj } from "./event";
-import { isFunction, wujieSupport, appRouteParse, requestIdleCallback } from "./utils";
+import { isFunction, wujieSupport, appRouteParse, requestIdleCallback, getCurUrl } from "./utils";
import { WUJIE_DATA_ATTACH_CSS_FLAG } from "./constant";
import { plugin, ScriptObjectLoader, loadErrorHandler } from "./index";
@@ -222,6 +222,7 @@ export default class Wujie {
}
await renderTemplateToShadowRoot(this.shadowRoot, iframeWindow, this.template);
+ this.patchCssRules();
// inject shadowRoot to app
this.provide.shadowRoot = this.shadowRoot;
@@ -321,7 +322,6 @@ export default class Wujie {
*/
public mount(): void {
if (this.mountFlag) return;
- this.attachHostCssRules();
if (isFunction(this.iframe.contentWindow.__WUJIE_MOUNT)) {
this.lifecycles?.beforeMount?.(this.iframe.contentWindow);
this.iframe.contentWindow.__WUJIE_MOUNT();
@@ -395,23 +395,33 @@ export default class Wujie {
rawElementAppendChild.call(this.degrade ? this.document.head : this.shadowRoot.head, styleSheetElement);
});
}
- this.attachHostCssRules();
+ this.patchCssRules();
}
- /** 兼容:root选择器样式到:host选择器上 */
- public attachHostCssRules(): void {
+ /**
+ * 子应用样式打补丁
+ * 1、兼容:root选择器样式到:host选择器上
+ * 2、将@font-face定义到shadowRoot外部
+ */
+ public patchCssRules(): void {
+ const curUrl = getCurUrl(this.proxyLocation as Location);
if (this.degrade) return;
if (this.shadowRoot.host.hasAttribute(WUJIE_DATA_ATTACH_CSS_FLAG)) return;
- const hostStyleSheetElement = getHostCssRules(
+ const [hostStyleSheetElement, fontStyleSheetElement] = getPatchStyleElements(
Array.from(this.iframe.contentDocument.querySelectorAll("style")).map(
(styleSheetElement) => styleSheetElement.sheet
- )
+ ),
+ curUrl
);
if (hostStyleSheetElement) {
this.shadowRoot.head.appendChild(hostStyleSheetElement);
this.styleSheetElements.push(hostStyleSheetElement);
- this.shadowRoot.host.setAttribute(WUJIE_DATA_ATTACH_CSS_FLAG, "");
}
+ if (fontStyleSheetElement) {
+ this.shadowRoot.host.appendChild(fontStyleSheetElement);
+ }
+ (hostStyleSheetElement || fontStyleSheetElement) &&
+ this.shadowRoot.host.setAttribute(WUJIE_DATA_ATTACH_CSS_FLAG, "");
}
/**
diff --git a/packages/wujie-core/src/shadow.ts b/packages/wujie-core/src/shadow.ts
index 547bd2208..68cfc5fd8 100644
--- a/packages/wujie-core/src/shadow.ts
+++ b/packages/wujie-core/src/shadow.ts
@@ -218,27 +218,54 @@ export function clearChild(root: ShadowRoot | Node): void {
}
/**
- * 获取:root选择器的样式到shadow的:host
+ * 获取修复好的样式元素
+ * 主要是针对对root样式和font-face样式
*/
-export function getHostCssRules(rootStyleSheets: Array
): void | HTMLStyleElement {
- const rootCssRule = [];
+export function getPatchStyleElements(
+ rootStyleSheets: Array,
+ baseUrl: string
+): Array {
+ const rootCssRules = [];
+ const fontCssRules = [];
const rootStyleReg = /:root/g;
+ const fontStyleReg = /(url\()([^)]*)(\))/g;
// 找出root的cssRules
for (let i = 0; i < rootStyleSheets.length; i++) {
const cssRules = rootStyleSheets[i]?.cssRules ?? [];
for (let j = 0; j < cssRules.length; j++) {
const cssRuleText = cssRules[j].cssText;
+ // 如果是root的cssRule
if (rootStyleReg.test(cssRuleText)) {
- rootCssRule.push(cssRuleText.replace(rootStyleReg, (match) => cssSelectorMap[match]));
+ rootCssRules.push(cssRuleText.replace(rootStyleReg, (match) => cssSelectorMap[match]));
+ }
+ // 如果是font-face的cssRule
+ if (cssRules[j].type === CSSRule.FONT_FACE_RULE) {
+ fontCssRules.push(
+ // 相对地址改绝对地址
+ cssRuleText.replace(fontStyleReg, (_m, pre, url, post) => {
+ const urlString = url.replace(/["']/g, "");
+ const absoluteUrl = new URL(urlString, baseUrl).href;
+ return pre + "'" + absoluteUrl + "'" + post;
+ })
+ );
}
}
}
+ let rootStyleSheetElement = null;
+ let fontStyleSheetElement = null;
+
// 复制到host上
- if (rootCssRule.length) {
- const styleSheetElement = window.document.createElement("style");
- styleSheetElement.innerHTML = rootCssRule.join("");
- return styleSheetElement;
+ if (rootCssRules.length) {
+ rootStyleSheetElement = window.document.createElement("style");
+ rootStyleSheetElement.innerHTML = rootCssRules.join("");
}
+
+ if (fontCssRules.length) {
+ fontStyleSheetElement = window.document.createElement("style");
+ fontStyleSheetElement.innerHTML = fontCssRules.join("");
+ }
+
+ return [rootStyleSheetElement, fontStyleSheetElement];
}
diff --git a/packages/wujie-core/src/utils.ts b/packages/wujie-core/src/utils.ts
index dc6357b6b..05b5c13f9 100644
--- a/packages/wujie-core/src/utils.ts
+++ b/packages/wujie-core/src/utils.ts
@@ -170,6 +170,10 @@ export function fixElementCtrSrcOrHref(
// TODO: innerHTML的处理
}
+export function getCurUrl(proxyLocation: Location): string {
+ return proxyLocation.protocol + "//" + proxyLocation.host + proxyLocation.pathname;
+}
+
/**
* 获取需要同步的url
*/
diff --git a/packages/wujie-doc/docs/question/README.md b/packages/wujie-doc/docs/question/README.md
index ec8cd8a69..37e7db18a 100644
--- a/packages/wujie-doc/docs/question/README.md
+++ b/packages/wujie-doc/docs/question/README.md
@@ -38,7 +38,7 @@ ctx.set("Access-Control-Allow-Origin", ctx.headers.origin);
**原因:** `@font-face`不会在`shadow`内部加载,[详见](https://github.com/mdn/interactive-examples/issues/887)
-**解决方案:** 将子应用需要到的`@font-face`在主应用进行加载
+**解决方案:** 框架已解决,会将子应用的`@font-face`放到`shadow`外部执行,注意子应用的自定义字体名和主应用的自定义字体名不能重复,否则可能存在覆盖问题
## 4、冒泡系列组件(比如下拉框)弹出位置不正确