前言
- 突然对shadowdom感兴趣了,然后实现发现了很多坑,特此总结一下。
实现原理
- 函数组件我实现的有点bug,就是子组件的自己的状态无法改变,暂时不清楚为啥,如果有需要可以看一下react-shadow的实现:https://github.com/Wildhoney/ReactShadow/blob/master/src/core/index.js#L19
- 类组件实现起来倒是没啥问题:
export class ShadowView extends React.Component {
state: {
root: null | ShadowRoot;
pdiv: null | HTMLDivElement;
div: null | HTMLDivElement;
} = {
root: null,
pdiv: null,
div: null,
};
setRoot = (pdiv: HTMLDivElement) => {
if (pdiv) {
const div = document.createElement("div");
const shadow = div.attachShadow({
mode: "open" });
pdiv.appendChild(div);
this.setState({
root: shadow, pdiv, div });
}
};
componentWillUnmount() {
if (this.state.pdiv && this.state.div) {
this.state.pdiv!.removeChild(this.state.div);
this.setState((pre) => ({
...pre, root: null, div: null }));
}
}
render() {
const {
children } = this.props;
const {
root } = this.state;
return (
<div ref={
this.setRoot}>
{
root &&
ReactDOM.createPortal(children, root as unknown as Element)}
</div>
);
}
}
export function Axxx(props: {
state: string; cl: Function }) {
const [state, setState] = useState(0);
return (
<div className="aaa">
xcsdsasdasdasdsad
{
props.state}
{
state}
<button
onClick={
() => {
setState((pre) => pre + 1);
props.cl();
}}
>
++++
</button>
<button
onClick={
() => {
setState((pre) => pre - 1);
props.cl();
}}
>
---
</button>
<a>xxx</a>
</div>
);
}
export class App extends React.Component {
state = {
message: "..." };
onBtnClick = () => {
this.setState({
message: this.state.message + "xx" });
};
render() {
return (
<ShadowView>
<Axxx state={
this.state.message} cl={
this.onBtnClick}></Axxx>
</ShadowView>
);
}
}
export default App;
- 主要是必须有一个shadowRoot,所以在每次更新时,会出现卸载问题,然后重新去给已有的shadowRoot附上shadow导致报错。在卸载时删掉即可。
- 至于样式,需要那种服务端渲染的样式写法,或者内联样式,否则就是样式隔离。
- 目前正常来说除了样式比较麻烦,事件状态什么的是都有的,等我再用一段时间看看还有啥问题。