{"componentChunkName":"component---src-templates-blog-post-js","path":"/2020/03/28/single-spa构建前端微服务（一）/","result":{"data":{"content":{"edges":[{"node":{"id":"1cf55332-ef1c-5e1c-86a7-66a7d1a83a91","html":"<h2 id=\"前言\" style=\"position:relative;\"><a href=\"#%E5%89%8D%E8%A8%80\" aria-label=\"前言 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>前言</h2>\n<p>对于各大互联网公司中后台业务线的前端团队来说，多项目多技术栈之间业务依赖不可避免。</p>\n<p>同学们最普遍的实现方案是采用新开页面实现系统关联的MPA模式。尽管部署简单、天然硬隔离，但域名变化与全页重刷又会带来用户体验上的痛点；使用iframe，也会暴露出UI不同步、内存无法共享、资源加载慢等操作上的不快。</p>\n<p>随着团队发展与人员变迁，每个普通SPA中小型项目又会演变成一个Monolith。维护成本高、上线部署耗时长，都给团队开发效率制造了很大的瓶颈。</p>\n<p>我们曾采用yog2做子项目拆分，一定程度上减小了巨石应用的体积，但其本质是路由分发思路。各project共享同一runtime，多框架场景支持不佳。</p>\n<h2 id=\"微前端架构\" style=\"position:relative;\"><a href=\"#%E5%BE%AE%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84\" aria-label=\"微前端架构 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>微前端架构</h2>\n<p><img src=\"https://2img.net/h/oi968.photobucket.com/albums/ae170/laughingjacky/Blog%20Assets%202020/portal_zps9qpfup5g.png\" alt=\"portal\">\n￼</p>\n<p>微前端理念来源于后端微服务实践，其架构大致可分为：</p>\n<ul>\n<li>单实例：同一时刻，只有一个子应用框架实例展示在页面上，具备完整生命周期</li>\n<li>多实例：引申web components的方案封装组件，同一时刻展示多个子应用，<a href=\"https://micro-frontends.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Micro Frontends</a>阐述的就是这套方案</li>\n</ul>\n<p>下面就借助single-spa框架，展示一种单实例场景下的设计方案。对大部分中后台应用都有参考价值。</p>\n<p>要实现的页面如图所示，支持React、Vue、Svelte、Riot、Inferno五种子应用的挂载与切换：</p>\n<p><img src=\"https://2img.net/h/oi968.photobucket.com/albums/ae170/laughingjacky/Blog%20Assets%202020/result_zpsa0xrqdml.png\" alt=\"size800\">\n￼</p>\n<h3 id=\"子应用编写\" style=\"position:relative;\"><a href=\"#%E5%AD%90%E5%BA%94%E7%94%A8%E7%BC%96%E5%86%99\" aria-label=\"子应用编写 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>子应用编写</h3>\n<p>我们知道，spa项目都需要一个dom挂载点，dom配置如下：</p>\n<div class=\"gatsby-highlight\" data-language=\"html\"><pre class=\"language-html\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>navbar<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token style-attr language-css\"><span class=\"token attr-name\"> <span class=\"token attr-name\">style</span></span><span class=\"token punctuation\">=\"</span><span class=\"token attr-value\"><span class=\"token property\">margin-top</span><span class=\"token punctuation\">:</span> 100px<span class=\"token punctuation\">;</span></span><span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>react<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>vue<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>svelte<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>inferno<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>riot<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span></code></pre></div>\n<p>接下来用五种UI lib编写基本页面</p>\n<p><img src=\"https://2img.net/h/oi968.photobucket.com/albums/ae170/laughingjacky/Blog%20Assets%202020/folder_zpsdvxj4cvx.png\" alt=\"size400\"></p>\n<p>其中, root文件是我们子应用的根入口。日后设计子应用路由也要由此而生;app.js就涉及到了我们的主题single-spa，该文件负责关联single-spa与子框架。</p>\n<h3 id=\"single-spa\" style=\"position:relative;\"><a href=\"#single-spa\" aria-label=\"single spa permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>single-spa</h3>\n<p>借助<a href=\"https://single-spa.js.org/docs/ecosystem\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">single-spa-ecosystem</a>提供的框架生命周期适配，可以抹平各框架之间的差异，拿到归一化的生命周期。</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span>render<span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'inferno'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> singleSpaInferno <span class=\"token keyword\">from</span> <span class=\"token string\">'single-spa-inferno'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span>createElement<span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'inferno-create-element'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">import</span> rootComponent <span class=\"token keyword\">from</span> <span class=\"token string\">'./root.component.js'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> infernoLifecycles <span class=\"token operator\">=</span> <span class=\"token function\">singleSpaInferno</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Inferno<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        render\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    createElement<span class=\"token punctuation\">,</span>\n    rootComponent<span class=\"token punctuation\">,</span>\n    <span class=\"token function-variable function\">domElementGetter</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> document<span class=\"token punctuation\">.</span><span class=\"token function\">getElementById</span><span class=\"token punctuation\">(</span><span class=\"token string\">'inferno'</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> bootstrap <span class=\"token operator\">=</span> infernoLifecycles<span class=\"token punctuation\">.</span>bootstrap<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> mount <span class=\"token operator\">=</span> infernoLifecycles<span class=\"token punctuation\">.</span>mount<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> unmount <span class=\"token operator\">=</span> infernoLifecycles<span class=\"token punctuation\">.</span>unmount<span class=\"token punctuation\">;</span></code></pre></div>\n<p>接下来我们需要将子应用注册到portal，并做异步加载：</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span>registerApplication<span class=\"token punctuation\">,</span> start<span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'single-spa'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">registerApplication</span><span class=\"token punctuation\">(</span>\n    <span class=\"token string\">'navbar'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">import</span><span class=\"token punctuation\">(</span><span class=\"token string\">'./src/navbar/main.app.js'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token boolean\">true</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">registerApplication</span><span class=\"token punctuation\">(</span>\n  <span class=\"token string\">'react'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">import</span><span class=\"token punctuation\">(</span><span class=\"token string\">'./src/react/main.app.js'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token parameter\">location</span> <span class=\"token operator\">=></span> location<span class=\"token punctuation\">.</span>pathname<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/react'</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token operator\">...</span>\n<span class=\"token function\">start</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>整体架构如下：\n￼<img src=\"https://2img.net/h/oi968.photobucket.com/albums/ae170/laughingjacky/Blog%20Assets%202020/Single%20SPA%20arch_zpsfmpnj036.png\" alt=\"single-spa-arch\"></p>\n<p>最后，我们需要处理五种框架的的打包构建,加入特定loader及babel plugins：</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getBabelConfig</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    presets<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token punctuation\">[</span>\n            <span class=\"token string\">'@babel/preset-env'</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">{</span>\n                targets<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n                    browsers<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'last 2 versions'</span><span class=\"token punctuation\">]</span>\n                <span class=\"token punctuation\">}</span>\n            <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">[</span><span class=\"token string\">'@babel/preset-react'</span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    plugins<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token string\">'@babel/plugin-syntax-dynamic-import'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">'@babel/plugin-proposal-object-rest-spread'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">'@babel/plugin-syntax-class-properties'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">'@babel/plugin-syntax-function-bind'</span>\n    <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> infernoBabelConfig <span class=\"token operator\">=</span> <span class=\"token function\">getBabelConfig</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\ninfernoBabelConfig<span class=\"token punctuation\">.</span>plugins<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token string\">'inferno'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token operator\">...</span>\n           <span class=\"token punctuation\">{</span>\n                test<span class=\"token operator\">:</span> <span class=\"token regex\">/\\.js$/</span><span class=\"token punctuation\">,</span>\n                exclude<span class=\"token operator\">:</span> <span class=\"token regex\">/node_modules|inferno/</span><span class=\"token punctuation\">,</span>\n                loader<span class=\"token operator\">:</span> <span class=\"token string\">'babel-loader'</span><span class=\"token punctuation\">,</span>\n                query<span class=\"token operator\">:</span> <span class=\"token function\">getBabelConfig</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">{</span>\n                test<span class=\"token operator\">:</span> <span class=\"token regex\">/inferno.+\\.js$/</span><span class=\"token punctuation\">,</span>\n                loader<span class=\"token operator\">:</span> <span class=\"token string\">'babel-loader'</span><span class=\"token punctuation\">,</span>\n                query<span class=\"token operator\">:</span> infernoBabelConfig\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">{</span>\n                test<span class=\"token operator\">:</span> <span class=\"token regex\">/\\.svelte$/</span><span class=\"token punctuation\">,</span>\n                loader<span class=\"token operator\">:</span> <span class=\"token string\">'svelte-loader'</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">{</span>\n                test<span class=\"token operator\">:</span> <span class=\"token regex\">/\\.vue$/</span><span class=\"token punctuation\">,</span>\n                loader<span class=\"token operator\">:</span> <span class=\"token string\">'vue-loader'</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">{</span>\n                test<span class=\"token operator\">:</span> <span class=\"token regex\">/\\.riot$/</span><span class=\"token punctuation\">,</span>\n                exclude<span class=\"token operator\">:</span> <span class=\"token regex\">/node_modules/</span><span class=\"token punctuation\">,</span>\n                loader<span class=\"token operator\">:</span> <span class=\"token string\">'@riotjs/webpack-loader'</span>\n            <span class=\"token punctuation\">}</span></code></pre></div>\n<p>注意到，由于inferno属于react-like框架，为避免构建时解析组件受到影响，我们配置了两套babel；也可以使用inferno-compat和alias避免两种框架的冲突。但从这里可以看出，目前将五种框架合并打包的方式有改进空间，并不算best practice。</p>\n<h2 id=\"总结\" style=\"position:relative;\"><a href=\"#%E6%80%BB%E7%BB%93\" aria-label=\"总结 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>总结</h2>\n<p>以上便是基于single-spa设计前端微服务的大致轮廓与思路，支持独立开发、独立运行。它还有很大的优化空间，如应用间通信、独立打包、样式隔离等等。</p>\n<p>附录资料里有篇文章，是社区对微前端利弊的一个激烈讨论。笔者认为，抛去portal的配置与维护上的复杂性，与web components一样，实践微前端的意义在于技术栈无关。</p>\n<p>正如描述引言，工程化是一种权衡，技术实现代表可行性分析。只有idea逐步落地，才有对遍地开花的憧憬。</p>\n<p>源码：<a href=\"https://github.com/LaughingJacky/simple-micro-frontend\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">simple-micro-frontend</a></p>\n<h2 id=\"参考资料\" style=\"position:relative;\"><a href=\"#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99\" aria-label=\"参考资料 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>参考资料</h2>\n<ol>\n<li><a href=\"https://tech.meituan.com/2018/09/06/fe-tiny-spa.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">用微前端的方式搭建类单页应用</a></li>\n<li><a href=\"https://zendev.com/2019/06/17/microfrontends-good-bad-ugly.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Microfrontends: the good, the bad, and the ugly</a></li>\n<li><a href=\"https://www.youtube.com/watch?v=pU1gXA0rfwc\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Avoiding monolithic frontends</a></li>\n<li><a href=\"https://single-spa.js.org/docs/recommended-setup/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Recommended Setup</a></li>\n</ol>","fields":{"slug":"/single-spa构建前端微服务（一）/"},"tableOfContents":"<ul>\n<li><a href=\"//#%E5%89%8D%E8%A8%80\">前言</a></li>\n<li>\n<p><a href=\"//#%E5%BE%AE%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84\">微前端架构</a></p>\n<ul>\n<li><a href=\"//#%E5%AD%90%E5%BA%94%E7%94%A8%E7%BC%96%E5%86%99\">子应用编写</a></li>\n<li><a href=\"//#single-spa\">single-spa</a></li>\n</ul>\n</li>\n<li><a href=\"//#%E6%80%BB%E7%BB%93\">总结</a></li>\n<li><a href=\"//#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99\">参考资料</a></li>\n</ul>","frontmatter":{"id":null,"description":"Engineering is all about tradeoffs, and micro frameworks give you another dimension along which you can make tradeoffs.","title":"Single-SPA构建前端微服务（一）","slug":"/","date":"2020年03月28日","tags":["single-spa","micro frontend"],"headerImage":"https://2img.net/h/oi968.photobucket.com/albums/ae170/laughingjacky/Blog%20Assets%202020/single-spa-head-image_zpsk3dwt2o4.jpeg"}},"previous":null,"next":null}]},"site":{"siteMetadata":{"title":"王晓博 - 银河系漫游指南","description":"如果这个博客好久不更新了,说明我更浑浑噩噩了"}}},"pageContext":{"id":"1cf55332-ef1c-5e1c-86a7-66a7d1a83a91","index":0}},"staticQueryHashes":[]}