微前端CSS隔离方案和JS沙箱

77 阅读2分钟

CSS

样式隔离主要有以下几种方案

  1. BEM(Block Element Modifier) 约定项目前缀
  2. CSS-Modules 打包时生成不同的选择器名
  3. Shadow Dom 真正意义上的隔离
  4. css-in-js

Shadow Dom

1、2、4点我们比较常见,Shadow Dom 不常见、接下来我们写个基于Shadow Dom 的demo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
  <main>
     <p>外层内容</p>
     <div id="shadow"></div>
  </main>

<script>
    
    // 创建一个影子dom
    const shadowDom = document.getElementById('shadow').attachShadow({mode: "closed"})
   
       
        const node = document.createElement('p')
        const style = document.createElement('style')
		
        // 给p设置样式
        style.innerHTML = `
            p {
                color: #f00
            }
        `
        node.innerHTML = '我是shadow下的p节点'

        shadowDom.appendChild(style)
        shadowDom.appendChild(node)
        
</script>
</body>
</html>

页面内容演示

image-1657805656617转存失败,建议直接上传图片文件 image-1657805749181转存失败,建议直接上传图片文件

我们可以看到shadow下的p节点样式并没有影响全局的的p,shadow下形成了一个局部的样式

JS沙箱

iframe

这个我们就不展开说了

快照沙箱


    class SnapshotSandbox {
        constructor() {
            this.proxy = window
            // 记录window上的修改
            this.modifyPropsMap = {}
            this.active()
        }


        active() {
            this.windowSnapshot = {}

            for(const prop in window) {
                if ( window.hasOwnProperty(prop) ) {
                    this.windowSnapshot[ prop ] = window[ prop ]
                }
            }

            Object.keys(this.modifyPropsMap).forEach(k => {
                window[ k ] = this.modifyPropsMap[ k ]
            })
        }

        inactive() {

            for(const prop in window) {

                if (window.hasOwnProperty(prop)) {
                    if( this.windowSnapshot[ prop ] !== window[ prop ]) {
                        this.modifyPropsMap[ prop ] =  window[ prop ]
                        window[ prop  ] = this.windowSnapshot[ prop ] 
                    }                
                }

            }

        }
    }

    // 快照沙箱
    let sandbox = new SnapshotSandbox()


    ;((window) => {

         window.a = 'aaa'

		// 输出aaa
        console.log(window.a)
		
        // 失活 , inactive其实是把新增的window属性做保存。 保存后还原window之前的属性
        sandbox.inactive()
		// 输出undefined
        console.log(window.a)
        // 激活
        sandbox.active()
        // 输出aaa
        console.log(window.a)



    })(sandbox.proxy);

proxy沙箱

我们基于Proxy来实现一个沙箱demo



class SandboxWindow {

    constructor(context, frameWindow) {


        // 主要利用proxy拦截特性
        return new Proxy(frameWindow, {
            get: function(target, prop) {
                // 优先取context数据,取不到再去取window
                if ( prop in context ) {
                    return context[ prop ]
                }

                return target[prop]
            },
            set: function(target, prop, value) {
            
               // 非2window上原有属性,我们存到context里
                if (!window.hasOwnProperty(prop) ) {
                    context[ prop ] = value
                } else {
                    target[ prop ] = value
                }

                return true

            }
        })

    }

}



let p1 = new SandboxWindow({a:'p1'}, window)

// 输出p1
console.log(p1.a)
// 输出location
console.log(p1.location)
// 存到了context上
p1.buzhanguo = 18
// 输出18
console.log(p1.buzhanguo)


推荐文章

说说微前端JS沙箱实现的几种方式