详解无界微前端是如何渲染子应用的demo解析

目录
  • 正文
  • 无界渲染子应用的步骤
  • 创建子应用 iframe
  • 解析入口 HTML
    • 处理 CSS 并重新嵌入 HTML
  • 创建 webComponent 并挂载 HTML
  • JS 的执行细节
    • 简单的实现
    • 将 UI 渲染到 shadowRoot
    • 挟持 document 的属性/方法
  • 副作用的处理
    • DOM 相关的副作用处理
      • 修正相对 URl
      • 修正 shadowRoot head、body
    • iframe 的副作用处理
      • History API
      • window/document 属性/事件
      • location 对象
  • 总结

正文

经过我们团队的调研,我们选择了无界作为微前端的技术栈。目前的使用效果非常好,不仅性能表现出色,而且使用体验也不错。

尽管在使用的过程中,我们也遇到了一些问题,但这些问题往往源于我们对框架实现的不熟悉。我们深入研究了无界技术的源码,并将在本文中与大家分享。本文将重点探讨无界微前端如何渲染子应用的。

无界渲染子应用的步骤

无界与其他微前端框架(例如qiankun)的主要区别在于其独特的 JS 沙箱机制。无界使用 iframe 来实现 JS 沙箱,由于这个设计,无界在以下方面表现得更加出色:

  • 应用切换没有清理成本
  • 允许一个页面同时激活多个子应用
  • 性能相对更优

无界渲染子应用,主要分为以下几个步骤:

  • 创建子应用 iframe
  • 解析入口 HTML
  • 创建 webComponent,并挂载 HTML
  • 运行 JS 渲染 UI

创建子应用 iframe

要在 iframe 中运行 JS,首先得有一个 iframe。

export function iframeGenerator(
  sandbox: WuJie,
  attrs: { [key: string]: any },
  mainHostPath: string,
  appHostPath: string,
  appRoutePath: string
): HTMLIFrameElement {
  // 创建 iframe 的 DOM
  const iframe = window.document.createElement("iframe");
  // 设置 iframe 的 attr
  setAttrsToElement(iframe, {
      // iframe 的 url 设置为主应用的域名
      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(() => {
      // 省略其他内容
  }
  // 注入无界的变量到 iframeWindow,例如 __WUJIE
  patchIframeVariable(iframeWindow, sandbox, appHostPath);
  // 省略其他内容
  return iframe;
}

创建 iframe 主要有以下流程:

  • 创建 iframe 的 DOM,并设置属性
  • 将 iframe 插入到 document 中(此时 iframe 会立即访问 src)
  • 停止 iframe 的加载(stopIframeLoading)

为什么要停止 iframe 的加载?

因为要创建一个纯净的 iframe,防止 iframe 被污染,假如该 url 的 JS 代码,声明了一些全局变量、函数,就可能影响到子应用的运行(假如子应用也有同名的变量、函数)

为什么 iframe 的 src 要设置为主应用的域名

为了实现应用间(iframe 间)通讯,无界子应用 iframe 的 url 会设置为主应用的域名(同域)

  • 主应用域名为 a.com
  • 子应用域名为 b.com,但它对应的 iframe 域名为 a.com,所以要设置 b.com 的资源能够允许跨域访问

因此 iframe 的 location.href 并不是子应用的 url。

解析入口 HTML

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

我们可以通过解析入口 HTML 来确定需要运行的 JS 内容

假设有以下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>

经过 importHTML 处理后,结果如下:

  • 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,获取所有内联和外部的 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 内容字符串的 Promise
	}
]
  • getExternalStyleSheets,获取所有内联和外部的 style
[
    {
        src: "https://wujie-micro.github.io/demo-react16/static/css/main.7d8ad73e.css",
        ignore: false,
        contentPromise: // 获取 style 内容字符串的 Promise
    }
]

为什么要将 script 和 style 从 HTML 中分离?

  • HTML 要作为 webComponent 的内容,挂载到微前端挂载点上
  • 因为无界有插件机制,需要单独对 js/style 进行处理,再插入到 webComponent 中
  • script 除了需要经过插件处理外,还需要放到 iframe 沙箱中执行,因此也要单独分离出来

external 是外部的意思,为什么 getExternalScripts 拿到的却是所有的 script,而不是外部的非内联 script?

external 是相对于解析后的 HTML 模板来说的,由于解析后的 HTML 不带有任何的 js 和 css,所以这里的 external,就是指模板外的所有 JS

无界与 qiankun 的在解析 HTML 上区别?

无界和 qiankun 都是以 HTML 为入口的微前端框架。qiankun 基于 import-html-entry 解析 HTML,而无界则是借鉴 import-html-entry 代码,实现了自己的 HTML 的解析,因此两者在解析 HTML 上的不同,主要是在importHTML 的实现上。

由于无界支持执行 esModule 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 内容字符串的 Promise
	}
]

import-html-entry 的分析结果中,只有 script 的 js 内容字符串。

无界是如何获取 HTML 的外部的 script、style 内容的?

分析 HTML,可以拿到外部 scriptstyle 的 url,用 fetch 发起 ajax 就可以获取到 scriptstyle 的内容。

但是 fetch 相对于原来 HTML script 标签,有一个坏处,就是 ajax 不能跨域,因此在使用无界的时候必须要给请求的资源设置允许跨域

处理 CSS 并重新嵌入 HTML

单独将 CSS 分离出来,是为了让无界插件能够对 对 CSS 代码进行修改,下面是一个 CSS loader 插件:

const plugins = [
  {
    // 对 css 脚本动态的进行替换
    // code 为样式代码、url为样式的地址(内联样式为'')、base为子应用当前的地址
    cssLoader: (code, url, base) => {
      console.log("css-loader", url, code.slice(0, 50) + "...");
      // do something
      return code;
    },
  },
];

无界会用以下代码遍历插件修改 CSS

// 将所有 plugin 的 CSSLoader 函数,合成一个 css-loader 处理函数
const composeCssLoader = compose(sandbox.plugins.map((plugin) => plugin.cssLoader));
const processedCssList: StyleResultList = getExternalStyleSheets().map(({
    src,
    contentPromise
}) => {
  return {
    src,
    // 传入 CSS 文本处理处理函数
    contentPromise: contentPromise.then((content) => composeCssLoader(content, src, curUrl)),
  };
});

修改后的 CSS,会存储在 processedCssList 数组中,需要遍历该数组的内容,将 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 */.
+ 		省略内容
+   <style/>
</head>
</head>
<body>
	<div id="root"></div>
</body>
</html>

将原来的 Link 标签替换成 style 标签,并写入 CSS 。

创建 webComponent 并挂载 HTML

在执行 JS 前,需要先把 HTML 的内容渲染出来。

无界子应用是挂载在 webComponent 中的,其定义如下:

class WujieApp extends HTMLElement {
  //  首次被插入文档 DOM 时调用
  connectedCallback(): void {
    if (this.shadowRoot) return;
    // 创建 shadowDOM
    const shadowRoot = this.attachShadow({ mode: "open" });
    // 通过 webComponent 的标签 WUJIE_DATA_ID,拿到子应用 id,再通过 id 拿到无界实例对象
    const sandbox = getWujieById(this.getAttribute(WUJIE_DATA_ID));
    // 保存 shadowDOM
    sandbox.shadowRoot = shadowRoot;
  }
  // 从文档 DOM 中删除时,被调用
  disconnectedCallback(): void {
    const sandbox = getWujieById(this.getAttribute(WUJIE_DATA_ID));
    sandbox?.unmount();
  }
}
customElements?.define("wujie-app", WujieApp);

于是就可以这样创建 webComponent

export function createWujieWebComponent(id: string): HTMLElement {
  const contentElement = window.document.createElement("wujie-app");
  // 设置 WUJIE_DATA_ID 标签,为子应用的 id‘
  contentElement.setAttribute(WUJIE_DATA_ID, id);
  return contentElement;
}

然后为 HTML 创建 DOM,这个非常简单

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

直接用 innerHTML 设置 html 的内容即可

然后再插入 CSS(上一小节的内容)

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

最后挂载到 shadowDOM

shadowRoot.appendChild(processedHtml);

这样就完成了 HTML 和 CSS 的挂载了,CSS 由于在 shadowDOM 内,样式也不会影响到外部,也不会受外部样式影响。

JS 的执行细节

HTML 渲染到 webComponent 之后,我们就可以执行 JS 了

简单的实现

export function insertScriptToIframe(
  scriptResult: ScriptObject | ScriptObjectLoader,
  iframeWindow: Window,
) {
  const {
     content, 	// js 的代码字符串
  } = scriptResult;
  const scriptElement = iframeWindow.document.createElement("script");
  scriptElement.textContent = content || "";
  // 获取 head 标签
  const container = rawDocumentQuerySelector.call(iframeWindow.document, "head");
  // 在 head 中插入 script 标签,就会运行 js
  container.appendChild(scriptElement);
}

创建 script 标签,并插入到 iframe 的 head 中,就在 iframe 中能运行对应的 JS 代码。

这样虽然能运行 JS,但是产生的副作用(例如渲染的 UI),也会留在 iframe 中。

如何理解这句话?

当我们在 iframe 中,使用 document.querySelector查找 #app 的 DOM 时,它只能在 iframe 中查找(副作用留在 iframe 中),但 UI 是渲染到 webComponent 中的,webComponent 不在 iframe 中,且 iframe 不可见。

因此在 iframe 中就会找不到 DOM

那要怎么办呢?

将 UI 渲染到 shadowRoot

我们先来看看现代的前端框架,是如何渲染 UI 的

以 Vue 为例,需要给 Vue 指定一个 DOM 作为挂载点,Vue 会将组件,挂载到该 DOM 上

import Comp from './comp.vue'
// 传入根组件
const app = createApp(Comp)
// 指定挂载点
app.mount('#app')

挂载到 #app,实际上使用 document.querySelector 查找 DOM,然后挂载到 DOM 里面

但是正如上一小节说的,在无界微前端会有问题:

  • 如果在 iframe 中运行 document.querySelector,就会在 iframe 中查找就会查找不到,因为子应用的 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) {
              // 相当于调用 shadowRoot.querySelector
              return target.apply(shadowRoot, args);
            },
          });
        }
      },
    }
);

这样修改之后,调用 proxyDocument.querySelector 就会从 shadowRoot 中查找元素,就能挂载到 shadowRoot 中的 DOM 中了。

Vue 的根组件,就能成功挂载上去,其他子组件,因为是挂载到根节点或它的子节点上,不需要修改挂载位置,就能够正确挂载。

到此为止,如果不考虑其他 js 非视图相关的 js 代码,整个DOM 树就已经挂载成功,UI 就已经能够渲染出来了。

挟持 document 的属性/方法

上一小节,通过 proxyDocument.querySelector,就能从 shadowRoot 查找元素

但这样有一个坏处,就是要将 document 改成 proxyDocument,代码才能正确运行。但这是有方法解决的。

假如我们要运行的是以下代码:

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

我们可以包一层函数:

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

这样就不需要修改子应用的源码,直接使用 document.querySelector

但是,这样做又会有新的问题:

  • esModule 的 import 必须要在函数最外层
  • var 声明的变量,原本是全局变量,包一层函数后,变量会被留在函数内

于是就有了下面的方案:

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

只要我们在 iframe 创建时(子应用 JS),先通过 Object.defineProperty 重写 querySelector,挟持 document 的属性/方法,然后从 proxyDocument 中取值,

这样,就能直接执行子应用的 JS 代码,不需要另外包一层函数执行 JS

在无界微前端中,有非常多像 querySelector 的属性/方法,需要对每个属性方法的副作用进行修正。因此除了 proxyDocument,还有 proxyWindowproxyLocation

很可惜的是,location 对象不能使用 Object.defineProperty 进行挟持,因此实际上,运行非 esModule 代码时,仍然需要用函数包一层运行,传入 proxyLocation 代替 location 对象。

但 esModule 由于不能在函数中运行,因此 esModule 代码中获取的 location 对象是错误的,这个无界的常见问题文档也有提到。

接下来稍微介绍一下无界对 DOM 和 iframe 副作用的一些处理

副作用的处理

无界通过创建代理对象、覆盖属性和函数等方式对原有的JavaScript对象进行挟持。需要注意的是,所有这些处理都必须在子应用 JS 运行之前,也就是在 iframe 创建时执行:

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 相关的副作用处理

下面是几个例子

修正相对 URl

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

当我们在 DOM 中使用相对 url 时,会用 DOM 节点的 baseURI 作为基准,其默认值为 document.location.href

但我们知道,子应用的 UI 是挂载在 shadowRoot,跟主应用是同一个 document 上下文,因此它的 baseURI 默认是主应用的 url,但实际上应该为子应用的 url 才对,因此需要修正。

下面是部分修正的伪代码:

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

事实上,除了 appendChild,还有其他的函数需要修正,在每个能够创建 DOM 的位置,都需要进行修正,例如 insertBefore

修正 shadowRoot head、body

shadowRoot 可以视为子应用的 document

在前端项目中,经常会在 JS 中引入 CSS,实际上 CSS 文本会以 style 标签的形式注入到 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 标签

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 应用中非常常见,例如 vue-router 就会使用到 history.pushStatehistory.replaceState 等 API。

当前 url 改变时

  • 需要改变 document.baseURI,而它是个只读的值,需要修改 document.head 中的 base 标签
  • 需要将子应用的 url,同步到父应用的地址栏中
history.pushState = function (data: any, title: string, url?: string): void {
  // 当前的 url
  const baseUrl = mainHostPath
  					+ iframeWindow.location.pathname
  					+ iframeWindow.location.search
  					+ iframeWindow.location.hash;
  // 根据当前 url,计算出即将跳转的 url 的绝对路径
  const mainUrl = getAbsolutePath(url?.replace(appHostPath, ""), baseUrl);
  // 调用原生的 history.pushState
  rawHistoryPushState.call(history, data, title, ignoreFlag ? undefined : mainUrl);
  // 更新 head 中的 base 标签
  updateBase(iframeWindow, appHostPath, mainHostPath);
  // 同步 url 到主应用地址栏
  syncUrlToWindow(iframeWindow);
};

window/document 属性/事件

有些属性,应该是使用主应用 window 的属性,例如:getComputedStyle

有些事件,需要挂载到主应用,有些需要挂载到 iframe 中。这里直接举个例子:

  • onunload 事件,需要挂载到 iframe 中
  • onkeyup 事件,需要挂载到主应用的 window 下(iframe 中没有 UI,UI 挂载到主应用 document 的 shadowRoot 下)

因此要挟持 onXXX 事件和 addEventListener,对每一个事件进行分发,将事件挂载到 window / iframeWindow

将事件挂载到window 的代码实现如下:

// 挟持 onXXX 函数
Object.defineProperty(iframeWindow, 'onXXX', {
    enumerable: true,
    configurable: true,
    // 从 window 取
    get: () => window['onXXX'],
    set: (handler) => {
        	// 设置到 window
            window['onXXX'] = typeof handler === "function"
                ? handler.bind(iframeWindow) 	// 将函数的 this 设置为 iframeWindow
            	: handler;
          }
});

通过 Object.defineProperty 挟持 onXXX,将事件设置到 window 上。

location 对象

当我们在子应用 iframe 中获取 location.hreflocation.host 等属性的时候,需要获取的是子应用的 hrefhost(iframe 的 location href 并不是子应用的 url),因此这里也是需要进行改造。

const proxyLocation = new Proxy(
  {},
  {
    get: function (_, propKey) {
      if (propKey === "href") {
        return // 获取子应用真正的 url
      }
	  // 省略其他属性的挟持
    },
  }
);

为什么 iframe 的 location href 不是子应用的 url?

为了实现应用间(iframe 间)通讯,无界子应用 iframe 的 url 会设置为主应用的域名(同域)

总结

本文介绍了无界渲染子应用的步骤:

  • 创建子应用 iframe
  • 解析入口 HTML
  • 创建 webComponent,并挂载 HTML
  • 运行 JS 渲染 UI

最后介绍了无界是处理副作用的一些细节。

目前主流的微前端框架多多少少多会有些问题,目前还没有一种非常完美的方法实现微前端。即使是经历过长时间迭代的 qiankun,其设计上也有问题,因此还配有一个常见问题的页面,给开发者提供帮助去避免问题。

在本文中也介绍到,虽然无界的设计思想更为优秀,但其设计也是有局限性的,例如必须要允许跨域、location 对象无法挟持等,这些都是开发中会遇到的问题,只有理解了无界的设计,才能更好的理解这些问题的本质原因,以及知道如何去避免它们。

以上就是详解无界微前端是如何渲染子应用的demo解析的详细内容,更多关于无界微前端渲染子应用的资料请关注我们其它相关文章!

(0)

相关推荐

  • go开源Hugo站点构建三步曲之集结渲染

    目录 Assemble PageState 动手实践 - Show Me the Code of Create a PageState Render 动手实践 - Show Me the Code of Publish Assemble Assemble所做的事情很纯粹,那就是创建站点页面实例 - pageState. 因为支持多站点,contentMaps有多个. 所以Assemble不仅要创建pageState,还需要管理好所有的pages,这就用到了PageMaps. type pageM

  • go开源Hugo站点渲染之模板词法解析

    目录 正文 了然于胸 - newTemplateExec时序图 词法解析 - parse 正文 Deps在准备好NewPathSpec,NewSpec,NewContentSpec,NewSourceSpec后,调用onCreate正式创建HugoSites,并在最后一步,加载模板执行器. 模板执行器只是提前将模板信息转换成了模板执行器,如何使用并没有涉及到. 为了让我们对模板生命周期有更全面的了解,让我们以robots.txt为例,来看看是如何通过模板执行器生成最终文件的. 了然于胸 - ne

  • 通过示例源码解读React首次渲染流程

    目录 说明 题目 首次渲染流程 render beginWork completeUnitOfWork commit 准备阶段 before mutation 阶段 mutation 阶段 切换 Fiber Tree layout 阶段 题目解析 总结 说明 本文结论均基于 React 16.13.1 得出,若有出入请参考对应版本源码.参考了 React 技术揭秘. 题目 在开始进行源码分析前,我们先来看几个题目: 题目一: 渲染下面的组件,打印顺序是什么? import React from

  • React中immutable的UI组件渲染性能详解

    目录 引言 UI组件渲染性能 方案一:shallow compare 方案二:直接对前后的对象进行deepCompare 总结: 引言 react 一直遵循UI = fn(state) 的原则,有时候我们的state却和UI不同步 有时候组件本身在业务上不需要渲染,却又会再一次re-render.之前在项目中遇到的一些问题,这里做一个简单的分析,大家可以一起交流一下 UI组件渲染性能 react每次触发页面的更新可大致分成两步: render(): 主要是计算v-dom的diff commit阶

  • electron渲染进程主进程相互传值示例解析

    目录 在electron中分为渲染进程和主进程 浏览器传值给主进程 浏览器环境 主进程 主进程传值给渲染进程 主进程 渲染进程 注意 在electron中分为渲染进程和主进程 渲染进程就是浏览器环境,主进程就是node环境 既然他们是不同的环境,那么为我们怎么让他们相互关联起来呢?或者说怎么传递值? 毕竟在开发中可能会遇到我想要的值只能在node环境中才能获取,然后node中可能也会需要浏览器环境的值:这个时候就需要两个环境联通起来相互传值 浏览器传值给主进程 浏览器环境 引入: import

  • 详解无界微前端是如何渲染子应用的demo解析

    目录 正文 无界渲染子应用的步骤 创建子应用 iframe 解析入口 HTML 处理 CSS 并重新嵌入 HTML 创建 webComponent 并挂载 HTML JS 的执行细节 简单的实现 将 UI 渲染到 shadowRoot 挟持 document 的属性/方法 副作用的处理 DOM 相关的副作用处理 修正相对 URl 修正 shadowRoot head.body iframe 的副作用处理 History API window/document 属性/事件 location 对象

  • 详解Go语言微服务开发框架之Go chassis

    引言 https://github.com/go-chassis/go-chassis是一个微服务开发框架,而微服务开发框架带来的其中一个课题就是:当单体应用向微服务转型后,有大量的配置需要管理,而你并不希望登录到远端机器去更改配置,并重启应用,尤其是现在已经是容器的时代了,也不希望因为一个配置的变更,而发布一个新的软件包.那么分布式系统中每个进程的动态配置管理及运行时热加载就成为了一个亟待解决的问题.https://github.com/go-chassis/go-archaius为gocha

  • 详解Vue.js3.0 组件是如何渲染为DOM的

    本文主要是讲述 Vue.js 3.0 中一个组件是如何转变为页面中真实 DOM 节点的.对于任何一个基于 Vue.js 的应用来说,一切的故事都要从应用初始化「根组件(通常会命名为 APP)挂载到 HTML 页面 DOM 节点(根组件容器)上」说起.所以,我们可以从应用的根组件为切入点. 主线思路:聚焦于一个组件是如何转变为 DOM 的. 辅助思路: 涉及到源代码的地方,需要明确标记源码所在文件,同时将 TS 简化为 JS 以便于直观理解 思路每前进一步要能够得出结论 尽量总结归纳出流程图 应用

  • 详解Spring Cloud微服务架构下的WebSocket解决方案

    WebSocket在现代浏览器中的应用已经算是比较普遍了,在某些业务场景下,要求必须能够在服务器端推送消息至客户端.在没有WebSocket的年代,我们使用过dwr,在那个时候dwr真实一个非常棒的方案.但是在WebSocket兴起之后,我们更愿意使用标准实现来解决问题. 首先交代一下,本篇文章不讲解WebSocket的配置,主要讲的是针对在微服务架构集群模式下解决方案的选择. 微服务架构大家应该都不陌生了,在微服务架构下,服务是分布式的,而且为了保证业务的可用性,每个服务都是以集群的形式存在.

  • 详解基于node的前端项目编译时内存溢出问题

    前段时间公司有个基于vue的项目在运行npm run build的时候会报内存溢出,今天在某个技术流交群也有位小伙伴基于angular的项目也出现了这个问题,所以查了一些相关的资料总结了一下,下面会详细说明前端三大框架编译时遇到这个问题具体怎么解决.首先看我模拟出的报错内容 具体截图如下 里面有句关键的话,CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory JavaScript堆内存不足,这里说的 JavaS

  • 详解能在多种前端框架下使用的表格控件

    近几年Web前端框架特别流行,比如AngularJS.AngularJS 2.ReactJS.KnockoutJS.VueJS等.表格控件是我们在开发中经常要用到的控件.有没有能够在多种前端控件下都能使用的表格控件?最近研究发现Wijmo中的FlexGrid是一款不错的表格控件,它能支持很多主流的框架.这里主要介绍在纯JavaScript和AngularJS下FlexGrid的使用. 一.在纯JavaScript下使用FlexGrid HTML文件: <!DOCTYPE html> <h

  • 详解Spring Boot微服务如何集成fescar解决分布式事务问题

    什么是fescar? 关于fescar的详细介绍,请参阅fescar wiki. 传统的2PC提交协议,会持有一个全局性的锁,所有局部事务预提交成功后一起提交,或有一个局部事务预提交失败后一起回滚,最后释放全局锁.锁持有的时间较长,会对并发造成较大的影响,死锁的风险也较高. fescar的创新之处在于,每个局部事务执行完立即提交,释放本地锁:它会去解析你代码中的sql,从数据库中获得事务提交前的事务资源即数据,存放到undo_log中,全局事务协调器在回滚的时候直接使用undo_log中的数据覆

  • 详解redis在微服务领域的贡献

    前言 说到redis,可能大家的脑海中蹦出的关键词是:NoSQL.KV.高性能.缓存等.但今天的文章从另一个角度--微服务来展开. 这篇文章的起因也是源自一次面试经历,在面试一位来自陌陌的候选人(就是那个交友的陌陌)时,他提到一点让我觉得很有意思,他说redis在陌陌被使用的非常广泛,除了常规的缓存外,某些场景下也当NoSQL数据库来使用,还用redis作为微服务的注册中心,甚至连RPC的调用协议都用了redis协议. 注册中心 最早了解到redis可以作为注册中心是从dubbo的源码中看到,但

  • 利用ajax提交form表单到数据库详解(无刷新)

    大家应该都知道,在静态页面提交表单到数据库很简单就是单纯的 <form action="test.php" method="post"> </form> 这个缺点是会刷新页面,会跳转页面的. 今天给大家带来的技术就是ajax提交表单 优点是不刷新页面,不跳转页面,静默提交的. 至于什么是ajax,自己去百度了解. 首先我们得要有一个表单提交页面: index.html 这个页面由两个部分组成 1.表单控件 2.jQuery+ajax处理脚本

  • 详解基于Angular4+ server render(服务端渲染)开发教程

    目标: 1.更好的 SEO,方便搜索爬虫抓取页面内容 2.更快的内容到达时间(time-to-content) 影响: 1.用户:比原来更快的看到渲染的页面,提升用户体验 2.开发人员:某些代码可能需要特殊处理,才能在服务器渲染应用程序中运行(window,document, navigator等) 安装: 1.nodejs 建议6+ 2.angular建议4.1+ 理论实现: 尽管这是一张来自vue官网服务器渲染的一张示意图,但是原理上和angular都是一样的,只是实现的代码不一致. SSR

随机推荐