NPM速度太慢,感觉生命都浪费在了等待上

为了解决史前社会访问NPM速度慢,有聪明的人类发明了CNPM,速度快了不少,但是也有很多遗憾。

很多包安装并不会理睬CNPM的存在,例如react-natice, yeoman等等。装一半还是要Ctrl + C 中断,然后再 cnpm install,感觉相当不舒服。

npm 参数

—verbose 加参数看进度

为了解决安装时卡着没反应,聪明的人类发现了可以加参数,例如:

npm install hexo -g --verbose

这样可以一直看到进程,某种程度上缓解焦虑症,然并卵,它不能从根本上解决问题。

—ignore-scripts 跳过安装脚本错误

跳过一些安装脚本报错,反正先给我装上再说啊

npm install hexo -g --verbose --ignore-scripts

smart-npm

github

这里有个少年,想到了封装npm,按照操作来自动判断使用npm还是cnpm,感觉上智能了很多。只是侵入感太强,要修改很多东西。

nrm

github

另一个少年,想到了切换registry源,可以先测试各地npm镜像速度,然后修改注册源的地址,从而达到快速访问效果。

写在最后

如果可能的话,使用cnpm搭建私有源,配合nrm感觉上是最靠谱的。

如果有一个高速VPN,以上都不是问题了~

图像受版权保护,联必删

PS:这张封面图都没见过吧,哈哈

在线文档

书籍

设计模式

要翻墙才能看到!

书中提到的23种设计模式分别是:

  1. 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 模版方法模式
  • 建造者模式
  • 代理模式
  • 原型模式
  • 中介者模式 
  • 命令模式
  • 责任链模式
  • 装饰模式
  • 策略模式
  • 适配器模式
  • 迭代器模式
  • 组合模式
  • 观察者模式 
  • 门面模式
  • 备忘录模式
  • 访问者模式
  • 状态模式
  • 解释器模式
  • 享元模式
  • 桥梁模式
  • 桥梁模式

特定的原则

使用面向对象设计,可以设计出优秀的软件,同样也可以设计出糟糕的软件。只有遵循一些特定的原则,才能设计出复用性高灵活性好的软件来。
在运用面向对象的思想进行软件设计时,需要遵循的原则一共有6个,他们是:

  1. 单一职责原则(Single Responsibility Principle)
  2. 里氏替换原则(Liskov Substitution Principle)
  3. 依赖倒置原则(Dependence Inversion Principle)
  4. 接口隔离原则(Interface Segregation Principle)
  5. 迪米特法则(Law Of Demeter)
  6. 开闭原则(Open Close Principle)

别人的读后感

Docker Toolbox 介绍和安装

最近又开始用Docker,发现boot2docker已经升级成Docker Toolbox了,里面包含了:精简的Boot2Docker Linux镜像,用于运行Docker、MAC工具kitematic (由Docker不久前收购)和Docker Compose。

  • Docker Toolbox网站下载
  • 安装的时候还会自动migrate以前由boot2docker建的VM

基本的命令变化如下:





































































































boot2dockerdocker-machinedocker-machine description
initcreateCreates a new docker host.
upstartStarts a stopped machine.
sshsshRuns a command or interactive ssh session on the machine.
save-Not applicable.
downstopStops a running machine.
poweroffstopStops a running machine.
resetrestartRestarts a running machine.
configinspectPrints machine configuration details.
statuslsLists all machines and their status.
infoinspectDisplays a machine’s details.
ipipDisplays the machine’s ip address.
shellinitenvDisplays shell commands needed to configure your shell to interact with a machine
deletermRemoves a machine.
download-Not applicable.
upgradeupgradeUpgrades a machine’s Docker client to the latest stable release.

解决网速问题

由于访问外网环境恶劣,经常pull一个镜像要漫长痛苦的等待,为此,通常解决方案是在国外建VPS,使用Docker hub建私有镜像库;还有就是DaoCloud做Docekr国内镜像。

挖坑待填

Docker常用命令

挖坑待填

参考资料

当输入网址按下回车键之后发生了什么 (转)?

https://github.com/skyline75489/what-happens-when-zh_CN

What-happens-when 的中文翻译,原仓库 https://github.com/alex/what-happens-when

===================

这个仓库试图回答一个古老的面试问题:当你在浏览器中输入 google.com 并且按下回车之后发生了什么?

不过我们不再局限于平常的回答,而是想办法回答地尽可能具体,不遗漏任何细节。

这将是一个协作的过程,所以深入挖掘吧,并且帮助我们一起完善它。仍然有大量的细节等待着你来添加,欢迎向我们发送 Pull Requset!

这些内容使用 Creative Commons Zero_ 协议发布。

目录

.. contents::
:backlinks: none
:local:

按下”g”键

接下来的内容介绍了物理键盘和系统中断的工作原理,但是有一部分内容却没有涉及。当你按下“g”键,浏览器接收到这个消息之后,会触发自动完成机制。浏览器根据自己的算法,以及你是否处于隐私浏览模式,会在浏览器的地址框下方给出输入建议。大部分算法会优先考虑根据你的搜索历史和书签等内容给出建议。你打算输入 “google.com”,因此给出的建议并不匹配。但是输入过程中仍然有大量的代码在后台运行,你的每一次按键都会使得给出的建议更加准确。甚至有可能在你输入之前,浏览器就将 “google.com” 建议给你。

回车键按下

为了从零开始,我们选择键盘上的回车键被按到最低处作为起点。在这个时刻,一个专用于回车键的电流回路被直接地或者通过电容器间接地闭合了,使得少量的电流进入了键盘的逻辑电路系统。这个系统会扫描每个键的状态,对于按键开关的电位弹跳变化进行噪音消除(debounce),并将其转化为键盘码值。在这里,回车的码值是13。键盘控制器在得到码值之后,将其编码,用于之后的传输。现在这个传输过程几乎都是通过通用串行总线(USB)或者蓝牙(Bluetooth)来进行的,以前是通过PS/2或者ADB连接进行。

USB键盘:

  • 键盘的USB元件通过计算机上的USB接口与USB控制器相连接,USB接口中的第一号针为它提供了5V的电压

  • 键码值存储在键盘内部电路一个叫做”endpoint”的寄存器内

  • USB控制器大概每隔10ms便查询一次”endpoint”以得到存储的键码值数据,这个最短时间间隔由键盘提供

  • 键值码值通过USB串行接口引擎被转换成一个或者多个遵循低层USB协议的USB数据包

  • 这些数据包通过D+针或者D-针(中间的两个针),以最高1.5Mb/s的速度从键盘传输至计算机。速度限制是因为人机交互设备总是被声明成”低速设备”(USB 2.0 compliance)

  • 这个串行信号在计算机的USB控制器处被解码,然后被人机交互设备通用键盘驱动进行进一步解释。之后按键的码值被传输到操作系统的硬件抽象层

虚拟键盘(触屏设备):

  • 在现代电容屏上,当用户把手指放在屏幕上时,一小部分电流从传导层的静电域经过手指传导,形成了一个回路,使得屏幕上触控的那一点电压下降,屏幕控制器产生一个中断,报告这次“点击”的坐标

  • 然后移动操作系统通知当前活跃的应用,有一个点击事件发生在它的某个GUI部件上了,现在这个部件是虚拟键盘的按钮

  • 虚拟键盘引发一个软中断,返回给OS一个“按键按下”消息

  • 这个消息又返回来向当前活跃的应用通知一个“按键按下”事件

产生中断[非USB键盘]

键盘在它的中断请求线(IRQ)上发送信号,信号会被中断控制器映射到一个中断向量,实际上就是一个整型数 。CPU使用中断描述符表(IDT)把中断向量映射到对应函数,这些函数被称为中断处理器,它们由操作系统内核提供。当一个中断到达时,CPU根据IDT和中断向量索引到对应的中断处理器,然后操作系统内核出场了。

(Windows)一个 WM_KEYDOWN 消息被发往应用程序

HID把键盘按下的事件传送给 KBDHID.sys 驱动,把HID的信号转换成一个扫描码(Scancode),这里回车的扫描码是 VK_RETURN(0x0d)KBDHID.sys 驱动和 KBDCLASS.sys (键盘类驱动,keyboard class driver)进行交互,这个驱动负责安全地处理所有键盘和小键盘的输入事件。之后它又去调用 Win32K.sys ,在这之前有可能把消息传递给安装的第三方键盘过滤器。这些都是发生在内核模式。

Win32K.sys 通过 GetForegroundWindow() API函数找到当前哪个窗口是活跃的。这个API函数提供了当前浏览器的地址栏的句柄。Windows系统的”message pump”机制调用 SendMessage(hWnd, WM_KEYDOWN, VK_RETURN, lParam) 函数, lParam 是一个用来指示这个按键的更多信息的掩码,这些信息包括按键重复次数(这里是0),实际扫描码(可能依赖于OEM厂商,不过通常不会是 VK_RETURN ),功能键(alt, shift, ctrl)是否被按下(在这里没有),以及一些其他状态。

Windows的 SendMessage API直接将消息添加到特定窗口句柄 hWnd 的消息队列中,之后赋给 hWnd 的主要消息处理函数 WindowProc 将会被调用,用于处理队列中的消息。

当前活跃的句柄 hWnd 实际上是一个edit control控件,这种情况下,WindowProc 有一个用于处理 WM_KEYDOWN 消息的处理器,这段代码会查看 SendMessage 传入的第三个参数 wParam ,因为这个参数是 VK_RETURN ,于是它知道用户按下了回车键。

(Mac OS X)一个 KeyDown NSEvent被发往应用程序

中断信号引发了I/O Kit Kext键盘驱动的中断处理事件,驱动把信号翻译成键码值,然后传给OS X的 WindowServer 进程。然后, WindowServer 将这个事件通过Mach端口分发给合适的(活跃的,或者正在监听的)应用程序,这个信号会被放到应用程序的消息队列里。队列中的消息可以被拥有足够高权限的线程使用 mach_ipc_dispatch 函数读取到。这个过程通常是由 NSApplication 主事件循环产生并且处理的,通过 NSEventTypeKeyDownNSEvent

(GNU/Linux)Xorg 服务器监听键码值

当使用图形化的 X Server 时,X Server 会按照特定的规则把键码值再一次映射,映射成扫描码。当这个映射过程完成之后, X Server 把这个按键字符发送给窗口管理器(DWM,metacity, i3等等),窗口管理器再把字符发送给当前窗口。当前窗口使用有关图形API把文字打印在输入框内。

解析URL

  • 浏览器通过 URL 能够知道下面的信息:

    • Protocol “http”
      使用HTTP协议
    • Resource “/“
      请求的资源是主页(index)

输入的是 URL 还是搜索的关键字?

当协议或主机名不合法时,浏览器会将地址栏中输入的文字传给默认的搜索引擎。大部分情况下,在把文字传递给搜索引擎的时候,URL会带有特定的一串字符,用来告诉搜索引擎这次搜索来自这个特定浏览器。

检查 HSTS 列表···

  • 浏览器检查自带的“预加载 HSTS(HTTP严格传输安全)”列表,这个列表里包含了那些请求浏览器只使用HTTPS进行连接的网站
  • 如果网站在这个列表里,浏览器会使用 HTTPS 而不是 HTTP 协议,否则,最初的请求会使用HTTP协议发送
  • 注意,一个网站哪怕不在 HSTS 列表里,也可以要求浏览器对自己使用 HSTS 政策进行访问。浏览器向网站发出第一个 HTTP 请求之后,网站会返回浏览器一个响应,请求浏览器只使用 HTTPS 发送请求。然而,就是这第一个 HTTP 请求,却可能会使用户收到 downgrade attack_ 的威胁,这也是为什么现代浏览器都预置了 HSTS 列表。

转换非 ASCII 的 Unicode 字符

  • 浏览器检查输入是否含有不是 a-zA-Z0-9- 或者 . 的字符
  • 这里主机名是 google.com ,所以没有非ASCII的字符,如果有的话,浏览器会对主机名部分使用 Punycode_ 编码

DNS 查询···

  • 浏览器检查域名是否在缓存当中
  • 如果缓存中没有,就去调用 gethostbyname 库函数(操作系统不同函数也不同)进行查询
  • gethostbyname 函数在试图进行DNS解析之前首先检查域名是否在本地 Hosts 里,Hosts 的位置 不同的操作系统有所不同_
  • 如果 gethostbyname 没有这个域名的缓存记录,也没有在 hosts 里找到,它将会向 DNS 服务器发送一条 DNS 查询请求。DNS 服务器是由网络通信栈提供的,通常是本地路由器或者 ISP 的缓存 DNS 服务器。

  • 查询本地 DNS 服务器

  • 如果 DNS 服务器和我们的主机在同一个子网内,系统会按照下面的 ARP 过程对 DNS 服务器进行 ARP查询
  • 如果 DNS 服务器和我们的主机在不同的子网,系统会按照下面的 ARP 过程对默认网关进行查询

ARP

要想发送 ARP 广播,我们需要有一个目标 IP 地址,同时还需要知道用于发送 ARP 广播的接口的 MAC 地址。

  • 首先查询 ARP 缓存,如果缓存命中,我们返回结果:目标 IP = MAC

如果缓存没有命中:

  • 查看路由表,看看目标 IP 地址是不是在本地路由表中的某个子网内。是的话,使用跟那个子网相连的接口,否则使用与默认网关相连的接口。
  • 查询选择的网络接口的 MAC 地址
  • 我们发送一个二层 ARP 请求:

ARP Request::

Sender MAC: interface:mac:address:here
Sender IP: interface.ip.goes.here
Target MAC: FF:FF:FF:FF:FF:FF (Broadcast)
Target IP: target.ip.goes.here

根据连接主机和路由器的硬件类型不同,可以分为以下几种情况:

直连:

  • 如果我们和路由器是直接连接的,路由器会返回一个 ARP Reply (见下面)。

集线器:

  • 如果我们连接到一个集线器,集线器会把 ARP 请求向所有其它端口广播,如果路由器也“连接”在其中,它会返回一个 ARP Reply

交换机:

  • 如果我们连接到了一个交换机,交换机会检查本地 CAM/MAC 表,看看哪个端口有我们要找的那个 MAC 地址,如果没有找到,交换机会向所有其它端口广播这个 ARP 请求。
  • 如果交换机的 MAC/CAM 表中有对应的条目,交换机会向有我们想要查询的 MAC 地址的那个端口发送 ARP 请求
  • 如果路由器也“连接”在其中,它会返回一个 ARP Reply

ARP Reply::

Sender MAC: target:mac:address:here
Sender IP: target.ip.goes.here
Target MAC: interface:mac:address:here
Target IP: interface.ip.goes.here

现在我们有了 DNS 服务器或者默认网关的 IP 地址,我们可以继续 DNS 请求了:

  • 使用 53 端口向 DNS 服务器发送 UDP 请求包,如果响应包太大,会使用 TCP 协议
  • 如果本地/ISP DNS 服务器没有找到结果,它会发送一个递归查询请求,一层一层向高层 DNS 服务器做查询,直到查询到起始授权机构,如果找到会把结果返回

使用套接字

当浏览器得到了目标服务器的 IP 地址,以及 URL 中给出来端口号(http 协议默认端口号是 80, https 默认端口号是 443),它会调用系统库函数 socket ,请求一个
TCP流套接字,对应的参数是 AF_INETSOCK_STREAM

  • 这个请求首先被交给传输层,在传输层请求被封装成 TCP segment。目标端口会会被加入头部,源端口会在系统内核的动态端口范围内选取(Linux下是ip_local_port_range)
  • TCP segment 被送往网络层,网络层会在其中再加入一个 IP 头部,里面包含了目标服务器的IP地址以及本机的IP地址,把它封装成一个TCP packet。
  • 这个 TCP packet 接下来会进入链路层,链路层会在封包中加入 frame头 部,里面包含了本地内置网卡的MAC地址以及网关(本地路由器)的 MAC 地址。像前面说的一样,如果内核不知道网关的 MAC 地址,它必须进行 ARP 广播来查询其地址。

到了现在,TCP 封包已经准备好了,可以使用下面的方式进行传输:

  • 以太网_
  • WiFi_
  • 蜂窝数据网络_

对于大部分家庭网络和小型企业网络来说,封包会从本地计算机出发,经过本地网络,再通过调制解调器把数字信号转换成模拟信号,使其适于在电话线路,有线电视光缆和无线电话线路上传输。在传输线路的另一端,是另外一个调制解调器,它把模拟信号转换回数字信号,交由下一个 网络节点_ 处理。节点的目标地址和源地址将在后面讨论。

大型企业和比较新的住宅通常使用光纤或直接以太网连接,这种情况下信号一直是数字的,会被直接传到下一个 网络节点_ 进行处理。

最终封包会到达管理本地子网的路由器。在那里出发,它会继续经过自治区域的边界路由器,其他自治区域,最终到达目标服务器。一路上经过的这些路由器会从IP数据报头部里提取出目标地址,并将封包正确地路由到下一个目的地。IP数据报头部TTL域的值每经过一个路由器就减1,如果封包的TTL变为0,或者路由器由于网络拥堵等原因封包队列满了,那么这个包会被路由器丢弃。

上面的发送和接受过程在 TCP 连接期间会发生很多次:

  • 客户端选择一个初始序列号(ISN),将设置了 SYN 位的封包发送给服务器端,表明自己要建立连接并设置了初始序列号
  • 服务器端接收到 SYN 包,如果它可以建立连接:
    • 服务器端选择它自己的初始序列号
    • 服务器端设置 SYN 位,表明自己选择了一个初始序列号
    • 服务器端把 (客户端ISN + 1) 复制到 ACK 域,并且设置 ACK 位,表明自己接收到了客户端的第一个封包
  • 客户端通过发送下面一个封包来确认这次连接:
    • 自己的序列号+1
    • 接收端 ACK+1
    • 设置 ACK 位
  • 数据通过下面的方式传输:
    • 当一方发送了N个 Bytes 的数据之后,将自己的 SEQ 序列号也增加N
    • 另一方确认接收到这个数据包(或者一系列数据包)之后,它发送一个 ACK 包,ACK 的值设置为接收到的数据包的最后一个序列号
  • 关闭连接时:
    • 要关闭连接的一方发送一个 FIN 包
    • 另一方确认这个 FIN 包,并且发送自己的 FIN 包
    • 要关闭的一方使用 ACK 包来确认接收到了 FIN

UDP 数据包

1
2
3
4
5
6
7
8
9
10
11
12
13

TLS 握手
--------

* 客户端发送一个 ``Client hello`` 消息到服务器端,消息中同时包含了它的TLS版本,可用的加密算法和压缩算法。
* 服务器端向客户端返回一个 ``Server hello`` 消息,消息中包含了服务器端的TLS版本,服务器选择了哪个加密和压缩算法,以及服务器的公开证书,证书中包含了公钥。客户端会使用这个公钥加密接下来的握手过程,直到协商生成一个新的对称密钥
* 客户端根据自己的信任CA列表,验证服务器端的证书是否有效。如果有效,客户端会生成一串伪随机数,使用服务器的公钥加密它。这串随机数会被用于生成新的对称密钥
* 服务器端使用自己的私钥解密上面提到的随机数,然后使用这串随机数生成自己的对称主密钥
* 客户端发送一个 ``Finished`` 消息给服务器端,使用对称密钥加密这次通讯的一个散列值
* 服务器端生成自己的 hash 值,然后解密客户端发送来的信息,检查这两个值是否对应。如果对应,就向客户端发送一个 ``Finished`` 消息,也使用协商好的对称密钥加密
* 从现在开始,接下来整个 TLS 会话都使用对称秘钥进行加密,传输应用层(HTTP)内容

TCP 数据包
~

HTTP 协议···

如果浏览器是 Google 出品的,它不会使用 HTTP 协议来获取页面信息,而是会与服务器端发送请求,商讨使用 SPDY 协议。

如果浏览器使用 HTTP 协议,它会向服务器发送这样的一个请求::

GET / HTTP/1.1
Host: google.com
[其他头部]

“其他头部”包含了一系列的由冒号分割开的键值对,它们的格式符合HTTP协议标准,它们之间由一个换行符分割开来。这里我们假设浏览器没有违反HTTP协议标准的bug,同时浏览器使用 HTTP/1.1 协议,不然的话头部可能不包含 Host 字段,同时 GET 请求中的版本号会变成 HTTP/1.0 或者 HTTP/0.9

HTTP/1.1 定义了“关闭连接”的选项 “close”,发送者使用这个选项指示这次连接在响应结束之后会断开::

Connection:close

不支持持久连接的 HTTP/1.1 必须在每条消息中都包含 “close” 选项。

在发送完这些请求和头部之后,浏览器发送一个换行符,表示要发送的内容已经结束了。

服务器端返回一个响应码,指示这次请求的状态,响应的形式是这样的::

200 OK
[响应头部]

然后是一个换行,接下来有效载荷(payload),也就是 www.google.com 的HTML内容。服务器下面可能会关闭连接,如果客户端请求保持连接的话,服务器端会保持连接打开,以供以后的请求重用。

如果浏览器发送的HTTP头部包含了足够多的信息(例如包含了 Etag 头部,以至于服务器可以判断出,浏览器缓存的文件版本自从上次获取之后没有再更改过,服务器可能会返回这样的响应::

304 Not Modified
[响应头部]

这个响应没有有效载荷,浏览器会从自己的缓存中取出想要的内容。

在解析完 HTM L之后,浏览器和客户端会重复上面的过程,直到HTML页面引入的所有资源(图片,CSS,favicon.ico等等)全部都获取完毕,区别只是头部的 GET / HTTP/1.1 会变成 GET /$(相对www.google.com的URL) HTTP/1.1

如果HTML引入了 www.google.com 域名之外的资源,浏览器会回到上面解析域名那一步,按照下面的步骤往下一步一步执行,请求中的 Host 头部会变成另外的域名。

HTTP 服务器请求处理

HTTPD(HTTP Daemon)在服务器端处理请求/相应。最常见的 HTTPD 有 Linux 上常用的 Apache 和 nginx,以及 Windows 上的 IIS。

  • HTTPD 接收请求
  • 服务器把请求拆分为以下几个参数:
    • HTTP 请求方法(GET, POST, HEAD, PUT 和 DELETE)。在访问 Google 这种情况下,使用的是 GET 方法
    • 域名:google.com
    • 请求路径/页面:/ (我们没有请求google.com下的指定的页面,因此 / 是默认的路径)
  • 服务器验证其上已经配置了 google.com 的虚拟主机
  • 服务器验证 google.com 接受 GET 方法
  • 服务器验证该用户可以使用 GET 方法(根据 IP 地址,身份信息等)
  • 如果服务器安装了 URL 重写模块(例如 Apache 的 mod_rewrite 和 IIS 的 URL Rewrite),服务器会尝试匹配重写规则,如果匹配上的话,服务器会按照规则重写这个请求
  • 服务器根据请求信息获取相应的响应内容,这种情况下由于访问路径是 “/“ ,会访问首页文件(你可以重写这个规则,但是这个是最常用的)。
  • 服务器会使用指定的处理程序分析处理这个文件,假如 Google 使用 PHP,服务器会使用 PHP 解析 index 文件,并捕获输出,把 PHP 的输出结果返回给请求者

浏览器背后的故事

当服务器提供了资源之后(HTML,CSS,JS,图片等),浏览器会执行下面的操作:

  • 解析 HTML,CSS,JS
  • 渲染——构建 DOM 树 -> 渲染 -> 布局 -> 绘制

浏览器

浏览器的功能是从服务器上取回你想要的资源,然后展示在浏览器窗口当中。资源通常是 HTML 文件,也可能是 PDF,图片,或者其他类型的内容。资源的位置通过用户提供的 URI(Uniform Resource Identifier) 来确定。

浏览器解释和展示 HTML 文件的方法,在 HTML 和 CSS 的标准中有详细介绍。这些标准由 Web 标准组织 W3C(World Wide Web Consortium) 维护。

不同浏览器的用户界面大都十分接近,有很多共同的 UI 元素:

  • 一个地址栏
  • 后退和前进按钮
  • 书签选项
  • 刷新和停止按钮
  • 主页按钮

浏览器高层架构

组成浏览器的组件有:

  • 用户界面 用户界面包含了地址栏,前进后退按钮,书签菜单等等,除了请求页面之外所有你看到的内容都是用户界面的一部分
  • 浏览器引擎 浏览器引擎负责让 UI 和渲染引擎协调工作
  • 渲染引擎 渲染引擎负责展示请求内容。如果请求的内容是 HTML,渲染引擎会解析 HTML 和 CSS,然后将内容展示在屏幕上
  • 网络组件 网络组件负责网络调用,例如 HTTP 请求等,使用一个平台无关接口,下层是针对不同平台的具体实现
  • UI后端 UI 后端用于绘制基本 UI 组件,例如下拉列表框和窗口。UI 后端暴露一个统一的平台无关的接口,下层使用操作系统的 UI 方法实现
  • Javascript 引擎 Javascript 引擎用于解析和执行 Javascript 代码
  • 数据存储 数据存储组件是一个持久层。浏览器可能需要在本地存储各种各样的数据,例如 Cookie 等。浏览器也需要支持诸如 localStorage,IndexedDB,WebSQL 和 FileSystem 之类的存储机制

HTML 解析

浏览器渲染引擎从网络层取得请求的文档,一般情况下文档会分成8kB大小的分块传输。

HTML 解析器的主要工作是对 HTML 文档进行解析,生成解析树。

解析树是以 DOM 元素以及属性为节点的树。DOM是文档对象模型(Document Object Model)的缩写,它是 HTML 文档的对象表示,同时也是 HTML 元素面向外部(如Javascript)的接口。树的根部是”Document”对象。整个 DOM 和 HTML 文档几乎是一对一的关系。

解析算法

HTML不能使用常见的自顶向下或自底向上方法来进行分析。主要原因有以下几点:

  • 语言本身的“宽容”特性
  • HTML 本身可能是残缺的,对于常见的残缺,浏览器需要有传统的容错机制来支持它们
  • 解析过程需要反复。对于其他语言来说,源码不会在解析过程中发生变化,但是对于 HTML 来说,动态代码,例如脚本元素中包含的 document.write() 方法会在源码中添加内容,也就是说,解析过程实际上会改变输入的内容

由于不能使用常用的解析技术,浏览器创造了专门用于解析 HTML 的解析器。解析算法在 HTML5 标准规范中有详细介绍,算法主要包含了两个阶段:标记化(tokenization)和树的构建。

解析结束之后

浏览器开始加载网页的外部资源(CSS,图像,Javascript 文件等)。

此时浏览器把文档标记为“可交互的”,浏览器开始解析处于“推迟”模式的脚本,也就是那些需要在文档解析完毕之后再执行的脚本。之后文档的状态会变为“完成”,浏览器会进行“加载”事件。

注意解析 HTML 网页时永远不会出现“语法错误”,浏览器会修复所有错误,然后继续解析。

执行同步 Javascript 代码。

CSS 解析

  • 根据 CSS词法和句法_ 分析CSS文件和 <style> 标签包含的内容
  • 每个CSS文件都被解析成一个样式表对象,这个对象里包含了带有选择器的CSS规则,和对应CSS语法的对象
  • CSS解析器可能是自顶向下的,也可能是使用解析器生成器生成的自底向上的解析器

页面渲染

  • 通过遍历DOM节点树创建一个“Frame 树”或“渲染树”,并计算每个节点的各个CSS样式值
  • 通过累加子节点的宽度,该节点的水平内边距(padding)、边框(border)和外边距(margin),自底向上的计算”Frame 树”中每个节点首的选(preferred)宽度
  • 通过自顶向下的给每个节点的子节点分配可行宽度,计算每个节点的实际宽度
  • 通过应用文字折行、累加子节点的高度和此节点的内边距(padding)、边框(border)和外边距(margin),自底向上的计算每个节点的高度
  • 使用上面的计算结果构建每个节点的坐标
  • 当存在元素使用 floated,位置有 absolutelyrelatively 属性的时候,会有更多复杂的计算,详见http://dev.w3.org/csswg/css2/http://www.w3.org/Style/CSS/current-work
  • 创建layer(层)来表示页面中的哪些部分可以成组的被绘制,而不用被重新栅格化处理。每个帧对象都被分配给一个层
  • 页面上的每个层都被分配了纹理(?)
  • 每个层的帧对象都会被遍历,计算机执行绘图命令绘制各个层,此过程可能由CPU执行栅格化处理,或者直接通过D2D/SkiaGL在GPU上绘制
  • 上面所有步骤都可能利用到最近一次页面渲染时计算出来的各个值,这样可以减少不少计算量
  • 计算出各个层的最终位置,一组命令由 Direct3D/OpenGL发出,GPU命令缓冲区清空,命令传至GPU并异步渲染,帧被送到Window Server。

GPU 渲染

  • 在渲染过程中,图形处理层可能使用通用用途的 CPU,也可能使用图形处理器 GPU
  • 当使用 GPU 用于图形渲染时,图形驱动软件会把任务分成多个部分,这样可以充分利用 GPU 强大的并行计算能力,用于在渲染过程中进行大量的浮点计算。

Window Server

后期渲染与用户引发的处理

渲染结束后,浏览器根据某些时间机制运行JavaScript代码(比如Google Doodle动画)或与用户交互(在搜索栏输入关键字获得搜索建议)。类似Flash和Java的插件也会运行,尽管Google主页里没有。这些脚本可以触发网络请求,也可能改变网页的内容和布局,产生又一轮渲染与绘制。

.. Creative Commons Zero: https://creativecommons.org/publicdomain/zero/1.0/
..
CSS词法和句法: http://www.w3.org/TR/CSS2/grammar.html
.. Punycode: https://en.wikipedia.org/wiki/Punycode
..
以太网: http://en.wikipedia.org/wiki/IEEE_802.3
.. WiFi: https://en.wikipedia.org/wiki/IEEE_802.11
..
蜂窝数据网络: https://en.wikipedia.org/wiki/Cellular_data_communication_protocol
.. analog-to-digital converter: https://en.wikipedia.org/wiki/Analog-to-digital_converter
..
网络节点: https://en.wikipedia.org/wiki/Computer_network#Network_nodes
.. 不同的操作系统有所不同 : https://en.wikipedia.org/wiki/Hosts%28file%29#Locationin_the_file_system
..
downgrade attack: http://en.wikipedia.org/wiki/SSL_stripping

OS X El Capitan传说9月9日就要发正式了,碰巧我电脑换了硬盘,干脆装一个耍耍~

特此收集了一下USB安装盘的做法,貌似和以前10.10的做法类似。

准备工作

  • 首先要准备一个8G以上的U盘,我特地去买了个支持type-c的U盘,以后可以用来installB
  • 去APPLE开发者社区下载测试版
  • 一台能装OS X El Capitan的电脑
  • 确认在”/Applications/“目录中已经正确下载了安装文件:”Install OS X El Capitan Developer Beta.app”
  • 如果是在网盘上下载压缩包的,记得解压缩之后复制”Install OS X El Capitan Developer Beta.app”到/“Applications/“目录去

如果要把本地下载好的.app压缩打包上网盘,可能需要切分

支持安装OS X El Capitan的电脑

  • iMac (Mid-2007 or, newer)
  • MacBook (13in Aluminum, Late 2008), (13in, Early 2009 or, newer)
  • MacBook Pro (13in, Mid-2009 or, newer), (15in, Mid/Late 2007 or, newer), (17in, Late 2007 or, newer)
  • MacBook Air (Late 2008 or, newer)
  • Mac Mini (Early 2009 or, newer)
  • Mac Pro (Early 2008 or, newer)
  • Xserve (Early 2009)

现在开始制作安装U盘

第一步 格式化U盘

http://ips.chotee.com/wp-content/uploads/2014/osx-yosemite-usb-install-drive/format-usb.jpg

  • 1 - 在左方列表中找到 U 盘的名称并点击
  • 右边顶部选择 2 -「分区」,然后在 3 -「分区布局」选择「1个分区」
  • 在分区信息中的 4 -「名称」输入「ElCapInstaller」 (由于后面的命令中会用到此名称,如果你要修改成其他(英文),请务必对应修改后面的命令)
  • 在「格式」中选择 5 -「Mac OS 扩展 (日志式)」
  • 这时,先别急着点“应用”,还要先在 6 -「选项」里面,如下图

http://ips.chotee.com/wp-content/uploads/2014/osx-yosemite-usb-install-drive/guid.jpg

  • 选择「GUID 分区表」,然后点击「好」
  • 最后再点「应用」开始对 U 盘进行格式化。
    *

第二步 输入终端命令开始制作启动盘

打开终端窗口,输入下面的命令

这里是和我下载的文件名一样的

sudo /Applications/Install\ OS\ X\ El\ Capitan\ Developer\ Beta.app/Contents/Resources/createinstallmedia --volume /Volumes/ElCapInstaller --applicationpath /Applications/Install\ OS\ X\ El\ Capitan\ Developer\ Beta.app --nointeraction

For OS X El Capitan Public Beta:

sudo /Applications/Install\ OS\ X\ El\ Capitan\ Public\ Beta.app/Contents/Resources/createinstallmedia --volume /Volumes/ElCapInstaller --applicationpath /Applications/Install\ OS\ X\ El\ Capitan\ Public\ Beta.app --nointeraction

For OS X 10.11 Developer Beta:

sudo /Applications/Install\ OS\ X\ 10.11\ Developer\ Beta.app/Contents/Resources/createinstallmedia --volume /Volumes/ElCapInstaller --applicationpath /Applications/Install\ OS\ X\ 10.11\ Developer\ Beta.app --nointeraction

然后按下“回车” 键,等待结果

第三步 顺利的话输出这个

“Erasing Disk: 0%… 10%… 20%… 30%…100%…
Copying installer files to disk…
Copy complete.
Making disk bootable…
Copying boot files…
Copy complete.
Done.”

看到Done就是说明好了,期间要等待好长时间,请喝杯茶,走一走,程序员要预防颈椎病~

第四步 测试一下

把做好的U盘推出系统,然后再插一下(好像很邪恶的说)。

如果正常就会弹出安装界面。

另外

新系统常常伴随着各种问题,勇作小白鼠真是伟大的情操。如果你有重要的资料请做好备份,或者可以再开一个分区,在分区里安装。

参考:

一开始只是随便看看

上周在看react的todo例子时碰巧看到了一个基于Firebase + AngularJS的实时TODO项目,感觉挺有趣的。这样的架构,就不需要为设置数据库服务而头疼了,甚至不需要为一个小应用花一毛钱。

简单来说,Firebase是一个提供实时数据的云服务平台,网页、APP应用使用它的接口就可以做到实时的数据同步。两个人在不同的电脑上打开了同一个应用,其中一个人在网页上进行了操作,网页上产生的变化就会实时在另一个人的APP上表现出来。

Firebase还提供了主流JS框架支持,为Angular,Ember,React,Backbone,Ionic什么都写了库,对前端开发来说是不错的利好消息。

BaaS

BaaS(后端即服务:Backend as a Service)指为移动应用开发者提供整合云后端的边界服务。

国内的替代项目

不过,他们在2014年10月份爆出新闻,被谷歌收购了。然后,我觉得可能需要找国内替代品了。

Bmob

Bmob

Bmob是国内第一家专注于移动应用云存储的平台,是基于PaaS模式的云计算平台。开发者可以使用Bmob设计存储框架、获取应用密钥、下载SDK并嵌入应用程序、调用相应API,应用发布后查看存储的统计分析数据,整个流程能够快速实现,并且Bmob的云服务是免费的。

野狗

wilddog

野狗,是国内第一家实时PaaS服务提供商,其创始团队来自360、人人网、百度等一线公司,之所以起了这个名字,除了能够让人过耳不忘之外,还因为其创始人刘之自认为性格偏向野性,对事业极具野心,因而自比“野狗”。

目前野狗的方向是做物联网后端数据云服务,是国内首家支持物联网国际标准CoAP (Constrained Application Protocol)的云服务商。CoAP采用UDP传输层协议来进行通讯,更适合在Google Thread等资源受限设备。CoAP比传统协议节省至少10倍流量,电池使用更加持久。

资料

用AngularJS和Firebase制作一个实时投票应用

话说有个编程题目,有两个整数a和b, 求他们的和, 但不能使用 + 等数学运算符。

这是leetcode上的题目,需要用位运算处理。

Rotate left logically

例如:

int Add(int a, int b)
{
    return b ? Add(a^b, (a&b)<<1) : a;
}

其实在Javascript中也有位运算,只是平时很少用到。例如下面这段摘抄的代码,可以猜猜生成什么:

var a = "10"| 0 ;
console.log("Bitwise Or a is : " +a);
var b = "s1132"|0;
console.log("Bitwise Or b is : " +b);
var c = [1,3,2]&1 ;
console.log("Bitwise And c is : " +c);
var d = [1]|0;
console.log("Bitwise Or d is : " +d);
var e = ~function(){}();
console.log("Bitwise Not e is : " +e);
var f = ({})|0;
console.log("Bitwise Or f is : " +f);
var g = ([1])|0;
console.log("Bitwise Or g is : " +g);
var h = "1ss"^0;
console.log("Bitwise Exclusive Or h is : " +h);

javascript中的位运算

  javascript中的位运算非常复杂,由于javascript试图创建完全无类型(弱类型)的数据,因此数字以64位浮点值存储,即双精度的浮点数。

  正如大家所料,javascript中没有你想使用的Integer(整型)类型。当我们需要用到integer类型时,javascript会内部执行 Toint32方法(浏览器内部函数,外部不可调用)将值直接转化为32位的integer以供调用,并将原值瞬间转换回双精度浮点型。

  javascript与其他语言的不同之一就是它奇怪的位运算。

细节请参考

Bitwise operators @ mozilla

javascript位运算

javascript位运算符

知乎上关于javascript位运算性能的讨论

位运算的应用

位运算详解(介绍及用途)

“位运算”在程序开发中的妙用!

前两天还在抱怨说为啥iOS不自带矩形识别之类的功能,今天就被抽脸了。

下午在Tim9Liu9的个人iOS开发总结项目中发现了这个IPDFCameraViewController用的就是iOS自带的CIDetector功能,屌炸天了。

IPDFCameraViewController

这个项目是InstaPDF公司开发的。他们以前的MAImagePickerController项目确实用的也是OpenCV。

现在IPDFCameraViewController支持相机定焦拍摄、滤镜、闪光、实时边框检测以及透视矫正功能,并有简单易用的API。

CIDetector是CoreImage的功能,现在常用于人脸识别、笑容识别、QRCode识别等领域。

xamarin上更多CoreImage的介绍

补一篇介绍 2015-08-10

iOS8 Day-by-Day :: Day 13 :: CoreImage Detectors

Detecting Rectangles

Detecting rectangles in images is often one of the first parts of a computer vision algorithm – whether it be automatic business card interpretation, or road sign processing. Although rectangle detection sounds like it should be a really simple process, as with many problems in computer vision, it’s far harder than you might expect. So it’s great that Apple have implemented an efficient algorithm as part of the CoreImage detectors.

The main class associated with CoreImage detectors is the aptly-named CIDetector. The same class is used for all the different types of detectors, and is instantiated with the CIDetector(ofType:, context:, options:) initializer. The type argument is a string, which for a rectangle detector is CIDetectorTypeRectangle. The options argument is a dictionary of settings associated with this detector. The following method created a CIDetector to be used for detecting rectangles:

func prepareRectangleDetector() -> CIDetector {
  let options: [String : AnyObject] = [CIDetectorAccuracy: CIDetectorAccuracyHigh, CIDetectorAspectRatio: 1.0]
  return CIDetector(ofType: CIDetectorTypeRectangle, context: nil, options: options)
}

You can see that the options here are specifying the accuracy as high, with the CIDetectorAccuracy key, and the CIDetectorAspectRatio key is used to specify that you’re searching for squares. This aspect ratio doesn’t mean that you’re only looking for squares, but it will be used in the ranking of possible rectangles to determine which is the most likely candidate. For example, if you know that you’re going to use the detector for business cards then setting the aspect ratio to 2.0 will likely yield better results.

Once you’ve created a CIDetector it’s actually really simple to use. The method featuresInImage() takes a CIImage and then returns an array of CIFeature objects (well, a subclass of) which represent the detected objects. In the case of a rectangle detector, the CIFeature subclass is CIRectangleFeature, which has CGPoint properties for each of the four corners of the detected rectangle.

The following method demonstrates how you can use the detector to find a rectangle in a supplied CIImage:

func performRectangleDetection(image: CIImage) -> CIImage? {
  var resultImage: CIImage?
  if let detector = detector {
// Get the detections
let features = detector.featuresInImage(image)
for feature in features as! [CIRectangleFeature] {
  resultImage = drawHighlightOverlayForPoints(image, topLeft: feature.topLeft, topRight: feature.topRight,
                                              bottomLeft: feature.bottomLeft, bottomRight: feature.bottomRight)
}
  }
  return resultImage
}

This unwraps the option detector, before using the featuresInImage() method to perform the detection itself. At the time of writing, the rectangle detector will only ever detect one rectangle in an image, so the features array will have either exactly one or zero CIRectangleFeature objects in it.

This method returns a new CIImage, which contains a red patch overlaid on the source image over the position of the detected rectangle. This uses the utility method drawHighlightOverlayForPoints() method:

func drawHighlightOverlayForPoints(image: CIImage, topLeft: CGPoint, topRight: CGPoint,
                               bottomLeft: CGPoint, bottomRight: CGPoint) -> CIImage {
  var overlay = CIImage(color: CIColor(red: 1.0, green: 0, blue: 0, alpha: 0.5))
  overlay = overlay.imageByCroppingToRect(image.extent())
  overlay = overlay.imageByApplyingFilter("CIPerspectiveTransformWithExtent",
withInputParameters: [
  "inputExtent": CIVector(CGRect: image.extent()),
  "inputTopLeft": CIVector(CGPoint: topLeft),
  "inputTopRight": CIVector(CGPoint: topRight),
  "inputBottomLeft": CIVector(CGPoint: bottomLeft),
  "inputBottomRight": CIVector(CGPoint: bottomRight)
])
  return overlay.imageByCompositingOverImage(image)
}

This method creates a colored image, and then uses the perspective transform filter to map it to the points provided. It then creates a new CIImage by overlaying this colored image with the source image.

In the LiveDetection sample project, the performRectangleDetection() method is used as part in the CoreImageVideoFilter class to run the detector on each of the frames received from the camera, before rendering it on screen. This class is a little more involved than you might expect it to be, and it isn’t within the scope of this article to go in to much detail, however an overview might be helpful.

An AVFoundation pipeline is created which uses the camera as input, and provides a pixel buffer of each frame to a delegate method.
In this delegate method a CIImage is created from this pixel buffer.
The provided filter function (which takes an input CIImage and returns a new CIImage, much like a map function) is passed the current image.
The image is cropped to match the view size it is to be rendered in.
The CIImage is then rendered on the OpenGLES surface using a pre-created CIRenderContext.
Rinse and repeat for each frame received from the camera.
If you need to do live video processing using CoreImage then it might be worth taking a look at this class in greater detail, but if you don’t then the performRectangleDetection() method just takes a CIImage, which you can create using one of the many constructors.

Running this app up will kick off the rectangle detection right away, and you’ll see results like the following on the screen:

Rectangle Detector

The performance is pretty good for real-time use – certainly the demo app copes well on an iPad 3.

人家说至少要iOS8,iPhone5以上。

呵呵