信息无障碍不是献爱心更不是做公益慈善

自从两年前加入阿里巴巴的信息无障碍小组,平时的学习当中就多了一项内容,关注信息无障碍相关的文章,也经常去 W3C 上看看技术文档。

由于今年把无障碍相关的学习研究提高了一个优先级,每逢周末就会去网上搜寻下相关的文章,也因此认识了一些盲人朋友。

很多人以为盲人就是拿着一根拐杖,四处探路,等着被人帮助。你可能还不知道,很多盲人都在使用电脑和手机,并且也会做股票投资、在淘宝开店等,当然,他们用的很痛苦。技术本可以让视障人士同我们一样,自由穿梭在互联网的每个角落,但互联网的搭建者们,似乎还没有意识到,有一个群体,期待被我们关注,期待我们把信息无障碍植入到网络中。

我转了几段几位视障朋友人说过的话:

顾伶磊:

在中国,互联网产品的无障碍化还非常遥远,没有几个人能真正理解无障碍化的含义。当你还把无障碍当成一项爱心事业在做的时候,你就已经注定失败了。

无障碍可用性本应该是一项规范,而不是一向爱心事业。如果你把他当成爱心事业去做,是注定不会长久的。在此我们呼吁,各大互联网企业和产品开发们,能帮忙推动无障碍的规范化,将无障碍可用性列入产品开发的规范中。

东东保2011:

现在很多企业把作无障碍看成是一种施舍,而并非是一种规范和义务。

史明明-百年孤独:

做无障碍不该被当成是做工艺、慈善、施舍,相信无障碍的相关法律法规一定会出台。

黄龙小生:

其实,无障碍它体现了人与人之间的平等,无障碍之所以有障碍表面乃是程序和工作问题,往深了说却是人性不平等的一面在作怪。在我们天朝大家认为做了这事情乃是公益事业乃是行善事,可是在人家那里,这些本来就是日常工作中的一部分而已。残障人身有残障,可是在这里却变得智有残障了。

以及一位 盲人技术开发者 的观点:

信息无障碍的目的在于让所有人包括残障人、老年人、儿童等都可以很方便的平等的获取信息。目前国内的现状是人们普遍缺乏对无障碍的理解和认识,同时也缺少有效的法律支持。

很长一段时间内最主要的工作是普及无障碍意识。推动信息无障碍是个长期的持续的工作。

现在大多数的信息障碍来源于信息提供者。互联网信息障碍主要来自于网站和软件的开发者。

这几年来,我一直与软件开发者交流,最大的感触是,他们是有爱心的,是愿意做无障碍工作的,唯一缺乏的是无障碍意识,他们不知道他们的产品会给残障人带来麻烦。而一旦他们了解到这些障碍之后,都是很愿意进行改进的。

所以,普及无障碍意识,从开发者入手是最直接、最有效的方式。

无障碍技术并没有很难,只是我们没把这件事当回事。后续我会发布一些信息无障碍相关的技术研究文章,希望可以带动一部分人一起做这件事情。

本文同步自 小胡子哥的个人网站,原文地址: http://www.barretlee.com/blog/2016/02/28/step-in-aria/

信息无障碍不是献爱心更不是做公益慈善

谈一谈博客的著作版权问题

古人对「著作」和「编述」两个词的含义区分的比较明显,子曰:“述而不作,信而好古”,意思是,只负责传承古代的优秀文化,不搞改革,相信并且喜欢玩味古代的东西。那个时代产出的知识体量小,而现在不一样,信息时代,很多内容都是在沉淀的知识中做微创新、微著作。

如果要把博客文章划进这两个词,著作或许更加适合些。我们在博客中的论述,稍微有点含量的,都可以称之为著作。

在中国,盗版应该是一个深入骨髓的词语,如果哪天发现身边的朋友没有使用盗版的软件、看盗版的数据、听盗版的音乐,你可能会乜斜着眼睛诧异的盯着这个人(心里默念着土豪)。近两年,国家在盗版的打击上做了大量工作,尤其是书籍和音乐两块。

最近看到技术圈的内容聚合平台如雨后春笋般崛起,然而有极少数的朋友在分享的时候并不太在意版权的问题。所以今天想把这个问题再提到纸面上说说,加深大家的印象。

大众没有版权保护的意识

近几年,国内出现了好几个支持静态部署的平台,加之技术学习门槛越来越低,很多人都玩起了博客。我平时也喜欢看别人写的东西,每每 Google 搜索都会掉进几个不错的博客,看到有意思的内容,便会削一个苹果坐在一旁,边吃边看🙈。

大约有一半的朋友会在自己的文章中备注版权信息,告诉路人,文章可以拿走,但是要记得带上名字和原文链接,这个习惯很好。可是也有很多朋友并没有意识到这个问题。前段看到团队号召各位攻城师把工作上不错的想法提炼成专利,并对我们做了相关的知识普及,我发现,很多专利其实并不需要什么高深的技术。其实著作也是类似的,用心去写,搞不好你某篇文章就可以给你带来一些正向影响。

以前的著作,权限控制很极端,一种是不给任何权利,一种是把所有权利都给出去。你是否也经常看到 Apache 协议、MIT 协议等等对代码开源的权利控制?我之前整理了一份文档,感兴趣的可以看看:《五种开源协议的比较(BSD,Apache,GPL,LGPL,MIT)》,适当对著作权做保留,一方面有利于将自己的知识成果分享出去,另一方面也是对自己权益的维护。

Creative Commons

博客文章写作一般会用到 Creative Commons 相关协议,中文叫做「知识共享协议」,简称 CC。对权利的归类分为四种:

  • 署名(Attribution,简写为 BY):必须提到原作者。
  • 非商业用途(Noncommercial,简写为 NC):不得用于盈利性目的。
  • 禁止演绎(No Derivative Works,简写为 ND):不得修改原作品, 不得再创作。
  • 相同方式共享(Share Alike,简写为 SA):允许修改原作品,但必须使用相同的许可证发布。

使用时,可以对以上随机组合,用的比较多的组合有这么几个:

我的博客使用上面第二种协议,可以在任何媒介以任何形式复制、发行博客作品,但是要保留署名,不允许商用,不能对内容进行修改和再创作。还算比较宽松,对一般的聚合平台而言,在醒目位置留下作者名字和原文链接即可。

不止于协议

大家都知道,技术内容是在不断革新的,上半年还在流行的东西,下半年可能就已经淹没在历史洪流之中。博客文章中的表述可能存在诸多错误或者一段时间后内容过时了,当作者发现这些错误的时候,便会对内容做修改,copy 方式的转载最大的问题就是没法即时与原文保持同步,倘若转载时还不保留原文地址,信息的传递就会出现断层,一些错误的表述和内容,容易对刚入门的新人造成负面的刻板印象,这一点是需要各位朋友在转载文章的时候注意的!

好吧,说了这么多,就当我啰嗦了下。

本文同步自 小胡子哥的个人网站,原文地址: http://www.barretlee.com/blog/2016/02/27/about-cc/

谈一谈博客的著作版权问题

WiFi 万能钥匙原理和危害探究

本文不会从代码角度分析 WiFi 万能钥匙这个软件是如何「破解」密码的,主要从使用这个软件后的感受出发,分析它可能会用到的手段以及可能存在的风险。

以前因为好奇,安装了 WiFi 万能钥匙,但是从来没有打开过,当时我心里很清楚,蹭网的前提就是自己家被蹭。晚上刷微博看到一张图片,iphone 设备的 WiFi 列表中竟然出现了「🔑一键免费连接🔑」相关的提示,我用自己的手机测试了下,果真如此:

WiFi 破解效果列表

软件对 WiFi 密码的攫取

获取安装该软件用户连接过的 WiFi 的密码,这是 WiFi 万能钥匙需要攻克的第一个难题,攻克之后,从一个用户身上可以挖到 1-10 个 WiFi id 和 WiFi 密码。攻克的方式嘛,可以去网上搜罗下,很多保存下来的密码都是明文的,或者是只经过了简单的加密操作,如果用户 root 了自己的手机,软件可以随意获取。

显然,WiFi 万能钥匙有一个自己的云端,储存了大量从用户手机里攫取到的 WiFi 信息,每个储存单元应该包含了如下信息(按照重要性从上往下依次排列):

  • WiFi 容器的物理地址(MAC 信息)
  • WiFi 的密码
  • WiFi 名称
  • WiFi 容器的区域信息
  • WiFi 容器的 IP 地址

由于民众对快速上网的极度渴望,偶然听到或者看到有这么个神器,当机立断将其下载到了手机上,在这两年时间间,该软件的用户量达到了 5 亿之多,月活跃用户在 2.3 亿(数据来自网络),其用户量之多、粘性之强,恐怕只有社交类的软件可以比拟了。由此,也可以想象 WiFi 万能钥匙的云端数据库有多么庞大。

WiFi 的连接

初次打开软件的时候,你应该看到了它会向你申请「获取位置信息」的请求,目的有两个,第一是获取你所在区域的大概位置,然后将该位置附近的 WiFi 信息全部缓存到你的客户端,这样做可以大大地减少对服务器的压力,其二,目前 WiFi 万能钥匙也会做一些商品/商家的推广,拿到位置信息方便个性化投放。

1. 密码匹配

以前手机没有提供权限给它获取 WiFi 列表,所以软件会引导用户将 WiFi 列表界面截图,然后通过图片分析拿到 WiFi 名称。而如今,iOS 设备不仅提供了获取周边 WiFi 列表的权限,而且还允许软件对每个 WiFi 进行文字备注,如最上面破解效果列表图所示。

所以我猜测,以前软件只能通过 WiFi 名称进行匹配,而现在可以使用 WiFi 的其他信息如(MAC 地址)进行匹配,匹配度更高,因为 WiFi 名称可能会存在重复问题。

2. 撞库分析

拿到了几个亿的数据,自然少不了对数据进行统计和分析,拿到一些常用的弱口令,如八个8、四个123、八个0等,了解路由设置和 WiFi 设置的人本来就不多,很多上门服务的师傅一般就将密码设置成简单好记的,这也很大程度提高了撞库的成功率。对于拿不到密码的 WiFi,软件毫无疑问会作出这种尝试,成本低、成功率还高。

3. 暴力破解

暴破应该不会用于实时的密码获取,而是会在沉默状态下,对未知 WiFi 进行暴力破解,破解成功的 WiFi 上传到云端服务器。这种方式可能在软件上线的初期使用,只是我的一种猜测。

被蹭网存在的危害

如果家里的网络被小白用户蹭了,无非就是大家同时上网的时候,网速会慢一点,而如果你家网络被一个具备黑客素质的人蹭上了,这个时候可能需要引起注意了。

如果你家里有 WiFi,那么一定会有一个路由器吧,路由器的密码还是初始状态的 guest/admin 么,或者被你设置成了六个8?如果我是这个攻击者,一定会想各种办法拿到你们家路由器的密码,如果运气好进去了,下一步要做的事情就是把路由器的网关设置成我自己的电脑,然后各种截获和注入。

如果没有攻克路由器,也可以利用在一个局域网内的条件,通过共享、网络广播等各种欺骗手段忽悠小白用户上当,方法总是很多的。只要打开一个口子,基本上你的手机/电脑就被控制了,投毒、欺骗、诱骗等,能用上的都会用上。

如何防止被蹭

你手机上没有安装 WiFi 万能钥匙,也没有将密码告诉旁边的邻居、路人,结果发现自己的网络还是被蹭到了。为啥呢?回想下,原来上个月你家外甥过来了,然后他手机上有这个软件…

防止被蹭的最好的方式就是,密码不告诉任何人,即便是外甥。现在的路由器默认可以设置两个 WiFi,并且可以对 sub-WiFi 进行流量限制。当然,有些 WiFi 做的比较成熟,可以通过自己的手机监控连接的设备,然后设置白名单和黑名单。不过估计用这种 WiFi 的人不会很多,一般的 WiFi 也支持在 Web 界面上控制上网设备。

还有一种方式是,在路由器中,将无线设置的 SSID 广播改为「隐藏」,周边设备就没办法找到你家的 WiFi 网络了。

常规的 WiFi 热点设置原理

分享一段跑题的内容。

以前使用 Window/Linux 系统的时候,尝试过将自己的电脑作为热点把网络分享给其他同学,刚开始使用了叫做 猎豹WiFi 的软件,后来自己也开始敲代码折腾,事实上,在 window 下一行代码就能产生一个无线热点:

netsh wlan set myWiFi mode=allow ssid=YOUR_WiFi_NAME key=WiFi_PASSWORD

上述命令会在系统的某个位置生成一个文件,其中 key 是明文保存的,然后通过如下命令就能开启 WiFi:

# 开启 WiFinet wlan start myWiFi # 关闭 WiFinet wlan stop myWiFi

Linux 下稍微费劲些,配合 hostpd 和 dnsmasq,不过也是差不多几行代码的事情。

最后

本文主要是对 WiFi 万能钥匙这个软件相关功能的一些猜测,具体如何实现,可以去网上观摩下骇客们对软件代码的反编译。

本文同步自 小胡子哥的个人网站,原文地址: http://www.barretlee.com/blog/2016/02/23/wifi-key/

WiFi 万能钥匙原理和危害探究

页面跳转时,统计数据丢失问题探讨

为了更好地了解用户对产品的使用情况,业务中,我们经常会收到埋点统计的需求,比如:

  • 收集一段时间内用户光标在页面中的运动情况,包括光标移动、点击等行为
  • 统计用户滚屏行为
  • 统计用户在站点的停留时长
  • 收集页面链接的点击数量等

无论是移动端还是 PC 端,相信很多朋友都遇到了这么几个十分让人头疼的问题:

  • 统计某个链接的点击量,但是这个链接点击后直接跳转走了
  • 统计页面时长问题,unload 的时候发送的统计丢失了
  • 统计脚本还没有初始化,用户不感兴趣已经走人了等

如果我们把这样的数据交给了产品同学,可能会让他们对用户行为产生错误的认知,一定程度上影响产品的下一步改善。

传统解决方案

上面提到的问题,从技术角度可以归纳为两点:

  • 用户关闭页面过早,统计脚本还未加载/初始化完成
  • 用户关闭或者跳出页面的时候,请求未发出

针对第一点,这个概率比较小,一般的处理方式,就是不要把统计脚本参合到其他脚本中,单独加载,并且放在前头,让它优先加载。很多公司的做法是,开发者不需要关心统计脚本的加载,用户请求页面的时候,Nginx 会在 Body 开始标签位置注入一段脚本。

对于问题二,处理方案就有很多了。

1. 阻塞式的 Ajax 请求

还记得 XMLHttpRequest::open 方法的第三个参数吧,如果设置为 false 就是同步加载,

window.addEventListener('unload', function(event) {  var xhr = new XMLHttpRequest(),  xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");  xhr.open('post', '/log', false); // 同步请求  xhr.send(data);});

阻塞页面关闭,当然可以在 readState 为 2 的时候就 abort 请求,因为我们不关心响应的内容,只要请求发出去就行了。

2. 暴力的死循环

原理跟上面差不多,只不过是使用一个空的死循环阻塞页面关闭,

window.addEventListener('unload', function(event) {  send(data);  var now = +new Date;  while(new Date - now >= 10) {} // 阻塞 10ms});

3. 发一个图片请求阻塞

大部分浏览器都会等待图片的加载,趁这个机会把统计数据发送出去

window.addEventListener('unload', function(event) {  send(data);  (new Image).src = 'http://example.com/s.gif';});

以上提到的几个方案都是一个原理,让浏览器继续保持阻塞状态,等数据发送出去后再跳转,这里存在的问题是:

  • 这些方案只在部分浏览器下奏效
  • 等待一会儿再跳转,用户体验上打了折扣,尤其是移动端上

是否有更好的方案解决这个问题呢,前端同学秉着「小强精神」也提出了两个可实践的方案。

优化方案

不就是埋点统计数据嘛,非得在当前页面发送出去?优化方案的思路具有一定的跳跃性,我们考虑将数据在下跳页中发送,那么问题就转换为,如何将数据传递给下跳页?

对于链接点击量的统计,我们可以将链接信息通过 url 传递给下跳页,传递思路如下:

1. url 传参

通过数组标识一个链接的位置信息,如 [站点id,页面id,模块id,链接index],通过四个参数可以惟一标识链接位置属性,使用 URL param 参数将这个数组数据传递给下跳页,然后由下跳页将数据发送出去。

这里存在的问题是,下跳页中必须部署同样的统计脚本,这个对一个系统来说,是很容易做到的。一般来说,我们页不会在自己的网页上放其他网站的链接吧,所以整个数据的统计都在一个闭环内。

2. 通过 window.name 传递数据

window.name 是浏览器给我们开发的一个接口,设置该属性的值后,即便页面发生了跳转,这个值依然不会变化,并且可以跨域使用。

这里存在的问题是,该属性可能被开发者用于其他途径。我们可以限制开发者直接使用 window.name,封装接口,通过接口调用,如 aralejs 提供的 nameStorage

nameStorage.setItem(key, value);nameStorage.getItem(key);nameStorage.removeItem(key);

储存形式为:

    scheme                  nameStorage datas      |                            |------------           ------------------------nameStorage:origin-name?key1=value1&key2=value2            -----------                 |         window origin name

虽然基本解决了数据丢失和体验差的问题,但是这就很大程度依赖开发者的编程习惯,不能随便玩耍 window.name;也对系统有一定的要求,必须在所有页面上部署同样的埋点脚本。

这件事情应该交给浏览器来解决

上面提到的各种方案,不乏黑科技,然而存在的问题还是一大堆,如果团队的开发者执行力不够,中途容易出现各种麻烦。真正能够解决这个问题的,当然是浏览器本身!

为什么不能给用户提供这样一个 API,即使页面跳转了,也能够将上个页面的请求发出去呢?庆幸的是,W3C 工作组也想到了这个问题,提出了 Beacon API草案

Beacon API 允许开发者发送少量错误分析和上报的信息,它的特点很明显:

  • 在空闲的时候异步发送统计,不影响页面诸如 JS、CSS Animation 等执行
  • 即使页面在 unload 状态下,也会异步发送统计,不影响页面过渡/跳转到下跳页
  • 能够被客户端优化发送,尤其在 Mobile 环境下,可以将 Beacon 请求合并到其他请求上,一同处理

sendBeacon 函数挂在在 navigator 上,在 unload 之前,这个函数一定是被初始化了的。其使用方式为:

window.addEventListener('unload', function(event) {  navigator.sendBeacon('/collector', data);});

navigator.sendBeacon(url, data);,第一个参数为数据上报的地址,第二个参数为要发送的数据,支持的数据格式有:ArrayBufferView, Blob, DOMString, 和 FormData。

Beacon 的还有一个非常实用的移动端使用场景,当用户从浏览器切换到其他 app 界面或者 Home 屏的时候,部分浏览器默认会停止页面脚本的执行,如果在这个时候使用了 unload 时间,可能会让你失望,因为 unload 事件并不会触发,此时,Beacon 就派上用途了,它是不会受影响的:

document.addEventListener('visibilitychange', function() {  if (document.visiblityState === 'hidden') {    var sessionData = buildSessionReport(); // 收集数据    navigator.sendBeacon('/collector', sessionData);  }});

最后

本文是对页面打点丢失问题的简单探讨,枚举了我们通常会用到的一些解决方案,可能不是很完善,如果你有更好的建议,可以提出来。

很多问题,我们绞尽脑汁,可能很少会考虑,这个问题是不是应该有我们来解决,或者说这个问题交给谁处理是最恰当的。本文的探讨可以看到,浏览器本身才是最好的问题解决方,当网站流量变大之后,上面提到的丢失问题就更加明显,这也迫使浏览器本身做了改善,自然也在情理之中。

本文同步自 小胡子哥的个人网站,原文地址: http://www.barretlee.com/blog/2016/02/20/navigator-beacon-api/

页面跳转时,统计数据丢失问题探讨

当前端也拥有 Server 的能力

今天看了不少文章,比较感兴趣的是 Cache API。它是浏览器 Request/Response 的缓存管理工具,其使用风格和运用场景让我瞬间联想到了 ServiceWorker 和 Fetch API,相信很多同学也多次看到过这两个东西,本文会对它们做一个简洁的介绍,并谈一谈我对这些新玩具的看法。

Fetch API

传统的 XMLHttpRequest,出了两个版本,在 XHR2.0 中引入了跨源请求、上传进度事件和对二进制数据的支持等,这些 API 的增强让 AJAX 可以很方便地与 HTML5 API 相结合,例如 File System API、Web Audio API、WebGL 等,让前端对音视频的处理和富客户端元素的处理更加有亲和力。

作为一个与后端交互的通道,XHR2.0 的接口封装依然过于底层。看看 jQuery 对 AJAX 的封装,再回头看看我们今天要介绍的 Fetch API,不得不惊叹,浏览器已经在应用层面思考着功能的拓展,依托着 Promise 产出了十分友好的新一套接口。

以前我们使用 XHR 去请求一个资源,会这么做:

// Just getting XHR is a mess!if (window.XMLHttpRequest) {  request = new XMLHttpRequest();} else if (window.ActiveXObject) {  try {    request = new ActiveXObject('Msxml2.XMLHTTP');  }   catch (e) {    try {      request = new ActiveXObject('Microsoft.XMLHTTP');    }     catch (e) {}  }}request.onreadstatechange = function(){  // handle data;};request.open('GET', 'http://barretlee.com/test.json', true);request.send(null);

而使用 Fetch API,我们只需要:

fetch('http://barretlee.com/test.json').then(function(response) {   // Convert to JSON  return response.json();}).then(function(val) {  console.log(val); });

对于 Text/HTML 和 Blob 等格式的请求和转化也是异常方便:

// Text/HTML 请求fetch('/next/page').then(function(response) {    return response.text();}).then(function(text) {  console.log(text); });// Blob 流fetch('flowers.jpg').then(function(response) {  return response.blob();}).then(function(blob) {  document.querySelector('img').src = URL.createObjectURL(blob);});

Fetch API 让我们更加关注请求和响应之间的交互,而不是聚焦在如何请求和如何处理响应两个问题上。

当然,它也存在几个相比 XHR 不足的地方,首先它不能 abort 请求,同时也不能获取请求过程中的 progress 状态,当然也没有 timeout 超时处理。Fetch API 是基于 Promise 的,而 Promise 的状态只有 pending、resolve、reject,不会出现诸如 pending(80%) 的状态提示;我们也无法对一个 Promise chains 做 abort 处理,这些都是能够理解并且接受的。

我也相信,Fetch API 有能力提供这些状态信息和附加的 API,只是在这个不成熟的环境下,它目前不需要迈这么大的步子。

ServiceWorker

ServiceWorker,简单而言就是一个放在前端的 HTTP 拦截器,比如我们要请求一个不存在的 URI 如:/test/a.html,直接请求就会响应 404,而如果我们预先在 ServiceWorker 中注册了这个地址,并且指定响应内容,当再次请求时,你会看到结果是存在的,举个例子:

<!—demo.html—><script>navigator.serviceWorker.register("worker.js", {  scope: ”/test/a.html"}).then(function(){  fetch(‘/test/a.html’).then(function(response) {    return response.text();  }).then(function(text) {    console.log(text);   });});</script>

在 demo.html 文件中,我们看到,将 /test/a.html 的请求交给 worker.js 来处理,处理方式为:

// workker.jsaddEventListener("fetch", function(evt) {  evt.respondWith(new Response(“Hi, Barret Lee”));});

demo.html 的回调中使用 Fetch 获取/test/a.html 这个并不存在的内容,被 ServiceWorker 捕获,交给 worker.js 处理并响应 Hi, Barret Lee 的文本,整个设计思路十分清晰,很轻松地拦截了来自客户端的请求,并作出了响应。

由于 ServiceWorker 是对 Promise 友好的,响应时也可以模拟服务器休眠状态:

addEventListener("fetch", function(evt) {  evt.respondWith(new Promise(function(resolve, reject){    setTimeout(function(){      resolve(new Response(“Hi, Barret Lee”));    }, 1000);  }));});

由于 Fetch API 提供了对 Header 头的修改,我们几乎可以利用 ServiceWorker 实现真实 HTTP Server 的基本功能。

ServiceWorker 一定程度上改变了 Web 协作的交互模式,传统情况下,我们需要开启一个 Web Server,或者让其他人提供 HTTP Server,前后端之间交互,沟通成本比较高。而 ServiceWorker 把 HTTP Server 搬到了客户端,我们可以在浏览器上轻松 Hold 住两端的操作。这也算是 Web 技术栈融合的表现吧。

当我们的目光放在 HTTP 的交互上,ServiceWorker 会有无限的想象空间,比如对 History API 的延伸思考,跨页面共享问题,前端请求合并和分拆问题,mock 数据问题,前后端的联调问题,类 graphQL 问题,数据的缓存更新和复用问题等等。

Cache API

Cache API,简而言之就是一个 Request/Response 的缓存对象组,它的生命周期跟 ServiceWorker 是紧密相连的,它没有失效时间,不删除就会一直保持原样。

caches.open('test-cache').then(function(cache) {  cache.add('/index.html');});

一个简单的操作,就将 /index.html 这个页面缓存了下来,如果你使用的是最新版的 Chrome,可以打开 DevTools > Resources > Cache Storage,多了一个 test-cache 的缓存表,表中多出一项,Request 为 http://barretlee.com/index.html, Response 为 OK。如下方式可以查看缓存内容:

caches.open('test-cache').then(function(cache) {   cache.keys().then(function(cachedRequests) {     console.log(cachedRequests);  });});

当浏览器处于 idle(空闲) 状态的时候,会将 Cache 资源预加载到本地。这也让我想起了 link 标签中有一个 prefetch 功能,也会有同学想到 Manifest,不过这两个东西都是不能友好控制的,而 Cache 给我们带来了这样的便利。

小结

我一直相当看好 Fetch API 系列相关的新接口,它的特点也很清晰,首先是基于 Promise 的实现,这个实现解决了回调和状态控制的问题,然后是提供了应用级别的接口访问,现在可以把一个 HTTP 请求作为可控的对象随意操作,无论是 Request 还是 Response 都在我们的掌握之中,同时也一定程度解决了跨页面资源共享的问题(至于跨页面通讯,我们有 postMessage 和 MessageChannel 等工具)。

目前浏览器对 Fetch API 和 ServiceWorker 的支持都是比较可观的,虽然 W3C 上的文档状态还是 Draft 模式,相信随着我们对业务需求的更加明确,对前端认知的的不断深入,这些东西将很快被定为 RFC。

本文没有对 API 的使用做深入的说明,一方面是因为这些东西能在 Google 上找到,其次,我觉得有些 API 的设计上还不够成熟,今后会有增删,感兴趣的同学可以去 W3C 提供的文档中深入学习下。

拓展阅读

本文同步自 小胡子哥的个人网站,原文地址: http://www.barretlee.com/blog/2016/02/16/when-fe-has-the-power-of-server/

当前端也拥有 Server 的能力

开启命令行下的社交

最近一直在命令行下工作,除了 Google Chrome,几乎很少接触 GUI 相关的软件。前段时间把手机上的 QQ 给卸载了,希望可以把时间凝聚在更加有价值的位置,今天突然又想起了这个软件,突发奇想,在命令行下玩弄 QQ。

在知乎和 V2ex 上搜了一番,在 github 上找到了一个还比较满意的开源项目,使用 perl 语言编写的,虽然不动 perl ,但是人家提供了丰富的 API 可以调用,于是就深入了解了下。

img

Mojo-Webqq 的安装和使用

项目名称叫做 Mojo-Webqq,它应该算是 smartQQ 的客户端非 GUI 框架,前几年玩 Linux 的人可能对 smartQQ 比较了解,就是一个网页上跑的 QQ,不过现在已经更名为 WebQQ 了,玩耍地址:http://web2.qq.com/

估计作者也是一个 Linux 玩家,所有的安装指南都是 Linux 上的说明,我用的 mac,也尝试按照 ReadMe 文档安装了下。

1.首先配置 cpan,直接在命令行输入 cpan 按照提示选择默认配置即可。

$ cpan

2.然后安装 cpanm 工具

$ can -i App:coanminus

3.使用 cpanm 在线安装 Mojo:Webqq 模块

$ cpanm -v Mojo::Webqq

不了解 Perl 语言,也不知道 cpanm 是个什么东西,估计跟 Nodejs 的 npm 是一样的,包管理工具。

如果期间安装失败,很可能是某个依赖包安装不成功,这个时候多留意下错误提示,然后 google 搜索怎样安装才是正确的姿势。

使用方式就比较简单了,创建一个实例跑起来:

#!/usr/bin/env perluse Mojo::Webqq;my ($qq,$host,$port,$post_api);$qq = 12345678;    #修改为你自己的实际QQ号码$host = "0.0.0.0"; #发送消息接口监听地址,修改为自己希望监听的地址$port = 5000;      #发送消息接口监听端口,修改为自己希望监听的端口$post_api = 'http://xxxx';  #接收到的消息上报接口,如果不需要接收消息上报,可以删除此行my $client = Mojo::Webqq->new(qq=>$qq);$client->login();$client->load("ShowMsg");$client->load("Openqq",data=>{listen=>[{host=>$host,port=>$port}], post_api=>$post_api});$client->run();

上述代码保存成 xxxx.pl 文件,然后使用 perl 来运行,就会完成 QQ 登录并在本机产生一个监听指定地址端口的 http server,发送好友消息的接口调用示例:

curl http://127.0.0.1:5000/openqq/send_message?qq=xxxxx&content=hello

具体可以翻阅 文档说明

IRC 相关学习

以前玩 Linux 的时候就接触过一些 IRC 的客户端,当时感觉找到了这个世界对程序员开放的窗口,各种技术 Channel,各种交流,很是激动(当然,现在不以为然)。

Google 找了下网上的推荐,一般都是使用 Weechat 或者 irssi。两个软件的安装都比较麻烦,依赖了很多软件包,不说安装和编译时间,就依赖软件包的下载时间就有半小时。

先选用的 Weechat ,安装好了之后,死活调不好中文设置,不知道这样是不是正确的:

/charset decode GB2312

反正我是没搞好,但是学会了 IRC 的基本使用。后面还是改用成 irssi,展示没有 weechat 友好,不过默认支持中文输入。

IRC 的使用,我觉得也不用太多地去看文档,进入交互命令行之后,键入 /help,系统会把所有的命令都打印出来,然后你感觉应该用哪个就去继续学习就行了,比如连接到一个频道,可以键入

/help server

或者

/help connect

这些关键词都是从 /help 中找到的,以 weechat 为例,给出几个设置命令以供入门。

# 添加一个 server/server add free node chat.freenode.net# 自动链接到 freenode/set irc.server.freenode.autoconnect on# 设置 nicks,username,realname/set irc.server.freenode.nicks “nickname”/set irc.server.freenode.username “username”/set irc.server.freenode.realname “realname”

输入框中键入 /connect freenode 就可连接到 freenode 的服务器,输入 /join #javascript 就可以加入到 #javascript 群组里了。

# 自动认证 nickname/set irc.server.freenode.command "/msg nickserv identify xxxxxx"# 自动加入群组/set irc.server.freenode.autojoin "#channel1,#channel2"

对鼠标的支持:

# 启动鼠标支持/moune enable# 打开时就支持/set weechat.look.mouse on

更多文档,可以在 google 中检索下。

有好多天没有码字了,今天学习的主题是 Mojo-Webqq 和 IRC,就先说这么多,后续会把每天学习和关注的知识点都记录下,方便自己,也方便他人。

本文同步自 小胡子哥的个人网站,原文地址: http://www.barretlee.com/blog/2016/02/14/mojo-webqq-and-irc/

开启命令行下的社交