《OneForAll框架搭建之旅》前端篇:微前端架构设计(Vue)

六眼飞鱼酱① 提交于 2020-03-04 16:23:12

心之所向,勇往直前!
记录开发过程中的那些小事,给自己加点经验值。

前言

作为一个.Net后端开发,在竞争愈加激烈的当下,掌握点前端配菜好像已经是家常便饭了。

刚好在工作的第5个年头,辞去小主管职务的我要再次踏上面试之路,为了要避免被面试官吊打,除了复习《吊打面试官》相关的题目,当然也要对自己掌握的技能温故知新。

  项目使用了Vue cli3.0作为基础架构,这个版本和2.0的有一些不同。具体参考:

  1. vue cli3.0快速搭建项目详解

  2. 《vue-cli2.0与vue-cli3.0》

  环境

  

  技术栈

    

上面是项目的一些基本情况,至于实际开发用到的组件这个每个人的项目都有可能不同,这里就不贴出来了;而且这个系列只是对一些关键点进行记录和说明,其他的在网上都可以找到资料的内容就不再重复。

架构

  微服务这个词可以说是大火特火,现在很多应用都在逐步朝着这方面转移。

  这个架构的好处,我想是不言而喻的。浅显点理解就是独立运行、灵活、扩展性强

  在调整后端架构的同时,我就想前端能不能也实现这种模式?在查找了几天资料(主要参考滴滴 webapp 5.0 Vue 2.0 重构经验分享)理清思路后,就抽出空余的时间之后就搞出这一套架构。不过距离真正的微前端还是有些差距。毕竟现在前端的框架那么多(Vue、React、Angular等等,如果要兼容每个框架,那么可能会出现一些预加载组件出现冗余,导致主页加载缓慢。)

 常见方案

  1. ifreame:简单易实现,但冗余html而且对SEO不友好
  2. 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  }}

结语

  本篇到此结束,如果有任何疑问或者指正,请发表在评论区。

  下一篇将讲述《自动构建路由》,以及子模块的接入

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!