从0实现一个前端微服务(上)

前端微服务的几种实现方式

什么是前端微服务,网上大把的介绍,我就不啰嗦了,简单来讲,就是把各个子项目整合到一块儿。css

《前端架构:从入门到微前端》这本书中介绍,微前端架构通常能够由如下几种方式进行:html

  1. 使用 HTTP 服务器的路由来重定向多个应用(也就是连接跳转)
  2. 在不一样的框架之上设计通信、加载机制,诸如 Mooa 和 Single-SPA
  3. 经过组合多个独立应用、组件来构建一个单体应用
  4. iFrame。使用 iFrame 及自定义消息传递机制
  5. 使用纯 Web Components 构建应用
  6. 结合 Web Components 构建

其中比较常见的就是iframesingle-spa,这二者各有千秋。前端

iframe和single-spa的优缺点

iframe的优缺点

缺点

  1. 页面加载问题: 影响主页面加载,阻塞onload事件,自己加载也很慢,页面缓存过多会致使电脑卡顿。(没法解决)vue

  2. 布局问题:iframe必须给一个指定的高度,不然会塌陷。解决办法:子系统实时计算高度并经过postMessage发送给主页面,主页面动态设置高度,修改子系统或者代理插入脚本。有些状况会出现多个滚动条,用户体验不佳。node

  3. 弹窗及遮罩层问题:只能在iframe范围内垂直水平居中,无法在整个页面垂直水平居中。react

    • 解决办法1:经过与框架页面消息同步解决,将弹窗消息发送给主页面,主页面来弹窗,对原项目改动大且影响原项目的使用。
    • 解决办法2:修改弹窗的样式:隐藏遮罩层,修改弹窗的位置。修改的办法就是经过代理服务器插入css样式。
    • 补充:iframe里面的内容没法实现占满屏幕的弹窗(非全屏),他只能在iframe范围内全屏,没法跳出iframe的限制在主页面全屏,不过这种状况也不多。
  4. 浏览器前进/后退问题:iframe和主页面共用一个浏览历史,iframe会影响页面的前进后退,大部分时候正常,iframe屡次重定向则会致使浏览器的前进后退功能没法正常使用,不是所有页面都会出现,基本能够忽略。可是iframe页面刷新会重置(好比说从列表页跳转到详情页,而后刷新,会返回到列表页),由于浏览器的地址栏没有变化。webpack

  5. iframe的页面跳转到其余页面出问题,好比两个iframe之间相互跳转,直接跳转会只在iframe范围内跳转,因此必须经过主页面来进行跳转。不过iframe跳转的状况不多ios

  6. 系统之间的通信须要经过postMessage,存在必定的安全性git

优势

  1. 彻底隔离了cssjs,避免了各个系统之间的样式和js污染
  2. 能够在子系统彻底不修改的状况下嵌入进来

single-spa的优缺点

缺点

  1. cssjs须要制定规范,进行隔离。不然容易形成全局污染,尤为是vue的全局组件,全局钩子。
  2. 须要子系统配合修改。可是不影响子系统独立开发部署,路由部分对子系统有一些改动,可是不影响功能。

优势

  1. 加载快,能够将全部系统共用的模块提取出来,实现按需加载,一次加载,其余的复用。
  2. 修改子系统的样式,不须要代理服务器,直接修改,由于同属于一个document
  3. 用户体验好、快,内容的改变不须要从新加载整个页面,避免了没必要要的跳转和重复渲染
  4. http请求少,服务器压力小。

single-spa和iframe对比

对比项 single-spa iframe 补充
加载速度 single-spa能够将全部系统共用的vue/vuex/vue-router等文件提取出来,只加载一次,各系统复用,加载速度很快,可是必须保证文件版本统一 iframe会占用主系统的http通道,影响主系统的加载,加载速度很慢 二者均可以经过http缓存提升必定的加载速度,可是对于vue这些通用文件无法作cdn,由于内部系统极可能没法访问外网
兼容性 single-spa只适用于vue、react、angular编写的系统,对一些jq写的老系统无能为力 iframe则能够嵌入任何页面
技术难度 single-spa须要必定的技术储备,有一些学习成本 iframe门槛则很低,无需额外学习
局限性 single-spa能够嵌入任何部件 iframe只能嵌入页面,固然了也能够把一个部件单独写成一个页面
改形成本 single-spa必定要对子系统进行改造,可是改造的内容并很少不少,半小时便可完成 iframe能够不对原系统进行改造,可是必须借助代理服务器进行插入脚本和css,增长了代理服务器也增长了系统的不稳定性(两台服务器中的任何一台挂掉都会致使系统不可用),服务器也须要成本。如对原系统进行改造,则工做量和single-spa至关 项目的源文件丢失或者其余一些没法改动源文件的状况,只能使用iframe

补充:github

  1. 对于SEOiframe没法解决,可是single-spa有办法解决(谷歌能支持单页应用的SEO,百度则须要SSR),可是内部系统,SEO的需求比较少。
  2. iframe存在安全隐患,两个iframe页面互相引用则会致使无限嵌套bug,会致使页面卡死,目前只能经过代理服务器检查iframe页面内容来处理

iframe和single-spa的实现及简单原理

iframe很简单,一个标签就实现了。single-spa比较陌生,我会详细介绍。

single-spa初探

vue为例,vue-cli4生成的项目打包生成的index.html文件内容以下(精简了一些无关的内容):

<!DOCTYPE html>
<html lang=en>
<head>
  <meta charset=utf-8>
  <title>my-app</title>
  <link href=/js/about.6b1cbb89.js rel=prefetch>
  <link href=/css/app.c8c4d97c.css rel=preload as=style>
  <link href=/js/app.6a6f1dda.js rel=preload as=script>
  <link href=/js/chunk-vendors.164d8230.js rel=preload as=script>
  <link href=/css/app.c8c4d97c.css rel=stylesheet>
</head>
<body>
  <noscript>
    <strong>We're sorry but my-app doesn't work properly without JavaScript enabled. Please enable it to
      continue.</strong>
  </noscript>
  <div id=app></div>
  <script src=/js/chunk-vendors.164d8230.js> </script>
  <script src=/js/app.6a6f1dda.js> </script>
</body>
</html>
复制代码

其中最核心的部分是:

<link href=/css/app.c8c4d97c.css rel=stylesheet>
<div id=app></div>
<script src=/js/chunk-vendors.164d8230.js> </script>
<script src=/js/app.6a6f1dda.js> </script> 
复制代码

猜测:可否借助node服务器,将子系统的index.html获取到,而后读取HTML,获取到这几个标签,返回给主系统,主系统直接插入到body中,可否呈现出子系统?

node代理代码实现,操做DOM使用的是cheerio插件:

const http = require('http')
// 引入cheerio模块
const cheerio = require('cheerio')
const axios = require('axios')
const server = http.createServer(function (request, response) {
  //请求子系统服务器,获取到index.html文件
  axios.get('http://localhost/').then(res => {
    response.writeHead(200, { 
      'Content-Type': 'application/xml' ,
      'Access-Control-Allow-Origin': '*'
    })
    // 加载HTML字符串
    const $ = cheerio.load(res.data)
    $('link').each(function () {
      $(this).attr('href', 'http://localhost' + $(this).attr('href'))
    })
    $('script').each(function () {
      $(this).attr('src', 'http://localhost' + $(this).attr('src'))
    })
    const resp = $('body').prepend($('link[rel=stylesheet]')).html();
    response.end(resp)
  }).catch(e => {
    console.log(e)
  })
})
server.listen(8080)
复制代码

须要注意的是:

  1. 这里面的引入js/css文件路径都是相对路径,须要拼上子项目的前缀。
  2. v-html插入的DOM片断,外链script不会生效,须要手动插入

结果是主系统中#app里面能渲染出子系统,可是#app里面动态生成的HTMLimg/video/audio等文件的路径是相对的,因此会请求到主系统上,可是这些文件并不在主系统,因此会404,一样,按需加载的路由页面对应的js/css文件也是相对路径,会请求出错。若是路由没按需加载,则不存在这个问题

结论:能够实现微服务效果,可是须要解决文件相对路径的问题,index.html里面的link/script还能够手动加上,可是动态生成的html里面的img/video/audio等,以及按需加载路由页面对应的js/css没法经过代理服务器解决。

解决思路:

  1. 这里面的js/css/img/video等都是相对路径,可否经过webpack打包,将这些路径所有打包成绝对路径?这样就能够解决文件请求失败的问题。

  2. 可否像CDN同样,一个服务器挂了,会去其余服务器上请求对应文件。或者说服务器之间的文件共享,主系统上的文件请求失败会自动去子服务器上找到并返回。

  3. 可否手动(或借助node)将子系统的文件所有拷贝到主项目服务器上,node监听子系统文件有更新,就自动拷贝过来,而且按js/css/img文件夹合并

查阅webpackvue-cli3官网后发现:

默认状况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上,例如 https://www.my-app.com/。若是应用被部署在一个子路径上 https://www.my-app.com/my-app/,而你使用的是history模式的路由,对于url:https://www.my-app.com/my-app/page1,vue没法区分my-app是真实路径,而page1是路由参数,这个时候须要设置 publicPath/my-app/,vue才能正确的请求文件资源和匹配路由。

这里能够将vue-cli3publicPath设置为https://www.my-app.com/my-app/,而后代码里面的js/css/img/video路径都会变成绝对路径,前缀是https://www.my-app.com/my-app/,这样就解决了url路径的问题。

这样就能够实现一个简单的single-spa应用,可是加载好的Vue子系统不会在切换到下一个系统的时候卸载掉,子系统过多则会致使卡顿,而且css/js污染的可能性增长,实用性不大。

最后

文章有什么疑问or错误,欢迎评论。下一篇预告:从0实现一个single-spa项目,包含完整的打包/开发调试流程,老项目如何改造等。

相关文章
相关标签/搜索