亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

基于 Vue 技術(shù)棧的微前端方案實(shí)踐

 

作者: mcuking

轉(zhuǎn)發(fā)連接:https://mp.weixin.qq.com/s/y_gPdEZ0lRdquxqRd_7kPQ

前言

項(xiàng)目地址:

preload-routes

https://github.com/micro-frontends-vue/preload-routes

async-routes

https://github.com/micro-frontends-vue/async-routes

mobile-web-best-practice

https://github.com/mcuking/mobile-web-best-practice

前幾天看到了 微前端在美團(tuán)外賣的實(shí)踐,感覺和筆者所在團(tuán)隊(duì)實(shí)踐了一年多的微前端方案非常類似,只不過我們是基于 Vue 技術(shù)棧的,所以也想總結(jié)一篇文章分享給大家。因?yàn)楣P者文筆不算太好,其中借用了一些美團(tuán)文章的一些總結(jié)性的文字,還請見諒哦~

更多Vue學(xué)習(xí)文章,請見本篇文章底部,有驚喜哦

背景介紹

對于大型前端項(xiàng)目,比如公司內(nèi)部管理系統(tǒng)(一般包括 OA、HR、CRM、會(huì)議預(yù)約等系統(tǒng)),如果將所有業(yè)務(wù)放在一個(gè)前端項(xiàng)目里,隨著業(yè)務(wù)功能不斷增加,就會(huì)導(dǎo)致如下這些問題:

  • 代碼規(guī)模龐大,導(dǎo)致編譯時(shí)間過長,開發(fā)、打包速度越來越慢
  • 項(xiàng)目文件越來越多,導(dǎo)致查找相關(guān)文件變得越來越困難
  • 某一個(gè)業(yè)務(wù)的小改動(dòng),導(dǎo)致整個(gè)項(xiàng)目的打包和部署

方案介紹

preload-routes 和 async-routes 是目前筆者所在團(tuán)隊(duì)使用的微前端方案,最終會(huì)將整個(gè)前端項(xiàng)目拆解成一個(gè)主項(xiàng)目和多個(gè)的項(xiàng)目,其中兩者作用如下:

  • 主項(xiàng)目:用于管理子項(xiàng)目的路由切換、注冊子項(xiàng)目的路由和全局 Store 層、提供全局庫和方法
  • 子項(xiàng)目:用于開發(fā)子業(yè)務(wù)線業(yè)務(wù)代碼,一個(gè)子項(xiàng)目對應(yīng)一個(gè)子業(yè)務(wù)線,并且包含兩端(PC + Mobile)代碼和復(fù)用層代碼(項(xiàng)目分層中的非視圖層)

結(jié)合筆者之前的采用分層架構(gòu)實(shí)現(xiàn)復(fù)用非視圖代碼的方式(感興趣的話請參考筆者之前的文章 前端分層架構(gòu)實(shí)踐心得),完整的方案如下:

基于 Vue 技術(shù)棧的微前端方案實(shí)踐

 

如圖所示,將整個(gè)前端項(xiàng)目按照業(yè)務(wù)線拆分出多個(gè)子項(xiàng)目,每個(gè)子項(xiàng)目都是獨(dú)立的倉庫,只包含了單個(gè)業(yè)務(wù)線的代碼,可以進(jìn)行獨(dú)立開發(fā)和部署,降低了項(xiàng)目維護(hù)的復(fù)雜度。

采用這套方案,使得我們的前端項(xiàng)目不僅保有了橫向上(多個(gè)子項(xiàng)目)的擴(kuò)展性,又擁有了縱向上(單個(gè)子項(xiàng)目)的復(fù)用性。那么這套方案具體是怎么實(shí)現(xiàn)的呢?下面就詳細(xì)說明方案的實(shí)現(xiàn)機(jī)制。

在講解之前,首先明確下這套方案有兩種實(shí)現(xiàn)方式,一種是預(yù)加載路由,另一種是懶加載路由,可以根據(jù)實(shí)際需求選擇其中一個(gè)即可。接下來就分別介紹這兩種方式的實(shí)現(xiàn)機(jī)制。

實(shí)現(xiàn)機(jī)制

預(yù)加載路由方式

preload-routes

1.子項(xiàng)目按照 vue-cli 3 的 library 模式進(jìn)行打包,以便后續(xù)主項(xiàng)目引用

注:在 library 模式中,Vue 是外置的。這意味著包中不會(huì)有 Vue,即便你在代碼中導(dǎo)入了 Vue。如果這個(gè)庫會(huì)通過一個(gè)打包器使用,它將嘗試通過打包器以依賴的方式加載 Vue;否則就會(huì)回退到一個(gè)全局的 Vue 變量。

2.在編譯主項(xiàng)目的時(shí)候,通過 InsertScriptPlugin 插件將子項(xiàng)目的入口文件 main.js 以 script 標(biāo)簽形式插入到主項(xiàng)目的 html 中

注:務(wù)必將子項(xiàng)目的入口文件 main.js 對應(yīng)的 script 標(biāo)簽放在主項(xiàng)目入口文件 App.js 的 script 標(biāo)簽之上,這是為了確保子項(xiàng)目的入口文件先于主項(xiàng)目的入口文件代碼執(zhí)行,接下來的步驟就會(huì)明白為什么這么做。

再注:本地開發(fā)環(huán)境下項(xiàng)目的入口文件編譯后的 main.js 是保存在內(nèi)存中的,所以磁盤上看不見,但是可以訪問。

InsertScriptPlugin 核心代碼如下:

compiler.hooks.compilation.tap('InsertScriptWebpackPlugin', compilation => {
  compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tap(
    'InsertScriptWebpackPlugin',
    htmlPluginData => {
      const {
        assets: { js }
      } = htmlPluginData;
      // 將傳入的 js 以 script 標(biāo)簽形式插入到 html 中
      // 注意:需要將子項(xiàng)目的入口文件 main.js 放在主項(xiàng)目入口文件 app.js 之前,因?yàn)樾枰禹?xiàng)目提前將自己的 route list 注冊到全局上
      js.unshift(...self.files);
    }
  );
});

3.主項(xiàng)目的 html 要訪問子項(xiàng)目里的編譯后的 js / css 等資源,需要進(jìn)行代理轉(zhuǎn)發(fā)

  • 如果是本地開發(fā)時(shí),可以通過 webpack 提供的 proxy,例如:
const PROXY = {
  '/app-a/': {
    target: 'http://localhost:10241/'
  }
};
  • 如果是線上部署時(shí),可以通過 Nginx 轉(zhuǎn)發(fā)或者將打包后的主項(xiàng)目和子項(xiàng)目放在一個(gè)文件夾中按照相對路徑引用。4.當(dāng)瀏覽器解析 html 時(shí),解析并執(zhí)行到子項(xiàng)目的入口文件 main.js,將子項(xiàng)目的 route list 注冊到 Vue.__share__.routes 上,以便后續(xù)主項(xiàng)目將其合并到總的路由中。

子項(xiàng)目 main.js 代碼如下:(為了盡量減少首次主項(xiàng)目頁面渲染時(shí)加載的資源,子項(xiàng)目的入口文件建議只做路由掛載)

import Vue from 'vue';
import routes from './routes';

const share = (Vue.__share__ = Vue.__share__ || {});
const routesPool = (share.routes = share.routes || {});

// 將子項(xiàng)目的 route list 掛載到 Vue.__share__.routes 上,以便后續(xù)主項(xiàng)目將其合并到總的路由中
routesPool[process.env.VUE_APP_NAME] = routes;

5.繼續(xù)向下解析 html,解析并執(zhí)行到主項(xiàng)目 main.js 時(shí),從 Vue.__share__.routes 獲取所有子項(xiàng)目的 route list,合并到總的路由表中,然后初始化一個(gè) vue-router 實(shí)例,并傳入到 new Vue 內(nèi)

相關(guān)關(guān)鍵代碼如下

// 從 Vue.__share__.routes 獲取所有子項(xiàng)目的 route list,合并到總的路由表中
const routes = Vue.__share__.routes;

export default new Router({
  routes: Object.values(routes).reduce((acc, prev) => acc.concat(prev), [
    {
      path: '/',
      redirect: '/app-a'
    }
  ])
});

到此就實(shí)現(xiàn)了單頁面應(yīng)用按照業(yè)務(wù)拆分成多個(gè)子項(xiàng)目,直白來說子項(xiàng)目的入口文件 main.js 就是將主項(xiàng)目和子項(xiàng)目聯(lián)系起來的橋梁。

另外如果需要使用 vuex,則和 vue-router 的順序恰好相反(先主項(xiàng)目后子項(xiàng)目):

1.首先在主項(xiàng)目的入口文件中初始化一個(gè) store 實(shí)例 new Vuex.Store,然后掛在到 Vue.__share__.store 上

2.然后在子項(xiàng)目的 App.vue 中獲取到 Vue.__share__.store 并調(diào)用 store.registerModule(‘app-x', store),將子項(xiàng)目的 store 作為子模塊注冊到 store 上

懶加載路由方式

async-routes

懶加載路由,顧名思義,就是說等到用戶點(diǎn)擊要進(jìn)入子項(xiàng)目模塊,通過解析即將跳轉(zhuǎn)的路由確定是哪一個(gè)子項(xiàng)目,然后再異步去加載該子項(xiàng)目的入口文件 main.js(可以通過 systemjs 或者自己寫一個(gè)動(dòng)態(tài)創(chuàng)建 script 標(biāo)簽并插入 body 的方法)。加載成功后就可以將子項(xiàng)目的路由動(dòng)態(tài)添加到主項(xiàng)目中的路由里了。

1.主項(xiàng)目 router.js 文件中定義了在 vue-router 的 beforeEach 鉤子去攔截路由,并根據(jù)即將跳轉(zhuǎn)的路由分析出需要哪個(gè)子項(xiàng)目,然后去異步加載對應(yīng)子項(xiàng)目入口文件,下面是核心代碼:

const cachedModules = new Set();

router.beforeEach(async (to, from, next) => {
  const [, module] = to.path.split('/');

  if (Reflect.has(modules, module)) {
    // 如果已經(jīng)加載過對應(yīng)子項(xiàng)目,則無需重復(fù)加載,直接跳轉(zhuǎn)即可
    if (!cachedModules.has(module)) {
      const { default: application } = await window.System.import(
        modules[module]
      );

      if (application && application.routes) {
        // 動(dòng)態(tài)添加子項(xiàng)目的 route-list
        router.addRoutes(application.routes);
      }

      cachedModules.add(module);
      next(to.path);
    } else {
      next();
    }
    return;
  }
});

2.子項(xiàng)目的入口文件 main.js 僅需要將子項(xiàng)目的 routes 暴露給主項(xiàng)目即可,代碼如下:

import routes from './routes';

export default {
  name: 'JAVAscript',
  routes,
  beforeEach(from, to, next) {
    console.log('JavaScript:', from.path, to.path);
    next();
  }
};

注意:這里除了暴露 routes 方法外,另外又暴露了 beforeEach 方法,其實(shí)就是為了支持通過路由守衛(wèi)對子項(xiàng)目進(jìn)行頁面權(quán)限限制,主項(xiàng)目拿到這個(gè)子項(xiàng)目的 beforeEach,可以在 vue-router 的 beforeEach 鉤子執(zhí)行,具體代碼請參考 async-routes。

除了主項(xiàng)目和子項(xiàng)目的交互方式不同,代理轉(zhuǎn)發(fā)子項(xiàng)目資源、vuex store 注冊等和上面的預(yù)加載路由完全一致。

優(yōu)缺點(diǎn)

下面談下這套方案的優(yōu)缺點(diǎn):

優(yōu)點(diǎn)

  • 子項(xiàng)目可單獨(dú)打包、單獨(dú)部署上線,提升了開發(fā)和打包的速度
  • 子項(xiàng)目之間開發(fā)互相獨(dú)立,互不影響,可在不同倉庫進(jìn)行維護(hù),減少的單個(gè)項(xiàng)目的規(guī)模
  • 保持單頁應(yīng)用的體驗(yàn),子項(xiàng)目之間切換不刷新
  • 改造成本低,對現(xiàn)有項(xiàng)目侵入度較低,業(yè)務(wù)線遷移成本也較低
  • 保證整體項(xiàng)目統(tǒng)一一個(gè)技術(shù)棧

缺點(diǎn)

  • 主項(xiàng)目和子項(xiàng)目需要共用一個(gè) Vue 實(shí)例,所以無法做到某個(gè)子項(xiàng)目單獨(dú)使用最新版 Vue(例如 Vue3)或者 React

部分問題解答

1.如果子項(xiàng)目代碼更新后,除了打包部署子項(xiàng)目之外,還需要打包部署主項(xiàng)目嗎?

不需要更新部署主項(xiàng)目。這里有個(gè) trick 上文忘記提及,就是子項(xiàng)目打包后的入口文件并沒有加上 chunkhash,直接就是 main.js(子項(xiàng)目其他的 js 都有 chunkhash)。也就是說主項(xiàng)目只需要記住子項(xiàng)目的名字,就可以通過 subapp-name/main.js 找到子項(xiàng)目的入口文件,所以子項(xiàng)目打包部署后,主項(xiàng)目并不需要更新任何東西。

2.針對第二個(gè)問題中的項(xiàng)目入口文件 main.js 不使用 chunkhash 的話,如何防止該文件始終被緩存呢?

可以在靜態(tài)資源服務(wù)器端針對子項(xiàng)目入口文件設(shè)置強(qiáng)制緩存為不緩存,下面是服務(wù)器為 nginx 情況的相關(guān)配置:

location / {
    set $expires_time 7d;
    ...
    if ($request_uri ~* /(contract|meeting|crm)-app/main.js(?.*)?$) {
        # 針對入口文件設(shè)置 expires_time -1,即expire是服務(wù)器時(shí)間的 -1s,始終過期
        set $expires_time -1;
    }
    expires $expires_time;
    ...
}

待完善

  • 可以通過寫一個(gè)腳手架來自動(dòng)生成子項(xiàng)目以及相關(guān)的配置

結(jié)尾

如果沒有在一個(gè)大型前端項(xiàng)目中使用多個(gè)技術(shù)棧的需求,還是很推薦筆者目前團(tuán)隊(duì)實(shí)踐的這個(gè)方案的。另外如果是 React 技術(shù)棧,也是可以按照這種思想去實(shí)現(xiàn)類似的方案的。這么好的實(shí)踐文章快去點(diǎn)個(gè)在看讓更多小伙伴看到吧!

分享到:
標(biāo)簽:Vue
用戶無頭像

網(wǎng)友整理

注冊時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定