;
JavaScript 启动性能瓶颈分析与解决方案
{"debug":false,"apiRoot":"","paySDK":"https://pay.zhihu.com/api/js","name":"production","instance":"column","tokens":{"X-XSRF-TOKEN":null,"X-UDID":null,"Authorization":"oauth c3cef7c66a1843f8b3a9e6a1e3160e20"}}
{"database":{"Post":{"25221314":{"title":"JavaScript 启动性能瓶颈分析与解决方案","author":"wxyyxc1992","content":"<blockquote><a href=\"https://zhuanlan.zhihu.com/p/25221314\" class=\"internal\">JavaScript 启动性能瓶颈分析与解决方案</a> 翻译自 Addy Osmani 的 <a href=\"https://link.zhihu.com/?target=https%3A//medium.com/%40addyosmani/javascript-start-up-performance-69200f43b201%23.58r6ky779\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">JavaScript Start-up Performance<i class=\"icon-external\"></i></a>,从属于笔者的<a href=\"https://link.zhihu.com/?target=https%3A//github.com/wxyyxc1992/Web-Frontend-Introduction-And-Engineering-Practices\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Web 前端入门与工程实践<i class=\"icon-external\"></i></a>。本文已获得原作者授权,为InfoQ中文站特供稿件,首发地址为<a href=\"https://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s%3F__biz%3DMzIwNjQwMzUwMQ%3D%3D%26mid%3D2247484987%26idx%3D1%26sn%3D7f20da20bc6baed62ca8ff115209942b\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">这里<i class=\"icon-external\"></i></a>;如需转载,请与InfoQ中文站联系。随着现代 Web 技术的发展与用户交互复杂度的增加,我们的网站变得日益臃肿,也要求着我们不断地优化网站性能以保证友好的用户体验。本文作者则着眼于 JavaScript 启动阶段优化,首先以大量的数据分析阐述了语法分析、编译等步骤耗时占比过多是很多网站的性能瓶颈之一。然后作者提供了一系列用于在现代浏览器中进行性能评测的工具,还分别从开发者工程实践与 JavaScript 引擎内部实现的角度阐述了应当如何提高解析与编译速度。</blockquote><p>在 Web 开发中,随着需求的增加与代码库的扩张,我们最终发布的 Web 页面也逐渐膨胀。不过这种膨胀远不止意味着占据更多的传输带宽,其还意味着用户浏览网页时可能更差劲的性能体验。浏览器在下载完某个页面依赖的脚本之后,其还需要经过语法分析、解释与运行这些步骤。而本文则会深入分析浏览器对于 JavaScript 的这些处理流程,挖掘出那些影响你应用启动时间的罪魁祸首,并且根据我个人的经验提出相对应的解决方案。回顾过去,我们还没有专门地考虑过如何去优化 JavaScript 解析/编译这些步骤;我们预想中的是解析器在发现<script>标签后会瞬时完成解析操作,不过这很明显是痴人说梦。下图是对于 V8 引擎工作原理的概述:</p><p><img src=\"https://pic2.zhimg.com/v2-37a794bb55b2d31caeedd479e0f9f96d_b.png\" data-rawwidth=\"2000\" data-rawheight=\"1443\" class=\"origin_image zh-lightbox-thumb\" width=\"2000\" data-original=\"https://pic2.zhimg.com/v2-37a794bb55b2d31caeedd479e0f9f96d_r.png\"><br>下面我们深入其中的关键步骤进行分析。</p><h1>到底是什么拖慢了我们应用的启动时间?</h1><p>在启动阶段,语法分析,编译与脚本执行占据了 JavaScript 引擎运行的绝大部分时间。换言之,这些过程造成的延迟会真实地反应到用户可交互时延上;譬如用户已经看到了某个按钮,但是要好几秒之后才能真正地去点击操作,这一点会大大影响用户体验。</p><p><img src=\"https://pic1.zhimg.com/v2-767f45d48e7e26ad3d25b38566d93ae0_b.png\" data-rawwidth=\"1600\" data-rawheight=\"389\" class=\"origin_image zh-lightbox-thumb\" width=\"1600\" data-original=\"https://pic1.zhimg.com/v2-767f45d48e7e26ad3d25b38566d93ae0_r.png\"><br>上图是我们使用 Chrome Canary 内置的 V8 RunTime Call Stats 对于某个网站的分析结果;需要注意的是桌面浏览器中语法解析与编译占用的时间还是蛮长的,而在移动端中占用的时间则更长。实际上,对于 Facebook, Wikipedia, Reddit 这些大型网站中语法解析与编译所占的时间也不容忽视:</p><p><img src=\"https://pic1.zhimg.com/v2-b42628b757b36616c27c4da95a740d48_b.png\" data-rawwidth=\"2000\" data-rawheight=\"1090\" class=\"origin_image zh-lightbox-thumb\" width=\"2000\" data-original=\"https://pic1.zhimg.com/v2-b42628b757b36616c27c4da95a740d48_r.png\"><br>上图中的粉色区域表示花费在 V8 与 Blink's C++ 中的时间,而橙色和黄色分别表示语法解析与编译的时间占比。Facebook 的 Sebastian Markbage 与 Google 的 Rob Wormald 也都在 Twitter 发文表示过 JavaScript 的语法解析时间过长已经成为了不可忽视的问题,后者还表示这也是 Angular 启动时主要的消耗之一。</p><img src=\"https://pic2.zhimg.com/v2-90d70523ed492892276b884770e5d639_b.jpg\" data-rawwidth=\"1200\" data-rawheight=\"654\" class=\"origin_image zh-lightbox-thumb\" width=\"1200\" data-original=\"https://pic2.zhimg.com/v2-90d70523ed492892276b884770e5d639_r.jpg\"><p>随着移动端浪潮的涌来,我们不得不面对一个残酷的事实:移动端对于相同包体的解析与编译过程要花费相当于桌面浏览器2~5倍的时间。当然,对于高配的 iPhone 或者 Pixel 这样的手机相较于 Moto G4 这样的中配手机表现会好很多;这一点提醒我们在测试的时候不能仅用身边那些高配的手机,而应该中高低配兼顾:</p><img src=\"https://pic4.zhimg.com/v2-91dc6176fffddb9453815cb5bf8f24cb_b.jpg\" data-rawwidth=\"1600\" data-rawheight=\"1049\" class=\"origin_image zh-lightbox-thumb\" width=\"1600\" data-original=\"https://pic4.zhimg.com/v2-91dc6176fffddb9453815cb5bf8f24cb_r.jpg\"><p>上图是部分桌面浏览器与移动端浏览器对于 1MB 的 JavaScript 包体进行解析的时间对比,显而易见的可以发现不同配置的移动端手机之间的巨大差异。当我们应用包体已经非常巨大的时候,使用一些现代的打包技巧,譬如代码分割,TreeShaking,Service Workder 缓存等等会对启动时间有很大的影响。另一个角度来看,即使是小模块,你代码写的很糟或者使用了很糟的依赖库都会导致你的主线程花费大量的时间在编译或者冗余的函数调用中。我们必须要清醒地认识到全面评测以挖掘出真正性能瓶颈的重要性。</p><h1>JavaScript 语法解析与编译是否成为了大部分网站的瓶颈?</h1><p>我曾不止一次听到有人说,我又不是 Facebook,你说的 JavaScript 语法解析与编译到<br>底会对其他网站造成什么样的影响呢?对于这个问题我也很好奇,于是我花费了两个月的时间对于超过 6000 个网站进行分析;这些网站囊括了 React,Angular,Ember,Vue 这些流行的框架或者库。大部分的测试是基于 WebPageTest 进行的,因此你可以很方便地重现这些测试结果。<strong>光纤接入的桌面浏览器大概需要 8 秒的时间才能允许用户交互,而 3G 环境下的 Moto G4 大概需要 16 秒 才能允许用户交互。</strong><br><img src=\"https://pic2.zhimg.com/v2-4e354d900a7943bc2ce384c960f9db19_b.png\" data-rawwidth=\"2000\" data-rawheight=\"1226\" class=\"origin_image zh-lightbox-thumb\" width=\"2000\" data-original=\"https://pic2.zhimg.com/v2-4e354d900a7943bc2ce384c960f9db19_r.png\"><br></p><p><strong>大部分应用在桌面浏览器中会耗费约 4 秒的时间进行 JavaScript 启动阶段(语法解析、编译、执行)</strong>:<br><img src=\"https://pic2.zhimg.com/v2-89dd3dd21a303870979b1453e4477795_b.jpg\" data-rawwidth=\"1594\" data-rawheight=\"940\" class=\"origin_image zh-lightbox-thumb\" width=\"1594\" data-original=\"https://pic2.zhimg.com/v2-89dd3dd21a303870979b1453e4477795_r.jpg\"></p><p>而在移动端浏览器中,大概要花费额外 36% 的时间来进行语法解析:</p><img src=\"https://pic4.zhimg.com/v2-a2a9ab93fa1d4f659864cc21a3751acb_b.jpg\" data-rawwidth=\"1784\" data-rawheight=\"702\" class=\"origin_image zh-lightbox-thumb\" width=\"1784\" data-original=\"https://pic4.zhimg.com/v2-a2a9ab93fa1d4f659864cc21a3751acb_r.jpg\"><br><p>另外,统计显示并不是所有的网站都甩给用户一个庞大的 JS 包体,用户下载的经过 Gzip 压缩的平均包体大小是 410KB,这一点与 HTTPArchive 之前发布的 420KB 的数据基本一致。不过最差劲的网站则是直接甩了 10MB 的脚本给用户,简直可怕。</p><img src=\"https://pic4.zhimg.com/v2-ed957de5713820746e518e7058280ab7_b.png\" data-rawwidth=\"1600\" data-rawheight=\"924\" class=\"origin_image zh-lightbox-thumb\" width=\"1600\" data-original=\"https://pic4.zhimg.com/v2-ed957de5713820746e518e7058280ab7_r.png\"><br><p>通过上面的统计我们可以发现,包体体积固然重要,但是其并非唯一因素,语法解析与编译的耗时也不一定随着包体体积的增长而线性增长。总体而言小的 JavaScript 包体是会加载地更快(忽略浏览器、设备与网络连接的差异),但是同样 200KB 的大小,不同开发者的包体在语法解析、编译上的时间却是天差地别,不可同日而语。</p><h1>现代 JavaScript 语法解析 & 编译性能评测</h1><h2>Chrome DevTools</h2><p>打开 Timeline( Performance panel ) > Bottom-Up/Call Tree/Event Log 就会显示出当前网站在语法解析/编译上的时间占比。如果你希望得到更完整的信息,那么可以打开 V8 的 Runtime Call Stats。在 Canary 中,其位于 Timeline 的 Experims > V8 Runtime Call Stats 下。</p><img src=\"https://pic1.zhimg.com/v2-374a821e1b55328b5415c9ae7f911930_b.jpg\" data-rawwidth=\"1224\" data-rawheight=\"512\" class=\"origin_image zh-lightbox-thumb\" width=\"1224\" data-original=\"https://pic1.zhimg.com/v2-374a821e1b55328b5415c9ae7f911930_r.jpg\"><br><h2>Chrome Tracing</h2><p>打开 about:tracing 页面,Chrome 提供的底层的追踪工具允许我们使用disabled-by-default-v8.runtime_stats来深度了解 V8 的时间消耗情况。V8 也提供了<a href=\"https://link.zhihu.com/?target=https%3A//docs.google.com/presentation/d/1Lq2DD28CGa7bxawVH_2OcmyiTiBn74dvC6vn2essroY/edit%23slide%3Did.g1a504e63c9_2_84\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">详细的指南<i class=\"icon-external\"></i></a>来介绍如何使用这个功能。</p><img src=\"https://pic4.zhimg.com/v2-fba0b8feb1b7b1e7376901a04c809a4f_b.jpg\" data-rawwidth=\"1074\" data-rawheight=\"464\" class=\"origin_image zh-lightbox-thumb\" width=\"1074\" data-original=\"https://pic4.zhimg.com/v2-fba0b8feb1b7b1e7376901a04c809a4f_r.jpg\"><br><h2>WebPageTest</h2><img src=\"https://pic4.zhimg.com/v2-ba51661f10578adbd248d366fedc21b7_b.png\" data-rawwidth=\"756\" data-rawheight=\"552\" class=\"origin_image zh-lightbox-thumb\" width=\"756\" data-original=\"https://pic4.zhimg.com/v2-ba51661f10578adbd248d366fedc21b7_r.png\"><br><p>WebPageTest 中 Processing Breakdown 页面在我们启用 Chrome > Capture Dev Tools Timeline 时会自动记录 V8 编译、EvaluateScript 以及 FunctionCall 的时间。我们同样可以通过指明disabled-by-default-v8.runtime_stats的方式来启用 Runtime Call Stats。</p><img src=\"https://pic4.zhimg.com/v2-5eadc712087c40c2c10b163016e579db_b.png\" data-rawwidth=\"1248\" data-rawheight=\"110\" class=\"origin_image zh-lightbox-thumb\" width=\"1248\" data-original=\"https://pic4.zhimg.com/v2-5eadc712087c40c2c10b163016e579db_r.png\"><br><p>更多使用说明参考我的<a href=\"https://link.zhihu.com/?target=https%3A//gist.github.com/addyosmani/45b135900a7e3296e22673148ae5165b\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">gist<i class=\"icon-external\"></i></a>。</p><h2>User Timing</h2><p>我们还可以使用 Nolan Lawson 推荐的<a href=\"https://link.zhihu.com/?target=https%3A//w3c.github.io/user-timing/%23dom-performance-mark\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">User Timing API<i class=\"icon-external\"></i></a>来评估语法解析的时间。不过这种方式可能会受 V8 预解析过程的影响,我们可以借鉴 Nolan 在 optimize-js 评测中的方式,在脚本的尾部添加随机字符串来解决这个问题。我基于 Google Analytics 使用相似的方式来评估真实用户与设备访问网站时候的解析时间:</p><img src=\"https://pic2.zhimg.com/v2-b06c69bb875878ba3f250ae2f3b99ec9_b.jpg\" data-rawwidth=\"1240\" data-rawheight=\"438\" class=\"origin_image zh-lightbox-thumb\" width=\"1240\" data-original=\"https://pic2.zhimg.com/v2-b06c69bb875878ba3f250ae2f3b99ec9_r.jpg\"><br><h2>DeviceTiming</h2><p>Etsy 的 <a href=\"https://link.zhihu.com/?target=https%3A//github.com/danielmendel/DeviceTiming\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">DeviceTiming<i class=\"icon-external\"></i></a> 工具能够模拟某些受限环境来评估页面的语法解析与执行时间。其将本地脚本包裹在了某个仪表工具代码内从而使我们的页面能够模拟从不同的设备中访问。可以阅读 Daniel Espeset 的<a href=\"https://link.zhihu.com/?target=http%3A//talks.desp.in/unpacking-the-black-box\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Benchmarking JS Parsing and Execution on Mobile Devices<i class=\"icon-external\"></i></a> 一文来了解更详细的使用方式。</p><img src=\"https://pic3.zhimg.com/v2-29339011c9025667305d9f40dbef0d1e_b.jpg\" data-rawwidth=\"1566\" data-rawheight=\"590\" class=\"origin_image zh-lightbox-thumb\" width=\"1566\" data-original=\"https://pic3.zhimg.com/v2-29339011c9025667305d9f40dbef0d1e_r.jpg\"><br><h1>我们可以做些什么以降低 JavaScript 的解析时间?</h1><ul><li><p>减少 JavaScript 包体体积。我们在上文中也提及,更小的包体往往意味着更少的解析工作量,也就能降低浏览器在解析与编译阶段的时间消耗。</p></li><li><p>使用代码分割工具来按需传递代码与懒加载剩余模块。这可能是最佳的方式了,类似于<a href=\"https://link.zhihu.com/?target=https%3A//developers.google.com/web/fundamentals/performance/prpl-pattern/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">PRPL<i class=\"icon-external\"></i></a>这样的模式鼓励基于路由的分组,目前被 Flipkart, Housing.com 与 Twitter 广泛使用。</p></li><li><p>Script streaming: 过去 V8 鼓励开发者使用async/defer来基于<a href=\"https://link.zhihu.com/?target=https%3A//blog.chromium.org/2015/03/new-javascript-techniques-for-rapid.html\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">script streaming<i class=\"icon-external\"></i></a>实现 10-20% 的性能提升。这个技术会允许 HTML 解析器将相应的脚本加载任务分配给专门的 script streaming 线程,从而避免阻塞文档解析。V8 推荐尽早加载较大的模块,毕竟我们只有一个 streamer 线程。</p></li><li><p>评估我们依赖的解析消耗。我们应该尽可能地选择具有相同功能但是加载地更快的依赖,譬如使用 Preact 或者 Inferno 来代替 React,二者相较于 React 体积更小具有更少的语法解析与编译时间。Paul Lewis 在最近的<a href=\"https://link.zhihu.com/?target=https%3A//aerotwist.com/blog/when-everything-is-important-nothing-is/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">一篇文章<i class=\"icon-external\"></i></a>中也讨论了框架启动的代价,与 Sebastian Markbage 的<a href=\"https://link.zhihu.com/?target=https%3A//twitter.com/sebmarkbage/status/829733454119989248\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">说法<i class=\"icon-external\"></i></a>不谋而合:最好地评测某个框架启动消耗的方式就是先渲染一个界面,然后删除,最后进行重新渲染。第一次渲染的过程会包含了分析与编译,通过对比就能发现该框架的启动消耗。</p></li></ul><p>如果你的 JavaScript 框架支持 AOT(ahead-of-time)编译模式,那么能够有效地减少解析与编译的时间。Angular 应用就受益于这种模式:</p><img src=\"https://pic2.zhimg.com/v2-10f6ab9bc0d1dd2048beaf923a1f5e89_b.png\" data-rawwidth=\"1600\" data-rawheight=\"735\" class=\"origin_image zh-lightbox-thumb\" width=\"1600\" data-original=\"https://pic2.zhimg.com/v2-10f6ab9bc0d1dd2048beaf923a1f5e89_r.png\"><br><h1>现代浏览器是如何提高解析与编译速度的?</h1><p>不用灰心,你并不是唯一纠结于如何提升启动时间的人,我们 V8 团队也一直在努力。我们发现之前的某个评测工具 Octane 是个不错的对于真实场景的模拟,它在微型框架与冷启动方面很符合真实的用户习惯。而基于这些工具,V8 团队在过去的工作中也实现了大约 25% 的启动性能提升:</p><img src=\"https://pic4.zhimg.com/v2-2becff510ab4d98b61fcaecddd983ea3_b.jpg\" data-rawwidth=\"1600\" data-rawheight=\"674\" class=\"origin_image zh-lightbox-thumb\" width=\"1600\" data-original=\"https://pic4.zhimg.com/v2-2becff510ab4d98b61fcaecddd983ea3_r.jpg\"><br><p>本部分我们就会对过去几年中我们使用的提升语法解析与编译时间的技巧进行阐述。</p><h2>代码缓存</h2><img src=\"https://pic4.zhimg.com/v2-fe36ca9d9e129eea455e2c77ccdec9fb_b.png\" data-rawwidth=\"577\" data-rawheight=\"321\" class=\"origin_image zh-lightbox-thumb\" width=\"577\" data-original=\"https://pic4.zhimg.com/v2-fe36ca9d9e129eea455e2c77ccdec9fb_r.png\"><br><p>Chrome 42 开始引入了所谓的<a href=\"https://link.zhihu.com/?target=http%3A//v8project.blogspot.com/2015/07/code-caching.html\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">代码缓存<i class=\"icon-external\"></i></a>的概念,为我们提供了一种存放编译后的代码副本的机制,从而当用户二次访问该页面时可以避免脚本抓取、解析与编译这些步骤。除以之外,我们还发现在重复访问的时候这种机制还能避免 40% 左右的编译时间,这里我会深入介绍一些内容:</p><ul><li><p>代码缓存会对于那些在 72 小时之内重复执行的脚本起作用。</p></li><li><p>对于 Service Worker 中的脚本,代码缓存同样对 72 小时之内的脚本起作用。</p></li><li><p>对于利用 Service Worker 缓存在 Cache Storage 中的脚本,代码缓存能在脚本首次执行的时候起作用。</p></li></ul><p>总而言之,对于主动缓存的 JavaScript 代码,最多在第三次调用的时候其能够跳过语法分析与编译的步骤。我们可以通过chrome://flags/#v8-cache-strategies-for-cache-storage来查看其中的差异,也可以设置 js-flags=profile-deserialization运行 Chrome 来查看代码是否加载自代码缓存。不过需要注意的是,代码缓存机制仅会缓存那些经过编译的代码,主要是指那些顶层的往往用于设置全局变量的代码。而对于类似于函数定义这样懒编译的代码并不会被缓存,不过 IIFE 同样被包含在了 V8 中,因此这些函数也是可以被缓存的。</p><h2>Script Streaming</h2><p><a href=\"https://link.zhihu.com/?target=https%3A//blog.chromium.org/2015/03/new-javascript-techniques-for-rapid.html\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Script Streaming<i class=\"icon-external\"></i></a>允许在后台线程中对异步脚本执行解析操作,可以对于页面加载时间有大概 10% 的提升。上文也提到过,这个机制同样会对同步脚本起作用。</p><p><img src=\"https://pic4.zhimg.com/v2-adad3f3a8954b0b3790eec3c415ab44b_b.png\" data-rawwidth=\"2000\" data-rawheight=\"1067\" class=\"origin_image zh-lightbox-thumb\" width=\"2000\" data-original=\"https://pic4.zhimg.com/v2-adad3f3a8954b0b3790eec3c415ab44b_r.png\"><br>这个特性倒是第一次提及,因此 V8 会允许所有的脚本,即使阻塞型的<script src=''>脚本也可以由后台线程进行解析。不过缺陷就是目前仅有一个 streaming 后台线程存在,因此我们建议首先解析大的、关键性的脚本。在实践中,我们建议将<script defer>添加到<head>块内,这样浏览器引擎就能够尽早地发现需要解析的脚本,然后将其分配给后台线程进行处理。我们也可以查看 DevTools Timeline 来确定脚本是否被后台解析,特别是当你存在某个关键性脚本需要解析的时候,更需要确定该脚本是由 streaming 线程解析的。</p><img src=\"https://pic3.zhimg.com/v2-69d8066656015e2cf2fa9962e8965f42_b.png\" data-rawwidth=\"1282\" data-rawheight=\"104\" class=\"origin_image zh-lightbox-thumb\" width=\"1282\" data-original=\"https://pic3.zhimg.com/v2-69d8066656015e2cf2fa9962e8965f42_r.png\"><br><h2>语法解析 & 编译优化</h2><p>我们同样致力于打造更轻量级、更快的解析器,目前 V8 主线程中最大的瓶颈在于所谓的非线性解析消耗。譬如我们有如下的代码片:</p><div class=\"highlight\"><pre><code class=\"language-text\"><span></span>(function (global, module) { … })(this, function module() { my functions })\n</code></pre></div><p>V8 并不知道我们编译主脚本的时候是否需要module这个模块,因此我们会暂时放弃编译它。而当我们打算编译module时,我们需要重分析所有的内部函数。这也就是所谓的 V8 解析时间非线性的原因,任何一个处于 N 层深度的函数都有可能被重新分析 N 次。V8 已经能够在首次编译的时候搜集所有内部函数的信息,因此在未来的编译过程中 V8 会忽略所有的内部函数。对于上面这种module形式的函数会是很大的性能提升,建议阅读<a href=\"https://link.zhihu.com/?target=https%3A//docs.google.com/presentation/d/1214p4CFjsF-NY4z9in0GEcJtjbyVQgU0A-UqEvovzCs/edit%23slide%3Did.p\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">The V8 Parser(s) — Design, Challenges, and Parsing JavaScript Better<i class=\"icon-external\"></i></a>来获取更多内容。V8 同样在寻找合适的分流机制以保证启动时能在后台线程中执行 JavaScript 编译过程。</p><h2>预编译 JavaScript?</h2><p>每隔几年就有人提出引擎应该提供一些处理预编译脚本的机制,换言之,开发者可以使用构建工具或者其他服务端工具将脚本转化为字节码,然后浏览器直接运行这些字节码即可。从我个人观点来看,直接传送字节码意味着更大的包体,势必会增加加载时间;并且我们需要去对代码进行签名以保证能够安全运行。目前我们对于 V8 的定位是尽可能地避免上文所说的内部重分析以提高启动时间,而预编译则会带来额外的风险。不过我们欢迎大家一起来讨论这个问题,虽然 V8 目前专注于提升编译效率以及推广利用 Service Worker 缓存脚本代码来提升启动效率。我们在 BlinkOn7 上与 Facebook 以及 Akamai 也讨论过<a href=\"https://link.zhihu.com/?target=https%3A//gist.github.com/addyosmani/4009ee1238c4b1ff6f2a2d8a5057c181\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">预编译相关内容<i class=\"icon-external\"></i></a>。</p><h2>Optimize JS 优化</h2><p>类似于 V8 这样的 JavaScript 引擎在进行完整的解析之前会对脚本中的大部分函数进行预解析,这主要是考虑到大部分页面中包含的 JavaScript 函数并不会立刻被执行。<br></p><p>预编译能够通过只处理那些浏览器运行所需要的最小函数集合来提升启动时间,不过这种机制在 IIFE 面前却反而降低了效率。尽管引擎希望避免对这些函数进行预处理,但是远不如<a href=\"https://link.zhihu.com/?target=https%3A//github.com/nolanlawson/optimize-js\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">optimize-js<i class=\"icon-external\"></i></a>这样的库有作用。optimize-js 会在引擎之前对于脚本进行处理,对于那些立即执行的函数插入圆括号从而保证更快速地执行。这种预处理对于 Browserify, Webpack 生成包体这样包含了大量即刻执行的小模块起到了非常不错的优化效果。尽管这种小技巧并非 V8 所希望使用的,但是在当前阶段不得不引入相应的优化机制。</p><h1>总结</h1><p>启动阶段的性能至关重要,缓慢的解析、编译与执行时间可能成为你网页性能的瓶颈所在。我们应该评估页面在这个阶段的时间占比并且选择合适的方式来优化。我们也会继续致力于提升 V8 的启动性能,尽我所能!</p><h1>延伸阅读</h1><ul><li><p><a href=\"https://link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3DRWLzUnESylc\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Planning for Performance<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//twitter.com/MSEdgeDev/status/819985530775404544\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Solving the Web Performance Crisis by Nolan Lawson<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//timkadlec.com/2014/09/js-parse-and-execution-time/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">JS Parse and Execution Time<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=http%3A//carlos.bueno.org/2010/02/measuring-javascript-parse-and-load.html\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Measuring Javascript Parse and Load<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//www.safaribooksonline.com/library/view/velocity-conference-new/9781491900406/part78.html\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Unpacking the Black Box: Benchmarking JS Parsing and Execution on Mobile Devices<i class=\"icon-external\"></i></a> (<a href=\"https://link.zhihu.com/?target=https%3A//speakerdeck.com/desp/unpacking-the-black-box-benchmarking-js-parsing-and-execution-on-mobile-devices\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">slides<i class=\"icon-external\"></i></a>)</p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//aerotwist.com/blog/when-everything-is-important-nothing-is/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">When everything’s important, nothing is!<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=http%3A//benediktmeurer.de/2016/12/16/the-truth-about-traditional-javascript-benchmarks/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">The truth about traditional JavaScript benchmarks<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=http%3A//stackoverflow.com/questions/1096907/do-browsers-parse-javascript-on-every-page-load/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Do Browsers Parse JavaScript On Every Page Load<i class=\"icon-external\"></i></a></p></li></ul>","updated":"2017-02-13T14:41:50.000Z","canComment":false,"commentPermission":"anyone","commentCount":2,"collapsedCount":0,"likeCount":102,"state":"published","isLiked":false,"slug":"25221314","lastestTipjarors":[{"isFollowed":false,"name":"Special","headline":"","avatarUrl":"https://pic2.zhimg.com/cec86a3e6e234e1a80f8c3edcae4efd1_s.jpg","isFollowing":false,"type":"people","slug":"liang-zhao-peng-4","profileUrl":"https://www.zhihu.com/people/liang-zhao-peng-4","bio":"","hash":"85d7109fafd618ec6e87fb74a842a705","uid":38592931954688,"isOrg":false,"description":"","isOrgWhiteList":false,"avatar":{"id":"cec86a3e6e234e1a80f8c3edcae4efd1","template":"https://pic2.zhimg.com/{id}_{size}.jpg"}},{"isFollowed":false,"name":"潘悟侃","headline":"一步,两步。。。","avatarUrl":"https://pic4.zhimg.com/514f569510435cdcf53ea1d7b8d279e3_s.jpg","isFollowing":false,"type":"people","slug":"pan-wu-kan","profileUrl":"https://www.zhihu.com/people/pan-wu-kan","bio":"小前端往前看","hash":"4b34237d58d6b9671d8ad72ab69801f6","uid":572817448823164900,"isOrg":false,"description":"一步,两步。。。","isOrgWhiteList":false,"avatar":{"id":"514f569510435cdcf53ea1d7b8d279e3","template":"https://pic4.zhimg.com/{id}_{size}.jpg"}},{"isFollowed":false,"name":"小镇电话","headline":"","avatarUrl":"https://pic1.zhimg.com/da8e974dc_s.jpg","isFollowing":false,"type":"people","slug":"townphone","profileUrl":"https://www.zhihu.com/people/townphone","bio":"程序猴儿","hash":"33ef963ee4f03daf176107e41c33aa76","uid":728669391960428500,"isOrg":false,"description":"","isOrgWhiteList":false,"avatar":{"id":"da8e974dc","template":"https://pic1.zhimg.com/{id}_{size}.jpg"}},{"isFollowed":false,"name":"Early","headline":"","avatarUrl":"https://pic1.zhimg.com/v2-1f0c43c46cd992d523d2284ab01c7b9c_s.jpg","isFollowing":false,"type":"people","slug":"zjczoo","profileUrl":"https://www.zhihu.com/people/zjczoo","bio":"","hash":"083cecf3af8f629239442d913dcbeb41","uid":27236140843008,"isOrg":false,"description":"","isOrgWhiteList":false,"avatar":{"id":"v2-1f0c43c46cd992d523d2284ab01c7b9c","template":"https://pic1.zhimg.com/{id}_{size}.jpg"}}],"isTitleImageFullScreen":true,"rating":"none","titleImage":"https://pic4.zhimg.com/v2-a17b424fa8d82b15bbbe6e7313e8772b_r.png","links":{"comments":"/api/posts/25221314/comments"},"reviewers":[],"topics":[{"url":"https://www.zhihu.com/topic/19552521","id":"19552521","name":"JavaScript"},{"url":"https://www.zhihu.com/topic/19633850","id":"19633850","name":"性能优化"},{"url":"https://www.zhihu.com/topic/19616230","id":"19616230","name":"V8"}],"titleImageSize":{"width":1920,"height":695},"href":"/api/posts/25221314","excerptTitle":"","column":{"slug":"wxyyxc1992","name":"某熊的全栈之路"},"tipjarState":"activated","tipjarTagLine":"大爷赏点呗~","sourceUrl":"","pageCommentsCount":2,"tipjarorCount":4,"snapshotUrl":"","publishedTime":"2017-02-13T22:41:50+08:00","url":"/p/25221314","lastestLikers":[{"profileUrl":"https://www.zhihu.com/people/peng-peng-peng-qi-fen-gang-hao","bio":"前端开发人员","hash":"e8277462f408b6d06002bacb11381700","uid":771070511424733200,"isOrg":false,"description":"小白,请关照","isOrgWhiteList":false,"slug":"peng-peng-peng-qi-fen-gang-hao","avatar":{"id":"v2-a74cff53b3b95303949b81ae993515fd","template":"https://pic2.zhimg.com/{id}_{size}.jpg"},"name":"彭彭彭气氛刚好"},{"profileUrl":"https://www.zhihu.com/people/li-zheng-fu","bio":"新领域技术爱好者","hash":"a8f91021b07fbdf3654ebdeb824f98a6","uid":28491445698560,"isOrg":false,"description":"干了再说","isOrgWhiteList":false,"slug":"li-zheng-fu","avatar":{"id":"da8e974dc","template":"https://pic1.zhimg.com/{id}_{size}.jpg"},"name":"李政甫"},{"profileUrl":"https://www.zhihu.com/people/onenewlife","bio":"love my tay better than code :)","hash":"a0385f41639f8c512e0344d319d6daac","uid":585968325071212500,"isOrg":false,"description":"Keep It Simple and Stupid.","isOrgWhiteList":false,"slug":"onenewlife","avatar":{"id":"v2-aa94b11b0437eeda6ce15383a86d8887","template":"https://pic4.zhimg.com/{id}_{size}.jpg"},"name":"Mars Wong"},{"profileUrl":"https://www.zhihu.com/people/zhou-lai-qing-51","bio":"孤独的程序猿","hash":"874fbd862b58e31dbcd84521b460e187","uid":668024904109133800,"isOrg":false,"description":"","isOrgWhiteList":false,"slug":"zhou-lai-qing-51","avatar":{"id":"da8e974dc","template":"https://pic1.zhimg.com/{id}_{size}.jpg"},"name":"周涞卿"},{"profileUrl":"https://www.zhihu.com/people/differui","bio":"画网页的","hash":"16016ccff88d153b04724292c2cc1669","uid":29626281754624,"isOrg":false,"description":"","isOrgWhiteList":false,"slug":"differui","avatar":{"id":"715164c14","template":"https://pic1.zhimg.com/{id}_{size}.jpg"},"name":"管斌瑞"}],"summary":"<img src=\"https://pic2.zhimg.com/v2-90d70523ed492892276b884770e5d639_200x112.jpg\" data-rawwidth=\"1200\" data-rawheight=\"654\" class=\"origin_image inline-img zh-lightbox-thumb\" data-original=\"https://pic2.zhimg.com/v2-90d70523ed492892276b884770e5d639_r.jpg\"><a href=\"https://zhuanlan.zhihu.com/p/25221314\" data-editable=\"true\" data-title=\"JavaScript 启动性能瓶颈分析与解决方案\">JavaScript 启动性能瓶颈分析与解决方案</a> 翻译自 Addy Osmani 的 <a href=\"https://medium.com/@addyosmani/javascript-start-up-performance-69200f43b201#.58r6ky779\" data-editable=\"true\" data-title=\"JavaScript Start-up Performance\">JavaScript Start-up Performance</a>,从属于笔者的<a href=\"https://github.com/wxyyxc1992/Web-Frontend-Introduction-And-Engineering-Practices\" data-editable=\"true\" data-title=\"Web 前端入门与工程实践\">Web 前端入门与工程实践</a>。本文已获得原作者授权,为InfoQ中文站特供稿件,首发地址为<a href=\"https://mp.weixin.qq.com/s?__biz=MzIwNjQwMzUwMQ==&mid=2247484987&idx=1&sn=7f20da20bc6baed62ca8ff115209942b\" data-editable=\"true\" data-title=\"这里\">这里</a>;如需转载,请与InfoQ中文站联系。随着现代 Web 技术…","reviewingCommentsCount":0,"meta":{"previous":{"isTitleImageFullScreen":false,"rating":"none","titleImage":"https://pic1.zhimg.com/v2-31f3864f99fa95842ad7e947965df74c_r.jpg","links":{"comments":"/api/posts/25192486/comments"},"topics":[{"url":"https://www.zhihu.com/topic/19550564","id":"19550564","name":"阅读"},{"url":"https://www.zhihu.com/topic/19552516","id":"19552516","name":"写作"},{"url":"https://www.zhihu.com/topic/19550516","id":"19550516","name":"Web 开发"}],"href":"/api/posts/25192486","excerptTitle":"","author":{"profileUrl":"https://www.zhihu.com/people/wxyyxc1992","bio":"王下邀月熊","hash":"ed4cd6b92a003a0ce8e801ae74196e19","uid":57299368411136,"isOrg":false,"description":"https://segmentfault.com/u/wxyyxc1992\n\nhttps://github.com/wxyyxc1992","isOrgWhiteList":false,"slug":"wxyyxc1992","avatar":{"id":"v2-a627d79d2ed03fe6f83a11743a18d909","template":"https://pic2.zhimg.com/{id}_{size}.jpg"},"name":"王下邀月熊"},"column":{"slug":"wxyyxc1992","name":"某熊的全栈之路"},"content":"<blockquote><p><a href=\"https://zhuanlan.zhihu.com/p/25191664\" class=\"internal\">某熊的阅读与写作日常</a> 从属于 <a href=\"http://link.zhihu.com/?target=https%3A//github.com/wxyyxc1992/Coder-Knowledge-Graph/tree/master/I-AM-Coder\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">程序熊的自我修养<i class=\"icon-external\"></i></a></p></blockquote><p>最近加入了 InfoQ 的社区编辑,负责一些线索搜寻与文章翻译,突然想写一篇文章记录下我的日常生活中的阅读与写作模式。一年多以来基本上所有的文章都是希望纯技术主题的;突然也想整理一个系列来讲讲个人对于技术的不严谨的看法与感悟,不敢妄称程序员,就叫<a href=\"http://link.zhihu.com/?target=https%3A//github.com/wxyyxc1992/Coder-Knowledge-Graph/tree/master/I-AM-Coder\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">程序熊的自我修养<i class=\"icon-external\"></i></a>吧,可能会包含以下部分:</p><ul><li>【程序熊的自我修养】我写代码这三年</li><li>【程序熊的自我修养】知识体系与自我定位</li><li>【程序熊的自我修养】阅读与积累</li><li>【程序熊的自我修养】工具与实战</li><li>【程序熊的自我修养】产品与工程化</li><li>【程序熊的自我修养】编程之道:编程语言、数据结构与算法、编程范式</li><li>【程序熊的自我修养】Web 前端</li><li>【程序熊的自我修养】服务端应用程序架构</li><li>【程序熊的自我修养】数据科学与人工智能</li></ul><p>言归正传,本文我希望来描述下每日的阅读与写作模式,重点是阅读的来源与使用的工具。首先需要强调的是,因为本文是日常阅读,所以并不会包括类似于\n SICP,CSAPP 这样的大部头。笔者目前兼职于某建筑行业软件公司,公司业务包括交易平台、交易 \nAPP、数据管理中心、数据分析报告等;笔者负责技术团队的日常运作,整个技术团队大约十个人;早期会负责产品架构与核心代码开发,不过随着日趋稳定笔者每天只是在现场打酱油。笔者这个阶段最主要的任务还是毕业论文,暂且把毕业论文与公司归为工作一类;总体来说笔者每日在工作与阅读学习上的时间开支正好对半分。</p><p>每天早上骑着小电驴和妹子(妹子是公司的产品经理)一起到公司之后,笔者首先会浏览下文章列表,然后将感兴趣的文章添加到 <a href=\"http://link.zhihu.com/?target=https%3A//getpocket.com\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Pocket<i class=\"icon-external\"></i></a> 中;如果是类似于 <a href=\"http://link.zhihu.com/?target=https%3A//exacity.github.io/deeplearningbook-chinese/Chapter1_introduction/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Deeplearning Textbook<i class=\"icon-external\"></i></a> 这样的在线书籍或者 CS224D 这样的课程会加入到 <a href=\"http://link.zhihu.com/?target=https%3A//chrome.google.com\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Chrome<i class=\"icon-external\"></i></a>书签中。然后在 iPad、手机上都可以随时同步 Pocket 与 Chrome 中的阅读记录与书签,Pocket 还支持离线阅读,都是非常推荐的应用。笔者在早期主要是刷中文社区,现在的话主要以英文文章为主,这里就涉及到所谓的科学上网的问题了,这里不予以讨论。 笔者每天首先会刷一波资讯阅读类网站,包括但不限于:</p><ul><li><a href=\"http://link.zhihu.com/?target=https%3A//www.reddit.com/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Reddit<i class=\"icon-external\"></i></a>,可以订阅一些感兴趣的主题。</li><li><a href=\"http://link.zhihu.com/?target=http%3A//regulargeek.com/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Regular Geek<i class=\"icon-external\"></i></a>,每日资讯合集。</li><li><a href=\"http://link.zhihu.com/?target=https%3A//www.mybridge.co/todaytop\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">MyBridge<i class=\"icon-external\"></i></a>,可以订阅一些感兴趣的主题。</li><li><a href=\"http://link.zhihu.com/?target=https%3A//github.com/trending\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Github Trending<i class=\"icon-external\"></i></a></li><li><a href=\"http://link.zhihu.com/?target=https%3A//twitter.com/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Twitter<i class=\"icon-external\"></i></a>,可以 Follow 一些大牛获取一手信息。</li><li><a href=\"http://link.zhihu.com/?target=https%3A//medium.com/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Medium<i class=\"icon-external\"></i></a>,这是笔者每日浏览最多的网站了,目前也是笔者认为质量最高的站点之一。</li></ul><p>然后中文社区的话推荐<a href=\"http://link.zhihu.com/?target=http%3A//www.tuicool.com/a/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">推酷<i class=\"icon-external\"></i></a>,<a href=\"http://link.zhihu.com/?target=https%3A//github.com/wxyyxc1992/Coder-Knowledge-Graph/blob/master/I-AM-Coder/toutiao.io\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">开发者头条<i class=\"icon-external\"></i></a>与<a href=\"http://link.zhihu.com/?target=https%3A//gold.xitu.io/new-entry\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">掘金<i class=\"icon-external\"></i></a>。还有一些公众号与个人博客笔者这里就暂不推荐了,基本上可以从上面这些聚合阅读类网站里面也可以发现。之前有不少可能是刚入门的同学讨论应该怎么学习,从我个人经历而言,我最初就是刷推酷每日推荐文章。大概每天会浏览\n 100 \n篇左右,最初的时候真的蛮痛苦的,看啥啥不会;不过过了一段时间这种填鸭式的阅读还是有一定效果的,能够帮你建立相对完善全面的技术体系世界观(笔者的<a href=\"https://zhuanlan.zhihu.com/p/24476917\" class=\"internal\">2016-我的技术之路:编程知识体系结构</a>就是从那时开始的)。等到某一天你可能浏览了数百篇文章但是发现只有一两篇有兴趣一读的,那么就可以开始多啃啃大部头了。</p><p>阅读只是前行的第一个准备,下面我们需要能够将阅读到的知识沉淀下来,也就是所谓的笔记与写作。对于写作工具笔者考量过<a href=\"http://link.zhihu.com/?target=https%3A//github.com/wxyyxc1992/Coder-Knowledge-Graph/blob/master/I-AM-Coder/www.web-wiz.com\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">为知笔记<i class=\"icon-external\"></i></a>,有道笔记,印象笔记等,不过笔者的基本需求包括:</p><ul><li>允许自定义多层目录结构,这样的话才方便将合适的笔记放置到合适的地方。</li><li>允许 MarkDown。</li><li>支持 LaTeX 数学表达式。</li></ul><p>综合考量之下貌似只有为知笔记符合以上三个需求。笔记是对于自己知识的积累与总结,而写作则能帮助你打造个人品牌,提升社区影响力,并且能让你的知识经过更多的批判与讨论,非常有助于个人的成长。初期能力不足的话建议以翻译为主,上文笔者推荐的不少每日阅读网站都是英文阅读网站,可以选取感兴趣的文章进行翻译。笔者最早是在\n CSDN 博客进行写作的,不过老实说体验不是很好。后来笔者迁到了<a href=\"http://link.zhihu.com/?target=https%3A//segmentfault.com/blog/wxyyxc1992\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">SegmentFault-某熊的全栈之路<i class=\"icon-external\"></i></a>与<a href=\"https://zhuanlan.zhihu.com/wxyyxc1992\" class=\"internal\">知乎-某熊的全栈之路</a>,这两个网站的用户体验,SEO 以及 读者沉淀做的都不错,值得一试。文章写好了之后我们自然希望能有更多的人阅读,做过互联网营销的相信都非常熟悉所谓的分享渠道,而我们也需要主动地将自己的文章分享到渠道中;笔者常用的渠道包括:</p><ul><li>CSDN 极客头条,有 Chrome 插件</li><li>SegmentFault 头条</li><li>开发者头条</li><li><a href=\"http://link.zhihu.com/?target=http%3A//Gank.io\" class=\" external\" target=\"_blank\" rel=\"nofollow noreferrer\"><span class=\"invisible\">http://</span><span class=\"visible\">Gank.io</span><span class=\"invisible\"></span><i class=\"icon-external\"></i></a></li><li>掘金</li><li>技术头条</li><li>奇舞周刊</li></ul><p>最后还想说一句,如果有什么笔者未提及的渠道欢迎告知~</p>","state":"published","sourceUrl":"","pageCommentsCount":0,"canComment":false,"snapshotUrl":"","slug":25192486,"publishedTime":"2017-02-11T18:16:14+08:00","url":"/p/25192486","title":"某熊的阅读与写作日常","summary":"<a href=\"https://zhuanlan.zhihu.com/p/25191664\" class=\"\" data-editable=\"true\" data-title=\"某熊的阅读与写作日常\">某熊的阅读与写作日常</a> 从属于 <a href=\"https://link.zhihu.com/?target=https%3A//github.com/wxyyxc1992/Coder-Knowledge-Graph/tree/master/I-AM-Coder\" class=\"\" data-editable=\"true\" data-title=\"程序熊的自我修养\">程序熊的自我修养</a>最近加入了 InfoQ 的社区编辑,负责一些线索搜寻与文章翻译,突然想写一篇文章记录下我的日常生活中的阅读与写作模式。一年多以来基本上所有的文章都是希望纯技术主题的;突然也想整理一个系列来讲讲个人对于…","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"commentPermission":"anyone","commentsCount":0,"likesCount":0},"next":{"isTitleImageFullScreen":true,"rating":"none","titleImage":"https://pic4.zhimg.com/v2-c5bd4581bea83cce590703c4b360d423_r.jpg","links":{"comments":"/api/posts/25224787/comments"},"topics":[{"url":"https://www.zhihu.com/topic/19550516","id":"19550516","name":"Web 开发"},{"url":"https://www.zhihu.com/topic/19559450","id":"19559450","name":"机器学习"},{"url":"https://www.zhihu.com/topic/19610969","id":"19610969","name":"移动开发"}],"href":"/api/posts/25224787","excerptTitle":"","author":{"profileUrl":"https://www.zhihu.com/people/wxyyxc1992","bio":"王下邀月熊","hash":"ed4cd6b92a003a0ce8e801ae74196e19","uid":57299368411136,"isOrg":false,"description":"https://segmentfault.com/u/wxyyxc1992\n\nhttps://github.com/wxyyxc1992","isOrgWhiteList":false,"slug":"wxyyxc1992","avatar":{"id":"v2-a627d79d2ed03fe6f83a11743a18d909","template":"https://pic2.zhimg.com/{id}_{size}.jpg"},"name":"王下邀月熊"},"column":{"slug":"wxyyxc1992","name":"某熊的全栈之路"},"content":"<blockquote><p><a href=\"https://zhuanlan.zhihu.com/p/25224787\" class=\"internal\">某熊周刊系列:一周推荐外文技术资料(2.3)</a>归纳于<a href=\"https://link.zhihu.com/?target=https%3A//github.com/wxyyxc1992/Coder-Knowledge-Graph/tree/master/Weekly\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">某熊周刊:一周推荐外文技术资料<i class=\"icon-external\"></i></a>是笔者每周浏览外文技术网站中时发现的不错的文章/项目/书籍/教程的集锦,可以关注笔者的专栏<a href=\"https://zhuanlan.zhihu.com/wxyyxc1992\" class=\"internal\">某熊的全栈之路</a>及时获取更新。资讯来源包括但不限于<a href=\"https://link.zhihu.com/?target=https%3A//medium.com/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Medium<i class=\"icon-external\"></i></a>、<a href=\"https://link.zhihu.com/?target=https%3A//twitter.com/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Twitter<i class=\"icon-external\"></i></a>、<a href=\"https://link.zhihu.com/?target=https%3A//plus.google.com/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Google Plus<i class=\"icon-external\"></i></a>、<a href=\"https://link.zhihu.com/?target=https%3A//www.reddit.com/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Reddit<i class=\"icon-external\"></i></a>、<a href=\"https://link.zhihu.com/?target=https%3A//news.ycombinator.com/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Hacker News<i class=\"icon-external\"></i></a>、<a href=\"https://link.zhihu.com/?target=https%3A//dzone.com/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">DZone<i class=\"icon-external\"></i></a>、<a href=\"https://link.zhihu.com/?target=https%3A//github.com/trending\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Github Trending<i class=\"icon-external\"></i></a>。另外,周刊中的技术知识框架图参照笔者的<a href=\"https://zhuanlan.zhihu.com/p/24476917?refer=wxyyxc1992\" class=\"internal\">2016:我的编程知识体系结构</a>。请自备梯子。</p></blockquote><h1>技术纵横</h1><ul><li><p><a href=\"https://link.zhihu.com/?target=https%3A//uxplanet.org/functional-minimalism-for-web-design-4290722cddbd%23.d651yrl11\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Functional Minimalism for Web Design<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//medium.freecodecamp.com/here-are-the-average-salaries-of-software-engineers-around-the-world-in-2017-f121af69f23e%23.rhlagbgzv\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Here are the average salaries of software engineers around the world in 2017<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//github.com/open-source/stories/yyx990803\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Evan You<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//www.behance.net/gallery/47810259/2017-Design-Trends-Guide\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">2017-Design-Trends-Guide<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//hackernoon.com/i-used-lamp-to-make-a-saas-with-3700-mo-profit-heres-how-1c47033900e9%23.idyvsdcrg\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">I used LAMP to make a SaaS with $3700/mo profit. Here’s how.<i class=\"icon-external\"></i></a></p></li></ul><h1>编程语言</h1><ul><li><p><a href=\"https://link.zhihu.com/?target=https%3A//zeroturnaround.com/rebellabs/solution-to-the-smallest-java-class-file-challenge/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Solution to the smallest Java class file challenge<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//medium.com/%40forbeslindesay/covariance-and-contravariance-c3b43d805611%23.1jh1c2s36\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Flow’s best kept secret<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=http%3A//stackoverflow.com/questions/28459498/why-are-java-streams-once-off/28513908%3Futm_source%3Dmybridge%26utm_medium%3Dweb%26utm_campaign%3Dread_more\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Why are Java Streams once-off?<i class=\"icon-external\"></i></a></p></li></ul><h1>编程基础</h1><ul><li><p><a href=\"https://link.zhihu.com/?target=https%3A//www.weheartswift.com/need-know-json/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">All you need to know about JSON<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//medium.freecodecamp.com/how-google-builds-a-web-framework-5eeddd691dea%23.fg4fzpo7m\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">How Google builds web frameworks<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//blog.skyliner.io/ship-small-diffs-741308bec0d1%23.ibmzg0x4g\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Ship Small Diffs<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=http%3A//dev-books.com/%23\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Top mentioned books on stackoverflow.com<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//lowlevelbits.org/java-papers/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">JAVA PAPERS Referred in OpenJDK<i class=\"icon-external\"></i></a></p></li></ul><h1>Web</h1><ul><li><p><a href=\"https://link.zhihu.com/?target=https%3A//hackernoon.com/straightforward-code-splitting-with-react-and-webpack-4b94c28f6c3f%23.u6yjaag0o\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Straightforward code splitting with React and Webpack<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//daveceddia.com/redux-action-creators/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">When to Define Action Creators in Redux<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//blog.hospodarets.com/native-ecmascript-modules-the-first-overview\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Native ECMAScript modules - the first overview<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//blog.hospodarets.com/native-ecmascript-modules-new-features%23detect-the-current-script-is-executed-as-a-module\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Native ECMAScript modules: the new features and differences from Webpack modules<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//www.nczonline.net/blog/2016/04/es6-module-loading-more-complicated-than-you-think/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">ES6 module loading: More complicated than you think<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//blog.hichroma.com/the-crucial-tool-for-modern-frontend-engineers-fb849b06187a%23.28fqdlbfj\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">UI Component Explorers — your new favorite tool<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//bitsofco.de/how-calc-works/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">How calc() Works<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//advancedweb.hu/2017/02/07/treeshaking/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Why Webpack 2's Tree Shaking is not as effective as you think<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//medium.com/fusebox/webpack-is-not-the-only-way-6ddb67e99be9%23.202gkbcxc\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">WebPack is not the only way<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//medium.com/%40steida/css-in-js-the-argument-refined-471c7eb83955%23.z6czxmcfm\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">CSS in JS: The Argument Refined<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//www.sitepoint.com/introducing-graphicsjs-a-powerful-lightweight-graphics-library/%3Futm_source%3Dmybridge%26utm_medium%3Dweb%26utm_campaign%3Dread_more\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Introducing GraphicsJS, a Powerful Lightweight Graphics Library<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//hackernoon.com/optimising-the-front-end-for-the-browser-f2f51a29c572%3Fsource%3Dreading_list---------1-1---------\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Optimising the front end for the browser<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//medium.com/statuscode/dissecting-twitters-redux-store-d7280b62c6b1%23.mimhnwf2m\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Dissecting Twitter’s Redux Store<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//hackernoon.com/the-advantages-of-building-your-website-from-scratch-da5748a1baaf%23.t0ufgn593\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">The advantages of building your website from scratch<i class=\"icon-external\"></i></a></p></li></ul><h1>服务端应用程序架构</h1><ul><li><p><a href=\"https://link.zhihu.com/?target=https%3A//gowebexamples.github.io/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Go Web Examples<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//github.com/Sixt/java-micro\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Lightweight framework for building java microservices<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//hackernoon.com/practical-scaling-techniques-for-web-sites-554a38dbd492%23.toyeo4mqa\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Practical scaling techniques for web sites<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//asafdav2.github.io/2017/node-js-timers/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">How does Node.js manage timers internally<i class=\"icon-external\"></i></a></p></li></ul><h1>数据科学与机器学习</h1><ul><li><p><a href=\"https://link.zhihu.com/?target=https%3A//www.codelitt.com/blog/doom-ai/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Building a deep learning DOOM bot<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//hackernoon.com/3-ways-to-level-up-your-chat-app-with-ibm-watson-d60513d6d71d%23.50s2p8izj\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">3 Ways to Level Up Your Chat App with IBM Watson<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//medium.com/intuitionmachine/deepmind-fuses-game-theory-and-deep-learning-661ec205a396%23.bz9s1poxs\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Greed, Fear, Game Theory and Deep Learning<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//hackernoon.com/machine-learning-our-cities-617ce005ba27%23.upgc7yit5\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">AI can help you choose where to live<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//toolsformachinelearning.blogspot.hk/2017/02/introduction-machine-learning-is.html%3Futm_content%3Dbufferc0361%26utm_medium%3Dsocial%26utm_source%3Dtwitter.com%26utm_campaign%3Dbuffer\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Fundamental Tools for Machine Learning<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=http%3A//www.erogol.com/duplicate-question-detection-deep-learning/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Duplicate Question Detection with Deep Learning on Quora Dataset<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//medium.com/%40ageitgey/abusing-generative-adversarial-networks-to-make-8-bit-pixel-art-e45d9b96cee7%23.evqgz1mx2\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Abusing Generative Adversarial Networks to Make 8-bit Pixel Art<i class=\"icon-external\"></i></a></p></li></ul><h1>基础架构</h1><ul><li><p><a href=\"https://link.zhihu.com/?target=https%3A//eng.uber.com/distributed-tracing/\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">EVOLVING DISTRIBUTED TRACING AT UBER ENGINEERING<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//medium.com/%40cwodtke/five-models-for-making-sense-of-complex-systems-134be897b6b3%23.4c0rxg3ei\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Five Models for Making Sense of Complex Systems<i class=\"icon-external\"></i></a></p></li></ul><h1>信息安全</h1><ul><li><p><a href=\"https://link.zhihu.com/?target=https%3A//iot-for-all.com/cryptonets-neural-networks-for-encrypted-data-5510e0a8066b%23.ohzb04iyy\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">CryptoNets: Neural Networks for Encrypted Data<i class=\"icon-external\"></i></a></p></li></ul><h1>移动开发</h1><ul><li><p><a href=\"https://link.zhihu.com/?target=https%3A//medium.com/%40aditlal/must-have-tools-for-android-development-d76ae66f409f\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">must-have-tools-for-android-development<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//medium.com/sketch-app-sources/auto-layout-v0-1-3-max-width-facelift-and-new-documentation-4f47e39a27ca%23.a3ew2wl6v\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Auto-Layout v0.1.3: Max Width, Facelift, and New Documentation<i class=\"icon-external\"></i></a></p></li><li><p><a href=\"https://link.zhihu.com/?target=https%3A//blog.mindorks.com/android-app-performance-metrics-a1176334186e%23.ddlakofwg\" class=\" wrap external\" target=\"_blank\" rel=\"nofollow noreferrer\">Android App Performance Metrics<i class=\"icon-external\"></i></a></p></li></ul><blockquote><p>延伸阅读:</p><ul><li><p><a href=\"https://zhuanlan.zhihu.com/wxyyxc1992\" class=\"internal\">知乎专栏:某熊的全栈之路</a></p></li><li><p><a href=\"https://zhuanlan.zhihu.com/c_67532981\" class=\"internal\">知乎专栏:前端当自强</a></p></li><li><p><a href=\"https://zhuanlan.zhihu.com/lotuc\" class=\"internal\">知乎专栏:lotuc的编程之路</a></p></li><li><p><a href=\"https://zhuanlan.zhihu.com/p/24476917?refer=wxyyxc1992\" class=\"internal\">2016-我的技术之路:编程知识体系结构</a></p></li><li><p><a href=\"https://zhuanlan.zhihu.com/p/24575395?refer=wxyyxc1992\" class=\"internal\">2016-我的前端之路:工具化与工程化</a></p></li></ul></blockquote>","state":"published","sourceUrl":"","pageCommentsCount":0,"canComment":false,"snapshotUrl":"","slug":25224787,"publishedTime":"2017-02-14T09:54:43+08:00","url":"/p/25224787","title":"某熊周刊系列:一周推荐外文技术资料(2.3)","summary":"<a href=\"https://zhuanlan.zhihu.com/p/25224787\" data-editable=\"true\" data-title=\"某熊周刊系列:一周推荐外文技术资料(2.3)\">某熊周刊系列:一周推荐外文技术资料(2.3)</a>归纳于<a href=\"https://github.com/wxyyxc1992/Coder-Knowledge-Graph/tree/master/Weekly\" data-editable=\"true\" data-title=\"某熊周刊:一周推荐外文技术资料\">某熊周刊:一周推荐外文技术资料</a>是笔者每周浏览外文技术网站中时发现的不错的文章/项目/书籍/教程的集锦,可以关注笔者的专栏<a href=\"https://zhuanlan.zhihu.com/wxyyxc1992\" data-editable=\"true\" data-title=\"某熊的全栈之路\">某熊的全栈之路</a>及时获取更新。资讯来源包括但不限于<a href=\"https://medium.com/\" data-editable=\"true\" data-title=\"Medium\">Medium</a>、<a href=\"https://twitter.com/\" data-editable=\"true\" data-title=\"Twitter\">Twitter</a>、<a href=\"https://plus.google.com/\" data-editable=\"true\" data-title=\"Google Plus\">Google Plus</a>、<a href=\"https://www.reddit.com/\" data-editable=\"true\" data-title=\"Reddit\">…</a>","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"commentPermission":"anyone","commentsCount":0,"likesCount":0}},"commentsCount":2,"likesCount":102,"FULLINFO":true}},"User":{"wxyyxc1992":{"isFollowed":false,"name":"王下邀月熊","headline":"https://segmentfault.com/u/wxyyxc1992\n\nhttps://github.com/wxyyxc1992","avatarUrl":"https://pic2.zhimg.com/v2-a627d79d2ed03fe6f83a11743a18d909_s.jpg","isFollowing":false,"type":"people","slug":"wxyyxc1992","bio":"王下邀月熊","hash":"ed4cd6b92a003a0ce8e801ae74196e19","uid":57299368411136,"isOrg":false,"description":"https://segmentfault.com/u/wxyyxc1992\n\nhttps://github.com/wxyyxc1992","profileUrl":"https://www.zhihu.com/people/wxyyxc1992","avatar":{"id":"v2-a627d79d2ed03fe6f83a11743a18d909","template":"https://pic2.zhimg.com/{id}_{size}.jpg"},"isOrgWhiteList":false,"badge":{"identity":null,"bestAnswerer":null}}},"Comment":{},"favlists":{}},"me":{},"global":{},"columns":{"wxyyxc1992":{"following":false,"canManage":false,"href":"/api/columns/wxyyxc1992","name":"某熊的全栈之路","creator":{"slug":"wxyyxc1992"},"url":"/wxyyxc1992","slug":"wxyyxc1992","avatar":{"id":"v2-3c441415846977fe344039486f932e19","template":"https://pic2.zhimg.com/{id}_{size}.jpg"}}},"columnPosts":{},"postComments":{},"postReviewComments":{"comments":[],"newComments":[],"hasMore":true},"favlistsByUser":{},"favlistRelations":{},"promotions":{}}