Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ShadowDOM总结及升级polyfill #72

Open
NathanHan1 opened this issue Jun 11, 2019 · 0 comments
Open

ShadowDOM总结及升级polyfill #72

NathanHan1 opened this issue Jun 11, 2019 · 0 comments

Comments

@NathanHan1
Copy link

NathanHan1 commented Jun 11, 2019

shadowDOM是什么?

ShadowDOM是一个HTML的规范,作用是将主文档的DOM独立开来,保持分离。在shadowDOM内部的css和dom节点都是独立的,这意味着外部的css不会影响到shadowDOM里的UI树。

为什么需要shadowDOM?

在gm-printer或者其他带有独立UI视图的库中,需要将业务css代码和通用的组件与其隔离。库本身是独立的,业务代码的css样式不应该影响到库,两者是解耦的。

shadowDOM的创建和要注意的点

创建一个shadowDOM

<p id='app'></p>
var p = document.querySelector('#app')
var shadow = p.attachShadow({mode: 'open'});

这里要注意的是:p是影子宿主,shadow是影子树

// 从影子宿主获得影子树
p.shadowRoot === shadow
// 从影子树获得影子宿主
shadow.host === p

shadowDOM的事件模型

关乎shadowDOM的时间模型比较特殊,在shadowDOM里触发的事件会传播出去的事件有:

聚焦事件:blurfocusfocusinfocusout

  • 鼠标事件:clickdblclickmousedownmouseentermousemove,等等

  • 滚轮事件:wheel

  • 输入事件:beforeinputinput

  • 键盘事件:keydownkeyup

  • 组合事件:compositionstartcompositionupdatecompositionend

  • 拖放事件:dragstartdragdragenddrop,等等

其他事件不会冒泡到shadowDOM外部。

关键的是:这些可以传播出去的事件,在外部监听到的事件起源目标(event.target)是影子宿主。这意味着没添加shadowDOM时,在document上注册的事件处理函数中如果使用到了event.target,会引发错误。

解决方案:将window.document挂载的事件都迁移到shadow层

window.document.addEventListner('click', this.handleClick) 
// 如果this.handleClick中使用到了event.target可能引发错误
window.shadowRoot.addEventListener('click', handleClick) 

ShadowDOM:独立的网络组件

给shadowDOM添加样式

  const style = window.document.createElement('style')
  style.type = 'text/css'
  style.appendChild(document.createTextNode(cssString))
  shadowRoot.appendChild(style)

其中cssString是css样式的字符串形式。

迁移css样式时注意:shadowDOM中默认没有body和html这两个标签(当然你可以添加),所以在css样式中body和html的样式会失效。

重置可继承样式

可继承样式(backgroundcolorfont 以及 line-height 等)可在 shadow DOM 中继续继承。 也就是说,默认情况下它们会突破 shadow DOM 边界。 如果您想从头开始,可在它们超出影子边界时,使用 all: initial; 将可继承样式重置为初始值。

在React中使用shadowDOM

componentDidMount() {
	ReactDOM.render(<App />, shadowRoot)
}

需要注意的是需要在componentWillUnmount这个生命周期下卸载<App />渲染。

componentWillUnmount () {
  ReactDOM.unmountComponentAtNode(window.shadowRoot) 
}

首先使用shadowDOM一般是库,库是给项目使用的,如果忘记unmountComponentAtNode库的vdom,再次渲染的时候会出现卡死等比较严重的问题,很可能是之前存在的vdom(或者其他react内部的状态)影响了新的渲染。

兼容性

image-20190611121036118
chrome53以上支持shadowDOM,可以看到兼容性一般,如果强调兼容的话可以考虑iframe来做隔离,但是使用了iframe主页面的数据传递也阻隔了。这就需要库的调用方做取舍了。

升级polyfill

babel帮我们做了很多将es6的一些语法转换成es5,但是babel也有不能转换的,转译出来的结果在默认情况下并不包括 ES6 对运行时的扩展,例如,builtins(内建,包括 Promise、Set、Map 等)、内建类型上的原型扩展(如 ES6 对 Array、Object、String 等内建类型原型上的扩展)以及Regenerator(用于generators / yield)等都不包括在内。

这时就需要polyfill来填补这个对运行时的扩展了,现所有的polyfill都需要依赖开源库core-js,他提供es6、7、8的新特性。

由于之前无意的使用到一个ES10的新特性flat,导致低版本浏览器运行错误,解决方案是升级babel-polyfill到7.4.4(babel-polyfill基于core-js再封装),可支持更多的es新特性。但最好的做法是使用core-js@3,然后自定义打包一个满足业务需求的polyfill。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant