zoukankan      html  css  js  c++  java
  • web components折腾记

    了解web组件化开发是最初是从了解reactjs开始,但是一直对框架有抵触情绪,另外喜欢不走寻常路,喜欢简单好用的东西,越简单越好,进而开始研究web components。
    web components这个技术因为太新,浏览器的支持还不完善,还没流行,也没啥中文资料参考,就是官方英文网站貌似都没看到有文档说明,折腾起来甚是费劲。
    最开始对web components技术还很懵懂,只知道它由几个子技术组成,包括Custom Elements和Shadow DOM还有HTML Imports等等,于是拿着Custom Elements就开始半知半解的写代码,以为发现了新大陆,惊喜万分,因为没有了解Shadow DOM,所以对自定义标签里面的内容封装完全是自己在胡乱拼凑,里面对元素子节点的处理甚是丑陋,甚至还专门自己包装innerHTML的获取和设置。在自定义元素里面只包含原生元素的时候,勉强工作正常,但是一旦两个以上的自定义元素嵌套使用,就开始失灵了,存在解析之后重复嵌套的问题,最后dom结构相当冗余重复,甚至还萌生出自己写代码解析dom结构的想法。从惊喜万分变成信心被重挫。
    自从买了本书认真啃一天之后,对它有了重新的认识,惊喜回来了,感叹技术的最高境界就是制定标准。
    这里做一下简单的备忘:
    组成web components技术的4部分:模板元素(template标签)、自定义元素(Custom Elements)、影子节点(Shadow DOM)还有html引入(HTML Imports)。
    template标签具有惰性,本身不会被html解析影响文档,只有它的结构被附加到真实的节点上才会影响文档,里面可以写style 还有script,style里面的css不会影响布局,script里面的脚本不会被执行,并且因为惰性,只能是内联的,不能是外部引入的。template标签对应的js对象模型有个content属性,是包含所有子节点的DocumentFragment对象。
    自定义元素通过document.createElement方法来创建自定义的元素,第一个参数是元素名字,w3c规范规定必须以减号分隔,防止和原生的元素名冲突,第二个元素时元素配置对象,可以指定元素的原型(一般继承自HTMLElement)和元素各个生命周期(createdCallback、attachedCallback、detachedCallback、attributeChangedCallback)的行为。
    影子节点是重点,通过节点的createShadowRoot方法创建影子根,这个虚拟的节点里面所有内容都不会在主文档里面出现,和主文档隔离,里面的结构、样式完全不影响主文档,id都可以和主文档节点中的id重名。另外还有个关键的技术,把自定义节点的内容通过content标签映射到影子根里面。
    html引入使引入组件不再麻烦,传统的引入需要单独引入css和js,html引入用link标签直接引入html,一个标签就可以引入一个组件,不管你又多少css和js文件。如此一来,感觉webpack等打包程序将来可能都用不上了,用不着费尽心力的把css处理成js文件。link标签一旦引入的是html文件,那么它的js对象模型里面就有个import属性,指向它引入的html的document对象。引入文档里面的css和js和主文档是共享同一个作用域的,dom节点树不是,是独立的。所以在引入html中写css和js要注意命名空间的干净。

    这样一来就可以随心所欲的写自定义元素了,附上一个例子:
    主文件(web components.html):

    <!DOCTYPE html>
    <html lang="zh">
    <head>
    <title>web components demo</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Cache-Control" content="no-cache">
    <meta http-equiv="Expires" content="0">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
    <link rel="import" href="widget.html"/>
    </head>
    <body>
    <center-middle><o-loading>加载中</o-loading></center-middle>
    </body>
    </html>

    引入文件widget.html:

    <template id="center-middle">
        <style>
            :host {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background-color: rgba(0,0,0,0.4);
                z-index: 1;
                display: block;
            }
        </style>
        <table style="100%;height: 100%;border-collapse: collapse;table-layout: auto;border: 0;">
            <tr style="100%;height: 100%;">
                <td style="100%;height: 100%;text-align: center;overflow: hidden;">
                    <!-- text-align: initial 还原成默认的值 -->
                    <div style="display:inline-block;vertical-align:middle;text-align: initial;">
                        <content></content>
                    </div>
                </td>
            </tr>
        </table>
    </template>
    <template id="o-loading">
        <style>
            @-webkit-keyframes loading {
                0% {
                    -webkit-transform: rotate(0deg);
                }
                100% {
                    -webkit-transform: rotate(360deg);
                }
            }
            @keyframes loading {
                0% {
                    transform: rotate(0deg);
                }
                100% {
                    transform: rotate(360deg);
                }
            }
            .loading {
                box-sizing: border-box;
                border: 0.5em solid #666;
                border-top-color: #fff;
                border-radius: 2em;
                width: 4em;
                height: 4em;
                display: inline-block;
                -webkit-animation: loading 1s linear 0.00001ms infinite normal;
                animation: loading 1s linear 0.00001ms infinite normal;
            }
        </style>
        <i class="loading">
        </i>
        <center>
            <content></content>
        <center>
    </template>
    <script>
        var parentDocument = document;
        var importDocument = parentDocument.currentScript.ownerDocument;
        parentDocument.registerElement("center-middle",{
            "prototype":Object.create(HTMLElement.prototype,{
                "createdCallback": {
                    "value":function (){
                        var content = parentDocument.importNode(importDocument.getElementById("center-middle").content,true);
                        this.createShadowRoot().appendChild(content);
                    }
                }
            })
        });
        parentDocument.registerElement("o-loading",{
            "prototype":Object.create(HTMLElement.prototype,{
                "createdCallback": {
                    "value":function (){
                        var content = parentDocument.importNode(importDocument.getElementById("o-loading").content,true);
                        this.createShadowRoot().appendChild(content);
                    }
                }
            })
        });
    </script>

    效果图:

    一个居中的loading

    兼容性测试:
    检测浏览器对各种技术的支持情况的代码:
    document.write("template:" + ("content" in document.createElement("template"))+"<br/>");
    document.write("createShadowRoot:" + ("createShadowRoot" in HTMLElement.prototype)+"<br/>");
    document.write("registerElement:" + ("registerElement" in document)+"<br/>");
    document.write("import:" + ("import" in document.createElement("link"))+"<br/>");
    检测结果:
    谷歌chrome肯定是不用测了,自己主导的标准,支持没得说,移动端上,QQ和微信上都支持,腾讯的产品自己有定制内核,我的android手机(4.2版本)自带的浏览器不支持,我用hbuilder开发的app(webview)在手机上也不支持,但是通过web components项目封装的js(为了兼容低版本浏览器写的库,高版本chrome不用引入)可以支持自定义元素,影子节点和html引入的js有报错不支持。

  • 相关阅读:
    自己总结的Java归并排序代码
    SpringDataJpa
    多态
    向上转型向下转型
    python面向对象装饰器
    Apache
    git
    μWSGI
    虚拟环境
    软件仓库(持续更新中)
  • 原文地址:https://www.cnblogs.com/omega8/p/5851440.html
Copyright © 2011-2022 走看看