微前端框架整体架构思路

0. 前言

在Tob项目如火如荼的今天,越来越多的前端项目规模开始急剧扩大,多团队分模块开发迭代是越来越不可以避免,微前端的形式也越来越有必要.

1.微前端的核心介绍

1.1 模块服务

管理模块和插件的核心服务.

1.2 模块

用户开发的统一模块.对外提供统一方法.可以称为应用

1.3 插件

平台或生态开发的插件机制,对模块的加载和业务开发起到辅助作用.

1.3 微前端服务核心

1.3.1 模块如何载入?何时载入?

如何载入?
目前腾讯云NMC中采用Seajs统一管理组织所有的核心或应用模块.
新产品的接入都是打包成一个CMD的模块通过script标签引入.SeaJs 使用起来较为方便.有明确的api.
我们这里采用何种的组织形势呢?requirejs/systemjs/es6? 考虑到微前端的重要优势之一,就是大项目的可以分粒度,技术选型灵活.对于旧的模块可以做到兼容.
新的技术项目使用ES6 import,老的模块化项目进行使用Seajs,RequireJs或Iframe的加载.
所以这里设计将模块的载入方法和时机都交给模块去定义.
服务核心仅调用用来注册模块名称载入方法和载入时机.
关于载入时机的问题?
事件点击?hashchange?onpopstate....
等等都可以注册到服务核心中去.当捕捉到事件之后去遍历所有已注册的载入时机,如果满足模块的载入时机,则调用模块的载入方法.不局限与路由切换,菜单点击等情况.同时我们也提供一些核心插件用于一些机制的注册.

1.3.2 具体实现

class Services{
    constructor(pluginService){
        this.entrys=new Map();
        this.plguinService=pluginService;
        //监听事件类型的注册
        this.eventTypeMoudles=new Map();

    }
    /**
     * 服务注册入口
     * @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)
    }
    
    /**
     * 注册全局加载时机
     */
    registerWindowLoadChance(eventType,moudleName){
        if(this.eventTypeMoudles.has(eventType)){
            this.eventTypeMoudles.get(eventType).add(moudleName);
        }else{
            this.eventTypeMoudles.set(eventType,new Set([moudleName]));
        }
        //更新监听方法
        window[eventType]=(event)=>{
            //触发set中所有的moudle的checkload方法
            [...this.eventTypeMoudles.get(eventType)].forEach((moudleName)=>{
                this.entrys.get(moudleName) && this.entrys.get(moudleName).onEvent(event,this)
            })
        }
    }
    /**
     * 直接开启某个模块
     */
    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.关于模块

关于微前端,网上有许多的文章描述和见解,微前端中的核心就是子应用被实现的方式.不外有以下三种方式

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

所以如何设计出一个好的模块设计,可以去适应不同的框架,不同的开发方式.

3.1 模块的目标

模块最终的目标是在指定位置渲染dom结构.
所以不管是什么类型的应用,本质上就是渲染dom结构,只是可能加载方式的,加载的前提,运行的时机不同罢了.这些都可以通过插件去解决.

3.2 模块的本质

模块的本质是一个被封装统一方法的前端资源集.
这里可以抽象一个模块为Entry和App

Entry
Entry 接受注册的全局监听事件去判断对于自身模块加载和卸载.且接受其他模块发送的信息.Entry就是App的管理者

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

App
app 就是模块的主要内容,满足以下接口设定即可

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最主要的原因就是减少网页第一次被加载时,减少加载资源的体积大小.

2.3.1 加载方式

requirejs

2.3.2 加载时机

可以由模块统一确定.无论是点击菜单,路由切换.

3. 关于插件

3.1 插件的作用

为模块的加载做环境准备
监控模块的运行情况
优化模块加载,如增加loading,避免重复加载基础环境

...

3.2 ReactPlugin实现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)=>{
                    /** 避免基础环境多次的加载 */
                    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'
    }
    //其他模块发送的信息,当其他模块加载时的动作
    onEntryNotice(entry,notice){
        //当其他的模块被加载时候我们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();
        }
    }
}
//默认加载React和Vue环境的加载
const microService=start();
microService.registerEntry(new ReactEntry("./test.js","test",["ReactPlugin"]))
microService.registerEntry(new VueEntry("./vuetest.js","vuetest",["VuePlugin"]))

//注册全局加载时机
microService.registerWindowLoadChance("onclick","test")
microService.registerWindowLoadChance("onhashchange","test")
window.microService=microService
//也可以直接加载模块
window.loadVueTest=function loadVueTest(){
    microService.loadMoudle("vuetest");
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容