Skip to content

Commit

Permalink
fix: 修复子应用字体无法加载的问题 (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
yiludege authored Aug 7, 2022
1 parent 4508e53 commit a33dec5
Show file tree
Hide file tree
Showing 15 changed files with 339 additions and 35 deletions.
1 change: 1 addition & 0 deletions examples/main-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
153 changes: 153 additions & 0 deletions examples/main-react/scripts/integration.js
Original file line number Diff line number Diff line change
@@ -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);
});
2 changes: 1 addition & 1 deletion examples/main-react/scripts/start.js
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
2 changes: 1 addition & 1 deletion examples/main-vue/vue.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = {
headers: {
"Access-Control-Allow-Origin": "*",
},
open: true,
open: process.env.NODE_ENV === "development",
port: "8000",
},
};
1 change: 1 addition & 0 deletions examples/react16/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 6 additions & 2 deletions examples/react16/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -42,7 +43,7 @@ export default function App() {
<nav>
<NavLink to="/home">首页</NavLink> | <NavLink to="/dialog">弹窗</NavLink> |{" "}
<NavLink to="/location">路由</NavLink> | <NavLink to="/communication">通信</NavLink> |{" "}
<NavLink to="/nest">内嵌</NavLink>
<NavLink to="/nest">内嵌</NavLink> | <NavLink to="/font">字体</NavLink>
</nav>

<div>
Expand All @@ -65,6 +66,9 @@ export default function App() {
<Route path="/nest">
<React17 />
</Route>
<Route path="/font">
<Font />
</Route>
<Route exact path="/">
<Redirect to="/home" />
</Route>
Expand Down
34 changes: 34 additions & 0 deletions examples/react16/src/Font.js
Original file line number Diff line number Diff line change
@@ -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 (
<div>
<h2>字体处理</h2>
<div className="content">
<h3>背景</h3>
<p>
子应用的 dom 挂载在 Web Component 的 shadowRoot 内,
<a href="https://github.com/mdn/interactive-examples/issues/887" target="_blank" rel="noreferrer">
shadowRoot 内部的字体文件不会加载
</a>
</p>
<h3>解决</h3>
<p>
框架加载子应用时将自定义字体样式提取到 shadowRoot 的外部,注意主应用和子应用的 @font-face 的 font-family
不要重名,否则会有字体覆盖的问题。
</p>
<h3>IconFont 图标示例</h3>
<p>TDesign icon</p>
<IconFont name="loading" size="2em" />
<IconFont name="close" size="2em" />
<IconFont name="check-circle-filled" size="2em" />
</div>
</div>
);
}
}
4 changes: 4 additions & 0 deletions packages/wujie-core/__test__/integration/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
57 changes: 57 additions & 0 deletions packages/wujie-core/__test__/integration/font.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
2 changes: 1 addition & 1 deletion packages/wujie-core/jest-puppeteer.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Loading

0 comments on commit a33dec5

Please sign in to comment.