《OneForAll框架搭建之旅》前端篇:微前端架构设计(Vue)
卷毛不太卷 人气:3心之所向,勇往直前!
记录开发过程中的那些小事,给自己加点经验值。
前言
作为一个.Net后端开发,在竞争愈加激烈的当下,掌握点前端配菜好像已经是家常便饭了。
刚好在工作的第5个年头,辞去小主管职务的我要再次踏上面试之路,为了要避免被面试官吊打,除了复习《吊打面试官》相关的题目,当然也要对自己掌握的技能温故知新。
项目使用了Vue cli3.0作为基础架构,这个版本和2.0的有一些不同。具体参考:
1. 《vue cli3.0快速搭建项目详解》
2. 《vue-cli2.0与vue-cli3.0》
环境
技术栈
上面是项目的一些基本情况,至于实际开发用到的组件这个每个人的项目都有可能不同,这里就不贴出来了;而且这个系列只是对一些关键点进行记录和说明,其他的在网上都可以找到资料的内容就不再重复。
架构
微服务这个词可以说是大火特火,现在很多应用都在逐步朝着这方面转移。
这个架构的好处,我想是不言而喻的。浅显点理解就是独立运行、灵活、扩展性强。
在调整后端架构的同时,我就想前端能不能也实现这种模式?在查找了几天资料(主要参考)理清思路后,就抽出空余的时间之后就搞出这一套架构。不过距离真正的微前端还是有些差距。毕竟现在前端的框架那么多(Vue、React、Angular等等,如果要兼容每个框架,那么可能会出现一些预加载组件出现冗余,导致主页加载缓慢。)
常见方案
- ifreame:简单易实现,但冗余html而且对SEO不友好
- WebComponents: 基本能实现功能,但兼容性不太行而且只对高版本浏览器有效(这是废话,用了Vue已经放弃IE)
在这里框架中我采用的以Vue为核心实现模块化加载。
核心思路
主要通过一个中央处理器(可以理解为浏览器或者iframe)
处理器主要用于解析后端返回的模块Url,根据地址发起Http请求拿到子模块的index.html。这个文件的容量很小,但是里面记录了该模块需要用到的css和js文件相对路径。然后通过正则表达式解析出script标签、style标签。最后将标签加载到主页的最底部(利用浏览器自动加载文件的特性),完成了子模块的Async加载。
子模块拥有自己独立的领域逻辑,组件,api接口文件(为了防止冲突,对命名有所规范)。各个模块之间相互独立,一般不会出现引用相同的插件的情况,造成项目冗余。
如图:
代码:
reLoadWebsite (host, html) {
// 解析内容页中的css/js引用,并插入父页面文档底部
let temp = []
let text = html
const page = { content: html, scripts: [], css: [] }
const regScript = /<script[\s]+(?:[^>]+=[\s]*[^>]+)*(?:src[\s]*=[\s]*['|"]?([^>]+(?:\.js))['|"]?)><\/script>/i
while ((temp = regScript.exec(text)) != null) {
text = text.replace(temp[0], '')
if (temp[1] && temp[1].length > 0) page.scripts.push(host + temp[1])
}
const regCss = /<link[^>]+(?:href=['|"]?([^>]+(?:\.css))['|"]?)[^>]*>/i
temp = []
text = html
while ((temp = regCss.exec(text)) != null) {
text = text.replace(temp[0], '')
if (temp[1] && temp[1].length > 0) page.css.push(host + temp[1])
}
this.loadCss(page.css)
this.loadScripts(page.scripts)
},
loadCss (css) {
var html = $('html').html()
for (var i = 0; i < css.length; i++) {
if (html.indexOf(css[i]) < 0) {
var link = document.createElement('link')
link.type = 'text/css'
link.rel = 'stylesheet'
link.href = css[i]
document.body.appendChild(link)
}
}
},
loadScripts (scripts) {
var html = $('html').html()
for (var i = 0; i < scripts.length; i++) {
if (html.indexOf(scripts[i]) < 0) {
var script = document.createElement('script')
script.type = 'text/javascript'
script.src = scripts[i]
document.body.appendChild(script)
}
}
},
路由装载
主模块中加载Vue-Router,先把一级路由创建出来。
然后在main.js中将Vue等公共对象暴露到window对象中,同时暴露一个registerChildRoutes方法,让子模块可以把独立的路由注册到主路由中。这样就可是实现模块化装载的功能了。基本上到了这步,已经是简单版的微前端框架。当然如果想要架构更加完整和坚固,还需要做更多的处理。
// 全局
const router = Router
const store = Store
window.Vue = Vue
window.AppData = {
Router,
Store,
Error,
registerChildRoutes: (routes) => {
const index = router.options.routes.find(w => w.name === INDEX.name)
if (index) {
routes.forEach(e => {
if (index.children.findIndex(w => w.name === e.name) < 0) {
index.children.push(e)
}
})
}
const newRouter = new VueRouter(router.options)
router.matcher = newRouter.matcher
}
}
结语
本篇到此结束,如果有任何疑问或者指正,请发表在评论区。
下一篇将讲述《自动构建路由》,以及子模块的接入
加载全部内容