• Home
  • Archives
  Evan的博客
  • Home
  • Archives
  • 面试
  • 原理笔记
  • 项目实践
  • 其他

Web Component

2020/10/10 posted in  原理笔记

现在的前端生态都推崇组件式开发,无论是 React,Vue 还是 Angular 都是组建开发的形式。原生的 HTML 其实也支持组建开发,所有的框架组件化都是在这个基础上做的二次改进。

Web Component 就是前端组件化的一种社区实践,由模板、shadow DOM、自定义标签三部分组成

HTML template & HTML slot

template 组件和和 Vue 的 template 语法类似。

<!-- html -->
<template id="custom-template>
    <p><slot name="custom-text">We can put whatever we want here!</slot></p>
</template>

它允许开发者定义一个直到被复制使用时才会进行渲染的 HTML 标签块。而 slot 标签允许开发者通过特定接入点来动态替换模板中的 HTML 内容。它用 name 属性来作为唯一识别标志

// js
// 获取 template
const template = document.getElementById("custom-template");
const templateContent = template.content;

// 复制节点
const templateInstance = templateContent.cloneNode(true);

// 渲染到真实的 DOM 上
const container = document.getElementById("container");
container.appendChild(templateInstance);

Shadow DOM

我们用 template 封装之后,虽然节点是可以复用了,但是里面的样式和属性都是对外暴露的,换句话说如果有一样的 class 或者 id 或者其他属性,都是会对环境造成污染的

Shadow DOM 就是用来隐藏节点的。他的作用就是将某些节点隔离进一个沙箱,是一颗不对外开放的,隐藏的 DOM 树

// 给 ele 节点添加一颗隐藏的 DOM 树,其内容是 template 的节点
// 开启 Shadow DOM 之后,ele 内的元素将被隔离
const element = document.getElementById('ele')
// mode: 'open' 表示可被读取 Shadow DOM 内部属性,如果是 closed 表示对外不可见,读取结果是 null
const shadowRoot = element.attachShadow({mode: 'open'});
shadowRoot.appendChild(templateContent.cloneNode(true));

Custom Element

有了 template 和 shadow DOM,其实就已经能安全复用组件了。但是总不能每次都引入一大坨东西进去,Custom Element 就是把组件内容封装成一个标签,如 <my-div></my-div> 这样可以方便引用

Custom Element 需要继承自 HTMLElement 类,并且需要通过 window.customElements.define 来声明标签名

class UserCard extends HTMLElement {constructor() {super();
    
    // 开启 shadow DOM
    var shadow = this.attachShadow({ mode: 'closed'} );
    
    // 获取模版内容
    var templateElem = document.getElementById('userCardTemplate');
    var content = templateElem.content.cloneNode(true);
    
    // 设置模板中的 DOM 节点
    content.querySelector('img').setAttribute('src', this.getAttribute('image'));
    content.querySelector('.container>.name').innerText = this.getAttribute('name');
    content.querySelector('.container>.email').innerText = this.getAttribute('email');
    
    // 插入 shadow DOM
    shadow.appendChild(content);
  }
}

// 声明标签
window.customElements.define('user-card', UserCard);

« 常见的排序

JS 常问手写题汇总 »

Evan的博客

Evan 的博客 - 非典型码农,bug永动机
Instagram Weibo GitHub Email RSS

Categories

面试 原理笔记 项目实践 其他 JS Vue 性能优化 算法 计算机网络

Recent Posts

  • 从 HTTP 发展历程重学计算机网络
  • 应届前端的逆袭(中)
  • 应届前端的逆袭(上)
  • 应届前端的逆袭(下)
  • 前端面经复盘

Copyright © 2015 Powered by MWeb,  Theme used GitHub CSS.