Backbone源码研究 – Backbone.Model

前言

都因为 IE8 不支持 Object.defineProperty,但是业务还不能脱离 IE7 和 IE8,故研究下 Backbone.Model 的实现机制,找机会给主流的 MVVM 框架补丁

伪代码

先来看看 Model 的构造函数

很简单的代码,做了一些初始化赋值的事情。

用到了一个小技巧  attrs = _.defaults(_.extend({}, defaults, attrs), defaults);  来防止误传入的 undefined 覆盖掉默认的 defaults 值。

Backbone 的精粹都在 set(){} 这个函数里面。

整个 set 里面,实际干活的就是 unset ? delete current[attr] : current[attr] = val;  。

没看明白 this._changing 和 this._pending 的使用场景,感觉是一个当多个 set 同时执行时候的一个标记位,但是 JS 是单线程执行,里面又都是 for 语句,按理说可以不用这两个标记位。又或者是我的理解有误。

more

看到这,给各种Observer打补丁就有了可行性,支持 Object.defineProperty 就用 Object.defineProperty,不支持的则降级到走 Backbone 的这种 for in 方式。

涨姿势 – 不一样的服务端长连接方案 – 客户端代理

仔细再看一次腾讯云的小程序解决方案,发现一个新大陆。

传统的 LAMP 架构,PHP-CGI  这种方式是很难处理长连接的。要么写死循环的方式来握住请求,要么使用 swoole 这种,通过 C 拓展来支持。

而 NodeJS 由于官方 DEMO 就是支持跑一个 HTTP 服务,所以处理这些长连接会方便一些(大家好懂一些,我觉得 PHP-CLI 方式也是一样的)。

信道服务

建立连接过程

  1. 小程序请求业务服务器
  2. 业务服务器和信道服务建立连接
  3. 业务服务器告诉小程序你可以和信道服务建立 ws 了
  4. 小程序和信道服务建立 ws
  5. 信道服务请求业务小程序的 ws 建立完成

连接成功后的通信方式

  1. 小程序请求信道服务,信道服务转发请求给业务
  2. 业务请求信道服务,信道服务推送到客户端

我最开始看的时候,没注意信道服务是一个云服务,琢磨着腾讯云的 SDK 难道有什么新的黑魔法来实现 PHP 的长连接。

https://github.com/tencentyun/wafer-php-server-sdk/blob/master/lib/Tunnel/TunnelService.php

看里面的代码,各种 onConnect\onRequest,看着就很像长连接的 API,但他基于 CI 是怎么实现的长连接了?看 composer.json 里面没有用什么黑魔法,搜索代码里面没没看到任何死循环。

客户端代理

为什么信道服务的 icon 是一朵云?

原来腾讯云把这个信道服务抽象成了一个 PaaS 的云服务,这根本就是一个客户端代理。而开源出来的 PHP-SDK,里面没有任何长连接的实现方案。

  • 业务服务器 -> 小程序:业务服务器只需要请求信道服务即可,信道服务接收到业务的请求后,会将 HTTP Body 部分,转成 WS 的消息推送给小程序。
  • 小程序 -> 业务服务器:则正常通过 WS 发送消息给信道服务,信道服务转成 HTTP 请求转发到业务服务器。

对于一些使用 PHP 开发的历史业务,大规模的长连接改造是非常困难,但部署一个长连接转发服务却容易很多(比如用 NodeJS 来写一个)。

腾讯云的这个思路可以帮助各种业务快速支持包括 SSE 和 WS 这两种长连接方案。并且由于业务服务器本身是 HTTP 方式,可以很容易给低版本 IE 做轮训兼容。

最后感谢腾讯云开源了他们的 wafer 方案。

preconnect & more

Resource Hints

preconnect 出现在 w3 组织 16 年制订 《Resource Hints》

《Resource Hints》是一套预加载机制的 w3 标准化产物,在没有标准化之前,我们常用诸如 XMLHttpRequest 等手段来预加载我们未来会使用到的资源。

《Resource Hints》文档通过 <link> 标签,定义了4个新的属性值。

PreConnect

文档一定定义了4个新属性值,今天由于某个案例,我们只讨论 preconnect。

The user agent should attempt to initiate a preconnect and perform the full connection handshake (DNS+TCP for HTTP, and DNS+TCP+TLS for HTTPS origins) whenever possible, but is allowed to elect to perform a partial handshake (DNS only for HTTP, and DNS or DNS+TCP for HTTPS origins), or skip it entirely, due to resource constraints or other reasons.

The optimal number of connections per origin is dependent on the negotiated protocol, users current connectivity profile, available device resources, global connection limits, and other context specific variables. As a result, the decision for how many connections should be opened is deferred to the user agent.

preconnect 用于客户端与目标资源服务器,在请求资源前,预先完成 DNS 查询 + TCP 握手(如果是 HTTPS,会把 TLS 握手也预先完成),当客户端需要请求目标资源的时候,下一个 TCP 首包就可以直接发送 HTTP 的有效载荷,省去握手耗时。

需要注意规范仅仅是一个建议行为,具体处理细节由浏览器根据当时环境决定。

Caniuse

qq20161206-02x

恩,Chrome、Firefox、Android 5.x 开始支持这个特性。 继续阅读“preconnect & more”

来聊聊 DOM 中的Node、Element、Text

Node

Node 是整个 DOM 的主要数据类型。

常见的 NodeType :

  • 1 – ELEMENT_NODE 表示 element 元素
  • 2 – ATTRIBUTE_NODE 表示属性
  • 3 – TEXT_NODE 表示元素或属性中的文本内容
  • 4 – CDATA_SECTION_NODE 表示文档中的 CDATA 区段(文本不会被解析器解析)
  • 8 – COMENT_NODE 表示注释
  • 9 – DOCUMENT_NODE 表示整个文档(DOM 树的根节点)
  • 11 – DOCUMENT_FRAGMENT_NODE 表示轻量级的 Document 对象

1所表示的ELEMENT_NODE 很常见,我们平时用的 div 等标签,其类型都是 ELEMENT_NODE。

3有一个很经典的案例,在旧版的 React 中,如果一段文本模板存在变量,你会发现最终输出的字符串,在可变部分是被套了一层 span 标签的。但新版的 React 不需要了,这是因为新版 React 把每一个可变的文本,单独用一个TEXT_NODE 来存放。这里果真记错了,React v15是改用 COMENT_NODE 来包裹连续文本中的可变文本。为什么要包裹而不直接保留一份 TextNode 的引用呢?React v15 CHANGELOG

11也有一个经典案例,Vue.js 的1.x 版本就是使用的 documentFragment 来做 virtualDOM。documentFragment 支持完整的 DOM 操作,但由于本身不在文档流中,频繁操作不会导致浏览器频繁执行 parseHTML。

Element

这个没什么好说的,平常用得最多的了。

我们平常习惯在 jQuery 选择器后面加索引,操作的就是元素节点。

Text

Text 节点表示 HTML 或者 XML 文档中的一系列纯文本。

Text 节点没有子节点,不可再分。

那怕是简单的 Element 节点,其文案内容,都是在 Text 节点上。

结语

富文本编辑器真是前端界里面的深坑,后续还有 Range 和 Selection 相关内容。

参考

诡异的iOS keep-alive bug

https://bugs.webkit.org/show_bug.cgi?id=155632

去年12月发现,偶发性,各种Charles抓包、rvictl流量复制等方式来定位分析,还恶补了TCP协议,都没找到原因,只是看上去觉得iOS这边处理HTTP请求的方式怪怪的。

终于尘埃落定,服务端根据UA识别出iOS,关掉HTTP的Keep-Alive功能来规避。

keep-alive是HTTP协议里面的

keepalive是TCP协议里面的

HTTP协议通过头部的 connection: keep-alive 来通知两端TCP建立一个keepalive链接,在keepalive有效期内,不需要重复3次握手动作,不需要重新慢启动

 

读书笔记《React-引领未来的用户界面开发框架》

React这么火,我们也来深入研究下吧。

买了本React相关的书籍,刚看了前十章,随手记一下读后感吧。

废话篇

这部分内容,人云亦云

垂直切分的组件

React对于配置多名前端开发的团队,在协作上有一定的优势。

其组件化思路,是一种垂直划分,每个组件高度自治。与我们习惯上的Html、JS、CSS三分离的水平划分思路不一样。

垂直划分,让每个组件自己决定自己的结构、行为、表现,调用方只需要拿来即可使用。使用JSX来定义组件结构,通过Sytle对象来inline样式属性。

这里有两个不爽的地方。

  • JSX语法太丑陋
  • style对象权重太高,外链样式难以做正常的样式覆盖

JSX语法问题,还好IDE能高亮,看上去稍微舒服点。内联style权重问题比较难解决,最近WebRebuild上萝卜分享过一个css_module的解决方案,挺暴力,但又十分有效。

枚举一切可变字段(state)

其实在Backbone的年代,已经有这样的东西,只是没有强调而已,且没有那么智能的去更新界面(Backbone需要手动监听字段变化,然后去执行对应的方法,一切都要手动)

在React的世界里面,一切的讨论都围绕着这些可枚举的state。为了能高效的实现刷新界面,大家都乐意去细化界面上每个可变元素,将其与组件的state映射起来(其实就是在JSX里面包个{this.state.something})

setState() => componentDidUpdate

由于所有可变因素(state)都被枚举出来了,且界面发生变化的原因有且仅有一个,这让React组件变得可预知性可测试性

大肚能容

ref、getDOMNode()、componentDidMount()、componentWillUnmount()让React有了兼容其他非React生态的JS库\组件的能力,这个还是挺赞的,对于已有业务,改造起来也稍微方便点。

高能篇

这部分脑洞比较大

论setter、getter的重要性

一个好框架\库,需要有一个统一的输入输出接口

在React里面,有一个很重要的概念,是一切改变,都必须通过setState()方法来传达。只有这样,所有的变化才是可控的、可预测的。

setter、getter这两个方法,其实在各种各样的库里面都有,但没有像React世界里面这么强调。

例如在某个中间环节,为了图快,时不时就出现直接修改原始对象属性的情况。这种变动,框架层面是没法侦测到的。

又例如读取某个带嵌套关系的对象,没用getter,一个不小心就把原始对象的引用给暴露的出来,然后极容易出现在某些边边角角发生引用被改动从而触发一些很隐晦的BUG。

论钩子的重要性

一个好框架\库,需要有丰富的外部钩子,便于拓展

WordPress占有率高吧,为啥?因为他易于定制、拓展,他有非常丰富完善的钩子机制来给各种主题、插件提供定制拓展能力。

React也有很多钩子,他强调的生命周期,其实就是一系列的钩子,给业务能非常容易的在想定制拓展的地方进行定制拓展。

Backbone有钩子吗?有,但少得可怜,没记错的话,Backbone.View默认只有initialize和render两个钩子,React组件单单存在期的钩子都比他多。

Marionette则弥补了Backbone.View在钩子上面的缺失,可惜太小众。

钩子要怎么做?简单来说就是在框架、库的生命周中埋下大量空函数供拓展的时候覆盖就好了。

未完待续

原生OR模拟

我最近又陷入了模拟控件和原生控件的纠结中。

早期我们做PC端网页开发,就已经讨论过一次模拟控件(表单)和原生控件(表单),当初各种各样的理由,我们很推崇原生控件(标准化、语义化、渐进增强)

但是最近这两年接触Mobile端的网页开发后,我的立场摇摆了。

原生控件

优点:使用简单,”兼容性强”

使用简单很好理解,表单元素就那么几个,其DOM Event和DOM Attr也屈指可数,使用起来非常方便。

为什么要给兼容性强打上引号呢?因为单独使用,的确兼容性很强,Web标准。

缺点:“兼容性差”

为什么又是兼容性呢?主要体现在Mobile端的浏览器,对于可交互元素,尤其是表单元素,有很多特殊的处理。

FastClick好用吗?iScoll好用吗?都很好用,但为什么网上那么多负面的评论?

去仔细看内部的源码,里面由非常多可交互元素的hack,并且都留有issue单。就是因为不同浏览器、甚至同一款浏览器的不同版本,对这种可交互元素的处理机制不一,导致各种各样未知bug。而这种bug,只能case by case的修复。

这里我又想黑一下自己的公司,X5也有很多case by case的自认为的对可交互元素的处理机制优化。

模拟控件

优点:完美还原交互,兼容性强

设计还原能力,就不用说了,自己写的业务实现,可以完美实现各种交互效果(当然仅限于当前科技范畴了)

而兼容性,因为采用的是最基础的手段,所以满大街的设备都会支持,并且都当做普通的DIV\CSS\JS来处理,不会有什么特殊情况,也不需要case by case的去解决一些兼容问题。

缺点:不标准,复用性不高,开发成本大

这种控件针对性很强,也就导致了复用性不高。

因为不是标准控件,所以所有东西都需要自己实现,大量边界条件需要自己想清楚,开发成本很大。

结语

其实我也不知道该怎么结束这个话题、我也在纠结中~~~

WebComponent也好,以前的htc也罢,还有React Canvas,不也是为了解决这种问题嘛(看上去这三个好像差很远,尤其是React Canvas。但其实都一样,都是用自定义的东西去统一实现输入输出,从而避免浏览器的差异化处理)。

也许未来移动端浏览器对于可交互元素的处理趋向一致和稳定后,我们的想法又会变得不一样。

 

最后补一句,我纠结的问题,是移动端富交互的场景,普通的展示型页面,还是该怎么来怎么来,不受影响的。