zoukankan      html  css  js  c++  java
  • WebComponent魔法堂:深究Custom Element 之 从过去看现在

    前言

     说起Custom Element那必然会想起那个相似而又以失败告终的HTML Component。HTML Component是在IE5开始引入的新技术,用于对原生元素作功能"增强",虽然仅仅被IE所支持,虽然IE10也开始放弃它了,虽然掌握了也用不上,但还是不影响我们以研究的心态去了解它的:)

    把玩HTML Component

     HTML Component简称HTC,它由定义和应用两部分组成。定义部分写在.htc文件中(MIME为text/x-component),由HTC独有标签、JScript和全局对象(element,window等)组成;而应用部分则写在html文件中,通过CSS的behavior规则附加到特定的元素上。

    定义部分

    HTC独有标签
    PUBLIC:COMPONENT, 根节点.
    PUBLIC:PROPERTY, 定义元素公开自定义属性/特性.
     属性
    NAME,html文件中使用的属性名
    INTERNALNAME,htc文件内使用的属性名,默认与NAME一致
    VALUE,属性默认值
    PUT,setter对应的函数名
    GET,getter对应的函数名
    PUBLIC:EVENT, 定义元素公开自定义事件.
     属性
    NAME,公开事件名称,如onheadingchange
    ID,htc内使用的事件名称,如ohc.然后通过ohc.fire(createEventObject())来触发事件
    PUBLIC:ATTACH,订阅事件
     属性
    EVENT,订阅的事件名称,如onheadingchange
    ONEVENT,事件处理函数体,如headingchangehandler()
    FOR,事件发生的宿主(element,document,window,默认是element)
    PUBLIC:METHOD, 定义元素公开方法
     属性
    NAME,html文件中使用的方法名
    INTERNALNAME,htc文件内使用的方法名,默认与NAME一致。在JScript中实现具体的方法体
    PUBLIC:DEFAULTS,设置HTC默认配置
    HTC生命周期事件
    ondocumentready, 添加到DOM tree时触发,在oncontentready后触发
    oncontentready, 添加到DOM tree时触发
    ondetach, 脱离DOM tree时触发, 刷新页面时也会触发
    oncontentsave, 当复制(ctrl-c)element内容时触发
    HTC全局对象
    element, 所附加到的元素实例
    runtimeStyle,所附加到的元素实例的style属性
    document,html的文档对象
    HTC全局函数
    createEventObject(),创建事件对象
    attachEvent(evtName, handler), 订阅事件.注意:一般不建议使用attachEvent来订阅事件,采用<PUBLIC:ATTACH>来订阅事件,它会自动帮我们执行detach操作,避免内存泄露.
    detachEvent(evtName[, handler]), 取消订阅事件

    应用部分

    引入.htc
    1.基本打开方式

    <style>
      css-selector{
        behavior: url(file.htc);
      }
    </style>
    

    2.打开多个

    <style>
      css-selector{
        behavior: url(file1.htc) url(file2.htc);
      }
    </style>
    

     可以看到是通过css-selector匹配元素然后将htc附加到元素上,感觉是不是跟AngularJS通过属性E指定附加元素的方式差不多呢!
    3.自定义元素

    <html xmlns:x>
        <head>
            <style>
                x:alert{
                    behavior: url(x-alert.htc);
                }
            </style>
        </head>
        <body>
            <x:alert></x:alert>
        </body>
    </html>
    

     自定义元素则有些麻烦,就是要为自定义元素指定命名空间x:alert,然后在html节点上列出命名空间xmlns:x。(可多个命名空间并存<html xmlns:x xmlns:y>)
     下面我们来尝试定义一个x:alert元素来看看具体怎么玩吧!

    自定义x:alert元素

    x-alert.htc

    <PUBLIC:COMPONENT>
        <PUBLIC:ATTACH EVENT="oncontentready" ONEVENT="onattach()"></PUBLIC:ATTACH>
        <PUBLIC:ATTACH EVENT="ondetach" ONEVENT="ondetach()"></PUBLIC:ATTACH>
    
        <PUBLIC:METHOD NAME="close"></PUBLIC:METHOD>
        <PUBLIC:METHOD NAME="show"></PUBLIC:METHOD>
    
        <PUBLIC:PROPERTY NAME="heading" PUT="putHeading" SET="setHeading"></PUBLIC:PROPERTY>
        <PUBLIC:EVENT NAME="onheadingchange" ID="ohc"></PUBLIC:EVENT>
        <PUBLIC:ATTACH EVENT="onclick" ONEVENT="onclick()"></PUBLIC:ATTACH>
    
        <script language="JScript">
            /* 
             * private region
             */
            function toArray(arrayLike, sIdx, eIdx){
               return Array.prototype.slice.call(arrayLike, sIdx || 0, eIdx || arrayLike.length)
            }
            function curry(fn /*, ...args*/){
                var len = fn.length
                  , args = toArray(arguments, 1)
    
                return len <= args.length 
                       ? fn.apply(null, args.slice(0, len)) 
                       : function next(args){
                            return function(){
                                var tmpArgs = args.concat(toArray(arguments))
                                return len <= tmpArgs.length ? fn.apply(null, tmpArgs.slice(0, len)) : next(tmpArgs)
                            }
                         }(args)
            }
            function compile(tpl, ctx){
                var k
                for (k in ctx){
                    tpl = tpl.replace(RegExp('${' + k + '}'), ctx[k]
                }
                return tpl
            }
    
            // 元素内部结构
            var tpl = '<div class="alert alert-warning alert-dismissible fade in">
                            <button type="button" class="close" aria-label="Close">
                              <span aria-hidden="true">&times;</span>
                            </button>
                            <div class="content">${raw}</div>
                          </div>'
            var getHtml = curry(compile, tpl)
            /* 
             * leftcycle region
             */
            var inited = 0, oHtml = ''
            function onattach(){
                if (inited) return
    
                oHtml = element.innerHTML
                var ctx = {
                    raw: heading + oHtml
                }
                var html = genHtml(ctx)
                element.innerHTML = html
    
                runtimeStyle.display = 'block'
                runtimeStyle.border = 'solid 1px red'
            }
            function ondetach(){}
            /* 
             * public method region
             */
            function show(){
                runtimeStyle.display = 'block'
            }
            function close(){
                runtimeStyle.display = 'none'
            }
            /*
             * public property region
             */
            var heading = ''
            function putHeading(val){
                if (heading !== val){
                    setTimeout(function(){
                        var evt = createEventObject()
                        evt.propertyName = 'heading'
                        ohc.fire(evt)
                    }, 0)
                }
                heading = val
            }
            function getHeading(){
                return heading
            }
    
            /*
             * attach event region
             */
            function onclick(){
                if (/^s*closes*$/.test(event.srcElement.className)){
                    close()
                }
            }
        </script>
    </PUBLIC:COMPONENT>
    

    引用x:alert

    index.html

    <html xmlns:x>
    <head>
        <title></title>
        <style>
            x:alert{
                behavior: url(x-alert.htc);
            }
        </style>
    </head>
    <body>
        <x:alert id="a" heading="Hello world!"></x:alert>    
        <script language="JScript">
            var a = document.getElementById('a')
            a.onheadingchange = function(){
                alert(event.propertyName + ':' + a.heading)
            } 
            // a.show()
            // a.close()
            // document.body.appendChilid(document.createElement('x:alert'))
        </script>
    </body>
    </html>
    

    感受

     在写HTC时我有种写C的感觉,先通过HTC独有标签声明事件、属性、方法等,然后通过JScript来提供具体实现,其实写Angular2时也有这样的感觉,不过这里的感觉更强烈一些。
    这里先列出开发时HTC给我惊喜的地方吧!

    1. htc文件内的JScript代码作用域为htc文件本身,并不污染html文件的脚本上下文;
    2. 带属性访问器的自定义属性大大提高我们对自定义属性的可控性;

    然后就是槽点了

    1. htc行为与元素绑定分离,好处是灵活,缺点是非自包含,每次引入都要应用者自己绑定一次太啰嗦了。我觉得Angular通过属性E绑定元素既灵活又实现自包含才是正路啊!
    2. API有bug。如ondocumentready事件说好了是html文档加载完就会触发,按理只会触发一下,可实际上它总会在oncontentready事件后触发,还有fireEvent的API根本就没有,只能说继承了IE一如既往的各种坑。
    3. 通过runtimeStyle来设置inline style,从而会丢失继承、层叠等CSS特性的好处;
    4. 自定义元素内部结构会受到外部JS、CSS的影响,并不是一个真正闭合的元素。

    总结

     很抱歉本文的内容十分对不住标题所述,更全面的观点请查看徐飞老师的《从HTML Components的衰落看Web Components的危机》。假如单独看Custom Element,其实它跟HTML Component无异,都没有完整的解决自定义元素/组件的问题,但WebComponent除了Custom Element,还有另外3个好伙伴(Shadow DOM,template,html imports)来一起为自定义元素提供完整的解决方案,其中Shadow DOM可谓是重中之重,后续继续研究研究:)
     尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/5987853.html _肥仔John

    感谢

    《从HTML Components的衰落看Web Components的危机》
    HTC Reference
    Using HTML Components to Implement DHTML Behaviors in Script

  • 相关阅读:
    sed替换
    【工作提升】好员工秘诀十二条
    【工作提升】怎么样做工作才有好的效果
    vim删除空行和注释
    Java实现几种常见排序方法
    数据背后的二八定律,揭示程序员担忧的主要问题
    【新闻】国内第一本介绍程序员996现象的技术图书上市,“再谈工作996,生病ICU”一文受关注
    10年磨一剑:《SOD框架“企业级”应用数据架构实战》一书正式上市
    程序员肺被切掉一块还得去加班... 再谈“工作996,生病ICU”
    win10编译pdfium
  • 原文地址:https://www.cnblogs.com/fsjohnhuang/p/5987853.html
Copyright © 2011-2022 走看看