《OneForAll框架搭建之旅》前端篇:微前端架構設計(Vue)

心之所向,勇往直前!
記錄開發過程中的那些小事,給自己加點經驗值。

前言

作為一個.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 = 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
  }
}

結語

  本篇到此結束,如果有任何疑問或者指正,請發表在評論區。

  下一篇將講述《自動構建路由》,以及子模塊的接入