JavaScript 踩坑心得— 为了高速(下)

2058 查看

一.前言

本文的上一篇 JavaScript 踩坑心得— 为了高速(上) 主要和大家分享的是 JavaScript 使用过程中的基本原则以及编写过程中的心得分享,本文主要和大家聊聊在各个使用场景下的 JavaScript 使用,以及在性能优化方面的优化经验等

二.各种场景下的 JavaScript
1.用于 UI 应用的 JavaScript

与大多数服务器端语言一样,用于客户端应用的 JavaScript 框架从来就不缺少。然而,和用在后端应用与服务中一样,笔者偏好使用较小的模块,将这些小模块组合为框架,从而实现可扩展性,可维护性以及较高的重用度。

并且,由于小模块的特性,后期拍错或者进行性能优化的时候也会非常方便,现在国内外针对前端 JavaScript 的性能行为分析工具也比较成熟了,例如 Browser InsightAPPdynamicRuxit,都是不错的选择

2.构建 Web 应用的 JavaScript

对于 Web 应用,笔者发现将 React 用于实现 UI,Redux 用于状态管理,Joi 用于数据验证,是建立可扩展客户端应用的最有效技术基础。这样建立起来的应用,往往易于操纵,测试以及调试

笔者非常喜爱该组合的一点,是他们使得遵循「 JavaScript 的两大支柱」变得更加简单。从 0.14 版本开始,React 增加了对纯函数的支持,Redux 也使用了纯函数与可组合的 reducers。看来,使用典型面向对象模式的脆弱应用已经一去不复返了。

该组合的另一大优点是可用的工具很多。React 与 Redux 都有 Chrome 开发者工具插件,使得调试与操作应用变得极为简单。而诸多支持模块,比如 React Hot Loader 与 Webpack 也使得反馈更加及时。笔者建议你仔细了解一下这两个模块。

3.构建移动应用

显而易见,如果是构建移动平台的应用,笔者肯定会推荐 React Native。目前,React Native 仅支持 iOS 与 Android 平台,因此,除非你真的需要 Windows 支持,笔者会统一推荐 React Native。

还有一个值得一说的就是,现在无论安卓还是 ios 的 APP 性能优化 也非常重要,但是很多公司只注重后端架构、代码质量等问题,却忽视了前端编写时可能存在的影响因素。并且,尽管 JS 测试对于确保应用如期运行非常重要,但人们却也常常在测试中花去太多时间,并不是说不应该编写测试代码,只是,要小心过度测试与过度模拟,所以上文提到过的 Browser InsightnewrelicAPPdynamic 等真心都是优化前端不错的选择。

三.测试用 JavaScript
1.仅测试公共 APIs

通过不专注于内部程序,只要不破坏模块内的公共 API,我们就能随心所欲地改变他们。这意味着,测试的变化无需那么频繁,而且你可以确保自己接收的数据正是应该收到的数据。

2.建立测试框架

说到实际应用的测试模块,笔者最近迷上了 tapenock。有了这两个模块,笔者就能覆盖 99% 的测试(有时候,笔者必须自行监控一些数据,并使用 Sinon)。Browser Insight 这款产品无论是线上还是线下,针对前端页面的 JS 错误都能准确的定位到,精确到代码行,非常方便。

3.测试用户体验(UX)

如果你在打造 Web 应用,可能会想在尽可能多的设备与浏览环境下测试用户体验。为了获得更为直接的反馈,笔者采用了 Browser Insight,这个工具的好处就是基于真实的用户体验,多维度的定位分析网站的性能问题,例如脚本错误、ajax调用、响应时间分布等板块,而且,这个工具支持 PC 端、移动浏览器、移动微信页面、APP 等多个使用场景,基本上能满足绝大多数场景的使用需求。

四.JavaScript 性能优化误区
1.JavaScript 模块化使用误区

加快 JavaScript 加载和执行的速度,一直是前端优化的一个热点。因此我们先来说下 JavaScript 模块化技术的相关知识,希望通过实践来体现模块化技术在使用时的注意事项,避免滥用。

为什么会有模块化技术?

长久以来,编写 JavaScript 一直以文件为单位,一般一个类型的 JavaScript 功能代码会被放在同一个文件里。在一个页面里,引用的文件一般是写死的,也就是不管页面用不用,只要你引入了这个文件,这个文件就会被加载。

举个例子,我们开发了一个内容复杂、功能强大的页面,JavaScript 文件大到 500K,当页面费劲的把这 500K 加载下来,然而用户真正只使用了这 500K 里极少的一部分功能,但我们又不得不把这 500K 加载下来,因为不同的用户使用的功能点可能不一样,我们必须满足所有需求。

而模块化技术提出 按需加载,也就是当用户触发该功能的时候,那个功能才真正的被加载。好比 500K 被拆成了 50 个模块,每个模块 10K,当用户触发一个功能时,加载 10K,再触发再加载,以这样懒加载的方式来加载模块,可以很大的提高响应速度。这样,管理模块懒加载的技术也随之诞生。

模块化技术并非到处靠谱!!

之前笔者在网上搜索到了一个模块化技术:SeaJS。它是一个遵循 CommonJS 规范的 JavaScript 模块加载框架,可以实现 JavaScript 的模块化开发及加载机制。与 JQuery 等 JavaScript 框架不同,SeaJS 不会扩展封装语言特性,而只是实现 JavaScript 的模块化及按模块加载。

SeaJS 的主要目的是令 JavaScript 开发模块化并可以轻松愉悦进行加载,将前端工程师从繁重的 JavaScript 文件及对象依赖处理中解放出来,可以专注于代码本身的逻辑。说白了就是有 Lazy Load 的特性,用到某模块时,SeaJS 才会去加载模块的 JS 文件。我们可以按功能划分多个模块,触发模块功能时,SeaJS 先加载功能模块的文件,然后执行相应的功能。

这个 SeaJS 拥有的特性,初看非常吸引人,它可以说是新定义了一种开发和管理 JavaScript 文件的模式。遵循这个模式,你会享受起 JavaScript 的开发。

实践证明,它也的确可以使 JavaScript 模块化,根据功能划分模块,每个模块对应一个 JavaScript 文件,当执行到模块的功能,或者你需要加载模块时,模块才会被下载,同时不会造成重复下载。这一切看起来如此的合理,如此的顺畅。。。。。

但是在使用后发现了一些 问题:由于当时开发的网站功能相对简单,JavaScript 文件并不是非常大,过多的模块,反而会导致总加载的时间变多了。

由于是 Lazy Load 特性,不适合的模块划分导致网站出现反应慢的现象,原因是得先加载模块的文件,才能执行模块的功能。当网络情况不好时,该现象表现的更为严重!!!

可以说,问题出在了对新技术的不了解上,从而出现了问题,预期是 SeaJS 可以处理 JavaScript 优化的问题,因为它具有避免加载不必要模块的功能,结果反而南辕北辙。

根据 大功能来划分模块 也是一个不错的尝试。

笔者尝试将所有模块划分为 基础模块功能模块 ,基础模块包括页面头部,尾部相关的公共部分,功能模块则根据前后台划分,前台部分,又根据具体的页面来划分,能合并到一起的,就合并成一个模块,增加粒度。结果 JavaScript 文件减少,也达到了预期的效果。

从实际体验来看,对于流量不大的网站来说,没有必要使用懒加载 JavaScript 的相关技术,因为本身 JavaScript 文件就不是非常大。因此在网页加载时,就可以先加载所需要的模块。避免不必要的延迟。

2.JavaScript 的位置问题

这一部分,我们来说说 JavaScript 的位置问题对网页网站性能的影响。

为什么要考虑位置问题?

其实不管是 CSS 还是 JavaScript,都需要考虑位置的问题,因为 HTML 的渲染和加载顺序是从上往下,也就是如果前面插入了 JavaScript 的引用,那么必须等到这个 JavaScript 下载完毕才会渲染后续的部分。

因此 JavaScript 的插入位置就成为一个值得考虑的问题,因为不适合的位置可能引起渲染的延迟等,造成不好的用户体验。

传统方案带来的问题和思考

CSS 放在头部,JavaScript 放在尾部,这是传统的经验,它的好处是可以让页面优先渲染, 从而页面可以快速显示。

但有事实往往没有我们预想的那么美好。

有的时候会出现这么一种情况:当页面已经渲染完毕时,我们立刻去使用网站的功能,但很多时候 功能按钮会没有反应。原因也很简单,就是 JavaScript 放在页面的尾部,还没来得及加载。。。。

这就纠结了。。。。

两种 JavaScript 放置方式,一种放在头部,一种放在尾部(暂时忽略部分放在头部,部分放在尾部的方式),一个牺牲了渲染速度,一个牺牲了用户体验。所以很多时候 js 的问题我们需要做权衡。

对一般的小型网站来说,用户体验问题要远远大于页面渲染的问题,就比如上文提到的那种功能按钮不可用的情况。而且,如果 JavaScript 不是很大的话,放在头部就很好,既不会有太久的页面空白,也能让其优先加载,二者得到了很好的平衡。

因此,很多经验上的东西并不是绝对的,一定要根据实际的情况,包括功能特点、服务器网络情况等来综合考虑。

为此,笔者写下一个自认为较为合理的位置选择方案,仅供参考。

图 1. 判断 JavaScript 放置位置决策表

从上面的分类介绍,我们也可以看出,将功能代码按类型归类到不同的 JavaScript 文件是多么的重要,比如应该放头部和应该放尾部的代码,最好不要合并在一起,不要等到出问题要优化的时候再去整理和重构,这样会增加很多不必要的工作量。

这不仅仅是为自己工作负责,也是为后面要读你代码的新人负责。养成好的设计编码习惯,也是技术积累的一部分。最后再根据 JavaScript 文件的功能类型,来决定是放在页面的头部还是尾部。

五.怎样确定是不是 JavaScript 的问题?

这个问题笔者在之前看过的的前端高性能优化(一)(二)中 get 到了新的技能点,在这里分享给大家。

随着信息爆炸时代的到来,网站本身性能也深刻影响着公司的形象、利益等问题。但是大多数前端测试工具都太碎片化,没有办法针对多个使用场景,而且很多都是像 yslow 这样简单打个分,也不是真实的用户体验。前一段时间在网上找到了一款前端性能优化分析工具——Browser Insight,里面的功能相当全面,而且可以针对多个使用场景,包括:PC端,移动微信,移动浏览器,移动webview,还是 真实的用户体验,也就是说,用户访问你的网页是什么样的,从这个工具中体现出的就是什么样子的。

基于 JavaScript 这个维度 Bi 做的也是相当丰富了。

首先是 脚本错误 板块。Bi 里面可以从不同的时间维度查看被监控页面出现过的脚本错误,具体信息包括:发生时间、设备类型、报错的浏览器及其版本号、错误堆栈信息都可以看到,不论是 线上还是线下测试或者页面维护 都是够用了。

不但能看到时间、系统、浏览器等,还可以具体定位到出错的代码行,这个确实很方便。

图 2.Bi 脚本错误

其次是页面响应时间板块,这个算是意外的收获了。通过响应时间板块里面的慢加载追踪,可以看到本次慢加载的页面资源加载情况,然后我们就知道该优化哪个页面的哪些 js 、css、img等。

图 3.Bi 资源列表-时序图

六.结语

JavaScript 具备许多独特的优势,笔者甚至敢说,JavaScript 很可能是目前最重要的语言,因为它能在几乎所有平台上运行,而且可以通过高度可重用、可组合的方式实现。

然而,不熟悉 JavaScript 性能与问题的开发者可能很快就会发现,对其代码库进行更改变得越发困难,而且这些改变可能会导致出其不意的逆反效果。

笔者建议,不要像用其他语言写程序那样编写 JavaScript 程序。尽可能利用 JavaScript 独有的性能,创建小而简单的模块。这有助于你保持冷静,并爱上 JavaScript 的强大功能。

Happy JavaScripting!

注:本文翻译自 Kurtis Kemple 的一篇文章,由小编加了一些自己的意见和看法。

原文地址:https://labs.mlssoccer.com/javascript-at-scale-achieving-high-velocity-160c7d78af03#.egfwqqz0a

Browser Insight 是一个基于真实用户的 Web 前端性能监控平台,能够帮大家定位网站性能瓶颈,网站加速效果可视化;支持浏览器、微信、App 浏览 HTML 和 HTML5 页面。想阅读更多技术文章,请访问OneAPM 官方技术博客

本文转自 OneAPM 官方博客