ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

微前端-02

2022-07-13 15:34:38  阅读:177  来源: 互联网

标签:02 const 前端 listenerList proxyWindow listener window type


上文学习了如何渲染微应用,那么这篇文章就记录一下,如果避免渲染之后存在的两个问题

  1. 如何解决全局变量污染的问题
  2. 微应用卸载后,如何自动卸载掉微应用挂载的全局事件

解决方案

通过模拟沙箱,将微应用的代码环境与基座应用分割开。
首先通过proxy模拟一个全局变量

export default class Sandbox {
	// ...
	microWindow = {}
	constructor() {
		this.proxyWindow = new Proxy(microWindow,{
			get(target,key){
				//...
			}
		})
	}
}

思路就是通过proxyWindow,充当微应用代码中的全局变量。

    bindScope(code) {
        window.proxyWindow = this.proxyWindow;
        return `(function(window, self){with(window){;${code}\n}}).call(window.proxyWindow, window.proxyWindow, window.proxyWindow)`
    }

这里通过with将微应用的window,也就是全局变量通过call指定为我们的proxyWindow,非常优秀。
然后proxyWindow里面的get拦截器需要注意的是,如果获取的是函数,则需要将函数绑定到原始window上

 get: (target, key) => {
                if (Reflect.has(target, key)) {
                    return Reflect.get(target, key)
                }

                const rawValue = Reflect.get(window, key);


                // 如果原生window的值是函数,则需要将this绑定到window上
                // 如:console alert

                if (typeof rawValue === 'function') {
                    const valueStr = rawValue.toString();
                    // 排除构造函数
                    if (!/^function\s+[A-Z]/.test(valueStr)
                        && !/^class\s+/.test(valueStr)
                    ) {
                        return rawValue.bind(window)
                    }
                }


                // 其他情况直接返回
                return rawValue;
            },

全局事件监听行为,通过重写addEventListener和removeEventListener来实现,微应用卸载时候自动清除事件

// 记录原生方法
const rawWindowAddEventListener = window.addEventListener;
const rawWindowRemoveEventListener = window.removeEventListener;

function effect(microWindow) {
    const eventListenerMap = new Map();

    microWindow.addEventListener = function (type, listener, options) {
        const listenerList = eventListenerMap.get(type);
        if (listenerList) {
            listenerList.add(listener)
        } else {
            eventListenerMap.set(type, new Set([listener]))
        }
        return rawWindowAddEventListener.call(window, type, listener, options)
    }


    microWindow.removeEventListener = function (type, listener, options) {
        const listenerList = eventListenerMap.get(type);
        if (listenerList?.size && listenerList.get(type)) {
            listenerList.delete(listener);
        }
        return rawWindowRemoveEventListener.call(window,type,listener,options)
    }

    return ()=> {
        // console.log("需要卸载的全局事件",eventListenerMap)
        if(eventListenerMap.size) {
            eventListenerMap.forEach((listenerList,type)=> {
                console.log('listenerList:',listenerList,type)
                if(listenerList.size) {
                    for (const listener of listenerList) {
                        rawWindowRemoveEventListener.call(window,type,listener)
                    }
                }
            })
            eventListenerMap.clear()
        }
    }
}

采用闭包的形式存储事件列表,然后,由于沙箱支持关闭,所以,需要一个变量记录状态,两个函数操作状态

export default class Sandbox {
     active = false;
    microWindow = {};
    injectedKeys = new Set();
	  constructor() {
	  	this.releaseEffect = effect(this.microWindow) // 保存一下清空函数
		// ...
	  }
	// ...
	    start() {
        if (!this.active)
            this.active = true;
    }

    stop() {
        if (this.active) {
            this.active = false;
            this.injectedKeys.forEach(k => {
                Reflect.deleteProperty(this.microWindow, k)
            })
            this.injectedKeys.clear()
            // 卸载全局事件
            this.releaseEffect()
        }
    }
}

使用

// src/app.js
    // 资源加载完成后进行渲染
    mount() {
        this.sandbox.start()
	// ....
	        this.source.scripts.forEach(info => {
            // (0, eval)(info.code)
            (0,eval)(this.sandbox.bindScope(info.code))
        })
	}
	
	   // 卸载应用
    unmount(destory) {
        this.sandbox.stop()
		//...
    }

这样一个简易的沙箱环境就制作完成

标签:02,const,前端,listenerList,proxyWindow,listener,window,type
来源: https://www.cnblogs.com/alone4436/p/16474060.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有