欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解無界微前端是如何渲染子應(yīng)用的demo解析

 更新時間:2023年04月18日 14:07:11   作者:candyTong  
這篇文章主要為大家介紹了詳解無界微前端是如何渲染子應(yīng)用demo解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

正文

經(jīng)過我們團隊的調(diào)研,我們選擇了無界作為微前端的技術(shù)棧。目前的使用效果非常好,不僅性能表現(xiàn)出色,而且使用體驗也不錯。

盡管在使用的過程中,我們也遇到了一些問題,但這些問題往往源于我們對框架實現(xiàn)的不熟悉。我們深入研究了無界技術(shù)的源碼,并將在本文中與大家分享。本文將重點探討無界微前端如何渲染子應(yīng)用的。

無界渲染子應(yīng)用的步驟

無界與其他微前端框架(例如qiankun)的主要區(qū)別在于其獨特的 JS 沙箱機制。無界使用 iframe 來實現(xiàn) JS 沙箱,由于這個設(shè)計,無界在以下方面表現(xiàn)得更加出色:

  • 應(yīng)用切換沒有清理成本
  • 允許一個頁面同時激活多個子應(yīng)用
  • 性能相對更優(yōu)

無界渲染子應(yīng)用,主要分為以下幾個步驟:

  • 創(chuàng)建子應(yīng)用 iframe
  • 解析入口 HTML
  • 創(chuàng)建 webComponent,并掛載 HTML
  • 運行 JS 渲染 UI

創(chuàng)建子應(yīng)用 iframe

要在 iframe 中運行 JS,首先得有一個 iframe。

export function iframeGenerator(
  sandbox: WuJie,
  attrs: { [key: string]: any },
  mainHostPath: string,
  appHostPath: string,
  appRoutePath: string
): HTMLIFrameElement {
  // 創(chuàng)建 iframe 的 DOM
  const iframe = window.document.createElement("iframe");
  // 設(shè)置 iframe 的 attr
  setAttrsToElement(iframe, { 
      // iframe 的 url 設(shè)置為主應(yīng)用的域名
      src: mainHostPath, 
      style: "display: none", 
      ...attrs, 
      name: sandbox.id, 
      [WUJIE_DATA_FLAG]: "" 
  });
  // 將 iframe 插入到 document 中
  window.document.body.appendChild(iframe);
  const iframeWindow = iframe.contentWindow;
  // 停止 iframe 的加載
  sandbox.iframeReady = stopIframeLoading(iframeWindow).then(() => {
      // 省略其他內(nèi)容
  }
  // 注入無界的變量到 iframeWindow,例如 __WUJIE
  patchIframeVariable(iframeWindow, sandbox, appHostPath);                                                           
  // 省略其他內(nèi)容
  return iframe;
}

創(chuàng)建 iframe 主要有以下流程:

  • 創(chuàng)建 iframe 的 DOM,并設(shè)置屬性
  • 將 iframe 插入到 document 中(此時 iframe 會立即訪問 src)
  • 停止 iframe 的加載(stopIframeLoading)

為什么要停止 iframe 的加載?

因為要創(chuàng)建一個純凈的 iframe,防止 iframe 被污染,假如該 url 的 JS 代碼,聲明了一些全局變量、函數(shù),就可能影響到子應(yīng)用的運行(假如子應(yīng)用也有同名的變量、函數(shù))

為什么 iframe 的 src 要設(shè)置為主應(yīng)用的域名

為了實現(xiàn)應(yīng)用間(iframe 間)通訊,無界子應(yīng)用 iframe 的 url 會設(shè)置為主應(yīng)用的域名(同域)

  • 主應(yīng)用域名為 a.com
  • 子應(yīng)用域名為 b.com,但它對應(yīng)的 iframe 域名為 a.com,所以要設(shè)置 b.com 的資源能夠允許跨域訪問

因此 iframe 的 location.href 并不是子應(yīng)用的 url。

解析入口 HTML

iframe 中運行 js,首先要知道要運行哪些 js

我們可以通過解析入口 HTML 來確定需要運行的 JS 內(nèi)容

假設(shè)有以下HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <script defer="defer" src="./static/js/main.4000cadb.js"></script>
    <link href="./static/css/main.7d8ad73e.css" rel="external nofollow"  rel="stylesheet">
</head>
<body>
	<div id="root"></div>
</body>
</html>

經(jīng)過 importHTML 處理后,結(jié)果如下:

  • template 模板部分,去掉了所有的 script 和 style
<!DOCTYPE html>
<html lang="en">
<head>
    <!-- defer script https://wujie-micro.github.io/demo-react16/static/js/main.4000cadb.js replaced by wujie -->
    <!--  link https://wujie-micro.github.io/demo-react16/static/css/main.7d8ad73e.css replaced by wujie -->
</head>
</head>
<body>
	<div id="root"></div>
</body>
</html>
  • getExternalScripts,獲取所有內(nèi)聯(lián)和外部的 script
[
    {
      async: false,
      defer: true,
      src: 'https://wujie-micro.github.io/demo-react16/static/js/main.4000cadb.js',
      module: false,
      crossorigin: false,
      crossoriginType: '',
      ignore: false,
      contentPromise: // 獲取 script 內(nèi)容字符串的 Promise
	}
]
  • getExternalStyleSheets,獲取所有內(nèi)聯(lián)和外部的 style
[
    {
        src: "https://wujie-micro.github.io/demo-react16/static/css/main.7d8ad73e.css",
        ignore: false,
        contentPromise: // 獲取 style 內(nèi)容字符串的 Promise
    }
]

為什么要將 script 和 style 從 HTML 中分離?

  • HTML 要作為 webComponent 的內(nèi)容,掛載到微前端掛載點上
  • 因為無界有插件機制,需要單獨對 js/style 進行處理,再插入到 webComponent 中
  • script 除了需要經(jīng)過插件處理外,還需要放到 iframe 沙箱中執(zhí)行,因此也要單獨分離出來

external 是外部的意思,為什么 getExternalScripts 拿到的卻是所有的 script,而不是外部的非內(nèi)聯(lián) script?

external 是相對于解析后的 HTML 模板來說的,由于解析后的 HTML 不帶有任何的 js 和 css,所以這里的 external,就是指模板外的所有 JS

無界與 qiankun 的在解析 HTML 上區(qū)別?

無界和 qiankun 都是以 HTML 為入口的微前端框架。qiankun 基于 import-html-entry 解析 HTML,而無界則是借鑒 import-html-entry 代碼,實現(xiàn)了自己的 HTML 的解析,因此兩者在解析 HTML 上的不同,主要是在importHTML 的實現(xiàn)上。

由于無界支持執(zhí)行 esModule script,需要在分析的結(jié)果中,保留更多的信息

[
    {
      async: false,
      defer: true,
      src: 'https://wujie-micro.github.io/demo-react16/static/js/main.4000cadb.js',
      module: false,
      crossorigin: false,
      crossoriginType: '',
      ignore: false,
      contentPromise: // 獲取 script 內(nèi)容字符串的 Promise
	}
]

import-html-entry 的分析結(jié)果中,只有 script 的 js 內(nèi)容字符串。

無界是如何獲取 HTML 的外部的 script、style 內(nèi)容的?

分析 HTML,可以拿到外部 scriptstyle 的 url,用 fetch 發(fā)起 ajax 就可以獲取到 scriptstyle 的內(nèi)容。

但是 fetch 相對于原來 HTML script 標(biāo)簽,有一個壞處,就是 ajax 不能跨域,因此在使用無界的時候必須要給請求的資源設(shè)置允許跨域

處理 CSS 并重新嵌入 HTML

單獨將 CSS 分離出來,是為了讓無界插件能夠?qū)?對 CSS 代碼進行修改,下面是一個 CSS loader 插件:

const plugins = [
  {
    // 對 css 腳本動態(tài)的進行替換
    // code 為樣式代碼、url為樣式的地址(內(nèi)聯(lián)樣式為'')、base為子應(yīng)用當(dāng)前的地址
    cssLoader: (code, url, base) => {
      console.log("css-loader", url, code.slice(0, 50) + "...");
      // do something
      return code;
    },
  },
];

無界會用以下代碼遍歷插件修改 CSS

// 將所有 plugin 的 CSSLoader 函數(shù),合成一個 css-loader 處理函數(shù)
const composeCssLoader = compose(sandbox.plugins.map((plugin) => plugin.cssLoader));
const processedCssList: StyleResultList = getExternalStyleSheets().map(({ 
    src, 
    contentPromise 
}) => {
  return {
    src,
    // 傳入 CSS 文本處理處理函數(shù)
    contentPromise: contentPromise.then((content) => composeCssLoader(content, src, curUrl)),
  };
});

修改后的 CSS,會存儲在 processedCssList 數(shù)組中,需要遍歷該數(shù)組的內(nèi)容,將 CSS 重新嵌入到 HTML 中。

舉個例子,這是我們之前的 HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- defer script https://wujie-micro.github.io/demo-react16/static/js/main.4000cadb.js replaced by wujie -->
    <!--  link https://wujie-micro.github.io/demo-react16/static/css/main.7d8ad73e.css replaced by wujie -->
</head>
</head>
<body>
	<div id="root"></div>
</body>
</html>

嵌入 CSS 之后的 HTML 是這樣子的

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- defer script https://wujie-micro.github.io/demo-react16/static/js/main.4000cadb.js replaced by wujie -->
-   <!--  link https://wujie-micro.github.io/demo-react16/static/css/main.7d8ad73e.css replaced by wujie -->
+   <style>
+     	/* https://wujie-micro.github.io/demo-react16/static/css/main.7d8ad73e.css */.
+ 		省略內(nèi)容
+   <style/>
</head>
</head>
<body>
	<div id="root"></div>
</body>
</html>

將原來的 Link 標(biāo)簽替換成 style 標(biāo)簽,并寫入 CSS 。

創(chuàng)建 webComponent 并掛載 HTML

在執(zhí)行 JS 前,需要先把 HTML 的內(nèi)容渲染出來。

無界子應(yīng)用是掛載在 webComponent 中的,其定義如下:

class WujieApp extends HTMLElement {
  //  首次被插入文檔 DOM 時調(diào)用
  connectedCallback(): void {
    if (this.shadowRoot) return;
    // 創(chuàng)建 shadowDOM
    const shadowRoot = this.attachShadow({ mode: "open" });
    // 通過 webComponent 的標(biāo)簽 WUJIE_DATA_ID,拿到子應(yīng)用 id,再通過 id 拿到無界實例對象
    const sandbox = getWujieById(this.getAttribute(WUJIE_DATA_ID));
    // 保存 shadowDOM
    sandbox.shadowRoot = shadowRoot;
  }
  // 從文檔 DOM 中刪除時,被調(diào)用
  disconnectedCallback(): void {
    const sandbox = getWujieById(this.getAttribute(WUJIE_DATA_ID));
    sandbox?.unmount();
  }
}
customElements?.define("wujie-app", WujieApp);

于是就可以這樣創(chuàng)建 webComponent

export function createWujieWebComponent(id: string): HTMLElement {
  const contentElement = window.document.createElement("wujie-app");
  // 設(shè)置 WUJIE_DATA_ID 標(biāo)簽,為子應(yīng)用的 id‘
  contentElement.setAttribute(WUJIE_DATA_ID, id);
  return contentElement;
}

然后為 HTML 創(chuàng)建 DOM,這個非常簡單

let html = document.createElement("html");
html.innerHTML = template;	// template 為解析處理后的 HTML

直接用 innerHTML 設(shè)置 html 的內(nèi)容即可

然后再插入 CSS(上一小節(jié)的內(nèi)容)

// processCssLoaderForTemplate 返回注入 CSS 的 html DOM 對象
const processedHtml = await processCssLoaderForTemplate(iframeWindow.__WUJIE, html)

最后掛載到 shadowDOM

shadowRoot.appendChild(processedHtml);

這樣就完成了 HTML 和 CSS 的掛載了,CSS 由于在 shadowDOM 內(nèi),樣式也不會影響到外部,也不會受外部樣式影響。

JS 的執(zhí)行細節(jié)

HTML 渲染到 webComponent 之后,我們就可以執(zhí)行 JS 了

簡單的實現(xiàn)

export function insertScriptToIframe(
  scriptResult: ScriptObject | ScriptObjectLoader,
  iframeWindow: Window,
) {
  const { 
     content, 	// js 的代碼字符串
  } = scriptResult;
  const scriptElement = iframeWindow.document.createElement("script");
  scriptElement.textContent = content || "";
  // 獲取 head 標(biāo)簽
  const container = rawDocumentQuerySelector.call(iframeWindow.document, "head");
  // 在 head 中插入 script 標(biāo)簽,就會運行 js
  container.appendChild(scriptElement);
}

創(chuàng)建 script 標(biāo)簽,并插入到 iframe 的 head 中,就在 iframe 中能運行對應(yīng)的 JS 代碼。

這樣雖然能運行 JS,但是產(chǎn)生的副作用(例如渲染的 UI),也會留在 iframe 中。

如何理解這句話?

當(dāng)我們在 iframe 中,使用 document.querySelector查找 #app 的 DOM 時,它只能在 iframe 中查找(副作用留在 iframe 中),但 UI 是渲染到 webComponent 中的,webComponent 不在 iframe 中,且 iframe 不可見。

因此在 iframe 中就會找不到 DOM

那要怎么辦呢?

將 UI 渲染到 shadowRoot

我們先來看看現(xiàn)代的前端框架,是如何渲染 UI 的

以 Vue 為例,需要給 Vue 指定一個 DOM 作為掛載點,Vue 會將組件,掛載到該 DOM 上

import Comp from './comp.vue' 
// 傳入根組件
const app = createApp(Comp)
// 指定掛載點
app.mount('#app')

掛載到 #app,實際上使用 document.querySelector 查找 DOM,然后掛載到 DOM 里面

但是正如上一小節(jié)說的,在無界微前端會有問題:

  • 如果在 iframe 中運行 document.querySelector,就會在 iframe 中查找就會查找不到,因為子應(yīng)用的 HTML 是渲染到外部的 shadowRoot

因此這里必須要對 iframedocument.querySelector 進行改造,改為從 shadowRoot 里面查找,才能使 Vue 組件能夠正確找到掛載點,偽代碼如下:

const proxyDocument = new Proxy(
    {},
    {
      get: function (_, propKey) {
        if (propKey === "querySelector" || propKey === "querySelectorAll") {
          // 代理 shadowRoot 的 querySelector/querySelectorAll 方法
          return new Proxy(shadowRoot[propKey], {
            apply(target, ctx, args) {
              // 相當(dāng)于調(diào)用 shadowRoot.querySelector
              return target.apply(shadowRoot, args);
            },
          });
        }
      },
    }
);

這樣修改之后,調(diào)用 proxyDocument.querySelector 就會從 shadowRoot 中查找元素,就能掛載到 shadowRoot 中的 DOM 中了。

Vue 的根組件,就能成功掛載上去,其他子組件,因為是掛載到根節(jié)點或它的子節(jié)點上,不需要修改掛載位置,就能夠正確掛載。

到此為止,如果不考慮其他 js 非視圖相關(guān)的 js 代碼,整個DOM 樹就已經(jīng)掛載成功,UI 就已經(jīng)能夠渲染出來了。

挾持 document 的屬性/方法

上一小節(jié),通過 proxyDocument.querySelector,就能從 shadowRoot 查找元素

但這樣有一個壞處,就是要將 document 改成 proxyDocument,代碼才能正確運行。但這是有方法解決的。

假如我們要運行的是以下代碼:

const app = document.querySelector('#app')
// do something

我們可以包一層函數(shù):

(function (document){
	const app = document.querySelector('#app')
	// do something  
})(proxyDocument)

這樣就不需要修改子應(yīng)用的源碼,直接使用 document.querySelector

但是,這樣做又會有新的問題:

  • esModule 的 import 必須要在函數(shù)最外層
  • var 聲明的變量,原本是全局變量,包一層函數(shù)后,變量會被留在函數(shù)內(nèi)

于是就有了下面的方案:

// 挾持 iframeWindow.Document.prototype 的 querySelector
// 從 proxyDocument 中獲取
Object.defineProperty(iframeWindow.Document.prototype, 'querySelector', {
    enumerable: true,
    configurable: true,
    get: () =&gt; sandbox.proxyDocument['querySelector'],
    set: undefined,
});

只要我們在 iframe 創(chuàng)建時(子應(yīng)用 JS),先通過 Object.defineProperty 重寫 querySelector,挾持 document 的屬性/方法,然后從 proxyDocument 中取值,

這樣,就能直接執(zhí)行子應(yīng)用的 JS 代碼,不需要另外包一層函數(shù)執(zhí)行 JS

在無界微前端中,有非常多像 querySelector 的屬性/方法,需要對每個屬性方法的副作用進行修正。因此除了 proxyDocument,還有 proxyWindow、proxyLocation

很可惜的是,location 對象不能使用 Object.defineProperty 進行挾持,因此實際上,運行非 esModule 代碼時,仍然需要用函數(shù)包一層運行,傳入 proxyLocation 代替 location 對象。

但 esModule 由于不能在函數(shù)中運行,因此 esModule 代碼中獲取的 location 對象是錯誤的,這個無界的常見問題文檔也有提到。

接下來稍微介紹一下無界對 DOM 和 iframe 副作用的一些處理

副作用的處理

無界通過創(chuàng)建代理對象、覆蓋屬性和函數(shù)等方式對原有的JavaScript對象進行挾持。需要注意的是,所有這些處理都必須在子應(yīng)用 JS 運行之前,也就是在 iframe 創(chuàng)建時執(zhí)行:

const iframe = window.document.createElement("iframe");
// 將 iframe 插入到 document 中
window.document.body.appendChild(iframe);
const iframeWindow = iframe.contentWindow;
// 停止 iframe 的加載
sandbox.iframeReady = stopIframeLoading(iframeWindow).then(() =&gt; {
  // 對副作用進行處理修正
}                                                  

stopIframeLoading 后,即停止 iframe 加載,獲得純凈的 iframe 后,再對副作用進行處理

無界微前端 JS 有非常多的副作用需要修正處理,文章不會一一列舉,這里會說一下大概,讓大家對這個有點概念。

DOM 相關(guān)的副作用處理

下面是幾個例子

修正相對 URl

<img src = "./images/test.png" alt = "Test Image" />

當(dāng)我們在 DOM 中使用相對 url 時,會用 DOM 節(jié)點的 baseURI 作為基準,其默認值為 document.location.href。

但我們知道,子應(yīng)用的 UI 是掛載在 shadowRoot,跟主應(yīng)用是同一個 document 上下文,因此它的 baseURI 默認是主應(yīng)用的 url,但實際上應(yīng)該為子應(yīng)用的 url 才對,因此需要修正。

下面是部分修正的偽代碼:

// 重寫 Node 原型的 appendChild,在新增 DOM 時修正
iframeWindow.Node.prototype.appendChild = function(node) {
    const res = rawAppendChild.call(this, node);
    // 修正 DOM 的 baseURI
    patchElementEffect(node, iframeWindow);
    return res;
};

事實上,除了 appendChild,還有其他的函數(shù)需要修正,在每個能夠創(chuàng)建 DOM 的位置,都需要進行修正,例如 insertBefore

修正 shadowRoot head、body

shadowRoot 可以視為子應(yīng)用的 document

在前端項目中,經(jīng)常會在 JS 中引入 CSS,實際上 CSS 文本會以 style 標(biāo)簽的形式注入到 docuement.head 中,偽代碼如下:

export default function styleInject(css) {
  const head = document.head
  const style = document.createElement('style')
  style.type = 'text/css'
  style.styleSheet.cssText = css
  head.appendChild(style)
}

在 iframe 中使用 document.head,需要用 Object.defineProperty 挾持 document 的 head 屬性,將其重定向到 shadowRoot 的 head 標(biāo)簽

Object.defineProperty(iframeWindow.document, 'head', {
  enumerable: true,
  configurable: true,
  // 改為從 proxyDocument 中取值
  get: () => sandbox.proxyDocument['head'],
  set: undefined,
});

proxyDocument 的 head 實際上為 shadowRoot 的 head

shadowRoot.head = shadowRoot.querySelector("head");
shadowRoot.body = shadowRoot.querySelector("body");

同樣的,很多組件庫的彈窗,都會往 document.body 插入彈窗的 DOM,因此也要處理

iframe 的副作用處理

History API

history API 在 SPA 應(yīng)用中非常常見,例如 vue-router 就會使用到 history.pushState、 history.replaceState 等 API。

當(dāng)前 url 改變時

  • 需要改變 document.baseURI,而它是個只讀的值,需要修改 document.head 中的 base 標(biāo)簽
  • 需要將子應(yīng)用的 url,同步到父應(yīng)用的地址欄中
history.pushState = function (data: any, title: string, url?: string): void {
  // 當(dāng)前的 url
  const baseUrl = mainHostPath 
  					+ iframeWindow.location.pathname
  					+ iframeWindow.location.search
  					+ iframeWindow.location.hash;
  // 根據(jù)當(dāng)前 url,計算出即將跳轉(zhuǎn)的 url 的絕對路徑
  const mainUrl = getAbsolutePath(url?.replace(appHostPath, ""), baseUrl);
  // 調(diào)用原生的 history.pushState
  rawHistoryPushState.call(history, data, title, ignoreFlag ? undefined : mainUrl);
  // 更新 head 中的 base 標(biāo)簽
  updateBase(iframeWindow, appHostPath, mainHostPath);
  // 同步 url 到主應(yīng)用地址欄
  syncUrlToWindow(iframeWindow);
};

window/document 屬性/事件

有些屬性,應(yīng)該是使用主應(yīng)用 window 的屬性,例如:getComputedStyle

有些事件,需要掛載到主應(yīng)用,有些需要掛載到 iframe 中。這里直接舉個例子:

  • onunload 事件,需要掛載到 iframe 中
  • onkeyup 事件,需要掛載到主應(yīng)用的 window 下(iframe 中沒有 UI,UI 掛載到主應(yīng)用 document 的 shadowRoot 下)

因此要挾持 onXXX 事件和 addEventListener,對每一個事件進行分發(fā),將事件掛載到 window / iframeWindow

將事件掛載到window 的代碼實現(xiàn)如下:

// 挾持 onXXX 函數(shù)
Object.defineProperty(iframeWindow, 'onXXX', {
    enumerable: true,
    configurable: true,
    // 從 window 取
    get: () => window['onXXX'],
    set: (handler) => {
        	// 設(shè)置到 window
            window['onXXX'] = typeof handler === "function" 
                ? handler.bind(iframeWindow) 	// 將函數(shù)的 this 設(shè)置為 iframeWindow
            	: handler;
          }
});

通過 Object.defineProperty 挾持 onXXX,將事件設(shè)置到 window 上。

location 對象

當(dāng)我們在子應(yīng)用 iframe 中獲取 location.hreflocation.host 等屬性的時候,需要獲取的是子應(yīng)用的 hrefhost(iframe 的 location href 并不是子應(yīng)用的 url),因此這里也是需要進行改造。

const proxyLocation = new Proxy(
  {},
  {
    get: function (_, propKey) {
      if (propKey === "href") {
        return // 獲取子應(yīng)用真正的 url
      }
	  // 省略其他屬性的挾持
    },
  }
);

為什么 iframe 的 location href 不是子應(yīng)用的 url?

為了實現(xiàn)應(yīng)用間(iframe 間)通訊,無界子應(yīng)用 iframe 的 url 會設(shè)置為主應(yīng)用的域名(同域)

總結(jié)

本文介紹了無界渲染子應(yīng)用的步驟:

  • 創(chuàng)建子應(yīng)用 iframe
  • 解析入口 HTML
  • 創(chuàng)建 webComponent,并掛載 HTML
  • 運行 JS 渲染 UI

最后介紹了無界是處理副作用的一些細節(jié)。

目前主流的微前端框架多多少少多會有些問題,目前還沒有一種非常完美的方法實現(xiàn)微前端。即使是經(jīng)歷過長時間迭代的 qiankun,其設(shè)計上也有問題,因此還配有一個常見問題的頁面,給開發(fā)者提供幫助去避免問題。

在本文中也介紹到,雖然無界的設(shè)計思想更為優(yōu)秀,但其設(shè)計也是有局限性的,例如必須要允許跨域、location 對象無法挾持等,這些都是開發(fā)中會遇到的問題,只有理解了無界的設(shè)計,才能更好的理解這些問題的本質(zhì)原因,以及知道如何去避免它們。

以上就是詳解無界微前端是如何渲染子應(yīng)用的demo解析的詳細內(nèi)容,更多關(guān)于無界微前端渲染子應(yīng)用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 前端自動化測試之Jest?進階教程示例

    前端自動化測試之Jest?進階教程示例

    這篇文章主要為大家介紹了前端自動化測試之Jest?進階教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • element彈窗表格的字體模糊bug解決

    element彈窗表格的字體模糊bug解決

    這篇文章主要為大家介紹了element彈窗表格的字體模糊bug解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • 4個頂級開源JavaScript圖表庫

    4個頂級開源JavaScript圖表庫

    今天小編就為大家分享一篇關(guān)于4個頂級開源JavaScript圖表庫,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-09-09
  • JS版的date函數(shù)(和PHP的date函數(shù)一樣)

    JS版的date函數(shù)(和PHP的date函數(shù)一樣)

    這篇文章主要介紹了JS版的date函數(shù),使用方法和PHP的date函數(shù)一樣,需要的朋友可以參考下
    2014-05-05
  • 最新評論