第三方JavaScript编程

978-7-115-39224-4
作者: [美] Ben Vinegar Anton Kovalyov
译者: 郭凯
编辑: 陈冀康

图书目录:

详情

本书由第三方应用程序开发专家编写,详细介绍了开发各种微件的技术,这些微件可以用来搜集数据以用于分析、提供有用的聚会和对话,或者实现诸如聊天和评论这样的功能。本书中的概念和实例,涵盖了这一新兴领域的很多最佳实践,而且这些是基于显示世界上百万用户数千小时的经验而得来的。

图书摘要

版权信息

书名:第三方JavaScript编程

ISBN:978-7-115-39224-4

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

• 著    [美] Ben Vinegar Anton Kovalyov

  译    郭 凯

  责任编辑 陈冀康

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

  邮编 100164  电子邮件 315@ptpress.com.cn

  网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

  反盗版热线:(010)81055315


Original English language edition, entitled Third-party__JavaScript by Ben Vinegar and Anton Kovalyov published by Manning Publications Co., 209 Bruce Park Avenue, Greenwich, CT 06830. Copyright © 2013 by Manning Publications Co.

Simplified Chinese-language edition copyright © 2015 by Posts & Telecom Press. All rights reserved.

本书中文简体字版由Manning Publications Co.授权人民邮电出版社独家出版。未经出版者书面许可,不得以任何方式复制或抄袭本书内容。

版权所有,侵权必究。


第三方JavaScript应用程序是自包含的应用组件,通常都是小脚本或插件,能够为Web站点增加功能。它们往往是由独立的组织或个人提供的,代码和文件都是来自远程的Web地址。

本书由两位第三方应用程序开发领域的专家编写完成,通过丰富的示例和讲解让读者掌握第三方Web开发的相关技术及如何开发运行在第三方环境的JavaScript代码。全书共10章,分别介绍了第三方JavaScript的定义、如何加载和执行代码、DOM渲染、第三方脚本和数据服务器之间的通信、跨窗口的消息传递方法、身份验证和Cookie、第三方应用程序的安全性、JavaScript SDK的发展、性能、测试和调试。

本书并非JavaScript语言的初级读本,适用于有一定第三方代码开发经验的开发者以及致力于研究其如何在外部环境运行的开发者们阅读。


第三方JavaScript是从一个远程Web服务的地址获得服务,并在发布者页面上独立运行的客户端代码。第三方JavaScript用于创造高度分布式的Web应用程序,例如从社交微件到数据跟踪分析,到功能齐全的嵌入式应用程序。

本书介绍了第三方JavaScript应用程序的开发,不仅告诉读者如何开发运行在第三方环境的JavaScript代码,也介绍了第三方Web开发的相关技术,包括HTML、CSS和HTTP等。本书适用于有第三方代码开发经验的开发者(例如在自己的网站上运行),也适用于希望致力于研究这些问题如何在外部环境运行的开发者们(在其他人的网站上运行)。

本书不是JavaScript编程语言的初级读本,也不介绍HTML和CSS的基本原理。本书介绍的要点包括动态脚本加载、Cookies、HTTPS以及其他中间件和高级Web开发技术。

线路图

本书包含如下章节。

第1章介绍了第三方JavaScript的定义。本章告诉读者什么是第三方JavaScript,并介绍了一些现实案例。本章以一个快速的第三方应用程序示例结束,并指出了第三方Web开发的难点。

第2章指导读者如何在一个内容提供者的网站上实际加载和执行他们的代码。描述了如何设置本地开发环境来模拟第三方开发,然后进入脚本加载最佳实践,如何从一个内容提供者的网站中提取配置变量。

第3章重点介绍DOM渲染。指导读者在开发过程中,在不能控制的内容提供商的DOM中呈现最佳实践。本章还包括使用CSS样式和iframe元素避免冲突的策略。

第4章介绍在第三方脚本和数据服务器之间的通信。首先讨论了同源策略,以及由于同源策略限制带来的跨域通信的困难。其次,重点关注在两个工作区进行跨域请求:JSONP和子域名代理。最后,介绍了跨源资源共享(Cross Origin Resource Sharing,CORS),一个新的HTML5浏览器特性,使得在现代浏览器中跨域请求成为可能。

第5章继续介绍跨窗口的消息传递方法,包括在iframe直接的数据传递方法。方法包括window.postMessage——一种HTML5提供的支持在多窗口之间传递消息的特性。其次,介绍了一系列不支持window.postMessage的浏览器。同时,介绍了easyXDM(一个开源JavaScript库),该库提供了与postMessage类似的特性,同时支持现代浏览器和老版本浏览器。

第6章是关于身份验证和Cookies。本章介绍了Cookies在第三方JavaScript中的行为,并提供了当第三方Cookies失效时的解决方案。同时,简要介绍了使用Cookies时需要注意的安全问题。

第7章讨论了第三方应用程序的安全性,涵盖了传统漏洞,包括基于JavaScript的应用程序的跨站点脚本攻击(XSS)、跨站点请求伪造(XSRF)的攻击,也包括特定于第三方应用程序的漏洞。

第8章介绍了JavaScript SDK(Software Development Kit)的发展。开发JavaScript SDK采用了前几章介绍到的技术,并封装成公共的方法。本章还演示了如何为一个基于HTTP的Web服务提供一个客户端JavaScript包装器的API。

第9章关于性能。涵盖的技术包括如何减少文件大小、如何减少应用程序发出的HTTP请求的数量。同时,也介绍了不阻塞浏览器或其他脚本的JavaScript代码的最佳实践。

第10章介绍测试和调试。本章演示了如何使用工具重写代理、如何从生产环境切换到调试应用程序代码的调试环境,并展示了如何为第三方代码编写单元测试。

编码规范和下载

所有的源代码清单使用等宽字体显示,以便与其他普通文字区分开来。代码注释中强调了许多重要的信息和概念。在某些情况下,通过项目符号标识特定的代码清单。

本书的配套源代码是在MIT许可下发布的。在出版社网站www.manning.com/Third- PartyJavaScript中提供免费下载。你也可以在GitHub查看源码,地址是http://github.com/thirdpartyjs

在线帮助

购买本书的读者可以免费访问Manning出版社运营的私有Web论坛,在该论坛中你可以发表关于本书的评论,发布技术问题,并能够获得作者以及其他读者的帮助。在该论坛上注册后,用浏览器打开www.manning.com/Third-PartyJavaScript页面。这个页面上有关于如何进入论坛、注册、能够获取哪些帮助以及论坛的规则等内容。

Manning论坛为读者提供了一个有意义的场所,是读者和读者、读者和作者之间对话的桥梁。任何作者的参与、读者的讨论都是自愿的。我们建议你试着问作者一些挑战性的问题以吸引他们的兴趣!

作者在线论坛以及讨论的档案可以从书中提到的出版商网站中获取。


本书的封面图标题是“联合指挥官”。插图来自法国19世纪版Sylvain Maréchal的第四卷——地区服饰习俗和军事制服。每个插图的刻画都很细致,并且手工着色。丰富多样的Maréchal集合生动地向我们揭示了200年前世界上不同城镇和地区的文化。彼此独立,人们讲不同的方言和语言。在街上或在乡下,仅仅根据一个人的穿着就能够很容易识别出他们生活的地方以及所从事的行业。

着装习俗自此改变了,当时地区的多样性着装风俗也逐渐消失了。现在很难分辨不同洲的居民,更不用说不同的城镇或地区了。或许我们已经由文化的多样性演变到了个人生活的多样性,当然还有更多样,快节奏的科技生活。

在很难区分不同计算机书籍的时代,Manning释放创造力并倡导用两个世纪前丰富多样的地区生活作为计算机图书封面,将人们带回图片中的生活时代。


Ben Vinegar是Disqus的一名软件工程师,Disqus是一个第三方评论平台,为超过30万的博客、在线出版物以及其他Web网站提供服务,其中包括CNN、连线杂志、每日电讯报和IGN。Ben在加入Disqus之前,在一个非常热门的Web记账应用公司(FreshBooks)担任软件开发团队的主管。

Anton Kovalyov是Mozilla的一名软件工程师,帮助Firefox浏览器编写开发者工具。他还负责JSHint——一个开源的JavaScript代码质量检查工具的开发和维护。在加入Mozilla之前,Anton曾经是Disqus的一名软件工程师,负责内嵌式评论应用的JavaScript开发工作。


作为一个第三方JavaScript的开发者,你会有很多的担忧:需要跨多个站点和平台管理并部署代码。像本书中这样深入地介绍如何编写坚实的第三方JavaScript的细节和最佳实践以前也从未有过。这可能是一个复杂的业务,因此请允许我向你介绍关于Douglas Crockford的一个故事来说明潜在的意想不到的后果,Douglas Crockford创造了一个流行很广泛的第三方脚本,以及一个名为OnlineBootyCall的站点。

JSON(JavaScript对象表示法)作为JavaScript的子集,是由Douglas设计的轻量级的数据交换语言,以文字为基础,且易于阅读。早在2005年,他编写了json.js——一个可以将JSON解析为JavaScript对象,反之也可以将JavaScript对象序列化为JSON的小型库。它深受喜爱并立即被大规模采用,但它添加了Object.prototype.toJSONString和String.prototype.parseJSON方法让很多人感到不解。

2007年10月,Douglas推出了json2.js。这并非开发者现在使用的JavaScript版本,而是Douglas自己分享出来的副本。很快,它的托管公司发邮件询问有关OnlineBootyCall.com流量很高的异常情况。Douglas在json2.js代码中包含了一段警告信息:“使用你自己的副本。从一个你不可控的服务器加载代码是很不明智的。”他添加了一个会阻塞浏览器,同步的JavaScript模态alert()提示。结果就使所有从OnlineBootyCall加载资源的页面都会弹出这则消息。

这种情况下,Douglas作为一个第三方脚本的开发者是在保护自己。但更多是因为其他原因导致的。例如,json2.js的出现部分原因是由于Douglas的Object.prototype扩展并不友好。

本书由两名在第三方JavaScript开发领域最专业的开发者编写,书中列出了当下所有的技术来帮你编写第一个初出茅庐的脚本并完成首次部署。我希望本书能够真正帮到你,并且像我一样对下一代网络感到兴奋。

PAUL IRISH

DEVELOPER RELATIONS:

GOOGLE CHROME, JQUERY

LEAD DEVELOPER:

MODERNIZR AND HTML5 BOILERPLATE


2010年2月,我接到Jason Yan的电话面试,Jason Yan是初创公司Disqus的CTO兼共同创始人。当时,Disqus虽然还是一家小公司,但是具有快速增长的评论应用程序,作为一个第三方脚本分发,并受到众多的博客作者和少数大型媒体公司的欢迎。Jason Yan计划专门聘请一位从事快速增长的客户端代码库的JavaScript工程师。

通用JavaScript面试题常常涉及一系列类、主要类型、作用域等。但是,Jason Yan采取了不同的策略。他问我以下(转述)的问题:“假设我使用了内置函数prototype,方法如Array.prototype.indexOf并将其指定为一个新值,你将如何获得其原值呢?”

我被吓懵了。这是我以前从未见过的一个问题,我也不知道答案。杰森跟我解释说他们无法控制Disqus应用程序的执行环境。在这些环境中,Disqus应用程序依靠的内置属性有时会被覆盖或被改写得支离破碎。

我不打算放弃这个问题。所以在中面时,我打开了浏览器的JavaScript控制台,并开始调试函数原型。在短短几分钟,我有了惊人的发现,发现可以使用JavaScript的delete操作符删除被修改的内置属性,浏览器将恢复原始值。

事实上Jason不知道这个解决方案。他亲自采用该方法进行调试,果然有效。我们对于这个新发现都非常兴奋。我们开始讨论Disqus当前解决这一问题的方法,而面试也从严肃的“审讯”变成兴奋讨论,我们讨论了iframes、浏览器hacks和其他脚本性能等。

当时我并不知道,但那是我第一次感受到第三方JavaScript,感受到解决只有运行在别人的Web环境时客户端应用程序才遇到的问题,感受到一些Web开发人员可能永远不会意识到的技术和实践,我完全被迷住了。

随后又经过几轮面试。两个月后,我加入了Disqus团队,当时是在旧金山,只有7名员工。就是在那儿,我认识了Anton Koval-yov,我的JavaScript新同事和未来的合作者。在接下来两年多的时间里,我和Anton Koval-yov负责维护和开发Disqus的客户端代码。Disqus继续快速增长。到2012年,成千上万的网页安装Disqus,每月页面浏览量超过50亿。其客户包括CNN、MLB、IGN、Time.com、滚石,以及几十个其他的主要网络和媒体公司。

安东和我在这段时间,学会了许多有用的第三方脚本编写的技巧、贴士和hack,其中大部分我们已经吸取了教训,其中有一些我们采取保密策略,因为这是我们的技术优势。

在这本书中,凝聚了我们集体的第三方JavaScript知识。我们认为本书不但能帮助无处不在的第三方脚本开发者,而且也认为我们讨论的做法可能让互联网变得更美好。我们希望通过阅读本书,你会同意我们的观点。

BEN VINEGAR


我们发现写这本书是一个艰巨、有挑战性的经历,我们也意识到别无他法,只能靠自己。我们想花一点时间感谢那些曾经直接或间接帮助过我们的人。

首先,感谢Daniel Ha、Jason Yan和Disqus团队,不仅雇佣了我们,还制作和维护了一个非常棒的平台,本书中大部分的重要内容均来自于此。其次,感谢Manning给予我们一个讨论的机会,其中许多话题都是小环境的。没有他们,本书将不存在。特别要感谢我们的编辑Renae Gregoire,在整个写作的过程中从头到尾提供帮助。也非常感谢Manning的编辑和制作团队,帮助我们调整文本、改善许多数据和图表等。

我们特别感谢我们的技术评论家Alex Sexton,分享了他在第三方JavaScript方面的宝贵经历;感谢John Ryan III临近出版前还在审查终稿;感谢Paul Irish贡献的前言,并同意我们这些微不足道的业余爱好者使用他的名号。

最后,感谢在不同的阶段阅读我们的手稿的许多评论家和顾问们,慷慨地分享他们的反馈、指出错误、反复确认我们的观点,他们有:Øyvind Sean Kinsey、Kyle Simpson、Henri d’Orgeval、Mike Pennisi、Peter DeHaan、Brian Arnold、Brian Chiasson、Brian Dillard、Brian Forester、David Vedder、Jake McCrary、Jeffrey Yustman、Jonas Bandi、Justin Pope、Margriet Bruggeman、Nikander Bruggeman、Sopan Shewale。

最后,感谢在manning.com论坛评论的每一位读者,在Twitter上@我们的人,以及私下评论本书的人,对于你们提供的每一点帮助,我们都非常感谢。

BEN VINEGAR

我想把这本书献给我的父母,David和Wendy。从我小时候起,他们就开始培养我对计算机的兴趣,如果没有他们,我完成不了这些。特别感谢我的搭档Esther,在这么富有挑战的项目里给我的鼓励和耐心。

ANTON KOVALYOV

我想把这本书献给我的父母(甚至是Ben写的那一部分),是你们在我把大部分的时间都奉献在电脑前时仍不断地支持我。感谢Pamela Fox鼓励我暂停我这边的项目全力致力于本书的编写中去,而不是袖手旁观。


本章包括

第三方JavaScript是一种JavaScript编程模式,可以用来创建高度分布式的Web应用程序。常规的Web应用需要通过一个特定的Web地址访问,而第三方JavaScript创建的应用,只需要引入一些简单的JavaScript脚本就可以加载到任意页面上。

你之前也许就曾遇到过第三方JavaScript。例如广告脚本,它可以在发布者网站上生成、定向投放广告。用户也许并不喜欢广告脚本,但是它却能帮助网站发布者获得收入并维持网站的运转。成千上万的网站上都能看到广告的身影。事实上,几乎所有的广告都是从单独的广告服务器加载的第三方脚本。

开发者通过第三方JavaScript可以解决许多问题,广告脚本仅是其中的一个使用场景。一些开发者用它们来创建独立的产品以满足网站发布者的需要。例如,位于旧金山的一个初创公司(Disqus)所开发的第三方评论应用,为Web发布者提供即时评论系统。本书的作者也是该公司的员工。还有一些开发者通过开发第三方JavaScript来扩展传统Web应用,从而获得其他网站的用户。例如,Facebook和Twitter开发了数十个社交微件展示在发布者的网站上。这些微件能够帮助社交网站在它们应用的正常系统之外扩展用户的规模。

小公司也可以受益于第三方JavaScript。比方说,你是一家B2B(business to business)Web应用的所有者,需要通过Web表单收集客户的信息。可能有一些潜在用户想使用你的应用,但是他们会犹豫,因为这样会把自己的用户重定向到一个外部的网站。通过第三方JavaScript,就可以让你的客户直接在他们自己的Web页面上加载你的应用,从而解决他们所顾虑的重定向问题。

第三方JavaScript并非表面看起来那么光鲜。写这些应用并非易事。在公开发布第三方JavaScript代码之前你需要克服很多困难。值得庆幸的是,本书会完整介绍第三方应用的开发过程,足以让你明白如何解决这些问题。

但是,当我们深入了解第三方JavaScript之前,需要先了解一些基础知识。为了便于理解,本章首先为第三方JavaScript下一个定义,其次了解一些公司在现实世界中的实现方法,再看一个简单的第三方应用实现的示例,最后谈论关于第三方应用开发面临的诸多挑战。

让我们开始更好地了解第三方JavaScript是什么,并且能够用它来做什么吧!

在一个典型的软件交换过程中,有两类重要的参与者:第一类是操作软件的使用者,第二类是软件的提供者或者作者。

在网络上,你可能认为操作浏览器的用户是第一部分。当他们访问一个Web页面时,浏览器会向内容提供者发起相应的请求。内容提供者为第二部分,它从对应的服务器向用户的浏览器回传Web页面所需的HTML、图片、样式表和脚本文件。

针对这样特别简单的Web传输过程,可能只有两个参与者。但是,如今大部分网站都提供着包含其他来源的内容,或者说是第三方的内容。如图1.1所示,第三方可能提供任何内容,从文章内容到头像存储,再到内嵌视频。从严格意义上讲,任何除了网站提供者之外的组织向客户端提供的内容都被认为是第三方的。

图1.1 如今的网站大量使用第三方服务

如果把“第三方服务”单纯归类为提供JavaScript代码,那么这个定义会很混乱。对于第三方JavaScript究竟如何构成,许多开发者执不同的意见。一些开发者将它归类为不仅仅为作者提供服务的JavaScript代码。这将会包括众多流行的JS库,如jQuery和Backbone.js。甚至从Stack Overflow这样的网站粘贴的解决方案也被包括在内,任何不是网站作者自己编写的代码都会被归类在这种定义之下。

其他开发者倾向于把从第三方服务器加载的代码认为是第三方JavaScript,即并不在内容提供者控制范围内的代码。原因在于,内容提供者管理的代码是在他们控制范围内的:内容提供者可以选择加载代码的时机,有权限对代码进行修改,最终他们对代码的行为负责。这与单独加载来自于第三方服务器的代码不同,第三方代码不能被内容提供者修改,而且可能随时发生改变,并不通知给使用者。下面,通过一个内容提供者HTML页面的示例,展示如何从本地和外部服务器加载JavaScript文件。

程序清单1.1 Web页面从本地和外部加载脚本的示例

两种定义没有对与错之分,你可以对其中任意一种定义的解释提出质疑。但是,基于本书的目的,我们对后者的定义更感兴趣。当我们提及第三方JavaScript时,我们即认为代码是如下状态。

那么TYPE="TEXT/JAVASCRIPT"去哪里了呢?你可能已经注意到了在这个例子中<script>标签的定义并没有指明type属性。对于一个“未定义类型”的<script>标签,浏览器默认会将其内容当作JavaScript处理,即使在旧版本浏览器中也是如此。为了使书中例子尽可能地简洁,我们放弃了使用类型属性。

到目前为止,我们已经了解了如何从内容提供者网站上加载第三方脚本。让我们改变一下角色,作为第三方JavaScript的开发者,我们期望在内容提供者网站上执行我们编写的脚本。为了把代码引入到提供者的网站,我们需要提供HTML代码片段插入到他们的页面中,从而使内容提供者的网站可以从我们的服务器加载JavaScript文件,如图1.2所示。我们并不隶属于网站提供者,我们仅仅是在他们的页面上加载脚本来为他们提供辅助性的库或者有用的自包含应用。

图1.2 放置在发布者页面上的代码片段来加载第三方JavaScript代码

如果你还在不知所措,不必过于担心。理解第三方脚本最简单的方式就是在实践中去锻炼。在接下来的部分,我们会介绍一些第三方脚本的真实案例,如果到了那时你还是像现在一样困惑,那么我们就真的算得上是三流技术作者了。

我们已经确切知道了第三方JavaScript是在某个网站上被执行的代码,这就使得第三方代码能够访问到网站的HTML元素和JavaScript上下文。因此,我们可以通过多种方式操作目标页面,例如在文档对象模型(DOM)中创建新的元素、插入自定义的样式表、注册浏览器事件以捕获用户行为。在绝大多数情况下,如同使用JavaScript操作自己的网站或者应用一样,第三方脚本可以执行同样的操作,不同的是,第三方脚本操作的是他人的网站。

拥有了远程操作Web页面的能力,依然会有这样的问题:它擅长做什么?在这部分,我们会看到一些第三方脚本真实的应用案例。

这并非一份完整的列表,但是你可以清晰地看到第三方JavaScript能够做什么。接下来我们会深入介绍第一项:内嵌微件。

第三方脚本中最常见的使用案例莫过于内嵌微件(通常是第三方微件)了。这些微件都是典型的小型、交互式应用,在发布者的网站上呈现并提供访问,但是这是从另一组单独的服务器加载和提交资源的。随着复杂程度不同微件千变万化;可以简单到作为图形显示您所处地理位置的天气情况,也可以复杂到一个功能完备的即时通信客户端。

微件使得网站发布者能够轻而易举地将应用嵌入到他们的Web页面中。通常它们安装简单,往往只需要网站发布者在他们的Web页面中插入一小段HTML代码即可。因为这些微件完全基于JavaScript,所以不需要发布者在他们的服务器上安装或者维护任何软件,这就意味着使用微件只需要很低的维护成本。

有些企业应用甚至完全建立在微件开发、分发的基础上。如之前我们提到的Disqus,Disqus开发了一个评论微件,如图1.3所示,用来为博客、在线出版物和其他网站提供评论功能。他们的产品几乎完全由第三方JavaScript驱动,通过JavaScript代码从服务端获取评论数据,将评论渲染为HTML展示在页面上,并且捕获其他评论者输入的表单数据。换句话说,这一切都是由JavaScript完成的。将它安装在网站上只需要一段共计5行的HTML代码片段。

图1.3 一个展示在发布者网站的评论部分示例,由Disqus评论微件驱动

Disqus是一个仅能够在分发形式下使用的产品案例,你需要访问一个发布者的网站来使用它。但是微件并非都是这样的独立产品。它们往往作为更大、更传统的一方,独立于Web应用的“便携式”扩展。

例如,Google Maps,可以称得上是网络上最受欢迎的地图应用。用户通过浏览地址“https://maps.google.com”,就可以用交互式地图看到世界各地。Google Maps还提供了汽车导航、公共交通、卫星影像,甚至还有选定位置的街景图片。

不可思议的是,这些神奇的功能都可以通过一个微件实现。网站发布者可以从Google Maps网站上获取一些简单的JavaScript代码片段,通过这些代码就可以将地图应用嵌入在他们自己的Web页面上。在此基础之上,Google公司还为发布者提供了一些公共函数以便修改地图的内容。

让我们来看一下,使用Google Maps(见程序清单1.2)将一个交互式地图嵌入在你的Web页面上是多么简单吧!这段代码示例首先通过一个简单的<script>标签引入了地图的JavaScript库。接着,当body的onload事件触发时,如果当前浏览器兼容的话,就会初始化一个新的地图并且根据给定的坐标定位在中心[1]

程序清单1.2 初始化Google Maps微件

大功告成了,大约用10行代码就完成了这样强大的功能!

以上是两个嵌入式微件的例子。实际上,在页面上嵌入任何应用的想法都是可行的。就我自己而言,我遇到过各种各样的微件:内容管理的微件,实时播放视频的微件,让你同客服人员实时交流的微件等。只要你能想到的,就能把它嵌入进来。

第三方JavaScript并非完全用作创建内嵌式微件,还有其他的重要用途,可以不涉及图形、交互式Web页面元素。通常它们在页面上默默地处理信息,用户并不知道它们的存在。最常见的使用案例就是统计和数据收集。

JavaScript最强大的特性之一就是它可以让开发者捕获和响应页面上发生的用户事件。例如,你可以通过JavaScript来响应一个网站访问者的鼠标移动和(或者)鼠标点击。第三方脚本也并不例外:它们也可以监听到浏览器的事件,并捕获访问者与页面发生的交互数据。从而可以跟踪一个访问者在页面上的停留时长,他们阅读网页时看到的内容,以及他们离开页面的去向。JavaScript代码可以捕获到几十种浏览器事件,从中你可以得到数百种不同的组合。

1.被动型脚本

Crazy Egg(一家互联网初创公司),就是通过这种方式使用第三方脚本的案例之一。他们的统计产品能够将用户在Web页面上的行为进行可视化展示,如图1.4所示。Crazy Egg通过在网站插入一个脚本来捕获访客的鼠标和滚动事件,从而获取这些数据。这些数据被提交回Crazy Egg的服务器,这些步骤都在同一个脚本中完成。最终Crazy Egg生成的可视化数据能够帮助网站发布者确定网站的哪些部分访问比较频繁,哪些部分被忽略了。发布者通过这些信息可以改进他们的Web设计并且优化网站内容。

图1.4 Crazy Egg在发布者网站描绘出可视化热点地图

Crazy Egg的第三方脚本被认为是一个被动的脚本:它同页面没有任何交互,只是为发布者的网站记录统计数据。发布者只需要在页面上引入脚本,其余的事情就可以自动完成了。

2.主动型脚本

并非所有统计脚本的行为都是被动的。Mixpanel是一家分析公司,它们的产品跟踪发布者自定义的用户行为,从而形成与网站访客或者应用程序用户有关的数据统计。不是通用的Web统计,如页面访问量或者访客数,Mixpanel是由发布者自己定义想要跟踪的关键应用程序事件。比如“用户点击注册按钮”,或者“用户播放视频”。发布者需要写些简单的JavaScript代码(见程序清单1.3),当识别到动作发生时,调用Mixpanel第三方脚本提供的跟踪方法注册事件。然后Mixpanel会将这些数据组装成有趣的漏斗统计来帮助解决像这样的问题:“用户在升级产品之前执行了哪些步骤?”

程序清单1.3 通过Mixpanel JS API跟踪用户注册

同Crazy Egg不同的是,Mixpanel的服务需要发布者进行一些前期的开发工作,定义和触发事件。好处就是,发布者可以围绕着用户行为收集自定义的数据,并且解决用户行为相关的问题。

关于Mixpanel使用第三方脚本还有一些其他有趣的事情。实际上,Mixpanel提供了一些客户端的函数来同他们的Web服务API(一组服务器的HTTP接口,包括跟踪和汇报事件)通信。这是一个实用的案例,可以扩展到任意数量的不同的服务。让我们更深入地了解第三方脚本吧!

你可能之前对它们并不熟悉,Web服务API是HTTP服务器的接口,可以为程序提供Web服务的访问支持。不像服务端应用那样向浏览器返回HTML,这些接口针对计算机程序的请求予以响应并返回结构化数据——往往是JSON或者XML格式。这里的应用程序可以是桌面应用,也可以是服务端应用,还可以是在浏览器里执行的一个Web页面上的JavaScript代码。

在浏览器里执行的JavaScript代码——是我们尤为感兴趣的。Web服务API的提供者可以在他们的平台上为开发者提供构建好的第三方脚本,从而简化客户端对他们API的访问。我们习惯上称这些脚本为Web服务API封装,因为它们将Web服务的API功能逻辑封装成了高效的JavaScript库。

1.例子:FACEBOOK GRAPH API

这有什么用呢?我们来看一个例子。假设有一个名叫Jill的独立Web开发者,他厌倦了自由职业者的生活,想要寻找一份全职工作。为了更好地引起潜在雇主的注意,他决定做一个非常棒的在线简历,放在他的个人网站上。这份简历大部分内容都是静态的——用于展示他的技能和他先前的工作经验,甚至还提到他喜欢在月光下划皮划艇。

为了证明他的Web开发实力,Jill决定在他的简历上加入一些动态的元素。而且他已经有绝妙的主意。倘若潜在雇主看到Jill的在线简历,就会看到他们自己是否同Jill有共同的好友或者熟人,如图1.5所示。这样做不仅能够证明自己的技能,而且共同的好友还可能是一块敲门砖。

图1.5 访问者在Jill简历的底部可以看到自己共同的好友

为了实现他的动态简历,Jill使用了Facebook的Graph API。这是Facebook的一个Web服务API,可以使软件应用能够访问或者修改Facebook的在线用户数据(当然需要有相应的权限)。Facebook也有一个JavaScript库提供了同API通信的方法。通过这个库,Jill就可以通过客户端代码在他简历上获取并且展示同访问者共同的好友。图1.6展示了客户端同两个服务器之间发生的事件顺序。

图1.6 通过客户端JavaScript在网站上内嵌Facebook的内容

程序清单1.4列出了他在简历上实现这个功能的代码。为了让代码更简洁,这个例子使用了jQuery,一个能够简化DOM操作的JavaScript库。你可以从“http://jquery.com”网站上获得更多信息。

程序清单1.4 通过Facebook Graph API获取并展示共同的好友

Jill通过很少的客户端JavaScript代码,就在他的简历中嵌入了一些强大的功能。有了这些令人印象深刻的功能,相信他找到一个顶级的软件开发工作应该没什么问题。

2.客户端API访问的好处

值得一提的是,这个例子不用客户端JavaScript也能实现。不同的是,Jill需要通过服务端应用调用Facebook Graph API查询需要的数据并渲染为HTML代码返回给浏览器。这样一来,浏览器从Jill的服务器下载HTML代码并将结果展现给用户,并没有借助JavaScript代码。

但是,最好还是让网站访问者在浏览器中执行这些工作,有如下原因。

代码在浏览器中执行而不在服务端执行,可以为服务器节省带宽和CPU。

更快——服务端的实现需要在展示内容前等待Facebook的API返回。

有些网站是纯静态的,因此客户端JavaScript对他们而言是唯一能够访问Web服务API的方式。我们刚刚提到的例子可能会被认为是一个很好的使用案例,但这可能只是其中一个应用。Facebook只是Web服务的API提供者之一,实际情况是,有众多流行的API,可以获得不同的数据或者使用不同的功能。除了像Facebook、Twitter、LinkedIn这样的社交应用之外,还有像Blogger和Wordpress这样的发布平台,或者像Google和Bing这样的搜索应用,都不同程度上通过API提供了自己数据的访问。

许多Web服务都提供API。但并非所有Web服务都会额外提供一个JavaScript库供客户端访问。这点很重要,因为在浏览器中JavaScript是一个最大的开发平台:每个网站,每个浏览器都支持。如果你或者你的团队在开发或者维护一个Web服务API,并且希望能将其尽可能的集成,你应该自己为开发人员提供一个客户端的API封装,我们将在后面详细讨论。

在本章前两小节中我们探讨了一些第三方JavaScript的流行用法。你已经了解了在开发微件、数据收集、作为客户端Web服务的API封装等方面是如何运用到它的。希望这能够给你一些启发,当你设计自己的第三方应用程序时候能够了解哪些功能是能够实现的。

现在,你已经看过了一些现实中的实例,是时候开发一些自己的东西了。让我们先从相对简单的入手:一个极其简单的嵌入式微件。

假设你在运行一个提供当地最新天气信息的网站。一般而言,用户直接访问你的网站是为了获得最新的天气消息。但是为了使用户更广,你决定更进一步,让用户可以在你的网站之外访问你的数据。你可以通过提供一个可嵌入的微件服务做到这一点,如图1.7所示。你需要向那些有兴趣通过引入第三方脚本即可为自己的用户提供本地天气信息的网站发布者推广这个微件。

图1.7 展示在发布者页面上的天气微件的效果

幸运的是,你已经找到了一个对此兴致盎然的网站发布者,而且他们已经开始试着使用你的微件了。为了让他们能够使用,你需要给他们提供HTML代码片段,从而在他们的Web页面上加载天气微件。网站发布者需要将代码片段粘贴到他们的HTML源代码中想要展现微件的位置。代码片段本身很简单:即一个<script>标签指明weathernearby.com所在服务器上第三方JavaScript的路径:

你会注意到脚本的URL中包含了一个zip参数。通过这个参数你就可以识别需要渲染什么位置的天气信息。

此时,当浏览器加载发布者的Web页面时,遇到这个<script>标签就会向weathernearby.com的服务器请求widget.js脚本文件。当widget.js下载完毕并且执行后,就会在发布者的页面上渲染出天气微件。不管怎么说,这就是我们想要达到的效果。

要做到这些,widget.js需要有权限访问公司的天气数据。这些数据可以直接发布到脚本文件中,但是考虑到美国大约有43 000个邮政编码,这样一来,一个请求中会有太多的数据。除非用户从瑞典或者韩国访问,因为这两个国家100Mbit/s的传输速度是常态。在通常情况下,微件需要请求天气数据可以通过使用AJAX来完成。为了简单起见,我们使用另一种不同的方式:服务端生成脚本。

与提供一个包含微件代码的静态JavaScript文件不同,你需要写一个服务端应用为每个请求生成一个JavaScript文件,如图1.8所示。因为服务器能够访问天气信息的数据库,它可以将所请求的天气数据插入到输出的JavaScript文件中。这就意味着,在发布者页面上渲染天气微件的所有代码和需要的数据都包含在JavaScript中,而无需发起任何额外的请求。

图1.8 服务端应用动态生成天气的微件代码过程

这个服务端应用可以用任何编程语言或者能够在服务器环境运行的平台,像Ruby、PHP、Java、ASP.NET——甚至服务端JavaScript,这些都是很不错的选择。本例中,我们选用很受欢迎的脚本语言Python来进行演示。本例用到了Flask,一个基于Python的小型框架,用于构建小型Web应用。如果你并不熟悉Python,也不用担心——代码很容易理解。如果你想自己尝试程序清单1.5中的例子,可以参照配套的程序源码,程序源码中同时也包含了安装Python(2.x)和Flask的介绍。

为什么选用Python

本书中一小部分服务端的例子都是使用Python语言。我们选用这种编程语言完全是因为偏爱:它是我们在Disqus每天都在使用的语言,并且我们对它最熟悉。倘若你不知道如何使用Python也没关系。本书首先是一本关于JavaScript的书,服务端的例子可以很容易的使用其他语言代替。

程序清单1.5 widget.js的服务端实现,基于Python和Flask—server.py

当这个服务端脚本启动后,一个请求“http://weathernearby.com/widgets.js?zip=94105”的<script>标签将返回如下JavaScript代码。从而渲染出你在本节开头看到的示例微件,如图1.7所示。同时,注意这些代码是Python应用产生并且对于浏览器请求而言是完全透明的。

现在看来,这是一个极其简单的例子,并不是开玩笑。这个毫无样式输出的天气微件几乎没有为用户提供交互界面。看似非常糟糕,而且可能会将你刚起步的公司陷入困境。但是它能够工作,而且展示了发布者网站同第三方代码之间的交互。

这里提及的一些技术,像使用document.write和服务端的Python,都不是唯一生成微件的方式。不要皱眉,我们稍后会解释原因。在后边的章节中,我们将探讨更好的替代方案,并介绍更为复杂的功能,如样式表,通过AJAX进行服务端通信以及用户间会话。

如果你对于Web开发相当有经验,可能会有这样的想法,“是不是通过iframe分发这个微件更容易?”乍一看似乎如此,但是有一些潜在的差异使得第三方脚本成为了更好的实现选择。为了知其所以然,首先我们看一下如何通过iframe重新创建之前的微件示例。

iframe是块级HTML元素,用来从URL嵌入外部内容。如果你对它并不熟悉也能够完全通过一个iframe元素很容易的重新实现天气微件示例,如下。

你会注意到src属性改变了:不再指向一个JavaScript文件,而是指向一个HTML文档。这一次你的服务器将返回一个完整的HTML文档,包含了微件的标记,完全避免了使用JavaScript。你也会注意到微件的尺寸作为iframe的style属性的一部分。iframe元素并不会扩大以适应其内容,因此需要为iframe设定明确的尺寸。

像这样使用iframe将产生同JavaScript示例相同的输出。那为什么要使用JavaScript代替iframe呢?有很多的原因,其中大部分原因都围绕iframe的特性:通过iframe加载的外部内容不能被父页面访问(发布者的页面),反之亦然。

灵活性:如果你希望改变微件的尺寸,就没有那么幸运了。因为iframe的尺寸是在父页面中iframe标签中指定的,而且这些属性不能被iframe加载的内容更改,因此没有办法动态的改变微件的尺寸。

美学:微件的外观需要完全独立于父页面的样式。微件将不能继承基础的样式,例如字体种类、字体大小或颜色等。

交互:微件需要访问或者修改发布者页面的DOM吗?发布者是否需要在微件的内容上附加一些交互?是否会有多个微件实例存在并且相互通信的情况呢?这些通过静态iframe都不可能做到。

报表:浏览器用户是否真能看到微件?在看到它之前,他们在页面上耗费了多长时间?收集这些和其他有价值的数据都需要在发布者页面上运行JavaScript。

这些只是一些简单的例子,但是你或许能看到一种趋势。iframe或许是天气微件示例最简单的分发方式,但是这么做你将失去第三方JavaScript提供的众多引人注目的能力。但是,不要让它改变了你对iframe的看法。它们是第三方JavaScript开发者工具集中一个宝贵的资产,在本书不同的任务中,我们都会频繁地用到它们。

你已经了解了第三方JavaScript是编写高度分发应用的一种强大方式。但是编写在其他人网站上执行的脚本,同传统的JavaScript编程相比有一系列独特的挑战。具体而言,你的代码将在一个完全不受控制、一个不同域的DOM环境中执行。这意味着你必须面对一些难以预料的复杂情况,比如未知的网页上下文,一个同其他第一方、第三方脚本共享的JavaScript环境,甚至浏览器的一些限制。我们快速的浏览一下涉及的挑战。

当一个发布者在他们的Web页面中引入你的第三方JavaScript,通常你很少知道它被放置的上下文。你的脚本可能会被诸多不同文档声明,不同DOM布局的页面引入,而且应当在这些页面中都能正常工作。

你不得不考虑发布者可能在他们页面顶部的<head>标签中引入你的脚本,也可能会在<body>底部引入。发布者也可能会在iframe中加载你的应用,也可能在一个<head>标签完全废弃的页面中加载;在HTML5中,<head>部分是可选的,而且并非所有浏览器都会在内部自动生成。如果你的脚本在查询或者操作DOM的时候用到了这些核心的元素,就会陷入麻烦了。

如果你在开发一个嵌入式微件,显示适当的样式也是一个需要被关注的问题。微件将被放置在一个浅色背景还是深色背景的Web页面中?你希望你的微件继承样式并“融入”发布者的页面设计吗?你希望你的微件在所有上下文中看起来都是一致的吗?如果发布者页面的HTML格式错误会出现什么情况,导致页面的渲染使用怪异模式吗?解决这些问题都需要精心编写CSS。我们会在后续的章节中给出这些问题的解决方案。

对于一个给定的Web环境,只有一个全局变量的命名空间被页面上执行的每一段代码所共享。你不仅要注意不能让你的全局变量胡乱修改命名空间,也要明白其他脚本,可能是同你一样的第三方应用,也能够修改你可能依赖的标准的对象和原型。

例如,全局的JSON对象。在现代浏览器中,这是一个原生的浏览器对象,可以极快地解析和序列化JSON(JavaScript对象表示方法)。不幸的是,它可以很容易地被任何人修改。如果你的应用依赖于这个对象的正常工作,并且它被其他代码改为了不兼容的方式,你的应用可能会产生不正确的结果甚至崩溃。

下面的示例代码演示了使用简单的变量赋值修改全局的JSON对象。

你自己可能会这么想,“为什么会有人这么做?”Web开发者往往加载他们自己的JSON方法来支持旧的浏览器没有提供的本地方法。但是有些这样的库不兼容。例如,很流行的Prototype JavaScript库在旧版本提供的JSON方法同原生方法相比,在处理undefined值时的输出并不相同,如下。

JSON对象只是浏览器原生对象可以被客户端代码改变的一个例子;还有数百种其他的对象。在本书中,我们会寻找恢复或者简单避免这些对象的解决方法。

同样,DOM是另外一个你不得不担心的全局应用程序命名空间。对于一个给定的Web页面而言,只有一个DOM树,并且它是被当前页面上运行的所有应用所共享。这就要求你操作它的时候要格外注意。任何插入到DOM的新元素需要同现有的元素和平共存,并且不干扰其他查询DOM的脚本执行。同样,如果范围不当,你的DOM查询也可能会意外的选择到不属于你的元素。反之亦然,如果你没有认真地选择唯一的ID和类名,其他应用也可能不经意间会查询到你的元素。

因为你的代码同其他脚本同属于相同的执行环境,安全性也是要考虑的。不仅要防止你的用户使用不当,还需要考虑页面上的其他脚本,甚至发布者也是一个潜在的威胁。例如,如果你在为一个大型的、受欢迎的服务编写微件或者脚本(像一个社交网站),发布者或许会有很浓厚的兴趣去尝试在他们的页面上做一个假的交互界面。

如果一个未知的文档上下文,多个全局命名空间,以及额外的安全问题还不够糟糕,Web浏览器禁止某些行为经常会影响到第三方脚本。例如,AJAX已然成为Web开发人员无刷新获取和提交数据的一个主要工具。但是Web浏览器的同源策略限制了XMLHttpRequest跨域进行通信,如图1.9所示。如果你正在编写第三方脚本,且需要同你的域所在的应用进行数据通信,那么你就不得不通过其他方式实现了。

图1.9 在Google Chrome中localhost到google.com的跨域AJAX请求失败

同样,Web浏览器通常也会限制应用程序读写第三方Cookies。如此一来,你的用户将无法“登录”你的应用或者记住后续请求的动作。根据应用的复杂度,无法读写第三方Cookies可能是一个真正的障碍。

不幸的是,这些挑战仅仅是冰山一角。第三方JavaScript的开发充满了陷阱,说到底,Web浏览器并非为嵌入式应用而生,也没有过多考虑第三方代码。浏览器日趋完善,且会引入一些新的特性,可以减轻一些第三方开发的负担,但仍旧是一场艰苦的战斗,且兼容旧的浏览器通常是任何类型的分布式应用都需要考虑的问题。

但是不必担心。你购买了本书,已经做了一个很好的决定,它包含了第三方JavaScript代码所面临的问题。作为回报,我们将会告诉你如何解决这些问题。

第三方JavaScript是构建嵌入式和高度分布式Web应用的一种强大的方式。这些应用形形色色,但是我们看其中这三种特定的使用场景:作为可交互的微件,作为被动的脚本收集数据,以及作为开发者的代码库提供第三方Web API的通信。同常规一方独立Web应用开发相比,第三方脚本面临很多挑战。你需要在一个未知的、共享的、且有潜在风险的浏览器环境中执行你的代码。

我们仅仅涉及了编写第三方脚本的皮毛。在下一章节中,将进一步详细介绍从前端到后端创建一个嵌入式微件的方法。这是第三方JavaScript最常见的使用场景之一,并作为了解第三方的概念和挑战的一个良好开端。

[1] 注:并非所有人都能牢记经纬度。好在Google公司提供了可以将街道地址转换为地理坐标的功能。可以访问http://code.google.com/apis/maps了解更多信息。


相关图书

深入浅出Spring Boot 3.x
深入浅出Spring Boot 3.x
JavaScript核心原理:规范、逻辑与设计
JavaScript核心原理:规范、逻辑与设计
JavaScript入门经典(第7版)
JavaScript入门经典(第7版)
JavaScript函数式编程指南
JavaScript函数式编程指南
PHP、MySQL和JavaScript入门经典(第6版)
PHP、MySQL和JavaScript入门经典(第6版)
JavaScript学习指南(第3版)
JavaScript学习指南(第3版)

相关文章

相关课程