详解vue3.2新增的defineCustomElement底层原理

 更新时间:2021年08月27日 09:35:53   作者:artist  
本文主要介绍了vue3.2新增的defineCustomElement底层原理,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

Web Components

Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。

相当于是浏览器原生的定义组件的方式,不用通过vue或者react这些框架实现组件的定义

customElements

概述

customElements 是Window对象上的一个只读属性,接口返回一个CustomElementRegistry 对象的引用,可用于注册新的 custom elements,或者获取之前定义过的自定义元素的信息。

HTMLTemplateElement 内容模板元素

概述

HTML内容模板(<template>)元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后可以(原文为 may be)在运行时使用JavaScript实例化。
将模板视为一个可存储在文档中以便后续使用的内容片段。虽然解析器在加载页面时确实会处理<template>元素的内容,但这样做只是为了确保这些内容有效;但元素内容不会被渲染。

常用属性

content 获取DocumentFragment 元素片段的内容
相当于通过document.createDocumentFragment()创建的元素片段,

  <!-- 定义template片段 -->
  <template id="element-template">
    <div>test-template</div>
  </template>

  <script>
    /* 获取template片段 */
    const ele = document.getElementById('element-template')
    ele.content instanceof DocumentFragment  //true

    /* 通过createDocumentFragment创建html片段*/
    const div = document.createDocumentFragment('div')
    div instanceof DocumentFragment    //true

    /* 结论 */
    // 定义在html上的template获取它的content相当于和通过createDocumentFragment创建的html片段是一个东西
  </script>

ShadowRoot

概述

Shadow DOM API 的 ShadowRoot 接口是一个 DOM 子树的根节点, 它与文档的主 DOM 树分开渲染。
你可以通过使用一个元素的 Element.shadowRoot 属性来检索它的参考,假设它是由 Element.attachShadow() 创建的并使 mode 设置为 open.

通过 Element.attachShadow()挂载影子DOM

完整的演示代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <test-shadow-root></test-shadow-root>

  <template id="temEle">
    <style>
      .main{
        color: #f00;
      }
    </style>
    <div class="main">
      我是template片段
      <!-- 使用插槽 -->
      <slot name="header"></slot>
    </div>
  </template>
  <test-template-ele>
    <!-- 定义插槽 -->
    <style>
      .slot{
        color: rgb(87, 28, 223);
      }
    </style>
    <div class="slot" slot="header">我是slot</div>
  </test-template-ele>

  <!-- 生命周期测试 -->
  <div id="moveDiv">
    <button id="add">添加</button>
    <button id="update">更新</button>
    <button id="move">移动</button>
    <button id="remove">删除</button>
  </div>

  <!-- 通过is挂载 -->
  <div is="test-is-com">
    <div>AAA</div>
  </div>


  <script>
    /* 自定义web-components */
    customElements.define('test-shadow-root', class extends HTMLElement {
      /* 当test-shadow-root组件被挂载到DOM上时,执行构造函数 */
      constructor() {
        super()
        const shadowRoot = this.attachShadow({mode: 'open'}) //给指定的元素挂载影子DOM
        // 当执行 this.attachShadow()方法时,shadowRoot被挂载构造函数中,可以通过this访问
        // mode open shadow root元素可以从js外部访问根节点
        // mode closed  拒绝从js外部访问关闭的shadow root节点
        // console.log('执行', this)
        const div = document.createElement('div')
        div.textContent = '我是div的内容'
        // shadowRoot.appendChild()
        // console.log('this', this.shadowRoot)
        shadowRoot.appendChild(div)
        // this.shadowRoot === shadowRoot  true
      }
    })

    /* 通过template自定义HTMLTemplateElement */
    customElements.define('test-template-ele', class extends HTMLElement {
      constructor() {
        super()
        const temEle = document.querySelector('#temEle')
        const templateContent = temEle.content //获取html片段
        // console.log('AA', templateContent instanceof DocumentFragment) //true
        // templateContent
        // 创建影子DOM,用于挂载template的片段
        const shadowRoot = this.attachShadow({mode: 'open'})
        // console.log('shadowRoot', shadowRoot)
        shadowRoot.appendChild(templateContent)
      }
    })

    /* 通过js创建web-组件,测试生命周期函数 */
      class LifeCycle extends HTMLElement {
        static get observedAttributes() {  //必须添加组件上的属性,才能触发attributeChangedCallback
          return ['c', 'l'];
        }

        constructor() {
          super()
          const shadowRoot = this.attachShadow({mode: 'open'})
          const div = `<div>
            <heaher>我的头</header>
            <div>内容</div>
            <footer>尾部</footer>
          </div>`
          shadowRoot.innerHTML = div
        }

        connectedCallback() {  //添加时,执行
          console.log('添加')
        }
        disconnectedCallback() {//删除时,执行
          console.log('disconnectedCallback')
        }
        adoptedCallback() {
          console.log('adoptedCallback')
        }
        attributeChangedCallback() {  //属性被改变时
          console.log('attributeChangedCallback')
        }
      }

      customElements.define('test-life-cycle', LifeCycle)

      const add = document.querySelector('#add')
      const update = document.querySelector('#update')
      const move = document.querySelector('#move')
      const remove = document.querySelector('#remove')
      const moveDiv = document.querySelector('#moveDiv')
      let testLifeDom = null

      function random(min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
      }

      add.addEventListener('click', () => {
        testLifeDom = document.createElement('test-life-cycle')  //创建上面定义的自定义组件
        // console.log('testLifeDom', testLifeDom)
        document.body.appendChild(testLifeDom);
        testLifeDom.setAttribute('l', '100');
        testLifeDom.setAttribute('c', 'red');
        console.log('add', testLifeDom)
      })

      update.addEventListener('click', () => {
        const div = '<div>更新后</div>'
        // console.log('update', testLifeDom.shadowRoot.innerHTML)
        testLifeDom.shadowRoot.innerHTML = div
        testLifeDom.setAttribute('l', random(50, 200));
        testLifeDom.setAttribute('c', `rgb(${random(0, 255)}, ${random(0, 255)}, ${random(0, 255)})`);
      })

      move.addEventListener('click', () => {
        console.log('moveDiv', moveDiv)
        moveDiv.appendChild(testLifeDom)
      })

      remove.addEventListener('click', () => {
        console.log('remove')
        document.body.removeChild(testLifeDom);
      })

      /* 通过is挂载组件 */

      customElements.define('test-is-com', class extends HTMLDivElement {
        constructor() {
          super()
          console.log('挂载', this.innerHTML)
          // 通过挂载,this,就是当前被挂载的元素实例,通过这种方式,可以实现一些操作
        }
      }, {extends: 'div'})

  </script>
</body>
</html>

到此这篇关于详解vue3.2新增的defineCustomElement底层原理的文章就介绍到这了,更多相关vue3.2 defineCustomElement内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue3中的toRef和toRefs的区别和用法示例小结

    Vue3中的toRef和toRefs的区别和用法示例小结

    toRef和toRefs可以用来复制reactive里面的属性然后转成 ref,它保留了响应式,也保留了引用,也就是你从 reactive 复制过来的属性进行修改后,除了视图会更新,原有 ractive 里面对应的值也会跟着更新,本文介绍Vue3中toRef和toRefs的区别和用法,需要的朋友可以参考下
    2024-08-08
  • vue解决this.$refs.xx在mounted中获取DOM元素为undefined问题

    vue解决this.$refs.xx在mounted中获取DOM元素为undefined问题

    这篇文章主要介绍了vue解决this.$refs.xx在mounted中获取DOM元素为undefined问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 在 Vue 中使用 iframe 嵌套页面的步骤

    在 Vue 中使用 iframe 嵌套页面的步骤

    这篇文章主要介绍了在 Vue 中使用 iframe 嵌套页面,使用 iframe 技术可以实现多个页面之间的数据传递和交互,提高了网站的整体性能和用户体验,需要的朋友可以参考下
    2023-05-05
  • 手写Vue弹窗Modal的实现代码

    手写Vue弹窗Modal的实现代码

    这篇文章主要介绍了手写Vue弹窗Modal的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • Vue 实现接口进度条示例详解

    Vue 实现接口进度条示例详解

    这篇文章主要介绍了Vue实现接口进度条功能,在请求数据的过程中,需要添加监听函数来监测数据请求的过程变化,并更新组件相应的属性和界面元素,本文结合实例代码详细讲解,需要的朋友可以参考下
    2023-04-04
  • Vue3 elementUI如何修改el-date-picker默认时间

    Vue3 elementUI如何修改el-date-picker默认时间

    这篇文章主要介绍了Vue3 elementUI如何修改el-date-picker默认时间,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • vue打包后的线上部署Apache、nginx全过程

    vue打包后的线上部署Apache、nginx全过程

    这篇文章主要介绍了vue打包后的线上部署Apache、nginx全过程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • vue实现简单计算商品价格

    vue实现简单计算商品价格

    这篇文章主要为大家详细介绍了vue实现简单计算商品价格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-09-09
  • vue实现动态显示与隐藏底部导航的方法分析

    vue实现动态显示与隐藏底部导航的方法分析

    这篇文章主要介绍了vue实现动态显示与隐藏底部导航的方法,结合实例形式分析了vue.js针对导航隐藏与显示的路由配置、事件监听等相关操作技巧,需要的朋友可以参考下
    2019-02-02
  • 如何在Vue3中创建动态主题切换功能

    如何在Vue3中创建动态主题切换功能

    在Vue3中实现动态主题切换功能,通过明亮和暗色主题的选择,提供个性化使用体验,使用setup语法糖优化代码,通过创建组件和响应式变量来进行主题切换,并动态加载CSS文件
    2024-09-09

最新评论