首页登陆欢迎您!
首页登陆 > 前端 > 最近学习JS的感悟-1

最近学习JS的感悟-1

时间:2019-12-15

自定义标签在IE6-8的困境

2015/07/20 · HTML5 · IE, 自定义标签

原文出处: 司徒正美   

或许未来前端组件化之路都是自定义标签,但这东西早在20年前,JSTL已在搞了。现在Web Component还只有webkit支持。但一个组件库,还需要一个特殊的标识它们是一块的。不过这个XML已经帮我们搞定了,使用scopeName,如”<xxx:dialog>”。在我继续往下想如何处理如何为这个标签绑定数据,与其他组件通信,管理生命周期,等等大事之前,我还有一个不得不面对的问题,就是如何兼容IE6-8!

比如以下一个页面:

图片 1

在chrome, firefox, IE11, IE11的IE6兼容模式分别如下:

图片 2
图片 3
图片 4
图片 5

我们会发现IE6下实际是多出许多标签,它是把闭标签也变成一个独立的元素节点

图片 6

这个AA:DIV标签被开膛破肚,里面子节点全部暴出来,成为其兄弟节点了。因此想兼容它,就要费点劲。有个两个情况需要考虑,1是用户已经将它写在页面上,情况同上;2是用户是将它放在字符串模版中,这个用正则搞定。不过正则要是碰上复杂的属性名,还是会晕掉。因此我还是打算使用原生的HTML parser。换言之,字符串,我还是会将它变成节点。这么办呢?!我想了许多办法,后来还是使用VML的命名空间法搞定!

我们将上面的页面改复杂点,再看看效果!

图片 7
图片 8

可以看到其套嵌关系现在完全正确,并且标签名不会大写化,也不会生成多余节点!

好了,我们再判定一下是否为自定义标签,或者准确地说,这个节点是否我们组件库中定义的自定义标签。某些情况下,一个页面可以存在多套组件库,包括avalon的,ploymer的,或者是直接用Web Component写的。

avalon的组件库将使用命名空间,这样就好区别开。在IE6-9中,判定element.scopeName是否为aa(这是组件库的命名空间,你可以改个更高大上的名字),在其他浏览器判定此元素的localName是否以aa:开头就行了!

JavaScript

function isWidget(el, uiName){ return el.scopeName ? el.scopeName === uiName: el.localName.indexOf(uiName+":") === 0 }

1
2
3
function isWidget(el, uiName){
  return   el.scopeName ? el.scopeName === uiName: el.localName.indexOf(uiName+":") === 0
}

这个难题解决后,我们就可以开搞基于自定义标签的UI库了!

1 赞 1 收藏 评论

图片 9

        写这个库,首先使用了命名空间,我比较喜欢toper,所以我首先定义了一个变量:

示例组件my-avatar

为了展示我们如何用这些库和框架构建最基本的组件,我们建立了一个带有UI,用于取回和显示用户头像的简单示例。在可能的情况下,该组件会有 my-avatar 标签,会从以下两个属性中取得头像:

  • service 允许设置一个服务。例如 twitter 或者 facebook
  • username 用于取回该用户名相对应的头像

          在使用事件代理的时候,我们经常要获取到事件的目标元素,而IE和非IE又是不一样的,所以需要单独写一个函数:

Shadow DOM

还记得iframe们吗?我们还在使用它们,是因为他们能确保组件和控件的JavaScript和CSS不会影响页面。 Shadow DOM 也能提供这样的保护,并且没有iframe带来的负担。正式的说法是:

Shadow DOM的设计是在shadow根下隐藏DOM子树从而提供封装机制。它提供了建立和保障DOM树之间的功能界限,以及给这些树提供交互的功能,从而在DOM树上提供了更好的功能封装。

         #aa这种比较简单,因为JS提供了API,也就是document.getElementById;input这种也比较好搞,因为JS有document.getElementsByTagName;但是.aa这种方式就比较纠结了,因为JS没有提供API,幸好,在一些浏览器中还是提供了API:document.getElementsByClassName,而那些没有提供这个API的就比较悲剧了,只能遍历所有节点,也就是使用document.getElementsByTagName(*):

Platform.js

但是,就像每次提到新特性一样,我们不能确定浏览器是否支持这些特性。

图片 10

截至2014年6月27日,Web Component 的浏览器支持情况

同样,我们也能通过一些神奇的兼容代码,开始使用某些Web Component所提供的功能。

图片 11

有了兼容库的Web Component支持情况

好消息是两个最先进的浏览器厂商Google和Mozilla正在努力完善兼容库 ,帮助我们使用Web Component。

以下示例展示使用platform.js后我们可以如何定义作为img元素扩展的my-avatar元素。最棒的是它能用到原生img元素的所有功能。

查看代码演示: 

点击 HTML5 Rocks Custom Elements tutorial 以查看创建自定义元素的更多信息。

注:如果你对platform.js感兴趣,也可以看看 bosonic。

原生技术的支持目的就是给我们提供相应的构建基础。所以Web Component并不是库和框架的末日信号。

        这种方式也是借鉴了tangram的写法,采用对象字面量的形式。这样所有toper定义的方法都放在了tp这个私有空间内了,比如对DOM的操作就放在tp.dom中。

X-Tag和Brick

Mozilla开发了自己的自定义元素 兼容库,叫做 X-Tag。X-Tag是一个为启用Web Component进行多项兼容的库,并即将提供对Web Component的完整支持。

以下就是使用X-Tag的 my-avatar 自定义组件,与标准文档十分类似:

查看代码演示:

Mozilla同时还创造了一个叫 Brick 的库,其中包括X-Tag,提供“一组用来方便快速构建Web应用程序的UI组件”,使用与Google的Polymer相似的方式。

var arr = new Array();
if(xxx) {
   for(var i = 0,len = arr.length ; i < len; i++) {

   }
} else {
   for(var i = 0,len = arr.length ; i < len; i++) {

   }
}

什么是组件?

软件开发是一个语义丰富(术语通常不止一个意思)的领域。很显然,这里的“组件”是一个很泛的称呼,所以有必要指明我们想要表达的,在前端Web应用的语言环境中的意思。

前端Web应用中的组件,是指一些设计为通用性的,用来构建较大型应用程序的软件,这些组件有多种表现形式。它可以是有UI(用户界面)的,也可以是作为 “服务”的纯逻辑代码。

因为有视觉上的表现形式,UI组件更容易理解。UI组件简单的例子包括按钮、输入框和文本域。不论是汉堡包状的菜单按钮(无论你是否喜欢)、标签页、日历、选项菜单或者所见即所得的富文本编辑器则是一些更加高级的例子。

提供服务类型的组件可能会让人难以理解,这种类型的例子包括跨浏览器的AJAX支持,日志记录或者提供某种数据持久化的功能。

基于组件开发,最重要的就是组件可以用来构成其他组件,而富文本编辑器就是个很好的例子。它是由按钮、下拉菜单和一些可视化组件等组成。另一个例子是HTML5上的video元素。它同样包含按钮,也同时含有一个能从视频数据流渲染内容的元素。

tp.event.on = function(element,event,fn) {
        _addEventListener(element,event,fn,false);
         };

模板

我们中的许多人已经使用像handlebars、mustache或者underscore.js中的模板这样的解决方案(就像我们在上面的Ember示例中用的一样)。Web Component通过 template元素 提供了模板的原生支持。

原生模板让你可以声明分类为“隐藏DOM”但是解析成HTML的标记片段。他们在页面加载时没有用处,但是可以在运行时实例化。他们可以 被检索到 ,但是在插入活动的DOM树前不会加载任何相关资源。

          常用的功能当然还是阻止事件冒泡以及阻止默认事件的发生,很遗憾,IE和非IE处理方式还是不一样的,比如阻止冒泡IE采用的是cancelBubble,而其他浏览器采用的是stopPropagation,所以还是需要写:

自定义元素

我们在上面关注的是用Angular、Ember和React构建 my-avatar 的例子。可能的情况下,这样的方式将以页面上或者模板上添加的自定义元素表示。Web Component包括通过自定义元素获得的原生支持 – 绝对是Web Component标准的基本组成部分。

定义新元素,包括访问元素生命周期的部分事件例如何时创建(createdCallback)、何时添加在DOM树上(attachedCallback)、何时从DOM树上分离(detachedCallback),何时元素属性改变(attributeChangedCallback(attrName, oldVal, newVal))。

自定义元素的一个重要的部分就是有能力从原有元素扩展,因而得到原有元素相应的功能。示例中我们扩展了 <img>元素 。

最终,我们所写的代码中,自定义元素正在并且倾向去做的就是将复杂的东西抽象化,让用户关注于单个组件产生的价值,从而用来构建更加丰富的功能。

       除了代码之外,工具也很重要,另一篇日志介绍JS工具的:

现在就开始构建组件

在 Caplin Systems 构建基于组件的自有应用程序时,我用到了几条原则和实践。这些原则由 BladeRunnerJS(BRJS) 开源工具集支撑。它被称作”BladeRunnerJS” 是因为我们将程序功能都封装在称作 Blades 的东西中。Blade是可以在某个应用中重用的功能特性,但是不可以在程序间重用。当功能 真的 变得更加通用的时候,我们将相应的定义移到库文件中,供各个程序间使用。特定应用中的组件(blade)和我们程序间的通用组件可以使用,我们只要找到最好满足需求的任何库和框架。

那么,现在什么库和框架能够帮助我们构建组件呢?

在决定构建应用时应使用何种技术时,只需要看看流行的 TodoMVC 网站就可以看到大量可供选择的前端库和框架。你也许会觉得任何一种方案都能用来构建基于组件的应用程序。然而,他们之中的一些方案内置了对组件的支持。其中比较有名的是AngularJS、Ember 和 React。

         从去年暑假的时候,我决定离开jquery了,jquery是一把双刃剑,开发的时候是方便,但是,作为一个初学者,我觉得这是很不利的。

模块

你可能听说过 “组件是天然模块”的说法。好吧,感谢它,我们又要解释这里的术语!

你可能会觉得“组件”的说法更加适合用来描述UI,而“模块”更适合描述提供服务的功能逻辑。而对于我来说,模块和组件意思相近,都提供组织、聚焦和封装,是与某个功能单位相关的。

      我之前写了一篇日志来实现AMD,当然,效率低下,反正大家看看就行了

AngularJS

AngularJS 可能是现在用于构建程序最流行的前端解决方案了。作为创建者的Google,重新思考HTML,考虑如何重新发明,满足如今Web开发的需要。

Angular中可以使用自定义指令定义组件。之后,你可以使用 HTML 标记声明自定义组件。

查看代码演示: 

这个例子展示了使用Angular指令的简单程度。值scope 定义了从  my-avatar 元素中取得,并且之后用来构建相应的img标签和渲染成用户头像的属性。

var _getElementsByClassName = null;
        if(document.getElementsByClassName) {
                _getElementsByClassName = function(str) {
                    var fetchedEles = document.getElementsByClassName(str),
                        eles = [];

                    for(var i = 0, len = fetchedEles.length; i < len; i++) {
                        eles.push(fetchedEles[i]);
                    }
                    return eles;
                };
        } else {
            _getElementsByClassName = function(str,openClassScan) {
                var eles = [],
                    allElements = document.getElementsByTagName("*"),
                    i;
                if(openClassScan) {
                    for (i = 0; i< allElements.length; i++ ) {
                        if (tp.dom.containClass(allElements[i],str)) {
                            eles.push(allElements[i]);
                        }
                    }
                } else {
                    for (i = 0; i< allElements.length; i++ ) {
                        if (str === allElements[i].className) {
                            eles.push(allElements[i]);
                        }
                    }
                }
                return eles;
            };
        }

高内聚

又是一个软件工程的高频词! 我们将相关的一些功能组织在一起,把一切封装起来,而在组件的例子中,就可能是相关的功能逻辑和静态资源:JavaScript、HTML、CSS以及图像等。这就是我们所说的内聚。

这种做法将让组件更容易维护,并且这么做之后,组件的可靠性也将提高。同时,它也能让组件的功能明确,增大组件重用的可能性。

       还记得我大二的时候开始接触JS,那个时候从图书馆借了N多的书籍,然后边看边用editplus写,然后遇到问题,各种DEBUG,在做项目的时候,各种兼容性问题,真是痛苦啊。由于项目需要尽快写完,所以就开始接触了jquery,还是从图书馆抱了几本书,然后下载了jquery源码,然后边看书籍边写代码,看了几章之后,觉得貌似不难,然后就从网上下载了jquery的文档,对照着文档,对其调用搞得算是比较清楚了。

Polymer

Polymer 是演示构建基于原生Web Component功能的最佳示例。它提供了精选的机制用来创建自定义的Polymer元素,并且提供了许多核心的UI组件,让你可以创建自己的应用程序。

图片 12

下面你可以看到 my-avatar 元素的简单创建过程,同时我们也得到了想要的标记。

查看代码演示: 

Google正在大力推动Polymer。请查看 Polymer getting started guide 查看更多示例。

         然后就开始下载JS的电子书,可能是自己比较浮躁吧,看书真心看不进去,我还是喜欢边看边写代码这种。写了一段时间,渐渐的觉得最开始的感觉慢慢回来了,当然,也遇到了N多的问题。

外部资源(英文)

  • Eric Bidelman – Google I/O 2014 – Polymer and Web Components change everything you know about Web development
  • Ryan Seddon – Web Directions – Web Components, The Future of Web Development
  • Addy Osmani – LXJS – Componentize The Web: Back To The Browser!
  • WebComponents.org a place to discuss and evolve web component best-practices

         这里我主要讲一下tp.dom.query,也就是查询怎么做的,首先看看常用的查询有:#aa,.aa,input。

本文由 埃姆杰 翻译。未经许可,禁止转载! 英文出处:Future Insights。

tp.event.preventDefault = function(event) {
        if(event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    };
    tp.event.stopPropagation = function(event) {
        if(event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    };

可互换

一个功能明确好组件的API能让人轻易地更改其内部的功能实现。要是程序内部的组件是松耦合的,那事实上可以用一个组件轻易地替换另一个组件,只要遵循相同的 API/接口/约定。

假如你使用GoInstant提供的实时功能服务组件,那他们这周关闭服务这样的新闻会影响到你。然而,只要提供了相同的数据同步API,你也可以自行构建使用一个 FirebaseComponent 组件或者 PubNubComponent 组件。

          感觉JS的兼容性真心很头疼啊,就比如在DOM这一块儿,为了兼容,我都做了很长时间。当然,DOM这一块儿肯定不止这么一点内容,暂时也不写了。

HTML导入

我们长时间以前就可以导入JavaScript和CSS了。 HTML导入功能提供了从其他HTML文档中导入和重用HTML文档的能力。这种简单性同时意味着可以很方便地用一些组件构建另一些组件。

最后,这样的格式很理想,适合可重用组件,并且可以用你最喜欢的包管理解决方案发布(例如: bower、 npm 或者 Component)。

       好吧,貌似又超时了,先就这样吧,感觉每次写这种日志都会耗费不少时间。

组件间是如何通信的?

在深入示例之前有必要简单地提到组件间通信的问题。如果组件之间是“独立”、“模块化”的,他们又是如何相互通信的呢?

最显而易见的答案就是让组件间相互引用并通过他们之间的API交互。这样做的问题就在于,这种做法会让组件相互依赖。短期内可能还好,一段时间以后,你在修改程序的时候程序会失控,修改一个组件就会对另一个组件产生极大的影响。决定移除一个不能带来预期价值组件可能会让你的应用程序停止工作,因为它背后会有数个组件依赖于它。

此时,解决方案是提供松耦合的,让组件之间很少或者几乎不知道彼此的方案。组件并不直接创建其他组件,在他们需要通信的时候,他们通过“接口/约定”或者通过 “服务”。我们在构建BRJS程序时考虑了很多这些方面的东西,并且使用 ServiceRegistry 访问用于组件间通讯的服务或者是Web API这样的资源。Angular和Ember采用了服务和依赖注入解决这类问题。

        然后就是浏览器判定,我是这么写的:

为什么要构建组件?

既然现在已经明白组件的意思,就看看使用组件的方法构建前端应用的好处。

      在没有写这个库之前,即使是我使用jquery,每一个JS文件我都是直接在HTML文件中使用script标签写进去的,而现在需要采用这种异步模块加载的方式,如果要使用非核心模块,那么需要:

可重用

你看到的示例组件,尤其是Web Component,更关心可重用的问题。功能明确,实现清晰,API易于理解。自然就能促进组件复用。通过构建可重用组件,我们不仅保持了 DRY(不要重复造轮子)原则,还得到了相应的好处。

这里要提醒: 不要过分尝试构建可重用组件。你更应该关注应用程序上所需要的那些特定部分。如果之后相应需求出现,或者组件的确到了可重用的地步,就花一点额外时间让组件重用。事实上,开发者都喜欢去创造可重用功能块(库、组件、模块、插件等),做得太早将会让你后来痛苦不堪。所以,吸取基于组件开发的其他好处,并且接受不是所有组件都能重用的事实。

       除了这种判定方式,还可以通过判定是否有某一个函数或某一个变量来判定,这种判定方式我忘记叫什么名字了,反正之前这种叫浏览器嗅探。

可组合

之前也讨论过,基于组件的架构让组件组合成新组件更加容易。这样的设计让组件更加专注,也让其他组件中构建和暴露的功能更好利用。

不论是给程序添加功能,还是用来制作完整的程序,更加复杂的功能也能如法炮制。这就是这种方法的主要好处。

是否有必要把所有的东西转换成组件,事实上取决于你自己。没有任何理由让你的程序由 你自己 的组件组合成你最惊叹的功能 ,乃至 最花哨的功能。而这些组件又反过来构成其他组件。如果你从这个方法中得到了好处,就想方设法地去坚持它。然而要注意的是,不要用同样的方法把事情变得复杂,你并不需要过分关注如何让组件重用。而是要关注呈现程序的功能。

         首先是ready的判定,关于这个可以看我另外一篇日志:

总结

使用基于组件的架构构建应用程序有诸多好处,你能从现有的框架中学到,也能在构建前端Web应用程序时从推荐的Web Component中学习到。

这场组件化Web王国的旅程,让我们在面临框架和工具的选择时犹豫不决。但是,Web Component会是最后的明灯!

Web Component会提供构建应用程序的原生统一的方法。现有的框架很有可能会转而使用Web Component或者说明如何与它一同使用。Ember的策略是让迁移到Web Component更加方便,而Facebook的React则是演示整合的好例子,已经有一个 ReactiveElements 演示它了。因为Angular和Polymer都是Google的项目,他们很有可能会走到一起。

        注意一下啊,由于JS变量作用域没有block,所以请不要使用下面这种:

React

React 虽然是个新人,但是却已经有很多的追随者。它由Facebook开发,并且已经全面用于Instagram的UI和部分Facebook的UI。

使用React构建组件的推荐方式是使用叫做 JSX 的东西来定义它们。这是一种“推荐在React上使用的JavaScript语法转换”。请不要因此分心。他们已经在文档中指出,这个想法就是用来帮助你在JavaScript中写出HTML标记的。

我不是说你并不可以直接在HTML中添加标签,而必须使用JSX创建自己的组件。但是,只要你定义了一个组件,你就可以使用这个组件创造其他组件。

查看代码演示: 

因此,组件使用的声明语法需要相应的HTML元素和对 React.RenderComponent 的调用。

           我认为,就这样一个功能比较简单的query就够了,不必要实现类似于jquery里面的如此复杂的查询,如果要使用它,其实也很简单,因为jquery的查询引擎sizzle已经开源,完全可以将它加入到这个库,而现在toper也是这么做的,要调用sizzle就使用:

内容提要

使用许多独立组件构建应用程序的想法并不新鲜。Web Component的出现,是重新回顾基于组件的应用程序开发模式的好时机。我们可以从这个过程中受益,了解如何使用现有技术完成目标,并且在未来做出自己的前端Web应用。

上一篇:前端浏览器缓存机制浅析 下一篇:没有了