微前端框架整體架構(gòu)思路

0. 前言

在Tob項(xiàng)目如火如荼的今天,越來(lái)越多的前端項(xiàng)目規(guī)模開(kāi)始急劇擴(kuò)大,多團(tuán)隊(duì)分模塊開(kāi)發(fā)迭代是越來(lái)越不可以避免,微前端的形式也越來(lái)越有必要.

1.微前端的核心介紹

1.1 模塊服務(wù)

管理模塊和插件的核心服務(wù).

1.2 模塊

用戶(hù)開(kāi)發(fā)的統(tǒng)一模塊.對(duì)外提供統(tǒng)一方法.可以稱(chēng)為應(yīng)用

1.3 插件

平臺(tái)或生態(tài)開(kāi)發(fā)的插件機(jī)制,對(duì)模塊的加載和業(yè)務(wù)開(kāi)發(fā)起到輔助作用.

1.3 微前端服務(wù)核心

1.3.1 模塊如何載入?何時(shí)載入?

如何載入?
目前騰訊云NMC中采用Seajs統(tǒng)一管理組織所有的核心或應(yīng)用模塊.
新產(chǎn)品的接入都是打包成一個(gè)CMD的模塊通過(guò)script標(biāo)簽引入.SeaJs 使用起來(lái)較為方便.有明確的api.
我們這里采用何種的組織形勢(shì)呢?requirejs/systemjs/es6? 考慮到微前端的重要優(yōu)勢(shì)之一,就是大項(xiàng)目的可以分粒度,技術(shù)選型靈活.對(duì)于舊的模塊可以做到兼容.
新的技術(shù)項(xiàng)目使用ES6 import,老的模塊化項(xiàng)目進(jìn)行使用Seajs,RequireJs或Iframe的加載.
所以這里設(shè)計(jì)將模塊的載入方法和時(shí)機(jī)都交給模塊去定義.
服務(wù)核心僅調(diào)用用來(lái)注冊(cè)模塊名稱(chēng)載入方法和載入時(shí)機(jī).
關(guān)于載入時(shí)機(jī)的問(wèn)題?
事件點(diǎn)擊?hashchange?onpopstate....
等等都可以注冊(cè)到服務(wù)核心中去.當(dāng)捕捉到事件之后去遍歷所有已注冊(cè)的載入時(shí)機(jī),如果滿(mǎn)足模塊的載入時(shí)機(jī),則調(diào)用模塊的載入方法.不局限與路由切換,菜單點(diǎn)擊等情況.同時(shí)我們也提供一些核心插件用于一些機(jī)制的注冊(cè).

1.3.2 具體實(shí)現(xiàn)

class Services{
    constructor(pluginService){
        this.entrys=new Map();
        this.plguinService=pluginService;
        //監(jiān)聽(tīng)事件類(lèi)型的注冊(cè)
        this.eventTypeMoudles=new Map();

    }
    /**
     * 服務(wù)注冊(cè)入口
     * @param {*} entry
     * @memberof Services
     */
    registerEntry(entry){
        console.log(entry.getName())
        this.entrys.set(entry.getName(),entry);       
    }
    
    getPluginService(){
        return this.plguinService;
    }
    registerPlugin(plugin){
        this.plguinService.register(plugin)
    }
    
    /**
     * 注冊(cè)全局加載時(shí)機(jī)
     */
    registerWindowLoadChance(eventType,moudleName){
        if(this.eventTypeMoudles.has(eventType)){
            this.eventTypeMoudles.get(eventType).add(moudleName);
        }else{
            this.eventTypeMoudles.set(eventType,new Set([moudleName]));
        }
        //更新監(jiān)聽(tīng)方法
        window[eventType]=(event)=>{
            //觸發(fā)set中所有的moudle的checkload方法
            [...this.eventTypeMoudles.get(eventType)].forEach((moudleName)=>{
                this.entrys.get(moudleName) && this.entrys.get(moudleName).onEvent(event,this)
            })
        }
    }
    /**
     * 直接開(kāi)啟某個(gè)模塊
     */
    loadMoudle(moudleName){
        this.entrys.get(moudleName) && this.entrys.get(moudleName).start(this)
    }
    /** 
     * 通知其他模塊.本模塊被加載了
    */
   noticeOtherEntry(sendEntry,notice){
        for(let [entryName,entry] of this.entrys){
            if(entryName!==sendEntry.getName()){
                entry.onEntryNotice(sendEntry,notice)
            }
        }
   }
    
}

3.關(guān)于模塊

關(guān)于微前端,網(wǎng)上有許多的文章描述和見(jiàn)解,微前端中的核心就是子應(yīng)用被實(shí)現(xiàn)的方式.不外有以下三種方式

  1. 三大框架(React,Vue,Angular)
  2. iframe
  3. web-component
  4. 原生(好像沒(méi)看到)

所以如何設(shè)計(jì)出一個(gè)好的模塊設(shè)計(jì),可以去適應(yīng)不同的框架,不同的開(kāi)發(fā)方式.

3.1 模塊的目標(biāo)

模塊最終的目標(biāo)是在指定位置渲染dom結(jié)構(gòu).
所以不管是什么類(lèi)型的應(yīng)用,本質(zhì)上就是渲染dom結(jié)構(gòu),只是可能加載方式的,加載的前提,運(yùn)行的時(shí)機(jī)不同罷了.這些都可以通過(guò)插件去解決.

3.2 模塊的本質(zhì)

模塊的本質(zhì)是一個(gè)被封裝統(tǒng)一方法的前端資源集.
這里可以抽象一個(gè)模塊為Entry和App

Entry
Entry 接受注冊(cè)的全局監(jiān)聽(tīng)事件去判斷對(duì)于自身模塊加載和卸載.且接受其他模塊發(fā)送的信息.Entry就是App的管理者

class Entry{
    constructor(jsUrl,name,plugins){
    }
    checkShouldLoad(event){
    }
    checkShouldUnload(event){
    }
    onEntryNotice(entry,notice){
    }
}

App
app 就是模塊的主要內(nèi)容,滿(mǎn)足以下接口設(shè)定即可

class App{
    render=()=>{
         ReactDOM.render(
          React.createElement('div', null, 'Hello Micro Front For React'),
            document.getElementById('root')
        );
    },
    destory=()=>{
        ReactDOM.unmountComponentAtNode(document.getElementById("root"))
    }
}

分離Moudle為Entry和App最主要的原因就是減少網(wǎng)頁(yè)第一次被加載時(shí),減少加載資源的體積大小.

2.3.1 加載方式

requirejs

2.3.2 加載時(shí)機(jī)

可以由模塊統(tǒng)一確定.無(wú)論是點(diǎn)擊菜單,路由切換.

3. 關(guān)于插件

3.1 插件的作用

為模塊的加載做環(huán)境準(zhǔn)備
監(jiān)控模塊的運(yùn)行情況
優(yōu)化模塊加載,如增加loading,避免重復(fù)加載基礎(chǔ)環(huán)境

...

3.2 ReactPlugin實(shí)現(xiàn)demo

import BasePlugin from "./../Core/Plugin"
import loader from "./../Utils/loader"
class ReactPlugin extends BasePlugin{
    lifeMethod(moudle,lifeName){
        switch(lifeName){
            case "beforeload":
                return new Promise((resolve,reject)=>{
                    /** 避免基礎(chǔ)環(huán)境多次的加載 */
                    if(window.React && window.ReactDOM){
                        resolve()
                        return
                    }
                    loader.load("./react16.development.js",()=>{
                        resolve()
                    })
                })
        }

    }
}
export default ReactPlugin

4.整體DEMO

http://microfront.dishenghk.cn

import BaseEntry from "./Core/Entry.js";
import {start} from "./Core/app"
class ReactEntry extends BaseEntry{
    //合適加載
    checkShouldLoad(event){
        console.log(event)
        return event.target && event.target.dataset && event.target.dataset.show==="react" 
    }
    //合適卸載
    checkShouldUnload(event){
        return event.type==='hashchange'
    }
    //其他模塊發(fā)送的信息,當(dāng)其他模塊加載時(shí)的動(dòng)作
    onEntryNotice(entry,notice){
        //當(dāng)其他的模塊被加載時(shí)候我們destroy即可
        console.log(entry,notice,this.module)
        if(notice && notice.type ==='loaded'){
            this.module && this.module.destory();
        }
    }
}
class VueEntry extends BaseEntry{
    onEntryNotice(entry,notice){
        if(notice && notice.type ==='loaded'){
           this.module &&  this.module.destory();
        }
    }
}
//默認(rèn)加載React和Vue環(huán)境的加載
const microService=start();
microService.registerEntry(new ReactEntry("./test.js","test",["ReactPlugin"]))
microService.registerEntry(new VueEntry("./vuetest.js","vuetest",["VuePlugin"]))

//注冊(cè)全局加載時(shí)機(jī)
microService.registerWindowLoadChance("onclick","test")
microService.registerWindowLoadChance("onhashchange","test")
window.microService=microService
//也可以直接加載模塊
window.loadVueTest=function loadVueTest(){
    microService.loadMoudle("vuetest");
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末翘县,一起剝皮案震驚了整個(gè)濱河市谴分,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌牺蹄,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件氓奈,死亡現(xiàn)場(chǎng)離奇詭異鼎天,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)育勺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)罗岖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人南蓬,你說(shuō)我怎么就攤上這事哑了。” “怎么了垒手?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)优妙,這世上最難降的妖魔是什么憎账? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮邪意,結(jié)果婚禮上反砌,老公的妹妹穿的比我還像新娘。我一直安慰自己宴树,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布又憨。 她就那樣靜靜地躺著锭吨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浪秘。 梳的紋絲不亂的頭發(fā)上埠况,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音辕翰,去河邊找鬼。 笑死沟沙,一個(gè)胖子當(dāng)著我的面吹牛壁榕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播牌里,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼喳篇!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起挺尿,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤炊邦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后窄俏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蜗细,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年踪区,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吊骤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡传泊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出眷细,到底是詐尸還是另有隱情鹃祖,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布校读,位于F島的核電站祖能,受9級(jí)特大地震影響歉秫,放射性物質(zhì)發(fā)生泄漏养铸。R本人自食惡果不足惜轧膘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一扶供、第九天 我趴在偏房一處隱蔽的房頂上張望裂明。 院中可真熱鬧,春花似錦闽晦、人聲如沸提岔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至哀墓,卻和暖如春喷兼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背季惯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工勉抓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贾漏,地道東北人藕筋。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓念逞,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親翎承。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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

  • https://www.cnblogs.com/HCJJ/p/6611669.html requireJS 可以很...
    小杺閱讀 836評(píng)論 0 3
  • 一.簡(jiǎn)述前端構(gòu)建工具啊胶。fis3,Gulp焰坪,Grunt聘惦,Webpack www.tuicool.com/articl...
    錢(qián)小強(qiáng)_閱讀 1,316評(píng)論 0 5
  • 模塊通常是指編程語(yǔ)言所提供的代碼組織機(jī)制,利用此機(jī)制可將程序拆解為獨(dú)立且通用的代碼單元善绎。所謂模塊化主要是解決代碼分...
    MapleLeafFall閱讀 1,165評(píng)論 0 0
  • 雖然從沒(méi)有認(rèn)為自己是一個(gè)前端開(kāi)發(fā)者禀酱,但不知不覺(jué)中也積累下了一些前端開(kāi)發(fā)的經(jīng)驗(yàn)。正巧之前碰到一道面試題剂跟,于是就順便梳...
    AlloVince閱讀 6,855評(píng)論 1 49
  • 1.幾種基本數(shù)據(jù)類(lèi)型?復(fù)雜數(shù)據(jù)類(lèi)型?值類(lèi)型和引用數(shù)據(jù)類(lèi)型?堆棧數(shù)據(jù)結(jié)構(gòu)? 基本數(shù)據(jù)類(lèi)型:Undefined曹洽、Nul...
    極樂(lè)君閱讀 5,498評(píng)論 0 106