【源碼】微前端qiankun源碼閱讀(1):Demo與single-spa流程

前言

老早就聽說微前端了,覺得很高大上的樣子。看了一些文章感覺一知半解,還是得自己搭建一下Demo,學習一下它到底是運作的。

微前端方案有很多,目前較為流行的方案的是自研框架。在自研方案中也有許多的框架,如Single-SpaQiankun。而qiankun也是基于single-spa開發的,現使用qiankun進行項目的搭建學習。

正文

(1)Demo搭建

為搭建一個Demo,首先準備幾個不同技術棧搭建的子應用(vue2、vue3、react15、react16),以及一個主應用(main):

image.png

這里的每個應用都可以獨立運行。

準備好各個子應用后,開始引入qiankun, qiankun的使用很簡單,按照官網 快速上手 文檔就好。

首先在主應用的 main.js 中,注冊微應用:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'


// 注冊、加載、啟動子應用
import { leftNav } from './store';
import { registerMicroApps, start } from 'qiankun';

registerMicroApps(leftNav.navList,
  // 主應用生命周期
  {
    beforeLoad: [
      () => {
        console.log('開始加載 -- ');
      },
    ],
    mounted: [
      () => {
        console.log('加載完成 -- ');
      },
    ],
    destoryed: [
      () => {
        console.log('卸載完成 -- ');
      },
    ],
  });
start();

createApp(App).use(router()).mount('#micro_web_main_app');

其中registerMicroApps傳入的list為各個子應用的信息:

export const navList = [
  {
    name: 'react15',// 唯一
    entry: '//localhost:9002',
    loading,
    container: '#micro-container',
    activeRule: '/react15',
  },
  {
    name: 'react16',
    entry: '//localhost:9003',
    loading,
    container: '#micro-container',
    activeRule: '/react16',
  },
  {
    name: 'vue2',
    entry: '//localhost:9004',
    loading,
    container: '#micro-container',
    activeRule: '/vue2',
  },
  {
    name: 'vue3',
    entry: '//localhost:9005',
    loading,
    container: '#micro-container',
    activeRule: '/vue3',
  },
];

當微應用信息注冊完之后,一旦瀏覽器的 url 發生變化,便會自動觸發 qiankun 的匹配邏輯,所有 activeRule 規則匹配上的微應用就會被插入到指定的 container 中,同時依次調用微應用暴露出的生命周期鉤子。

最后按照 微應用 文檔改造一下子應用,并啟動每個子應用,就可以在查看展示效果了。

image.png

image.png

可以看到,主應用提供了micro-container容器,然后根據當前的url加載到對應的子應用,而子應用的內容就是之前單頁面應用時的id="app"。

(2)源碼閱讀:registerMicroApps、start

可以看到,我們就是引入了qiankun中的兩個方法便成功運行了,下面看一下源碼實現。

import { registerMicroApps, start } from 'qiankun';

這里閱讀是版本是 qiankun v2.7.1。

src/apis.ts可以看到,registerApplication, start兩個關鍵的方法都是從single-spa中引入的,所以說qiankun也是基于single-spa開發的。

這里直接跳到single-spa,閱讀版本是 single-spa v5.9.3。(注意:以下文件路徑都是指在single-spa項目下的文件)
src/applications/apps.js中,主要就是將傳遞進來的apps信息保存,并暴露各個操作apps的方法。

image.png

保存完后調用reroute,在 src/navigation/reroute.js 中查看reroute:

  if (isStarted()) {
    appChangeUnderway = true;
    appsThatChanged = appsToUnload.concat(
      appsToLoad,
      appsToUnmount,
      appsToMount
    );
    return performAppChanges();
  } else {
    appsThatChanged = appsToLoad;
    return loadApps();
  }

可以看到如果應用已經start,調用performAppChanges,否則調用loadApps。這里的start就前面提到的start。在src/start.js

let started = false;

export function start(opts) {
  started = true;
  if (opts && opts.urlRerouteOnly) {
    setUrlRerouteOnly(opts.urlRerouteOnly);
  }
  if (isInBrowser) {
    reroute();
  }
}

export function isStarted() {
  return started;
}

performAppChanges:用于在路由切換時,調用toLoadPromise,加載對應的子應用。
loadApps做了兩件重要的事情:1是路由劫持、2也是使用toLoadPromise加載子應用。

路由劫持在src/navigation/navigation-events.js中,可以看到文件中直接重寫了pushState、replaceState。為什么要劫持路由變化?是為了在路由切換時加載對應的子路由。

  window.history.pushState = patchedUpdateState(
    window.history.pushState,
    "pushState"
  );
  window.history.replaceState = patchedUpdateState(
    window.history.replaceState,
    "replaceState"
  );

在 src/lifecycles/load.js 中查看toLoadPromise,可以看到toLoadPromise就是將已經加載好的app處理一下后返回。這里的app,就是子應用前面打包時output配置的library,運行打包文件(vendors/bundle)后輸出的app module,以便在window[library]中訪問到。

image.png

可以看到,single-spa要求我們自己提供并加載好app,那我們要如何運行打包文件獲取到app呢?可以使用動態腳本加載方式。下面single-spa子應用配置的一個例子:


const createScript = (url) => {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = url;
    script.onload = resolve;
    script.onerror = reject;
    const first = document.getElementsByTagName('script')[0];
    first.parentNode.insertBefore(script, first);
  });
}

const loadApp = async (urls, name) => {
  await Promise.all(urls.map((url) => createScript(url)));
  return window[name];
}

export const navList = [
  {
    name: 'vue3',
    app: loadApp([ // loadApp需要自己實現,自己加載js。所以single-spa和qiankun相比,少了沙箱隔離等功能
      'http://localhost:9005/static/js/chunk-vendors.js',
      'http://localhost:9005/vue3.js'
    ], 'vue3'),
    activeWhen: (location) => location.pathname.startsWith('/vue3'), 
    customProps: {},
  },
];

總結

上面寫得有點亂,但大概整體框架還是挺簡單的。就是主應用提供一個容器,然后根據路由切換動態加載不同子應用的bundle,然后bundle會接管容器的內容展示,這里就和單頁面應用幾乎一樣。

上面主要描述了single-spa的功能,但可以發現會有些問題:
1.single-spa需要開發者自己加載好app,然后傳遞給app配置。很麻煩。
2.各個子應用間沒有隔離,比如在一個子應用中修改了window中的字段,切換到另一個子應用后,window沒有復原,被污染了。

所以基于single-spa封裝的qiankun進行了加強,對于上面兩個問題做了處理:
1.可以看一開始qiankun的app配置,開發者只需提供entry字段,qiankun會自己去加載子應用的資源。
2.qiankun提供沙箱隔離,避免全局污染。
自此基本是講的是single-spa,后面會繼續介紹qiankun的上面兩個功能的實現。

另外對于微前端框架的使用場景,由于我在項目中也沒有使用過,不知道它可以帶來哪些好處。比如各個子應用已經有自己的站點并獨立運行,在主應用里直接鏈接跳轉過去,和這種微前端加載相比,微前端的優勢在哪里?這里看到一些理由是說:每當用戶當切換系統時,鏈接跳轉的方式導致頁面都會刷新,體驗很差。

參考

微前端-最容易看懂的微前端知識
微前端01 : 乾坤的Js隔離機制原理剖析(快照沙箱、兩種代理沙箱)
每日優鮮供應鏈前端團隊微前端改造

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373