终于有人能用一句话把Service Worker,Web Worker,WebSocket说清楚

Service Worker

处理网络请求的后台服务。适用于离线和后台同步数据或推送信息。不能直接和dom交互。通过postMessage方法交互。

Web Worker

模拟多线程,允许复杂计算功能的脚本在后台运行而不会阻碍到其他脚本的运行。适用于处理器占用量大而又不阻碍的情形。不能直接与dom交互。通过postMessage方法交互。

WebSocket

在客户端和服务端之间建立保持双向通信的连接。适用于需要保持长推送的情形,如聊天应用,在线游戏或运动直播等。不能直接与dom交互。通过send方法交互。


作者:Qubook
出处:http://www.cnblogs.com/hard/
关于作者:从事社区服务,生活工具方面的开发,在项目管理以及企业经营方面寻求发展之路
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。 
联系方式: 个人QQ  1005354833; 
github: https://github.com/xiaopinggai-webrtc

Rollup vs Webpack2 and tree shaking

from: http://david-rodenas.com/posts/rollup-vs-webpack-and-tree-shaking

Rollup introduced tree-shaking in the bundle creation process. Recently, webpack 2 added tree shanking to its bundle generation. But both processes are substantially different. In this post both generated code will be compared and conclussions shown.

$ rollup entry.js > rollup-bundle.js

$ webpack entry.js webpack-bundle.js

Tree-shaking is lukig a technique based in ES2015 definition modules that allows to remove everything that it is not used from the module.

Given the following example:

// entry.jsimport { nextPage, currentPage } from './barrel';console.log('before:', currentPage);nextPage();console.log('after:', currentPage);
// barrel.jsexport * from './pager';
// pager.jsexport var currentPage = 0;export function nextPage() {	currentPage++;}export function prevPage() {	currentPage--;}

We can see that pager.js is exporting three symbols: currentPagenextPage, and prevPage.

If we look carefully to entry.js we see that only symbols currentPage and nextPage are in use. In this application prevPage is not used.

Tree-shaking analyses all imports and exports of an application and removes all symbols that are unused. In this case prevPage dissapears from the bundle.

Rollup

Rollup not only does tree-shaking, but also hoists all modules into a single scope. That means that there are no need for new function closures.

The code through rollup is like the following:

// rollup-bundle.unminimized.jsvar currentPage = 0;function nextPage() {	currentPage++;}console.log('before:', currentPage);nextPage();console.log('after:', currentPage);

It is just the code that it is being used by the app. Once it is minimized it looks like:

function o() { n++ }var n = 0;console.log("before:", n), o(), console.log("after:", n)

Webpack

Webpack uses the traditional approach of giving a closure function to each module and it only applies tree-shaking during the minimization process.

The code through webpack including minimization looks like the following:

! function(n) {  // .. omitted webpack loader ..}([function(n, t, r) {    "use strict";    var e = r(1);    r.d(t, "a", function() {        return e.a    }), r.d(t, "b", function() {        return e.b    })}, function(n, t, r) {    "use strict";    function e() { o++ }    r.d(t, "a", function() {        return o    }), t.b = e;    var o = 0}, function(n, t, r) {    "use strict";    var e = r(0);    console.log("before:", e.a), r.i(e.b)(), console.log("after:", e.a)}]);

Variables

Variables defined inside modules and exported must have consistent values across all imports. That means that any change to a variable it has to be visible through all modules.

I test it with the currentPage variable, which is modified inside the pager.js module but read from the entry.js module.

Rollup achieves this behaviour by just sharing the same scope: any function can access directly to the shared variable.

Webpack has a problem: because variables are attached to exports objects it have problems to be consistent. In order to achieve this consistency webpack uses javascript computed properties:

// the loader defines this helper function:t.d = function(n, t, r) {  Object.defineProperty(n, t, { configurable: !1, enumerable: !0, get: r });};// each module creates a computed property to export a variable:r.d(t, "a", function() {  return o;});

Barrels

Barrels are aggregators of functionalities of one package or library. It makes possible to export all symbols through one single point.

export * from './pager.js`;

Rollup makes them disappear. They do not have meaning by themselves, they are a simple shortcut.

Webpack exports them into the bundle:

function(n, t, r) {    "use strict";    var e = r(1);    r.d(t, "a", function() {        return e.a    }), r.d(t, "b", function() {        return e.b    })},

Comparison

In overall, output sizes results are the following:

Tool minimized gzipped
RollupJS 108 bytes 94 bytes
Webpack 793 bytes 393 bytes
Webpack (without loader) 325 bytes 177 bytes

Webpack loader size is constant regardless the size of the bundle, that means that it is an overhead that does not worth to consider.

Although there are important differences in bundle sizes (rollup generated size is almost a half from the best of webpack) there are other important points:

  • getters: webpack creates a getter for each variable export, that means, that we, programmers, are penalised for each export that we build, larger export chain larger number of getters and more functions to execute each time that we get a value,

  • barrels: barrels are just “syntax sugar” for the programmer, we use them to make the code easier to manage, they have no meaning by themselves in the application bundle, but webpack creates a closure and export object for each one, which penalizes us, as programmers, for using them.

Conclusion

Scope-hoisting strategy of rollup gives a very good results: it makes barrels disappear completely and it requires no getters to implement the ES2015 semantics when exporting variables.

As overall although webpack 2 implements tree-shaking it fails implementing the objective of doing tree-shaking: remove all unused code and let the programmer express itself as it needs to have a maintainable code.

More information

See:

  • Code example: https://github.com/drpicox/rollup-vs-webpack/tree/basic
  • Barrel: https://angular.io/docs/ts/latest/guide/glossary.html#!#barrel

如何提高网站的点击率

最近VC一直在纠结一个问题,为什么百度index指数有100的关键词排名第一,可真正的流量只有10%?理论上排名第一应该获得40%以上的点击。原因之一可能是百度的index指数不准确,可能不是精准关键词的index指数,而是一些长尾也算了进去。当然还有另外一种可能就是你的网页在搜索引擎上的展示不够好,页面点击率低。那么今天我们就来聊聊如何提高网页在搜索引擎上的点击率。

 

做搜索引擎营销的人都知道,PPC和SEO在很多方面都是相关的,尤其是在关键词分析、落地页面优化以及广告撰写。虽然在技术上有一些稍微的不同,但是其中一个方面的变化都可以帮助网站主在另一个方面进行优化。

 

因此,要提升SEO的点击率(click-through rate)可以借鉴PPC的广告写作方法。无论是文章的标题和首段文字,还是meta标签,借鉴PPC的写作方式都可以提升SEO的点击率。

 

下面介绍五种可以用于SEO的PPC提升点击率的方法:

 

一、使用感叹号

 

PPC行业众所周知,感叹号可以增加点击率。

 

为什么?可能是因为感叹号感觉提升了你的声音,更容易获得别人的关注。在SEO的标题中尝试这个方法。虽然感叹号不能够提升你的排名,但是你却可以在社会化媒体上获得更能多的转发和评论。例如,你在投诉企业的时候,“百度,你怎么了?!”比“百度,你让我生气了。”更容易获得关注。

 

二、使用数据和证据

 

事实证明,图形和数据能够显著提升点击率,这些也可以利用到PPC上。例如,“负面的关键词会减低PPC花费”不如“负面的关键词能够减少30%以上的PPC花费”。不一定是数字,任何真实的数据都是可以的。相比于PPC广告,SEO可以在页面优化中有更大的空间来引用你的数据来源。

 

三、尝试虚拟动态关键词嵌入

 

PPC可以使用动态关键词嵌入(dynamic keyword insertion)来在基于猜测的动态基础上整合所有来自搜索提交端的关键词。例如,亚马逊可以通过广告标题镜像,把那些搜索各种各样的鞋子款式的消费者都引导到一个鞋子的分类页面。例如,消费者搜索“露脚趾的谢”,那么页面的标题就是“露脚趾的鞋”,描述就是通用的描述“寻找各种各样的鞋子。免运费。”由于消费者希望看到他们希望找到的,动态关键词嵌入可以显著提升点击率。

 

对于SEO来说,标题是不可能随意变动的,但是可以模仿这种概念提前使用不同的替代品来引导消费者到你的页面。很明显,最重要的就是要提前做好关键词分析。如果你知道一系列类似的关键词搜索,例如“SEO工具”“SEO软件”“SEO咨询”,可以建立三个内容相似的页面,每一个页面都针对不同的关键词进行优化。那么,你就可以在三个关键词上都获得排名。

 

四、像PPC一样不断的测试

 

PPC要不断的测试,SEO同样可以。SEO的测试不能像PPC那样是或否那么简单,但是,也不是非常困难,结果是可以承受的。可以使用Google Website Optimizer来测试SEO的两个版本。假设这个页面是落地页,即使没有PPC,你仍然希望你的读者能够参与进来,引导他们进入计划好的行为。测试的要素包括:标题、长度(字数)、图像(图片数量、布局、图片的内容)、提议(包括文字和提议的种类)、工具条链接等等。通常你可以通过一个页面的结果来进行类推,从而提升其他SEO页面效果,而不用一个一个页面的测试。

 

五、不要浪费空间

 

PPC要精炼信息,避免冗余。撰写一个AdWords广告非常困难,那么少的字数要写出非常聪明并且有意思的话题非常困难。虽然SEO不用那么精简,但是精确依然是有价值的。罗嗦的话不会显得你聪明,而只会浪费读者的时间。

 

做好以上的五个步骤,相信你的网页点击率能有很大的提高,让你的SEO更有价值!

浏览器缓存机制剖析


缓存一直是前端优化的主战场, 利用好缓存就成功了一半. 本篇从http请求和响应的头域入手, 让你对浏览器缓存有个整体的概念. 最终你会发现强缓存, 协商缓存 和 启发式缓存是如此的简单.

导读

我不知道拖延症是有多严重, 反正去年3月开的题, 直到今年4月才开始写.(请尽情吐槽吧)

浏览器对于请求资源, 拥有一系列成熟的缓存策略. 按照发生的时间顺序分别为存储策略过期策略协商策略, 其中存储策略在收到响应后应用, 过期策略协商策略在发送请求前应用. 流程图如下所示.

废话不多说, 我们先来看两张表格.

1.http header中与缓存有关的key.

key 描述 存储策略 过期策略 协商策略
Cache-Control 指定缓存机制,覆盖其它设置 ✔️ ✔️
Pragma http1.0字段,指定缓存机制 ✔️
Expires http1.0字段,指定缓存的过期时间 ✔️
Last-Modified 资源最后一次的修改时间 ✔️
ETag 唯一标识请求资源的字符串 ✔️

2.缓存协商策略用于重新验证缓存资源是否有效, 有关的key如下.

key 描述
If-Modified-Since 缓存校验字段, 值为资源最后一次的修改时间, 即上次收到的Last-Modified值
If-Unmodified-Since 同上, 处理方式与之相反
If-Match 缓存校验字段, 值为唯一标识请求资源的字符串, 即上次收到的ETag值
If-None-Match 同上, 处理方式与之相反

下面我们来看下各个头域(key)的作用.

Cache-Control

浏览器缓存里, Cache-Control是金字塔顶尖的规则, 它藐视一切其他设置, 只要其他设置与其抵触, 一律覆盖之.

不仅如此, 它还是一个复合规则, 包含多种值, 横跨 存储策略过期策略 两种, 同时在请求头和响应头都可设置.

语法为: "Cache-Control : cache-directive".

Cache-directive共有如下12种(其中请求中指令7种, 响应中指令9种):

Cache-directive 描述 存储策略 过期策略 请求字段 响应字段
public 资源将被客户端和代理服务器缓存 ✔️ ✔️
private 资源仅被客户端缓存, 代理服务器不缓存 ✔️ ✔️
no-store 请求和响应都不缓存 ✔️ ✔️ ✔️
no-cache 相当于max-age:0,must-revalidate即资源被缓存, 但是缓存立刻过期, 同时下次访问时强制验证资源有效性 ✔️ ✔️ ✔️ ✔️
max-age 缓存资源, 但是在指定时间(单位为秒)后缓存过期 ✔️ ✔️ ✔️ ✔️
s-maxage 同上, 依赖public设置, 覆盖max-age, 且只在代理服务器上有效. ✔️ ✔️ ✔️
max-stale 指定时间内, 即使缓存过时, 资源依然有效 ✔️ ✔️
min-fresh 缓存的资源至少要保持指定时间的新鲜期 ✔️ ✔️
must-revalidation / proxy-revalidation 如果缓存失效, 强制重新向服务器(或代理)发起验证(因为max-stale等字段可能改变缓存的失效时间) ✔️ ✔️
only-if-cached 仅仅返回已经缓存的资源, 不访问网络, 若无缓存则返回504 ✔️
no-transform 强制要求代理服务器不要对资源进行转换, 禁止代理服务器对 Content-EncodingContent-RangeContent-Type字段的修改(因此代理的gzip压缩将不被允许) ✔️ ✔️

假设所请求资源于4月5日缓存, 且在4月12日过期.

当max-age 与 max-stale 和 min-fresh 同时使用时, 它们的设置相互之间独立生效, 最为保守的缓存策略总是有效. 这意味着, 如果max-age=10 days, max-stale=2 days, min-fresh=3 days, 那么:

  • 根据max-age的设置, 覆盖原缓存周期, 缓存资源将在4月15日失效(5+10=15);
  • 根据max-stale的设置, 缓存过期后两天依然有效, 此时响应将返回110(Response is stale)状态码, 缓存资源将在4月14日失效(12+2=14);
  • 根据min-fresh的设置, 至少要留有3天的新鲜期, 缓存资源将在4月9日失效(12-3=9);

由于客户端总是采用最保守的缓存策略, 因此, 4月9日后, 对于该资源的请求将重新向服务器发起验证.

Pragma

http1.0字段, 通常设置为Pragma:no-cache, 作用同Cache-Control:no-cache. 当一个no-cache请求发送给一个不遵循HTTP/1.1的服务器时, 客户端应该包含pragma指令. 为此, 勾选☑️ 上disable cache时, 浏览器自动带上了pragma字段. 如下:

Pragma:no-cache

Expires

Expires:Wed, 05 Apr 2017 00:55:35 GMT

即到期时间, 以服务器时间为参考系, 其优先级比 Cache-Control:max-age 低, 两者同时出现在响应头时, Expires将被后者覆盖. 如果ExpiresCache-Control: max-age, 或 Cache-Control:s-maxage 都没有在响应头中出现, 并且也没有其它缓存的设置, 那么浏览器默认会采用一个启发式的算法, 通常会取响应头的Date_value - Last-Modified_value值的10%作为缓存时间.

如下资源便采取了启发式缓存算法.

启发式缓存生效

其缓存时间为 (Date_value - Last-Modified_value) * 10%, 计算如下:

const Date_value = new Date('Thu, 06 Apr 2017 01:30:56 GMT').getTime();const LastModified_value = new Date('Thu, 01 Dec 2016 06:23:23 GMT').getTime();const cacheTime = (Date_value - LastModified_value) / 10;const Expires_timestamp = Date_value + cacheTime;const Expires_value = new Date(Expires_timestamp);console.log('Expires:', Expires_value); // Expires: Tue Apr 18 2017 23:25:41 GMT+0800 (CST)

可见该资源将于2017年4月18日23点25分41秒过期, 尝试以下两步进行验证:

1) 试着把本地时间修改为2017年4月18日23点25分40秒, 迅速刷新页面, 发现强缓存依然有效(依旧是200 OK (from disk cache)).

2) 然后又修改本地时间为2017年4月18日23点26分40秒(即往后拨1分钟), 刷新页面, 发现缓存已过期, 此时浏览器重新向服务器发起了验证, 且命中了304协商缓存, 如下所示.

缓存过期, 重新发起验证, 命中304协商缓存

3) 将本地时间恢复正常(即 2017-04-06 09:54:19). 刷新页面, 发现Date依然是4月18日, 如下所示.

本地时间恢复正常, 缓存依然有效

⚠️ Provisional headers are shown 和Date字段可以看出来, 浏览器并未发出请求, 缓存依然有效, 只不过此时Status Code显示为200 OK. (甚至我还专门打开了charles, 也没有发现该资源的任何请求, 可见这个200 OK多少有些误导人的意味)

可见, 启发式缓存算法采用的缓存时间可长可短, 因此对于常规资源, 建议明确设置缓存时间(如指定max-age 或 expires).

ETag

ETag:"fcb82312d92970bdf0d18a4eca08ebc7efede4fe"

实体标签, 服务器资源的唯一标识符, 浏览器可以根据ETag值缓存数据, 节省带宽. 如果资源已经改变, etag可以帮助防止同步更新资源的相互覆盖. ETag 优先级比 Last-Modified 高.

If-Match

语法: If-Match: ETag_value 或者 If-Match: ETag_value, ETag_value, …

缓存校验字段, 其值为上次收到的一个或多个etag 值. 常用于判断条件是否满足, 如下两种场景:

  • 对于 GET 或 HEAD 请求, 结合 Range 头字段, 它可以保证新范围的请求和前一个来自相同的源, 如果不匹配, 服务器将返回一个416(Range Not Satisfiable)状态码的响应.
  • 对于 PUT 或者其他不安全的请求, If-Match 可用于阻止错误的更新操作, 如果不匹配, 服务器将返回一个412(Precondition Failed)状态码的响应.

If-None-Match

语法: If-None-Match: ETag_value 或者 If-None-Match: ETag_value, ETag_value, …

缓存校验字段, 结合ETag字段, 常用于判断缓存资源是否有效, 优先级比If-Modified-Since高.

  • 对于 GET 或 HEAD 请求, 如果其etags列表均不匹配, 服务器将返回200状态码的响应, 反之, 将返回304(Not Modified)状态码的响应. 无论是200还是304响应, 都至少返回 Cache-ControlContent-LocationDateETagExpires, and Vary 中之一的字段.
  • 对于其他更新服务器资源的请求, 如果其etags列表匹配, 服务器将执行更新, 反之, 将返回412(Precondition Failed)状态码的响应.

Last-Modified

语法: Last-Modified: 星期,日期 月份 年份 时:分:秒 GMT

Last-Modified: Tue, 04 Apr 2017 10:01:15 GMT

用于标记请求资源的最后一次修改时间, 格式为GMT(格林尼治标准时间). 如可用 new Date().toGMTString()获取当前GMT时间. Last-Modified 是 ETag 的fallback机制, 优先级比 ETag 低, 且只能精确到秒, 因此不太适合短时间内频繁改动的资源. 不仅如此, 服务器端的静态资源, 通常需要编译打包, 可能出现资源内容没有改变, 而Last-Modified却改变的情况.

If-Modified-Since

语法同上, 如:

If-Modified-Since: Tue, 04 Apr 2017 10:12:27 GMT

缓存校验字段, 其值为上次响应头的Last-Modified值, 若与请求资源当前的Last-Modified值相同, 那么将返回304状态码的响应, 反之, 将返回200状态码响应.

If-Unmodified-Since

缓存校验字段, 语法同上. 表示资源未修改则正常执行更新, 否则返回412(Precondition Failed)状态码的响应. 常用于如下两种场景:

  • 不安全的请求, 比如说使用post请求更新wiki文档, 文档未修改时才执行更新.
  • 与 If-Range 字段同时使用时, 可以用来保证新的片段请求来自一个未修改的文档.

强缓存

一旦资源命中强缓存, 浏览器便不会向服务器发送请求, 而是直接读取缓存. Chrome下的现象是 200 OK (from disk cache) 或者 200 OK (from memory cache). 如下:

200 OK (from disk cache)

200 OK (from memory cache)

对于常规请求, 只要存在该资源的缓存, 且Cache-Control:max-age 或者expires没有过期, 那么就能命中强缓存.

协商缓存

缓存过期后, 继续请求该资源, 对于现代浏览器, 拥有如下两种做法:

  • 根据上次响应中的ETag_value, 自动往request header中添加If-None-Match字段. 服务器收到请求后, 拿If-None-Match字段的值与资源的ETag值进行比较, 若相同, 则命中协商缓存, 返回304响应.
  • 根据上次响应中的Last-Modified_value, 自动往request header中添加If-Modified-Since字段. 服务器收到请求后, 拿If-Modified-Since字段的值与资源的Last-Modified值进行比较, 若相同, 则命中协商缓存, 返回304响应.

以上, ETag优先级比Last-Modified高, 同时存在时, 前者覆盖后者. 下面通过实例来理解下强缓存和协商缓存.

如下忽略首次访问, 第二次通过 If-Modified-Since 命中了304协商缓存.

304

协商缓存的响应结果, 不仅验证了资源的有效性, 同时还更新了浏览器缓存. 主要更新内容如下:

Age:0Cache-Control:max-age=600Date: Wed, 05 Apr 2017 13:09:36 GMTExpires:Wed, 05 Apr 2017 00:55:35 GMT

Age:0 表示命中了代理服务器的缓存, age值为0表示代理服务器刚刚刷新了一次缓存.

Cache-Control:max-age=600 覆盖 Expires 字段, 表示从Date_value, 即 Wed, 05 Apr 2017 13:09:36 GMT 起, 10分钟之后缓存过期. 因此10分钟之内访问, 将会命中强缓存, 如下所示:

200 from cache

当然, 除了上述与缓存直接相关的字段外, http header中还包括如下间接相关的字段.

Age

出现此字段, 表示命中代理服务器的缓存. 它指的是代理服务器对于请求资源的已缓存时间, 单位为秒. 如下:

Age:2383321Date:Wed, 08 Mar 2017 16:12:42 GMT

以上指的是, 代理服务器在2017年3月8日16:12:42时向源服务器发起了对该资源的请求, 目前已缓存了该资源2383321秒.

Date

指的是响应生成的时间. 请求经过代理服务器时, 返回的Date未必是最新的, 通常这个时候, 代理服务器将增加一个Age字段告知该资源已缓存了多久.

Vary

对于服务器而言, 资源文件可能不止一个版本, 比如说压缩和未压缩, 针对不同的客户端, 通常需要返回不同的资源版本. 比如说老式的浏览器可能不支持解压缩, 这个时候, 就需要返回一个未压缩的版本; 对于新的浏览器, 支持压缩, 返回一个压缩的版本, 有利于节省带宽, 提升体验. 那么怎么区分这个版本呢, 这个时候就需要Vary了.

服务器通过指定Vary: Accept-Encoding, 告知代理服务器, 对于这个资源, 需要缓存两个版本: 压缩和未压缩. 这样老式浏览器和新的浏览器, 通过代理, 就分别拿到了未压缩和压缩版本的资源, 避免了都拿同一个资源的尴尬.

Vary:Accept-Encoding,User-Agent

如上设置, 代理服务器将针对是否压缩和浏览器类型两个维度去缓存资源. 如此一来, 同一个url, 就能针对PC和Mobile返回不同的缓存内容.

怎么让浏览器不缓存静态资源

实际上, 工作中很多场景都需要避免浏览器缓存, 除了浏览器隐私模式, 请求时想要禁用缓存, 还可以设置请求头: Cache-Control: no-cache, no-store, must-revalidate .

当然, 还有一种常用做法: 即给请求的资源增加一个版本号, 如下:

<link rel="stylesheet" type="text/css" href="../css/style.css?version=1.8.9"/>

这样做的好处就是你可以自由控制什么时候加载最新的资源.

不仅如此, HTML也可以禁用缓存, 即在页面的\节点中加入\标签, 代码如下:

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/>

上述虽能禁用缓存, 但只有部分浏览器支持, 而且由于代理不解析HTML文档, 故代理服务器也不支持这种方式.

IE8的异常表现

实际上, 上述缓存有关的规律, 并非所有浏览器都完全遵循. 比如说IE8.

资源缓存是否有效相关.

浏览器 前提 操作 表现 正常表现
IE8 资源缓存有效 新开一个窗口加载网页 重新发送请求(返回200) 展示缓存的页面
IE8 资源缓存失效 原浏览器窗口中单击 Enter 按钮 展示缓存的页面 重新发送请求(返回200)

Last-Modified / E-Tag 相关.

浏览器 前提 操作 表现 正常表现
IE8 资源内容没有修改 新开一个窗口加载网页 浏览器重新发送请求(返回200) 重新发送请求(返回304)
IE8 资源内容已修改 原浏览器窗口中单击 Enter 按钮 浏览器展示缓存的页面 重新发送请求(返回200)

本问就讨论这么多内容,大家有什么问题或好的想法欢迎在下方参与留言和评论.

本文作者: louis

本文链接: http://louiszhai.github.io/2017/04/07/http-cache/

参考文章

16年踩过的前端坑

1. 锁好npm package的第三方包版本,坑你没商量

2. 不要用仍然不稳定的属性,即使已经有了规范

3. js不要轻易用异步加载包~~