心之所向,勇往直前!
记录开发过程中的那些小事,给自己加点经验值。
前言
作为一个.Net后端开发,在竞争愈加激烈的当下,掌握点前端配菜好像已经是家常便饭了。
刚好在工作的第5个年头,辞去小主管职务的我要再次踏上面试之路,为了要避免被面试官吊打,除了复习《吊打面试官》相关的题目,当然也要对自己掌握的技能温故知新。
项目使用了Vue cli3.0作为基础架构,这个版本和2.0的有一些不同。具体参考:
环境
技术栈
上面是项目的一些基本情况,至于实际开发用到的组件这个每个人的项目都有可能不同,这里就不贴出来了;而且这个系列只是对一些关键点进行记录和说明,其他的在网上都可以找到资料的内容就不再重复。
架构
微服务这个词可以说是大火特火,现在很多应用都在逐步朝着这方面转移。
这个架构的好处,我想是不言而喻的。浅显点理解就是独立运行、灵活、扩展性强。
在调整后端架构的同时,我就想前端能不能也实现这种模式?在查找了几天资料(主要参考《滴滴 webapp 5.0 Vue 2.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 = Routerconst store = Storewindow.Vue = Vuewindow.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 }}
结语
本篇到此结束,如果有任何疑问或者指正,请发表在评论区。
下一篇将讲述《自动构建路由》,以及子模块的接入
来源:https://www.cnblogs.com/AssertionBird/p/12408061.html