广告位联系
返回顶部
分享到

微前端之Web组件自定义元素示例介绍

JavaScript 来源:互联网 作者:佚名 发布时间:2022-08-24 10:55:45 人浏览
摘要

我们知道的 第一:我们熟知的HTML标签有 a, p, div, section, ul, li, h2, article, head, body, strong, video, audio 等等 第二:我们知道,a标签是链接,p标签是段落,div是块级,h2是字体,strong 是粗体

我们知道的

第一:我们熟知的HTML标签有 a, p, div, section, ul, li, h2, article, head, body, strong, video, audio 等等

第二:我们知道,a标签是链接,p标签是段落,div是块级,h2是字体,strong 是粗体,video 可以播放视频,标签可以添加click等事件等等

所以,那么显然我们知道这些标签的名称,而且知道他们的默认的css样式,而且知道他们默认的js事件

由此,我们是否可以自定义标签呢,由我们自己规定名称,规定默认css样式,规定标签默认显示dom结构,规定默认事件呢?

答案当然是肯定的!!下面就介绍主角Web组件

Web组件使用

名称规范

  • 在定义它们时必须使用至少两个单词和一个连字符
  • 必须小写
  • 自定义元素不能自闭合

1

2

3

4

5

6

class UserCard extends HTMLElement {

  constructor() {

    super();

  }

}

window.customElements.define('user-card', UserCard);

组件传参数并可以写模板包括js和css

组件的样式应该与代码封装在一起,只对自定义元素生效,不影响外部的全局样式,:host伪类,指代自定义元素本身

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

<html>

    <head></head>

    <body>

        <user-card name="Marty Mcfly"></user-card>

        <template id="userCardTemplate">

          <div class="profile-picture">

            <img src="marty.png" alt="Marty Mcfly" />

          </div>

          <div class="name"></div>

          <style>

           :host {

             display: flex;

             align-items: center;

             width: 450px;

             height: 180px;

             background-color: #d4d4d4;

             border: 1px solid #d5d5d5;

             box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);

             border-radius: 3px;

             overflow: hidden;

             padding: 10px;

             box-sizing: border-box;

             font-family: 'Poppins', sans-serif;

           }

           .image {

             flex: 0 0 auto;

             width: 160px;

             height: 160px;

             vertical-align: middle;

             border-radius: 5px;

           }

        </style>

        </template>

        <script>

            class UserCard extends HTMLElement {

              constructor() {

                super();

                var templateElem = document.getElementById('userCardTemplate');

                var content = templateElem.content.cloneNode(true);

                // 使用name参数

                content.querySelector('.container>.name').innerText = this.getAttribute('name');

                this.appendChild(content);

              }

            }

            window.customElements.define('user-card', UserCard);   

        </script>

    </body>

</html>

Shadow Dom 影子节点

  • 没有开启Shadow Dom 前template的内容会直接append到user-card节点上
  • 开启Shadow Dom后,template和user-card中间多了一个节点叫shadowRoot
  • attachShadow 的mode参数有 open 和 closed 两个不同参数,区别是open时,外部能访问内部节点,closed时完全隔离
  • attachShadow 是大多数标签都支持的,比如 div,p,selection,但是a,ul,li等不支持

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<script>

    class UserCard extends HTMLElement {

      constructor() {

        super();

        var shadow = this.attachShadow( { mode: 'closed' } );   /******这行开启shadow*****/

        var templateElem = document.getElementById('userCardTemplate');

        var content = templateElem.content.cloneNode(true);

        // 使用name参数

        content.querySelector('.container>.name').innerText = this.getAttribute('name');

        shadow.appendChild(content);         /******这行template添加到shadow*****/

      }

    }

    window.customElements.define('user-card', UserCard);   

</script>

类中的构造函数和钩子函数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

class UserCard extends HTMLElement {

  // attributeChangedCallback 能监听的属性

  static get observedAttributes() {return ['name', 'url']; }

  constructor() {

    super();

    // 可以创Shadom,通过this.shadowRoot获取

    // this.attachShadow({ mode: 'open' })

  }

  connectedCallback (){

      console.log('钩子,元素append到ducument触发')

  }

  disconnectedCallback (){

      console.log('钩子,元素从document删除触发')

  }

  // 只有observedAttributes 中监听的属性name,url变化会触发下面回调

  attributeChangedCallback (attr, oldVal, newVal){

      console.log('钩子,元素属性改变时触发')

  }

}

window.customElements.define('user-card', UserCard);

姓名 何时调用
constructor 元素的一个实例被创建。对于初始化状态、设置事件监听器或创建shadow Dom。
connectedCallback 每次将元素插入 DOM 时调用。一般主要工作代码写在这里。
disconnectedCallback 每次从 DOM 中删除元素时调用。一般写清理的一些代码。
attributeChangedCallback(attrName, oldVal, newVal) 观察属性在添加、删除、更新、替换时被调用。只有属性中列出的observedAttributes属性才会收到此回调。
adoptedCallback 自定义元素已被移动到一个新的document(例如有人称为document.adoptNode(el))。

getter/setter属性和属性反射

html中attribute 和 类中property 是各自独立,想要建立映射需要手动设置

getter和setter生效

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

<html>

  <head></head>

  <body>

    <user-card id="usercard" name="Marty"></user-card>

    <script>

      class UserCard extends HTMLElement {

        _name = null;

        constructor() {

          super();

        }

        set name(value) {

          this._name = name;

        }

        get name() {

          return this._name;

        }

      }

      window.customElements.define("user-card", UserCard);

    </script>

  </body>

</html>

测试

1

2

3

4

5

document.getElementById('usercard').name = 'jack'  // 会进入setter

document.getElementById('usercard').name  // 会进入getter,返回jack

document.getElementById('usercard').getAttribute('name')  // 不会进入getter,返回Marty

document.getElementById('usercard').setAttribute('name','bob')  // 不会进入setter

document.getElementById('usercard').getAttribute('name')  // 不会进入getter,返回bob

此时,我们看到 setAttribute 和 gettAttribute 都不会触发类中的 getter 和 setter 方法,但是可以看到如果是 .name 这样的方式可以触发 ,那么改造方式如下:

方式一 重写 setAttribute :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

<html>

  <head></head>

  <body>

    <user-card id="usercard" name="Marty"></user-card>

    <user-card id="uc" name="Marty Mcfly"></user-card>

    <script>

      const rawSetAttribute = Element.prototype.setAttribute;

      class UserCard extends HTMLElement {

        _name = null;

        constructor() {

          super();

          Element.prototype.setAttribute = function setAttribute(key, value) {

            // 特定的指定name

            if (key == "name") {

              this.name = value; // 这样就能触发 setter

            }

            rawSetAttribute.call(this, key, value);

          };

        }

        set name(value) {

          debugger;

          this._name = name;

        }

        get name() {

          debugger;

          return this._name;

        }

      }

      window.customElements.define("user-card", UserCard);

    </script>

  </body>

</html>

方式二 重写 observedAttributes和attributeChangedCallback 监听 name :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

<html>

  <head></head>

  <body>

    <user-card id="uc" name="Marty Mcfly"></user-card>

    <script>

      class UserCard extends HTMLElement {

        static get observedAttributes() {

          return ["name"];

        }

        _name = null;

        constructor() {

          super();

        }

        attributeChangedCallback(attr, _oldVal, newVal) {

          if (attr == "name") {

            this.name = newVal;

          }

        }

        set name(value) {

          debugger;

          this._name = name;

        }

        get name() {

          debugger;

          return this._name;

        }

      }

      window.customElements.define("user-card", UserCard);

    </script>

  </body>

</html>

由此,可以看到组件在页面渲染时会进入setter方法,而且 setAttribute,getAttribute 均进入到setter方法

1

2

document.getElementById('usercard').setAttribute('name','bob')  // 会进入setter

document.getElementById('usercard').getAttribute('name')  // 会进入getter,返回bob

扩展原生 HTML

假设您想创建一个更高级的<button>. 与其复制<button>的行为和功能,更好的选择是使用自定义元素逐步增强现有元素。

  • 扩展现有元素的主要好处是获得其所有特性(DOM 属性、方法、可访问性)。
  • Safari浏览器兼容性待提高
  • 要扩展一个元素,您需要创建一个继承自正确 DOM 接口的类定义。前面例子都是继承HTMLElement,现在更多继承例如,按钮HTMLButtonElement,图片HTMLImageElement

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

class FancyButton extends HTMLButtonElement {

    constructor() {

        super();

        this.addEventListener('click', e => this.drawRipple(e.offsetX, e.offsetY));

    }

    drawRipple(x, y) {

        let div = document.createElement('div');

        div.classList.add('ripple');

        this.appendChild(div);

        div.style.top = `${y - div.clientHeight/2}px`;

        div.style.left = `${x - div.clientWidth/2}px`;

        div.style.backgroundColor = 'currentColor';

        div.classList.add('run');

        div.addEventListener('transitionend', e => div.remove());

    }

}

customElements.define('fancy-button', FancyButton, {extends: 'button'});

注意:扩展基础元素时,对的调用 define() 略有变化。必需的第三个参数告诉浏览器您正在扩展哪个标签

  • 调用方式一:

1

<button is="fancy-button" disabled>Fancy button!</button>

  • 调用方式二:

1

2

3

4

let button = document.createElement('button', {is: 'fancy-button'});

button.textContent = 'Fancy button!';

button.disabled = true;

document.body.appendChild(button);

  • 调用范式三

1

2

3

let button = new FancyButton();

button.textContent = 'Fancy button!';

button.disabled = true;


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://juejin.cn/post/7134675733229600799/
相关文章
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计