嵌套自定义HTML元素时内层元素无法渲染的原因及解决方法
TL;DR:你在LeftPane和RightPane这两个自定义元素的构造函数里,克隆模板时犯了个小疏忽——你直接克隆了<template>元素本身,而不是它内部的content内容,导致内层元素的shadow DOM里根本没有实际的插槽,自然没法渲染Thing 3。只要把克隆代码改成和PageBase里一样的写法就行。
问题到底出在哪?
咱们先拆解下你的场景:<page-base>里的Thing 1、Thing 2都能正常显示,但嵌套在<right-pane>里的Thing 3就是不出来,单独用自定义元素时又没问题。其实核心bug就藏在LeftPane和RightPane的构造代码里:
看这段有问题的代码:
class LeftPane extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({ mode: "closed" }); // 这里错了!克隆的是整个template标签,不是它的内容 let clone = paneTemplate.cloneNode(true); shadow.append(clone); } }
<template>标签本身是个“容器”,它的实际内容都存在.content属性里,直接把<template>标签插入shadow DOM的话,浏览器是不会渲染它里面的插槽的——相当于你给shadow DOM塞了个没激活的模板,而不是可用的插槽元素,那<p slot="content">Thing 3</p>自然找不到对应的插槽,也就没法显示了。
怎么修复?
很简单,把LeftPane和RightPane里的克隆代码,改成和PageBase一致的写法:克隆模板的content部分,而不是模板本身。
修改后的代码如下:
class LeftPane extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({ mode: "closed" }); // 正确克隆template的内容部分 let clone = paneTemplate.content.cloneNode(true); shadow.append(clone); } } class RightPane extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({ mode: "closed" }); // 同样修改这里 let clone = paneTemplate.content.cloneNode(true); shadow.append(clone); } }
改完之后,<right-pane>的shadow DOM里就会正确插入<slot name="content">插槽,它的子元素<p slot="content">Thing 3</p>就能被插槽捕获并渲染出来,同时<right-pane>本身也会正常被<page-base>的right-pane插槽渲染,完美解决问题!
备注:内容来源于stack exchange,提问作者firewuf

