Skip to content

Commit

Permalink
feat: 将子应用样式中相对地址默认转换成绝对地址 (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
yiludege authored Aug 7, 2022
1 parent 1a6af76 commit a74f236
Show file tree
Hide file tree
Showing 17 changed files with 171 additions and 90 deletions.
2 changes: 1 addition & 1 deletion examples/main-react/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ h3 {
.content {
flex: 1;
height: 100vh;
overflow: scroll;
overflow: hidden scroll;
width: 1px;
}

Expand Down
2 changes: 1 addition & 1 deletion examples/main-vue/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ h3 {
.content {
flex: 1;
height: 100vh;
overflow: scroll;
overflow: hidden scroll;
width: 1px;
}
Expand Down
16 changes: 12 additions & 4 deletions examples/react16/src/Font.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IconFont } from "tdesign-icons-react";

export default class Font extends React.Component {
componentDidMount() {
console.log("react16 font mounted")
console.log("react16 font mounted");
}
render() {
return (
Expand All @@ -24,9 +24,17 @@ export default class Font extends React.Component {
</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" />
<p>
<IconFont name="loading" size="2em" />
<IconFont name="close" size="2em" />
<IconFont name="check-circle-filled" size="2em" />
</p>
<h3>相对地址</h3>
<p>框架会将子应用的 css 文件中的相对地址换成绝对地址</p>
<p>比如 TDesign icon 的 css 文件地址为: </p>
<p> https://tdesign.gtimg.com/icon/0.1.1/fonts/index.css</p>
<p>index.css 文件中 @font-face 中 url('./t.woff') 最终转换为:</p>
<p> https://tdesign.gtimg.com/icon/0.1.1/fonts/t.woff</p>
</div>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions examples/react16/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ nav .active {
text-align: left;
max-width: 740px;
margin: 0 auto;
overflow: hidden;
}

p, div.p {
Expand Down
4 changes: 2 additions & 2 deletions packages/wujie-core/__test__/integration/font.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe("main react startApp", () => {
// 等待字体加载
await page.waitForResponse((response) => response.url().includes("https://tdesign.gtimg.com/icon/0.1.1/fonts"));
// 等待字体装载
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise((resolve) => setTimeout(resolve, 1000));
// 检查字体是否生效
expect(await page.evaluate(() => document.fonts.check("12px t", "E07F"))).toBe(true);
});
Expand All @@ -50,7 +50,7 @@ describe("main vue startApp", () => {
// 等待字体加载
await page.waitForResponse((response) => response.url().includes("https://tdesign.gtimg.com/icon/0.1.1/fonts"));
// 等待字体装载
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise((resolve) => setTimeout(resolve, 1000));
// 检查字体是否生效
expect(await page.evaluate(() => document.fonts.check("12px t", "E07F"))).toBe(true);
});
Expand Down
41 changes: 18 additions & 23 deletions packages/wujie-core/src/effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
isFunction,
isHijackingTag,
requestIdleCallback,
compose,
error,
warn,
nextTick,
Expand All @@ -21,6 +20,7 @@ import {
import { insertScriptToIframe } from "./iframe";
import Wujie from "./sandbox";
import { getPatchStyleElements } from "./shadow";
import { getCssLoader } from "./plugin";
import { WUJIE_DATA_ID, WUJIE_DATA_FLAG, WUJIE_TIPS_REPEAT_RENDER } from "./constant";
import { ScriptObject } from "./template";

Expand Down Expand Up @@ -56,13 +56,9 @@ function manualInvokeElementEvent(element: HTMLLinkElement | HTMLScriptElement,
/**
* 样式元素的css变量处理
*/
function handleStylesheetElementPatch(stylesheetElement: HTMLStyleElement, sandbox: Wujie, baseUrl?: string) {
function handleStylesheetElementPatch(stylesheetElement: HTMLStyleElement, sandbox: Wujie) {
if (!stylesheetElement.innerHTML || sandbox.degrade) return;
const curUrl = getCurUrl(sandbox.proxyLocation as Location);
const [hostStyleSheetElement, fontStyleSheetElement] = getPatchStyleElements(
[stylesheetElement.sheet],
baseUrl ? baseUrl : curUrl
);
const [hostStyleSheetElement, fontStyleSheetElement] = getPatchStyleElements([stylesheetElement.sheet]);
if (hostStyleSheetElement) {
sandbox.shadowRoot.head.appendChild(hostStyleSheetElement);
}
Expand All @@ -76,8 +72,9 @@ function handleStylesheetElementPatch(stylesheetElement: HTMLStyleElement, sandb
*/
function patchStylesheetElement(
stylesheetElement: HTMLStyleElement,
cssLoader: (code: string) => string,
sandbox: Wujie
cssLoader: (code: string, url: string, base: string) => string,
sandbox: Wujie,
curUrl: string
) {
const innerHTMLDesc = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML");
const innerTextDesc = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "innerText");
Expand All @@ -88,7 +85,7 @@ function patchStylesheetElement(
return innerHTMLDesc.get.call(stylesheetElement);
},
set: function (code: string) {
innerHTMLDesc.set.call(stylesheetElement, cssLoader(code));
innerHTMLDesc.set.call(stylesheetElement, cssLoader(code, "", curUrl));
nextTick(() => handleStylesheetElementPatch(this, sandbox));
},
},
Expand All @@ -97,7 +94,7 @@ function patchStylesheetElement(
return innerTextDesc.get.call(stylesheetElement);
},
set: function (code: string) {
innerTextDesc.set.call(stylesheetElement, cssLoader(code));
innerTextDesc.set.call(stylesheetElement, cssLoader(code, "", curUrl));
nextTick(() => handleStylesheetElementPatch(this, sandbox));
},
},
Expand All @@ -106,7 +103,7 @@ function patchStylesheetElement(
return textContentDesc.get.call(stylesheetElement);
},
set: function (code: string) {
textContentDesc.set.call(stylesheetElement, cssLoader(code));
textContentDesc.set.call(stylesheetElement, cssLoader(code, "", curUrl));
nextTick(() => handleStylesheetElementPatch(this, sandbox));
},
},
Expand All @@ -116,7 +113,7 @@ function patchStylesheetElement(
if (node.nodeType === Node.TEXT_NODE) {
return rawAppendChild.call(
stylesheetElement,
stylesheetElement.ownerDocument.createTextNode(cssLoader(node.textContent))
stylesheetElement.ownerDocument.createTextNode(cssLoader(node.textContent, "", curUrl))
);
} else return rawAppendChild(node);
},
Expand All @@ -141,8 +138,9 @@ function rewriteAppendOrInsertChild(opts: {
return rawDOMAppendOrInsertBefore.call(this, element, refChild) as T;
}

const { styleSheetElements, replace, fetch, plugins, iframe, lifecycles } = sandbox;
const { styleSheetElements, replace, fetch, plugins, iframe, lifecycles, proxyLocation } = sandbox;
const iframeDocument = iframe.contentDocument;
const curUrl = getCurUrl(proxyLocation);

// TODO 过滤可以开放
if (element.tagName) {
Expand All @@ -164,14 +162,12 @@ function rewriteAppendOrInsertChild(opts: {
// 记录js插入样式,子应用重新激活时恢复
const stylesheetElement = iframeDocument.createElement("style");
// 处理css-loader插件
stylesheetElement.innerHTML = compose(plugins.map((plugin) => plugin.cssLoader))(
replace ? replace(content) : content,
src
);
const cssLoader = getCssLoader({ plugins, replace });
stylesheetElement.innerHTML = cssLoader(content, src, curUrl);
styleSheetElements.push(stylesheetElement);
rawDOMAppendOrInsertBefore.call(this, stylesheetElement, refChild);
// 处理样式补丁
handleStylesheetElementPatch(stylesheetElement, sandbox, href);
handleStylesheetElementPatch(stylesheetElement, sandbox);
manualInvokeElementEvent(element, "load");
element = null;
},
Expand All @@ -190,10 +186,9 @@ function rewriteAppendOrInsertChild(opts: {
const stylesheetElement: HTMLLinkElement | HTMLStyleElement = newChild as any;
styleSheetElements.push(stylesheetElement);
const content = stylesheetElement.innerHTML;
const cssLoader = (content) =>
compose(plugins.map((plugin) => plugin.cssLoader))(replace ? replace(content) : content);
content && (stylesheetElement.innerHTML = cssLoader(content));
patchStylesheetElement(stylesheetElement, cssLoader, sandbox);
const cssLoader = getCssLoader({ plugins, replace });
content && (stylesheetElement.innerHTML = cssLoader(content, "", curUrl));
patchStylesheetElement(stylesheetElement, cssLoader, sandbox, curUrl);
const res = rawDOMAppendOrInsertBefore.call(this, element, refChild);
// 处理样式补丁
handleStylesheetElementPatch(stylesheetElement, sandbox);
Expand Down
13 changes: 11 additions & 2 deletions packages/wujie-core/src/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ import processTpl, {
ScriptBaseObject,
StyleObject,
} from "./template";
import { defaultGetPublicPath, getInlineCode, requestIdleCallback, error, compose, getExcludes } from "./utils";
import {
defaultGetPublicPath,
getInlineCode,
requestIdleCallback,
error,
compose,
getExcludes,
getCurUrl,
} from "./utils";
import { WUJIE_TIPS_NO_FETCH, WUJIE_TIPS_SCRIPT_ERROR_REQUESTED, WUJIE_TIPS_CSS_ERROR_REQUESTED } from "./constant";
import Wujie from "./sandbox";
import { plugin, loadErrorHandler } from "./index";
Expand Down Expand Up @@ -51,11 +59,12 @@ export async function processCssLoader(
template: string,
getExternalStyleSheets: () => StyleResultList
): Promise<string> {
const curUrl = getCurUrl(sandbox.proxyLocation);
/** css-loader */
const composeCssLoader = compose(sandbox.plugins.map((plugin) => plugin.cssLoader));
const processedCssList: StyleResultList = getExternalStyleSheets().map(({ src, contentPromise }) => ({
src,
contentPromise: contentPromise.then((content) => composeCssLoader(content, src)),
contentPromise: contentPromise.then((content) => composeCssLoader(content, src, curUrl)),
}));
const embedHTML = await getEmbedHTML(template, processedCssList);
return sandbox.replace ? sandbox.replace(embedHTML) : embedHTML;
Expand Down
8 changes: 5 additions & 3 deletions packages/wujie-core/src/iframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
isMatchSyncQueryById,
warn,
error,
compose,
execHooks,
getCurUrl,
} from "./utils";
import {
documentProxyProperties,
Expand All @@ -23,6 +23,7 @@ import {
windowProxyProperties,
windowRegWhiteList,
} from "./common";
import { getJsLoader } from "./plugin";
import { WUJIE_TIPS_EMPTY_CALLBACK, WUJIE_TIPS_SCRIPT_ERROR_REQUESTED, WUJIE_DATA_FLAG } from "./constant";
import { ScriptObjectLoader } from "./index";

Expand Down Expand Up @@ -611,8 +612,9 @@ export function insertScriptToIframe(scriptResult: ScriptObject | ScriptObjectLo
const { src, module, content, crossorigin, crossoriginType, callback } = scriptResult as ScriptObjectLoader;
const scriptElement = iframeWindow.document.createElement("script");
const nextScriptElement = iframeWindow.document.createElement("script");
const { replace, plugins } = iframeWindow.__WUJIE;
let code = compose(plugins.map((plugin) => plugin.jsLoader))(replace ? replace(content) : content, src);
const { replace, plugins, proxyLocation } = iframeWindow.__WUJIE;
const jsLoader = getJsLoader({ plugins, replace });
let code = jsLoader(content, src, getCurUrl(proxyLocation));

// 内联脚本
if (content) {
Expand Down
4 changes: 2 additions & 2 deletions packages/wujie-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ export interface plugin {
/** 处理js加载前的loader */
jsBeforeLoaders?: Array<ScriptObjectLoader>;
/** 处理js的loader */
jsLoader?: (code: string, url: string) => string;
jsLoader?: (code: string, url: string, base: string) => string;
/** 处理js加载后的loader */
jsAfterLoaders?: Array<ScriptObjectLoader>;
/** css排除列表 */
cssExcludes?: Array<string | RegExp>;
/** 处理css加载前的loader */
cssBeforeLoaders?: Array<StyleObject>;
/** 处理css的loader */
cssLoader?: (code: string, url: string) => string;
cssLoader?: (code: string, url: string, base: string) => string;
/** 处理css加载后的loader */
cssAfterLoaders?: Array<StyleObject>;
/** 子应用 window addEventListener 钩子回调 */
Expand Down
81 changes: 81 additions & 0 deletions packages/wujie-core/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { plugin } from "./index";
import { compose, getAbsolutePath } from "./utils";

interface loaderOption {
plugins: Array<plugin>;
replace: (code: string) => string;
}

/**
* 获取柯里化 cssLoader
*/
export function getCssLoader({ plugins, replace }: loaderOption) {
return (code: string, src: string = "", base: string): string =>
compose(plugins.map((plugin) => plugin.cssLoader))(replace ? replace(code) : code, src, base);
}

/**
* 获取柯里化 jsLoader
*/
export function getJsLoader({ plugins, replace }: loaderOption) {
return (code: string, src: string = "", base: string): string =>
compose(plugins.map((plugin) => plugin.jsLoader))(replace ? replace(code) : code, src, base);
}

/**
* 获取有效的 cssBeforeLoaders
*/
export function getCssBeforeLoaders(plugins: Array<plugin>) {
return plugins
.map((plugin) => plugin.cssBeforeLoaders)
.reduce((preLoaders, curLoaders) => preLoaders.concat(curLoaders), [])
.filter((cssLoader) => typeof cssLoader === "object")
.reverse();
}

/**
* 获取有效的 cssAfterLoaders
*/
export function getCssAfterLoaders(plugins: Array<plugin>) {
return plugins
.map((plugin) => plugin.cssAfterLoaders)
.reduce((preLoaders, curLoaders) => preLoaders.concat(curLoaders), [])
.filter((afterLoader) => typeof afterLoader === "object");
}

/**
* 获取有效的 jsBeforeLoaders
*/
export function getJsBeforeLoaders(plugins: Array<plugin>) {
return plugins
.map((plugin) => plugin.jsBeforeLoaders)
.reduce((preLoaders, curLoaders) => preLoaders.concat(curLoaders), [])
.filter((preLoader) => typeof preLoader === "object");
}

/**
* 获取有效的 jsAfterLoaders
*/
export function getJsAfterLoaders(plugins: Array<plugin>) {
return plugins
.map((plugin) => plugin.jsAfterLoaders)
.reduce((preLoaders, curLoaders) => preLoaders.concat(curLoaders), [])
.filter((afterLoader) => typeof afterLoader === "object");
}

/**
* 转换子应用css内的相对地址成绝对地址
*/
function cssRelativePathResolve(code: string, src: string, base: string) {
const baseUrl = src ? getAbsolutePath(src, base) : base;
const urlReg = /(url\()([^)]*)(\))/g;
return code.replace(urlReg, (_m, pre, url, post) => {
const urlString = url.replace(/["']/g, "");
const absoluteUrl = getAbsolutePath(urlString, baseUrl);
return pre + "'" + absoluteUrl + "'" + post;
});
}

export default {
cssLoader: cssRelativePathResolve,
};
Loading

0 comments on commit a74f236

Please sign in to comment.