唯快不破:Web 应用的 13 个优化步骤

举报
四大饿人 发表于 2017/12/28 21:51:53 2017/12/28
【摘要】 时过境迁,Web 应用比以往任何时候都更具交互性。搞定性能可以帮助你极大地改善终端用户的体验。阅读以下的技巧并学以致用,看看哪些可以用来改善延迟,渲染时间以及整体性能吧!更快的 Web 应用优化 Web 应用是一项费劲的工作。Web 应用不仅处于客户端和服务器端的两部分组件当中,通常来说也是由多种多样的技术栈构建而成:数据库,后端组件(一般也是搭建在不同技术架构之上的),以及前端(HTML + J

时过境迁,Web 应用比以往任何时候都更具交互性。搞定性能可以帮助你极大地改善终端用户的体验。阅读以下的技巧并学以致用,看看哪些可以用来改善延迟,渲染时间以及整体性能吧!

更快的 Web 应用

优化 Web 应用是一项费劲的工作。Web 应用不仅处于客户端和服务器端的两部分组件当中,通常来说也是由多种多样的技术栈构建而成:数据库,后端组件(一般也是搭建在不同技术架构之上的),以及前端(HTML + JavaScript + CSS + 转译器)。运行时也是变化多端的:iOS,Android,Chrome,Firefox,Edge。如果你曾经工作在一个不同的单一庞大的平台之上,通常情况下性能优化只针对于单一目标(甚至只是目标的单一版本而已),但是现在的话你就可能会意识到任务复杂度要远超于此。这就对了。但这儿也有一些通用的优化指南可以大大优化一个应用。我们将会在接下来的章节中探讨这些指南的内容。

一份 Bing 的研究表明,页面加载时间每增加 10ms,网站的年收入就会减少 25 万美元。 —— Rob Trace 和 David Walp,微软高级程序经理

过早优化?

优化最难的地方就是如何在开发生命周期中最适当的时候去做优化。Donald Knuth 有一句名言:「过早优化乃万恶之源」。这句话背后的原因非常简单:因为一不小心就会浪费时间去优化某个 1% 的地方,但是结果却并不会对性能造成什么重大影响。与此同时,一些优化还妨碍了可读性或者是可维护性,甚至还会引入新的 Bug。换句话说,优化不应当被认为是「意味着得到应用程序的最佳性能」,而是「探索优化应用的正确的方式,并得到最大的效益」。再换句话说,盲目的优化可能会导致效率的丢失,而收益却很小。在你应用以下技巧的时候请将此铭记在心。你最好的朋友就是分析工具:找到你可以进行通过优化获得最大程度改善的性能点,而不用损害应用开发的进程或者可维护性。

程序员们浪费了大量时间来思考,或者说是担忧,他们的程序中非关键部分的运行速度。并且他们对于性能的这些尝试,实际上却对代码的调试和维护有着非常消极的影响。我们应当忘记那些不重要的性能影响,在 97% 的时间里都可以这么说:过早优化乃万恶之源。当然我们也不应当在那关键的 3% 上放弃我们的机会。—— Donald Knuth

1. JavaScript 压缩和模块打包

JavaScript 应用是以源码形式进行分发的,而源码解析的效率是要比字节码低的。对于一小段脚本来说,区别可以忽略不计。但是对于更大型的应用,脚本的大小会对应用启动时间有着负面的影响。事实上,寄期望于使用 WebAssembly 而获得最大程度的改善,其中之一就是可以得到更快的启动时间。

另一方面,模块打包则用于将不同脚本打包在一起并放进同一文件。更少的 HTTP 请求和单个文件解析都可以减少加载时间。通常情况下,单独一种工具就可以处理打包和压缩。Webpack 就是其中之一。

示例代码:

function insert(i) {
    document.write("Sample " + i);}for(var i = 0; i < 30; ++i) {
    insert(i);}

结果如下:

!function(r){function t(o){if(e[o])return e[o].exports;var n=e[o]={exports:{},id:o,loaded:!1};return r[o].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var e={};return t.m=r,t.c=e,t.p="",t(0)}([function(r,t){function e(r){document.write("Sample "+r)}for(var o=0;30>o;++o)e(o)}]);//# sourceMappingURL=bundle.min.js.map

进一步打包

你也可以使用 Webpack 打包 CSS 文件以及合并图片。这些特性都可以有助于改善启动时间。研究一下 Webpack 文档来做些测试吧!

2. 按需加载资源

资源(特别是图片)的按需加载或者说惰性加载,可以有助于你的 Web 应用在整体上获得更好的性能。对于使用大量图片的页面来说惰性加载有着显著的三个好处:

  • 减少向服务器发出的并发请求数量(这就使得页面的其他部分获得更快的加载时间)

  • 减少浏览器的内存使用率(更少的图片,更少的内存)

  • 减少服务器端的负载

大体上的理念就是只在必要的时候才去加载图片或资源(如视频),比如在第一次被显示的时候,或者是在将要显示的时候对其进行加载。由于这种方式跟你建站的方式密切相关,惰性加载的解决方案通常需要借助其他库的插件或者扩展来实现。举个例子,react-lazy-load 就是一个用于处理 React 惰性加载图片的插件:

const MyComponent = () => (
  <div>
    Scroll to load images.
    <div className="filler" />
    <LazyLoad height={762} offsetVertical={300}>
      <img src='http://apod.nasa.gov/apod/image/1502/HDR_MVMQ20Feb2015ouellet1024.jpg' />
    </LazyLoad>
    (...)

一个非常好的实践范例就像 Goggle Images 的搜索工具一样。点击前面的链接并且滑动页面滚动条就可以看到效果了。

3. 在使用 DOM 操作库时用上 array-ids

如果你正在使用 ReactEmberAngular 或者其他 DOM 操作库,使用 array-ids(或者 Angular 1.x 中的 track-by 特性)非常有助于实现高性能,对于***   页尤其如此。我们已经在上一篇程序衡量标准的文章中看到这个特性的效果了: More Benchmarks: Virtual DOM vs Angular 1 & 2 vs Mithril.js vs cito.js vs The Rest (Updated and Improved!)

此特性背后的主要概念就是尽可能多地重用已有的节点。Array ids 使得 DOM 操作引擎可以「知道」在什么时候某个节点可以被映射到数组当中的某个元素。没有 array-ids 或者track-by 的话,大部分库都会进行重新排序而摧毁已有的节点并重新创建新的。这就非常损耗性能了。

4. 缓存

Caches 是用于存储那些被频繁存取的静态数据的组件,便于随后对于这个数据的请求可以更快地被响应,或者说请求方式更加高效。由于 Web 应用是由很多可拆卸的部件组合而成,缓存就可以存在于架构中的很多部分。举例来说,缓存可以被放在动态内容服务器和客户端之间,就可以避免公共请求以减少服务器的负载,与此同时改善响应时间。其他缓存可能被放置在代码里,以优化某些用于脚本存取的通用模式,还有些缓存可能被放置在数据库或者是长运行进程之前。

简而言之,在 Web 应用中使用缓存是一种改善响应时间和减少 CPU 使用的绝佳方式。难点就在于搞清楚哪里才是在架构中存放缓存的地方。再一次,答案就是性能分析:常见的瓶颈在哪里?数据或者结果可缓存吗?他们都太容易失效吗?这都是一些棘手的问题,需要从原理上来一点一点回答。

缓存的使用在 Web 环境中富有创造性。比如,basket.js 就是一个使用Local Storage 来缓存应用脚本的库。所以你的 Web 应用在第二次运行脚本的时候就可以几乎瞬间加载了。

如今一个广受欢迎的缓存服务就是亚马逊的 CloudFront。CloudFront 就跟通常的内容分发网络(CDN)用途一样,可以被设置作为动态内容的缓存。

我们的一个读者指出了一个非常重要的遗漏:图片编码优化。PNGs 和 JPGs 在 Web 发布时都会使用次优的设置进行编码。通过改变编码器和它的设置,对于需要大量图片的网站来说可以获得有效的改善。流行的解决方案包括 OptiPNG 和jpegtran

A guide to PNG optimization 详细描述了 OptiPNG 可以如何用于优化 PNGs。

The man page for jpegtran 对它的一些特性提供了很好的介绍。

如果你发现这些指南相对于你的要求来说都太复杂了的话,这儿有一些在线网站可以提供优化服务。也有一些像 RIOT 一样的图形化界面,非常有助于批量操作和结果检查。

5. 启用 HTTP/2

越来越多的浏览器都开始支持 HTTP/2。这可能听起来没有必要,但是 HTTP/2 为同一服务器的并发连接问题带来了很多好处。换句话说,如果有很多小型资源需要加载(如果你打包过的话就没有必要了),在延迟和性能方面 HTTP/2 秒杀 HTTP/1。试试 Akamai 的 HTTP/2 demo,可以在最新的浏览器中看到区别。


6. 应用性能分析

性能分析是优化任何应用程序时的重要一步。就像介绍中所提到的那样,盲目尝试优化应用经常会导致效率的浪费,微不足道的收益和更差的可维护性。执行性能分析是识别你的应用问题所在的一个重要步骤。

对于 Web 应用来说,延迟时间是最大的抱怨之一,所以你需要确保数据的加载和显示都尽可能得快。Chrome 提供了非常棒的性能分析工具。特别是 Chrome Dev Tools 中的时间线和网络视图都对于定位延迟问题有着很大的帮助

找到性能损耗的中心可以让你有效率地达到优化的目标。

对后端的性能分析会更加困难。通常情况下,确认一个耗费较多时间的请求可以让你明确应该优先分析哪一个服务。对于后端的分析工具来说,则取决于所构建的技术栈。

一个关于算法的注意事项

在大多数情况下,选择一个更优的算法,比围绕着小成本中心所实现的具体优化策略能够获得更大的收益。在某种程度上,CPU 和内存分析应该可以帮你找到大的性能瓶颈。当这些瓶颈跟编码问题并不相关时,则是时候考虑考虑不同的算法了。

7. 使用负载均衡方案


8. 为了更快的启动时间考虑一下同构


9. 使用索引加速数据库查询


10. 使用更快的转译方案


11. 避免或最小化 JavaScript 和 CSS 的使用而阻塞渲染


12. 用于未来的一个建议:使用 service workers + 流


13. 图片编码优化



作者:吕立青
链接:https://www.zhihu.com/question/21658448/answer/107679627
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

扩展阅读

你可以在下面的链接中阅读更多信息,以及找到有助于优化网站的工具:

悄悄话:Auth0 中常见的优化

我们是一个 Web 公司。就以这种身份来说,我们为我们的基础设施的某些部分部署了一些特定的优化。举例来说,在登录页面你可以发现,在我们域名的 /learn 路径下(比如,登录页面的单点登录),我们采用了一种特别的优化:为了方便我们使用 CMS 来创建每篇文章。因为文章都没有中心索引,但是为了能够被搜索引擎发现,使用了 webtask 的爬虫来预渲染每个页面并生成了一个静态版本然后上传到我们 CDN。这减少了我们在服务器端上的压力,因为无须为每个访客都生成动态的服务器端内容。与此同时还改善了延迟(并且隔离了我们发现与 CMS 相关的安全问题)。

对于文档部分,我们正在使用同构 JavaScript,这让我们获得了非常棒的启动时间,并且使我们的后端和前端团队能够轻松集成。

结论

由于应用程序变得越来越大和越来越复杂,性能优化对于 Web 开发来说正在变得越来越重要。在做出任何值得的时间和潜在的未来成本的优化尝试时,有针对性的改进都是必不可少的。Web 应用程序早已突破了大多数静态内容的边界,学习常见模式进行优化则是令人愉悦的应用和完全不可用的应用之间最大的区别(这是让你的访客留下来的长远之计!)。没有什么规则是绝对的,但是:性能分析和研究特定软件技术栈的错综复杂之处,是找出如何优化它的唯一方式。你曾经发现过对你的应用产生巨大影响的其他建议吗?请留言让我们知道。Hack on!

转自:https://zhuanlan.zhihu.com/p/21417465?refer=no-backend


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。