likes
comments
collection
share

实测Web Components代替Vue?好像还差点意思

作者站长头像
站长
· 阅读数 19

前沿

先说说为什么要实测下Web Components。都知道现在其实有Vue、React很成熟框架,那有没有认真考虑过为什么要使用框架呢,其实最终目的就只是减少工作量,框架可以减少重复的代码,但框架也引进了不少原生不支持的东西,增加了心智负担。当看到Web Components我就在想能不能尝试替代下Vue,毕竟Vue最大的特点就是组件封装复用。

Web Components构成

从构成来看和vue的模版非常的相似,主要以下三部分的组成:

  • Custom element(自定义元素) :一组 JavaScript API,允许你定义 custom elements 及其行为,然后可以在你的用户界面中按照需要使用它们。
  • Shadow DOM(影子 DOM) :一组 JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 分开呈现)并控制其关联的功能。通过这种方式,你可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
  • HTML template(HTML 模板):  <template> 和 <slot> 元素使你可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。

使用

网上博客或者教程讲的都比较八股文,很多点都没有提及。先讲基本使用再讲一些可能你没有思考的点。

自定义组件 WebCompent.js

class ExtendP extends HTMLElement {

    constructor() {
        super()
        // 创建模版
        const template = document.createElement('template')
        template.innerHTML = `
        <style>
            article {
                width: 20%;
                margin: 20px auto;
                border: solid 1px gray;
                padding: 8px;
            }
    
            header {
                background: lightblue;
                color: #fff;
                font-size: 24px;
                border: solid 1px lightblue;
            }
        </style>
        <article>
            <header>
                <slot name='title'>博客标题</slot>
            </header>
            <section>
                <slot name='cont'>博客内容博客内容博客内容博客内容博客内容博客内容...</slot>
            </section>
        </article>
        `
        // 获取影子dom
        const iRoot = this.attachShadow({ mode: 'open' })
        //将模版添加到影子dom中
        iRoot.appendChild(template.content.cloneNode(true))
    }
    
    static get observedAttributes() {
        return ["color", "size"];
    }
    get size() {
        return this.getAttribute('size');
    }
    sayHello() {
        console.log('Hello customElements');
    }

    connectedCallback() {
        console.log("自定义组件添加至页面。");
        this.ready();
    }

    disconnectedCallback() {
        console.log("自定义组件从页面中移除。");
    }

    adoptedCallback() {
        console.log("自定义组件移动至新页面。");
    }

    attributeChangedCallback(name, oldValue, newValue) {
        console.log(`属性 ${name} 已变更${oldValue}${newValue}`);
    }
}

customElements.define('extend-p', ExtendP)
  1. 自定义元素必须继承HTMLElement(或者其子类)
  2. createElement('template')创建模版
  3. 使用attachShadow获取影子dom
  4. appendChild添加模版内容,使用cloneNode,避免模版污染
  5. customElements.define('extend-p', ExtendP)定义一个组件名,以便使用

最基本的自定义组件到此其实已经完成。

扩展点:

  • 最初我使用的是WebCompent.html的形式去写这个自定义组件,这样有一个好处,可以直接使用<template></template>标签去写样式和标签,这样就能支持编辑器提示、排版,而不用像上面一样只能使用字符串模版了。但在html中导入另一html做不到(link标签现在不支持直接导入html了),当然非要的话可以使用get请求加载,但个人感觉这样就失去了意义。
  • 监听属性变化必须要使用static get observedAttributes声明需要监听哪些属性,然后就能在attributeChangedCallback回掉中监听到了。
  • 也是有生命周期的不过就三个,相对简单,上面有写。
  • 也是支持插槽slot的,和vue一样使用<slot name='cont'></slot>

在html中使用

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>test</title>
    <script src="./WebCompent.js" defer></script>
</head>

<body>
    <extend-p id="extendP" size="100">
        <div slot="title">zidingyititle</div>
    </extend-p>
</body>
<script>
    const extendP = document.getElementById('extendP')
    extendP.ready = () => {
        extendP.sayHello();
        extendP.setAttribute('size', 200)
    }

    window.onload = (e) => {
        extendP.setAttribute('color', 'b')
        extendP.sayHello();
    }
</script>

</html>
  1. 引入自定义个人建议加上defer,原因后面再讲。
  2. 元素标签使用和vue基本相同,就是组件define时的名字
  3. slot="title"指定插槽名字
  4. 在vue的组件中直接 import "./components/WebCompent/WebCompent.js";就能直接像上面一样使用

使用很简单,但这里又有几个非常坑的地方:

  • 正常情况下,自定义组件内部会有业务,父组件肯定也有一些业务,需要暴露给自组件的。就像上面自定义组件中被添加到页面connectedCallback后,需要通知父组件,所以调用了this.ready()方法,这个方法是父组件定义的,ready的定义一定要早于自定义组件。如果connectedCallback执行比ready定义早就会报错,所以在1中要加个defer。
  • 还有如果要调用自定义组件,必须要自定义组件创建完成。所以上面我调用的时候定义了ready方法,等connectedCallback后执行,或者等页面onload后执行。结合上面的,定义要在前,调用要在后,父子交错,还是挺割裂的。
  • 属性监听一定要使用setAttribute去修改,直接extendP.attributes['size']=200修改是不会触发监听的。
  • slot传值没百度到,应该没有。

结尾

怎么说呢,功能上来讲也算七七八八,基本的也都有了。感觉下来单控件的实现应该不错,代码原生,不需要编译。最主要的问题是使用上还有一些割裂的感觉;如果按现在的去定义一些方法、属性,使用时会不知道从何下手(并不是代码有多难),代码提示没有,基本只能靠作者文档。

转载自:https://juejin.cn/post/7309693162368565299
评论
请登录