qiankun 框架分析

qiankun是基于 single-spa 做的二次封裝蕴掏,主要解決了single-spa 的一些痛點(diǎn)和不足。

single-spa存在的問題调鲸?

  • 1盛杰、對(duì)微應(yīng)用侵入性太強(qiáng)
    微應(yīng)用的改造步驟:
    • 微應(yīng)用路由改造,添加一個(gè)特定的前綴
    • 微應(yīng)用入口改造藐石,掛載點(diǎn)變更和生命周期函數(shù)導(dǎo)出
    • 打包工具配置更改

single-spa 采用JS Entry 的方式接入微應(yīng)用即供。也就是說single-spa 接入微應(yīng)用需要將微應(yīng)用整個(gè)打包成一個(gè)JS文件,發(fā)布到靜態(tài)資源服務(wù)器于微,然后再主應(yīng)用中配置該JS文件的地址告訴 single-spa 去這個(gè)地址加載微應(yīng)用逗嫡。問題出現(xiàn)了,如按需加載株依、首屏資源加載優(yōu)化驱证、css獨(dú)立打包等優(yōu)化沒有了。

  • 2勺三、樣式隔離
    single-spa 沒有做雷滚。怎么做到主應(yīng)用和微應(yīng)用之間的樣式需曾,微應(yīng)用和微應(yīng)用的樣式互不影響吗坚?這個(gè)只能通過約定命名規(guī)范來實(shí)現(xiàn)祈远,比如應(yīng)用樣式以自己的應(yīng)用名稱開頭。

  • 3商源、JS隔離
    single-spa 沒有做车份。JS全局對(duì)象污染,A應(yīng)用在window上加一個(gè)自己的屬性window.A牡彻,微應(yīng)用B 也能訪問到扫沼。

  • 4、資源預(yù)加載
    single-spa 沒有做庄吼。例如怎么實(shí)現(xiàn)在第一個(gè)微應(yīng)用加載完后缎除,后臺(tái)悄悄加載其他微應(yīng)用。

  • 5总寻、應(yīng)用間通信
    single-spa 沒有做器罐。它只在注冊(cè)微應(yīng)用時(shí)給微應(yīng)用注入一些狀態(tài)信息,后續(xù)就不管了渐行,沒有任何通信的手段轰坊。

qiankun 如何解決以上問題

  • 1、HTML Entry
    qiankun 通過HTML Entry 的方式來解決JS Entry帶來的問題

  • 2祟印、樣式隔離
    采用shadow dom 包裹沒一個(gè)微應(yīng)用肴沫,從而確保微應(yīng)用的樣式互不干擾
    采用css scoped 方式(實(shí)驗(yàn)性)動(dòng)態(tài)改寫 css 選擇器來實(shí)現(xiàn)

  • 3、運(yùn)行時(shí)沙箱
    qiankun的運(yùn)行時(shí)沙箱分為 JS 沙箱和樣式沙箱

  • 4蕴忆、資源預(yù)加載
    qiankun 實(shí)現(xiàn)預(yù)加載的思路有兩種颤芬,一種是當(dāng)主應(yīng)用執(zhí)行 start 方法啟動(dòng) qiankun 以后立即去預(yù)加載微應(yīng)用的靜態(tài)資源,另一種是在第一個(gè)微應(yīng)用掛載以后預(yù)加載其它微應(yīng)用的靜態(tài)資源套鹅,這個(gè)是利用 single-spa 提供的 single-spa:first-mount 事件來實(shí)現(xiàn)的

  • 5驻襟、應(yīng)用間通信
    qiankun 通過發(fā)布訂閱模式來實(shí)現(xiàn)應(yīng)用間通信

示例項(xiàng)目

官網(wǎng)地址
源碼地址

yarn examples:install
yarn examples:start

qiankun 提供了6種實(shí)例,vue芋哭、vue3沉衣、react15、react16减牺、angular9豌习、purehtml。

image.png

主應(yīng)用在 examples/main 目錄下拔疚,提供了兩種實(shí)現(xiàn)方式肥隆,基于路由配置的 registerMicroApps 和 手動(dòng)加載微應(yīng)用的loadMicroApp。通過 webpak.config.js 的 entry 可以知道有兩個(gè)入口文件 multiple.js 和 index.js稚失。

  • 1栋艳、基于路由配置
    在 examples/main/index.js 中,將微應(yīng)用關(guān)聯(lián)到一些 url 規(guī)則句各,實(shí)現(xiàn)當(dāng)瀏覽器 url 發(fā)生變化時(shí)吸占,自動(dòng)加載相應(yīng)的微應(yīng)用晴叨。主應(yīng)用可以使用react進(jìn)行運(yùn)行,也可以使用vue進(jìn)行運(yùn)行矾屯。
registerMicroApps(
  [
    {
      name: 'vue',
      entry: '//localhost:7101',
      container: '#subapp-viewport',
      loader,
      activeRule: '/vue',
    },
  ],
  {
    beforeLoad: [
      app => {
        console.log('[LifeCycle] before load %c%s', 'color: green;', app.name);
      },
    ],
    beforeMount: [
      app => {
        console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
      },
    ],
    afterUnmount: [
      app => {
        console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name);
      },
    ],
  },
);
  • 2兼蕊、手動(dòng)加載微應(yīng)用
    在 examples/main/multiple.js 中有l(wèi)oadMicroApp實(shí)現(xiàn)的例子
function mount() {
  app = loadMicroApp(
    { name: 'react15', entry: '//localhost:7102', container: '#react15' },
    { sandbox: { experimentalStyleIsolation: true } },
  );
}

vue微應(yīng)用引入,需要修改 vue.config.js 和 mian.js 件蚕、public-path.js

{
  ...
  // publicPath 沒在這里設(shè)置孙技,是通過 webpack 提供的全局變量 __webpack_public_path__ 來即時(shí)設(shè)置的,webpackjs.com/guides/public-path/
  devServer: {
    ...
    // 設(shè)置跨域排作,因?yàn)橹鲬?yīng)用需要通過 fetch 去獲取微應(yīng)用引入的靜態(tài)資源的牵啦,所以必須要求這些靜態(tài)資源支持跨域
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },
  output: {
    // 把子應(yīng)用打包成 umd 庫格式
    library: `${name}-[name]`,  // 庫名稱,唯一
    libraryTarget: 'umd',
    jsonpFunction: `webpackJsonp_${name}`,
  }
  ...
}
let router = null;
let instance = null;

function render(props = {}) {
  const { container } = props;
  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/vue' : '/',
    mode: 'history',
    routes,
  });

  instance = new Vue({
    router,
    store,
    render: h => h(App),
  }).$mount(container ? container.querySelector('#app') : '#app');
}

if (!window.__POWERED_BY_QIANKUN__) {
  render();
}
export async function bootstrap() {
  console.log('[vue] vue app bootstraped');
}

export async function mount(props) {
  console.log('[vue] props from main framework', props);
  render(props);
}

export async function unmount() {
  instance.$destroy();
  instance.$el.innerHTML = '';
  instance = null;
  router = null;
}
if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

運(yùn)行時(shí)沙箱

運(yùn)行時(shí)沙箱包括 JS 沙箱 和 樣式沙箱

JS 沙箱

JS 沙箱是通過 proxy 代理 window 對(duì)象妄痪,記錄window對(duì)象上屬性的增刪改查

  • 單例模式
    直接代理了原生 window 對(duì)象蕾久,記錄原生 window 對(duì)象的增刪改查,當(dāng) window 對(duì)象激活時(shí)恢復(fù) window 對(duì)象到上次即將失活時(shí)的狀態(tài)拌夏,失活時(shí)恢復(fù) window 對(duì)象到初始初始狀態(tài)
  • 多例模式
    代理了一個(gè)全新的對(duì)象僧著,這個(gè)對(duì)象是復(fù)制的 window 對(duì)象的一部分不可配置屬性,所有的更改都是基于這個(gè) fakeWindow 對(duì)象障簿,從而保證多個(gè)實(shí)例之間屬性互不影響

樣式沙箱

樣式沙箱實(shí)際做的事情其實(shí)很簡(jiǎn)單盹愚,就是將動(dòng)態(tài)添加的 script、link站故、style 這三個(gè)元素插入到對(duì)的位置皆怕,屬于主應(yīng)用的插入主應(yīng)用,屬于微應(yīng)用的插入到對(duì)應(yīng)的微應(yīng)用中西篓,方便微應(yīng)用卸載的時(shí)候一起刪除愈腾,當(dāng)然樣式沙箱還額外做了兩件事:
(1)在卸載之前為動(dòng)態(tài)添加樣式做緩存,在微應(yīng)用重新掛載時(shí)再插入到微應(yīng)用內(nèi)
(2)將 proxy 對(duì)象傳遞給 execScripts 函數(shù)岂津,將其設(shè)置為微應(yīng)用的執(zhí)行上下文

  • 樣式隔離
    qiankun 的樣式隔離有兩種方式虱黄,一種是嚴(yán)格樣式隔離,通過 shadow dom 來實(shí)現(xiàn)吮成,另一種是實(shí)驗(yàn)性的樣式隔離橱乱,就是 scoped css,兩種方式不可共存粱甫。

    在 qiankun 中的嚴(yán)格樣式隔離泳叠,就是在這個(gè) createElement 方法中做的,通過 shadow dom 來實(shí)現(xiàn)茶宵, shadow dom 是瀏覽器原生提供的一種能力危纫,在過去的很長(zhǎng)一段時(shí)間里,瀏覽器用它來封裝一些元素的內(nèi)部結(jié)構(gòu)。以一個(gè)有著默認(rèn)播放控制按鈕的 <video> 元素為例种蝶,實(shí)際上契耿,在它的 Shadow DOM 中,包含來一系列的按鈕和其他控制器

  • 實(shí)驗(yàn)性樣式隔離
    實(shí)驗(yàn)性樣式的隔離方式其實(shí)就是 scoped css蛤吓,qiankun 會(huì)通過動(dòng)態(tài)改寫一個(gè)特殊的選擇器約束來限制 css 的生效范圍

HTML Entry

HTML Entry 是由 import-html-entry 庫實(shí)現(xiàn)的宵喂,通過 http 請(qǐng)求加載指定地址的首屏內(nèi)容即 html 頁面糠赦,然后解析這個(gè) html 模版得到 template, scripts , entry, styles会傲。

{
  template: 經(jīng)過處理的腳本,link拙泽、script 標(biāo)簽都被注釋掉了,
  scripts: [腳本的http地址 或者 { async: true, src: xx } 或者 代碼塊],
  styles: [樣式的http地址],
  entry: 入口腳本的地址淌山,要不是標(biāo)有 entry 的 script 的 src,要不就是最后一個(gè) script 標(biāo)簽的 src
}

然后遠(yuǎn)程加載 styles 中的樣式內(nèi)容顾瞻,將 template 模版中注釋掉的 link 標(biāo)簽替換為相應(yīng)的 style 元素泼疑。然后向外暴露一個(gè) Promise 對(duì)象。

{
    // template 是 link 替換為 style 后的 template
    template: embedHTML,
    // 靜態(tài)資源地址
    assetPublicPath,
    // 獲取外部腳本荷荤,最終得到所有腳本的代碼內(nèi)容
    getExternalScripts: () => getExternalScripts(scripts, fetch),
    // 獲取外部樣式文件的內(nèi)容
    getExternalStyleSheets: () => getExternalStyleSheets(styles, fetch),
    // 腳本執(zhí)行器退渗,讓 JS 代碼(scripts)在指定 上下文 中運(yùn)行
    execScripts: (proxy, strictGlobal) => {
        if (!scripts.length) {
            return Promise.resolve();
        }
        return execScripts(entry, scripts, proxy, { fetch, strictGlobal });
    }
}

HTML Entry 最終會(huì)返回一個(gè) Promise 對(duì)象,qiankun 就用了這個(gè)對(duì)象中的 template蕴纳、assetPublicPath 和 execScripts 三項(xiàng)会油,將 template 通過 DOM 操作添加到主應(yīng)用中,執(zhí)行 execScripts 方法得到微應(yīng)用導(dǎo)出的生命周期方法古毛,并且還順便解決了 JS 全局污染的問題翻翩,因?yàn)閳?zhí)行 execScripts 方法的時(shí)候可以通過 proxy 參數(shù)指定 JS 的執(zhí)行上下文。

內(nèi)容來源

微前端框架 之 qiankun 從入門到源碼分析
qiankun 2.x 運(yùn)行時(shí)沙箱 源碼分析
HTML Entry 源碼分析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末稻薇,一起剝皮案震驚了整個(gè)濱河市嫂冻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌塞椎,老刑警劉巖桨仿,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異案狠,居然都是意外死亡蹬敲,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門莺戒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伴嗡,“玉大人,你說我怎么就攤上這事从铲”裥#” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)阱扬。 經(jīng)常有香客問我泣懊,道長(zhǎng),這世上最難降的妖魔是什么麻惶? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任馍刮,我火速辦了婚禮,結(jié)果婚禮上窃蹋,老公的妹妹穿的比我還像新娘卡啰。我一直安慰自己,他們只是感情好警没,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布匈辱。 她就那樣靜靜地躺著,像睡著了一般杀迹。 火紅的嫁衣襯著肌膚如雪亡脸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天树酪,我揣著相機(jī)與錄音浅碾,去河邊找鬼。 笑死续语,一個(gè)胖子當(dāng)著我的面吹牛垂谢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绵载,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼埂陆,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了娃豹?” 一聲冷哼從身側(cè)響起焚虱,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎懂版,沒想到半個(gè)月后鹃栽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躯畴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年民鼓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓬抄。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡丰嘉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嚷缭,到底是詐尸還是另有隱情饮亏,我是刑警寧澤耍贾,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站路幸,受9級(jí)特大地震影響荐开,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜简肴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一晃听、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧砰识,春花似錦能扒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽观话。三九已至予借,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間频蛔,已是汗流浹背灵迫。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晦溪,地道東北人瀑粥。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像三圆,于是被迫代替她去往敵國(guó)和親狞换。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 本文將針對(duì)微前端框架 qiankun 的源碼進(jìn)行深入解析舟肉,在源碼講解之前修噪,我們先來了解一下什么是 微前端。 微前端...
    昵稱不用太拉風(fēng)閱讀 9,586評(píng)論 4 31
  • 背景 隨著項(xiàng)目的演進(jìn)路媚,前端的業(yè)務(wù)架構(gòu)也會(huì)變得更加龐大黄琼、復(fù)雜,并常常會(huì)出現(xiàn)需要模塊復(fù)用的場(chǎng)景:1整慎、組件復(fù)用脏款,例如統(tǒng)一...
    Ygria閱讀 5,157評(píng)論 1 5
  • 微前端架構(gòu)之single-spa single-spa是什么 Single-spa 是一個(gè)將多個(gè)單頁面應(yīng)用聚合為一...
    yolkpie閱讀 734評(píng)論 0 0
  • 什么是微前端 微前端是一種多個(gè)團(tuán)隊(duì)通過獨(dú)立發(fā)布功能的方式來共同構(gòu)建現(xiàn)代化 web 應(yīng)用的技術(shù)手段及方法策略. 微前...
    lean_閱讀 2,525評(píng)論 0 7
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者,不喜歡去冒險(xiǎn)裤园,但是人生放棄了冒險(xiǎn)撤师,也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 6,033評(píng)論 0 4