然后写的时候遇到各种问题,使用许多独立组件

2019-10-07 07:55栏目:美高梅开户
TAG:

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

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

原文出处: 司徒正美   

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

比如以下一个页面:

美高梅开户送58元官网 1

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

美高梅开户送58元官网 2
美高梅开户送58元官网 3
美高梅开户送58元官网 4
美高梅开户送58元官网 5

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

美高梅开户送58元官网 6

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

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

美高梅开户送58元官网 7
美高梅开户送58元官网 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 收藏 评论

美高梅开户送58元官网 9

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

内容提要

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

        现在看来,我觉得学习jquery反而使我走了弯路,用jquery是比较方便,也不用考虑兼容性问题了,而且调用非常简单优雅,但是,反而我对原生js感觉越来越陌生了,也导致了后面感觉完全离不开jquery了,想去写一个XXX组件,想了一下,思路有了,然后写的时候遇到各种问题,然后就又回到jquery了。

什么是组件?

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

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

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

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

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

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

为什么要构建组件?

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

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

模块

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

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

        到寒假的时候,决定自己的毕设不使用现在成熟的JS库,反而自己来写一个不完善的库,这样学习的更多,当然,也比较费时间。

高内聚

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

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

        开始写的感觉真是痛苦啊,什么都不懂,所以就去看了看tangram的源码,为什么看tangram呢,其实原因比较搞笑,当时校招的时候我面试百度前端,被刷掉了,当时面试官让我看看它们百度使用的JS库tangram,我就想看看它们那个库到底有什么了不起的。。。

可重用

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

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

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

可互换

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

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

var tp = tp || {};

可组合

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

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

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

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

现在就开始构建组件

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

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

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

       由于这个库完全是为毕设做的,所以这里面的很多文件都是为实现毕设的某些功能而写的。

组件间是如何通信的?

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

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

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

      我采用的结构是core+组件的方式,tp.core.js(压缩后为tp.core-min.js),而其他的组件每个组件一个文件,而组件之间可能存在依赖关系,这种依赖关系就通过AMD解决。

示例组件my-avatar

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

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

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

AngularJS

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

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

查看代码演示: 

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

tp.use(["tp.a","tp.b"],function(a,b) {

})

Ember

框架与库的争论旷日持久,总的来说框架是强制你按某种方式做事情,所以它是邪恶的。很显然,Angular是个框架,而Ember的作者,Yehuda Katz和Tom Dale也很乐意把Ember看作框架。

Ember 有对它称作组件的内建支持。Ember Components背后的理念是尽可能的向Web Components看齐,当浏览器支持允许时,就可以很方便地迁移到Web Components中。

查看代码演示: 

上面的例子中使用了 handlebars 做模板,所以元素的定义不是同一种语法。

      使用use方式,它会自动去下载tp.a.js和tp.b.js,下载完成之后,执行回调函数。

React

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

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

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

查看代码演示: 

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

      同样,在tp.a.js中,也不能使用普通的JS的写法了,而要使用:

未来:Web Component和其他

Web Component才是未来!正如名字所表示的那样,他们承诺将带来可以将功能封装成组件的浏览器原生支持。

我将简单展示Web Component并且演示我们现在可以如何使用它。更加深入的内容请参考本文末尾的 “外部资源” 一节。

他们提供的功能包括:

 

自定义元素

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

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

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

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

define("tp.a",["tp.c","tp.d"],function(c,d) {
   tp.modules.add("tp.a",function() {

    });
});

Shadow DOM

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

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

     define的第一个参数是该组件的名字(需要唯一,所以我还是按照命名空间的方式写的),第二个参数是这个组件所依赖的组件,第三个参数是回调函数,也就是当依赖的组件下载完成之后,回调执行,而tp.modules.add就可以将这个模块加载到整个库中,这样的话才能使用tp.use调用。

HTML导入

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

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

      这种方式我在tangram中没有看到,我是看了淘宝的KISSY之后学习到的,也就是所谓的AMD(异步模块定义)。

模板

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

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

      暂时AMD的实现方式是通过setInterval,但是即将被重构美高梅开户送58元官网 10

Platform.js

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

美高梅开户送58元官网 11

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

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

美高梅开户送58元官网 12

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

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

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

查看代码演示: 

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

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

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

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

Polymer

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

美高梅开户送58元官网 13

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

查看代码演示: 

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

      然后就是事件了,事件是一个比较恼火的事情,东西比较多,我把它放在了tp.event这个空间中。

X-Tag和Brick

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

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

美高梅开户送58元官网,查看代码演示:

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

      首先是添加和移除事件监听器,由于IE和非IE采用的方式不一样,IE采用attachEvent和detechEvent,非IE采用addEventListener和removeEventListener,而且IE只支持冒泡(从当前元素冒泡到根元素),而非IE支持冒泡和捕获(从根元素捕获到当前元素)。最开始我是这样做的:

总结

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

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

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

版权声明:本文由美高梅开户送58元官网发布于美高梅开户,转载请注明出处:然后写的时候遇到各种问题,使用许多独立组件