什么是前端微服务,网上大把的介绍,我就不啰嗦了,简单来讲,就是把各个子项目整合到一块儿。css
《前端架构:从入门到微前端》这本书中介绍,微前端架构通常能够由如下几种方式进行:html
其中比较常见的就是iframe
和single-spa
,这二者各有千秋。前端
页面加载问题: 影响主页面加载,阻塞onload
事件,自己加载也很慢,页面缓存过多会致使电脑卡顿。(没法解决)vue
布局问题:iframe
必须给一个指定的高度,不然会塌陷。解决办法:子系统实时计算高度并经过postMessage
发送给主页面,主页面动态设置高度,修改子系统或者代理插入脚本。有些状况会出现多个滚动条,用户体验不佳。node
弹窗及遮罩层问题:只能在iframe
范围内垂直水平居中,无法在整个页面垂直水平居中。react
iframe
里面的内容没法实现占满屏幕的弹窗(非全屏),他只能在iframe范围内全屏,没法跳出iframe的限制在主页面全屏,不过这种状况也不多。浏览器前进/后退问题:iframe
和主页面共用一个浏览历史,iframe
会影响页面的前进后退,大部分时候正常,iframe
屡次重定向则会致使浏览器的前进后退功能没法正常使用,不是所有页面都会出现,基本能够忽略。可是iframe
页面刷新会重置(好比说从列表页跳转到详情页,而后刷新,会返回到列表页),由于浏览器的地址栏没有变化。webpack
iframe
的页面跳转到其余页面出问题,好比两个iframe
之间相互跳转,直接跳转会只在iframe
范围内跳转,因此必须经过主页面来进行跳转。不过iframe
跳转的状况不多ios
系统之间的通信须要经过postMessage
,存在必定的安全性git
css
和js
,避免了各个系统之间的样式和js
污染css
和js
须要制定规范,进行隔离。不然容易形成全局污染,尤为是vue
的全局组件,全局钩子。document
。http
请求少,服务器压力小。对比项 | 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
SEO
,iframe
没法解决,可是single-spa
有办法解决(谷歌能支持单页应用的SEO
,百度则须要SSR
),可是内部系统,SEO的需求比较少。iframe
存在安全隐患,两个iframe
页面互相引用则会致使无限嵌套bug
,会致使页面卡死,目前只能经过代理服务器检查iframe
页面内容来处理iframe很简单,一个标签就实现了。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)
复制代码
须要注意的是:
js/css
文件路径都是相对路径,须要拼上子项目的前缀。v-html
插入的DOM
片断,外链script
不会生效,须要手动插入结果是主系统中#app
里面能渲染出子系统,可是#app
里面动态生成的HTML
,img/video/audio
等文件的路径是相对的,因此会请求到主系统上,可是这些文件并不在主系统,因此会404,一样,按需加载的路由页面对应的js/css
文件也是相对路径,会请求出错。若是路由没按需加载,则不存在这个问题
结论:能够实现微服务效果,可是须要解决文件相对路径的问题,index.html
里面的link/script
还能够手动加上,可是动态生成的html
里面的img/video/audio
等,以及按需加载路由页面对应的js/css
没法经过代理服务器解决。
解决思路:
这里面的js/css/img/video
等都是相对路径,可否经过webpack
打包,将这些路径所有打包成绝对路径?这样就能够解决文件请求失败的问题。
可否像CDN同样,一个服务器挂了,会去其余服务器上请求对应文件。或者说服务器之间的文件共享,主系统上的文件请求失败会自动去子服务器上找到并返回。
可否手动(或借助node
)将子系统的文件所有拷贝到主项目服务器上,node
监听子系统文件有更新,就自动拷贝过来,而且按js/css/img
文件夹合并
查阅webpack
和vue-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-cli3
的 publicPath
设置为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
项目,包含完整的打包/开发调试流程,老项目如何改造等。