使用未做任何清理的 innerHTML 和模板字符串可能会导致脚本注入的安全问题。过去,开发者使用各种变通方法来实现 HTML 模板,但这些变通方法都存在问题。
这就是 <template> 元素发挥作用的地方;模板提供了真正的惰性 DOM、一种高性能的节点克隆方法以及可复用的模板功能。
接下来,将组件转换为使用 HTML 模板:
<body> <template id="rating-element-template"> <style> :host { display: inline-flex; align-items: center; } button { background: transparent; border: none; cursor: pointer; } </style> <button class="thumb_down" > <svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg> </button> <span class="rating"></span> <button class="thumb_up"> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg> </button> </template>
<rating-element> <div> This is the light DOM! </div> </rating-element></body> 这里你将 DOM 内容移到了主文档 DOM 中的 <template> 标签里。现在重构自定义元素的定义:
connectedCallback() { const shadowRoot = this.attachShadow({mode: 'open'}); const templateContent = document.querySelector<HTMLTemplateElement>('#rating-element-template')!.content; const clonedContent = templateContent.cloneNode(true); shadowRoot.appendChild(clonedContent);
this.shadowRoot!.querySelector<HTMLElement>('.rating')!.innerText = `${this.rating}`;} connectedCallback() { const shadowRoot = this.attachShadow({mode: 'open'}); const templateContent = document.querySelector('#rating-element-template').content; const clonedContent = templateContent.cloneNode(true); shadowRoot.appendChild(clonedContent);
this.shadowRoot.querySelector('.rating').innerText = `${this.rating}`;} 使用这个 <template> 元素的步骤:
templateContent.cloneNode 克隆这些节点。true 参数执行深克隆。恭喜,现在你的 Web Component 的 DOM 已经被封装了,但 DOM 仍然是静态的。在接下来的步骤中,你将添加更新评分的支持。