1. 程式人生 > >《OneForAll框架搭建之旅》前端篇:微前端架構設計(Vue)

《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
  }
}

結語

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

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