前言

  • 突然对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导致报错。在卸载时删掉即可。
  • 至于样式,需要那种服务端渲染的样式写法,或者内联样式,否则就是样式隔离。
  • 目前正常来说除了样式比较麻烦,事件状态什么的是都有的,等我再用一段时间看看还有啥问题。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐