亲宝软件园·资讯

展开

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

卷毛不太卷 人气:3

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

前言

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

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

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

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

  2. 《vue-cli2.0与vue-cli3.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 = 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
  }
}

结语

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

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

加载全部内容

相关教程
猜你喜欢
用户评论