Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 修复子应用字体无法加载的问题 #33

Merged
merged 1 commit into from
Aug 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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