Reading view

There are new articles available, click to refresh the page.

使用 AMP 构建超快的移动页面

AMP (Accelerated Mobile Pages) 是 Google 的一个开源的项目,通过使用 AMP HTML,可以极其显著的提升网页加载速度。最近我在移动设备上使用 Google 的时候,发现很多网站都开始使用了 AMP。在从 Google 上进入一个 AMP 页面时速度更是异常的快,快到超乎想象——延迟几乎为零。本站也配置了 AMP,本文对 AMP 的一些特性进行一些分析,并介绍如何在 WordPress 上支持 AMP,并开发插件对其自定义。

AMP HTML

AMP HTML 也是一种 HTML,一个符合标准的 AMP 页面只能使用有限的标签,而且很多标签都并非 HTML 标准,如 <amp-img><amp-video> 等。 一个 Hello, AMPs 的例子:

<!doctype html><html amp lang="en">  <head>    <meta charset="utf-8">    <script async src="https://cdn.ampproject.org/v0.js"></script>    <title>Hello, AMPs</title>    <link rel="canonical" href="http://example.ampproject.org/article-metadata.html" />    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">    <script type="application/ld+json">      {        "@context": "http://schema.org",        "@type": "NewsArticle",        "headline": "Open-source framework for publishing content",        "datePublished": "2015-10-07T12:02:41Z",        "image": [          "logo.jpg"        ]      }    </script>    <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>  </head>  <body>    <h1>Welcome to the mobile web</h1>  </body></html>

此代码可以在主流浏览器中直接运行,和普通的 HTML 相比,它有以下区别:

  • AMP HTML 在 <html> 中增加了 amp 属性,来标记这是个 AMP 页面
  • <head> 中必须引用一个 JS:<script async src="https://cdn.ampproject.org/v0.js"></script>
  • 需要有amp-boilerplate 属性的 style,其中一个在 <noscript> 中。
  • <head> 中需要有一个 <script type="application/ld+json"> 去标记文档的 metadata。

在 AMP HTML 中插入一个图片的例子:

<amp-img src="450px.jpg" srcset="450px.jpg 450w, 1200px.jpg 1200w"></amp-img>

会发现其实这和普通的 img 标签的语法没有什么区别,只是换了一个名字,实际上通过这样引入图片,AMP Runtime(就是在头部所引用的 v0.js) 就会自动地解析这个 AMP Components,并会根据设备的分辨率去选择一张图片加载(原理只不过是通过 js 再创建一个 img 标签去让浏览器识别),甚至都不需要浏览器去支持 srcset 属性;而且还能够自动的延迟加载图片。插入视频和音频同样也需要使用 AMP 特定的方法去做。如果浏览器禁用了 JS,那么网页上的所有图片和视频等使用非标准 HTML 标签的内容都无法显示。

在 AMP 中引入更多的交互

AMP 中禁止使用任何自己的 JS,这样做只是为了保证 JS 的加载速度——因为移动设备的性能不高,尽量减少 JS 或避免使用低质量 JS 的使用能够提高用户体验。所以,如果想使用更多的交互,那么必须通过 AMP HTML Extensions 的功能来实现,所以只能使用有限的交互功能。或者可以通过纯 CSS 来实现各种复杂的功能。

验证 AMP

这个网站上可以在线对 AMP 页面进行验证

为何那么快?

你其实会发现,网页中可能会阻碍加载的内容只有一个外部的 JS(也就是 AMP Runtime),而且这个 JS 还被标记上了异步加载。所有的 CSS 都是 Inline 的。

在国内的应用

AMP 所必须要引用的 AMP Runtime 这个核心 JS 必须要使用 Google 提供的那个特定的域名,现在 Google 已经针对中国解析到了中国服务器,中国访问还挺快! 现在,我发往微信公众号上的文章就使用了 AMP 技术,直接推送 AMP 版本的网页,放弃使用微信素材库,实际测试加载速度比微信素材库的文章还要快。并且由于 AMP 页面适合缓存,我也针对中国进行了缓存的优化。

在自己的网站上实现 AMP

为了支持 AMP,需要让每个文章拥有两个页面:一个正常版本的页面,一个符合 AMP 标准的 AMP 页面。当然,也可以通过直接修改页面本身让其符合 AMP 规范,只不过这种方法做的改动会比较大。最简单实现 AMP 的方式就是通过插件的方式去实现,尤其是如果你正在使用 WordPress 的话:

在 WordPress 上实现

Automattic(其实就是 WordPress 官方)制作了一个 AMP 插件,安装并激活这个插件之后,无需任何配置——这是我喜欢这个插件的原因之一,就能够激活 AMP 功能了。这个插件会自动生成每个文章的对应页面,并会相应的在原始页面添加 AMP 的 metadata。安装后表面上没有任何变化,但是当在一个文章页面后添加上 /amp/ 或者 ?amp=1 后,就能看到文章对应的 AMP 页面了。 这个插件之所以免去任何配置,是因为它实际上所提供的功能只是一个框架。用户(实际上是开发者)可以自己开发一个插件对其进行自定义。官方已经给出了具体的介绍,我列举一些我所自定义的东西:

使用无衬线字体,不引用 Google 字体库

这个插件默认使用了一个衬线字体,然而对于中文的使用引用外部字体毫无作用,甚至还会导致中英文字体错乱的问题。 下载这个 CSS,找到:

font-family: 'Merriweather', 'Times New Roman', Times, Serif;

替换为:

font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen-Sans", "Ubuntu", "Cantarell", "Helvetica Neue", sans-serif;

把修改后的文件保存在你制作的插件的文件夹中的 templates目录(如果没有,就创建一个)下的 my-amp-style.php。 在插件中添加下面代码:

add_filter( 'amp_post_template_file', 'tlo_amp_set_custom_template', 10, 3 );function tlo_amp_set_custom_template( $file, $type, $post ) {    if ( 'style' === $type ) {        // Use stystem font        $file = dirname( __FILE__ ) . '/templates/amp-style.php';    }    return $file;}add_action( 'amp_post_template_head', 'tlo_remove_fonts', 2 );function tlo_remove_fonts(){    remove_action( 'amp_post_template_head', 'amp_post_template_add_fonts' ); // Remove Google fonts.}

这将使用系统默认的无衬线字体,在苹果设备上中文字体就是苹方,同时还会移除页面上的 Google 字体。

添加自定义的统计内容

AMP 本来就支持 Google Analytics,但是如果需要添加自己的统计,虽不能通过 JS 的方法,但是最简单的方式是通过添加一个空的 Gif:

add_action( 'amp_post_template_footer', 'tlo_amp_add_pixel' );function tlo_amp_add_pixel( $amp_template ) {    //$post_id = $amp_template->get( 'post_id' );    $piwik_id = $GLOBALS\['wp-piwik'\]->getOption( 'site_id' );    ?>    <amp-pixel src="https://piwik.example.com/piwik.php?idsite=<?php echo $piwik_id; ?>&rec=1&action_name=TITLE&urlref=DOCUMENT_REFERRER&url=CANONICAL_URL&rand=RANDOM"></amp-pixel>    <?php}

这是一个使用 Piwik 工具进行统计的例子,与 WP-Piwik 插件配合,可以自动获取站点 ID(需要手动替换域名)。

实际测试(视频)

在激活了 AMP 一段时间后,使用移动设备在 Google 上搜索网站的内容,就可以从 AMP 中显著获益了,视频右侧包含了资源列表,使用的是 iOS 10 自带的 Safari 浏览器(不要眨眼!):

可以看到,还在停留在搜索结果的时候,AMP Runtime 等 AMP 内容就已经开始下载了,同时开始下载的还有我网站上的 Logo 以及网页中的第一张图片。 在点击第一条搜索结果时,页面没有任何重新加载的迹象,直接通过类似 ajax 的方式呈现出来,此时整个页面已经预加载完毕,所以加载时间几乎为零(由于使用的是 iOS 模拟器,配置较低,实际真机测试白屏时间更短,可以说是毫无感知)。页面顶部可以看到网址域名。在滚动页面时,可以看到之后的几张图片是延迟加载的,延迟加载的时间恰到好处,几乎很难感受到是在延迟加载。 值得注意的是,这个页面的所有内容几乎都是由 Google 的服务器提供的,包括 HTML 和图像在内(经测试,视频资源依然回源),只有我的统计代码没有被 Google 代理。如果此时分享 URL,那么分享出去的域名仍是 Google 的,在打开时(或者刷新一次页面之后)左上角的关闭将不再有,其余都完全一样。如果这个网址在非移动设备上打开,那么会跳转到原始文章(不是 AMP 的)页面。 点击左上角的关闭按钮之后,就又返回到搜索结果页面了,整个过程十分顺滑。如果搜索结果有多个 AMP 页面,那么进入这些其他的页面也同样秒开。 可以想到,之所以 AMP 做这么多限制,很大一部分原因是为了防止在 google.com 上呈现 AMP 页面时网站所有者 “作恶”,即使浏览器不是 Google 可控的(如 Safari)。

总结

Google 的 AMP 功能非常类似于 Facebook 的 Instant Article,只不过后者甚至都不需要自己去托管服务器。AMP 中大量的 CSS 仍能自己控制,这相比国内绝大多数搜索引擎的转码页面的效果还是要好不少。网页中仍能自己投放广告内容,这也是 AMP 能被接受的那么快的原因。它在各种不开放的限制中也保留了众多开放的东西。 启用 AMP 不仅能在 Google 搜索结果页面中载入获益,AMP 页面本身其实还可以作为专门给移动设备适配的页面——如果网站本身没有移动版页面,那么移动版页面可以直接拿 AMP 做,然后自动跳转。 当然,就像之前说的,网页可以通过直接修改页面本身让其符合 AMP 规范,或者在制作之初就按照 AMP 的规范作,AMP 官网就是一个典型的例子。然而这种做法未免过于激进,兼容性也不是很好(比如其对于 JS 的强烈依赖),使用前应该做好充分考虑。

关于 MIP 的补充

百度从 AMP 借鉴了很多技术并做了 MIP,其与 AMP 十分类似。 经过简单的实验,我发现 AMP 可以很轻松的变为 MIP,只需要将 AMP 页面做以下内容的替换或删除即可:

  • <html amp 替换为 <html mip
  • <script src="https://cdn.ampproject.org/v0.js" async></script> 替换为 <link rel="stylesheet" type="text/css" href="https://mipcache.bdstatic.com/static/v1/mip.css">
  • amp-pixel 替换为 mip-pix
  • amp- 替换为 mip-
  • </body> 替换为 <script src="https://mipcache.bdstatic.com/static/v1/mip.js"></script></body>
  • 删除 <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>

修改完的 AMP 页面可以直接通过 MIP 测试,这相当于不需要二次开发就能够同时兼容 AMP 和 MIP,多方便!启用了 MIP 的网站在百度搜索结果上也会预加载,于是就能实现秒开的效果。

MIP 的一些坑

CSS 的不同

MIP 自带的 CSS 竟然声明了这个样式:

* {    margin: 0;    padding: 0}

由于它的存在,会导致很多地方的排版出现问题,并会覆盖掉浏览器的默认样式(例如 h1、h2 这些标签),所以你可能还要为百度单独添加一些样式。

video 标签的 Bug

经测试,<mip-video> 标签不能够支持 <source> 标签,使用了这种标签的视频无法加载。

几个 WordPress 的加速建议

WordPress 是目前最流行的内容管理系统,本网站正是使用着它。但对于一个全新安装的 WordPress 来说,它的性能并不是很高,当网站的访问量突然增加时,优化性能就显得十分重要了。通过实施以下几个方案,可以大大提升 WordPress 访问速度:

配置缓存

WordPress 是一个动态的系统,如果不配置缓存,每次请求都需要服务器去读取数据库,生成页面内容,对于不同性能的主机,这可能就需要 20ms~1000ms 甚至更慢。如果能够正确配置缓存,就可以明显的加速,并且减少主机的运算资源。

配置页面缓存

使用插件来配置缓存是最简单的方法。在此推荐 WP Super Cache,这是 WordPress.com 出品的缓存插件,就页面缓存来说,功能非常全面,它支持多种缓存模式,包括 mod_rewrite,如果你使用 Nginx,那么可以使用我这个配置文件,这样可以直接跳过 PHP 而直接服务静态文件,页面相应速度有显著提升。 同时,为浏览器返回正确的 Cache-Control 也是十分有必要的,尤其是 CSS 和 JS 文件。

配置对象缓存

对象缓存比页面缓存更灵活,使用范围更广,但速度肯定不如页面缓存。在此推荐 APCu 缓存系统。在 Ubuntu/Debian 安装方法如下:

$ apt install php-apcu

然后重启 Web server,安装 APCu Object Cache Backend 即可。 Redis、Memcached 等都算此类型的缓存,不过 APCu 配置最为简单,速度也很快。

建立分布式缓存系统

比如我的网站使用北美东岸(主要)和亚洲的 VPS,主服务器配置了 Nginx,PHP 和 MySQL;亚洲的服务器只配置了 Nginx。在这些服务器上都配置好缓存,并用 lsyncd 同步缓存内容。每次访问时 Nginx 检查缓存,仅当没有缓存时代理,这样可以大大减少首页面的延迟。

使用 CDN

使用全站 CDN

使用全站 CDN,可以免去在自己的服务器上配置缓存的问题,还可以为服务器增加 HTTPS、HTTP/2 等功能,同时还能过滤非法流量,防御 DDOS(前提是你的 IP 没有被暴露,或者你设置好了白名单)。 除此之外,使用 CDN 后还能更加明显的提高网站加载速度,让访客从中受益。

使用 CloudFlare

CloudFlare 是可以免费使用的,使用 CloudFlare 前需要更改 DNS 服务商,然后无需额外配置就能使用了。但是它只会缓存 CSS、JS 和多媒体文件,不会缓存 HTML 页面,也就是说用户每次访问时还是会返回到原始服务器,页面本身的速度不会有明显提高,在原始服务器上配置缓存也是必要的。

使用 KeyCDN 并配合插件

KeyCDN 是一个按流量使用付费的 CDN 提供商,使用我制作的插件 Full Site Cache for KeyCDN 可以简单的对其配置,这个插件会自动刷新缓存。 KeyCDN 相比 CloudFlare,可以缓存 HTML 页面,大大减少源服务器的压力,同时在刷新缓存时可以通过 Tag 方式刷新,避免不必要的刷新。 在网站访问量较大时,使用 KeyCDN 就能明显的提高速度,缓存命中率也会有很大的提高。

仅资源部份使用 CDN

你可以配置另一个域名,在那个域名上使用 CDN,然后通过插件重写页面地址实现部分 CDN。上文提到的 WP Super Cache 就能配置 CDN,或者使用 CDN Enabler 也能实现部分 CDN 功能。至于 CDN 的选择无所谓,只要支持 Origin Pull(也就是请求回源)就行。

服务器性能

提高服务器本身的性能是最简单的方法,使用更大的内存,更多核心的 CPU 能明显提速。除此之外,提高服务器上应用的性能也很重要:

脚本性能

PHP 脚本的处理速度是 WordPress 的一大瓶颈,使用最新版本的 PHP,可以获得更高的性能,例如 7.0 就比 5.6 快了 3 倍

其次,少用插件能减少 PHP 需要执行的脚本,因为在加载每一个页面时,WordPress 都会加载一遍所有的插件。少量的插件(10个以下)对 WordPress 速度的影响不大,当然也取决于插件本身。

数据库性能

数据库是 WordPress 性能的瓶颈之一,在数据库上优化能提高一定的速度。 一般情况下,如果正确的使用 WordPress,并不需要清理数据库。但可能会有某些插件可能在数据库中创建了太多没用的表,这时服务器的响应速度就会大大降低(约 1~3 倍),推荐使用 WP-Optimize 进行清理。 不是太多的文章数量,是不太会的影响加载速度(1 万篇文章以下速度其实都还能接受,不过你写那么多文章干嘛,质量比数量更重要嘛)。

图片优化

图片占据着网页中很大一部分的大小,同时也关系着用户体验。

图片压缩

对图片压缩不仅可以提高访问时图片加载速度,还能减少服务器带宽 推荐使用免费的 EWWW Image Optimizer,可以在服务器上对图片进行处理。如果你的服务器性能有限,可以使用 Optimus 在线处理,免费版功能十分有限。

响应式图片

对于不同的设备加载不同的图片,比如在手机上加载的图片,就可以比视网膜屏幕的电脑上要加载的图片小的多。使用本站曾经提到过的 srcset 技术可以最简单的实现这个功能,只要你的主题支持就可以了(官方的最新默认主题已经支持),如果主题本身不支持,也可以通过插件实现。

禁用不需要的服务

WordPress 中有一些自带的服务你可能并不需要,禁用它们可以减少页面所需要加载的资源以及服务器需要做的事情。

Emoji 支持

WordPress 支持 Emoji 表情符号,但会因此在页面中引入额外的 CSS,使用这个脚本可以禁用它(如果你不需要的话)。

Google Fonts

Google Font 在国内加载非常慢,而且加载完成之前页面会一直白屏。你可以专门安装 Disable Google Fonts 的插件,或者在下文要提到的 Autoptimize 插件中的设置里禁用它。

Pingback

进入 设置 => 讨论 中可以禁用 “尝试通知文章中链接的博客” 和 “允许其他博客发送链接通知(pingback和trackback)到新文章” 功能(如果你不需要的话)。 此功能并不影响页面加载时间。

减少请求数和页面大小

减少请求数在也一些情况下也能提高加载速度,减少页面大小能缩短下载页面所需要的时间。推荐使用 Autoptimize,它可以 minify CSS、JS 和 HTML,还能 combine CSS 和 JS 以减少请求数。

然而,如果你的博客启用了 HTTP/2 协议,那么减少请求数就没什么必要了,不过为了启用 HTTP/2,必须要使用 HTTPS,所以最终下来是快是慢也不好说。不过还是强烈推荐使用 HTTPS,为了安全,牺牲点速度算什么。

总结

做到上面几点,就能有效提速了,我的网站做到以上几点,在国内无缓存的 Wi-Fi 情况下本网站的时间线如下:

在 1 秒钟内完成包括图片在内的加载

谈谈视频在互联网上的流播

视频在互联网上分发,最普遍的方式,是通过万维网的方式分发——用户通过直接输入网页链接/搜索引擎搜索/其它网站的链接,或者通过任何阅读器(包括 Podcasts 客户端在内)播放。这样做完全不局限在任何平台,但是意味着你得为你的视频流量付费,不过咱们先不谈这方面的事(这只是最理想的方案)。 首先,先从视频的格式来说。想要让用户播放出视频,你需要使用用户能够解码的格式。你可能希望有一种自由的格式,在所有平台上都能够播放,可事实并不存在这样的一种格式。

用 MP4 封装的 H.264 + AAC 格式是目前兼容性最强的格式,能够在绝大多数的设备上播放,但是它不是一个开源的格式,在一些只用开源软件的客户端或者服务器上就不能使用这个格式。典型的例子就是维基百科背后的媒体资源站 Wikimedia Commons 就没有使用 MP4 格式,导致了在维基百科上的视频在一些浏览器上根本无法播放。不过对于大多数情况来说,使用 MP4 封装的 H.264 + AAC 格式就足够了。 其次,需要将视频展示出来,在 21 世纪来说,这已经不是什么难事了。如果你想在网页上展示,一个好消息是:所有的主流浏览器都遵循着一套统一的规范(HTML5),只要你也使用这套规范,那么就可以在主流的浏览器上嵌入视频了(虽然它们遵循同一套规范,但是支持的编码有所不同。就好比说现在用户的信箱都是统一的规格,你可以将你要寄的书放到这些信箱里。但是你的书是用中文写的,那么不懂中文的人就无法知道书中的内容。)。

同一个视频,在互联网上流播的版本是与在本地播放的质量是有所不同的。为了能够让视频在互联网上流畅播放,会降低视频的画质。很多视频网站也会准备一个视频的不同画质的多个版本,方便在不同网络环境下播放更好的画质的版本。

其实写本文的目的,还是想说说中国的视频网站与国外的差距。有些是不能避免的因素,但是有一些是这些网站本应该做,却没有做。 国外的 YouTube 视频画面分辨率能到 7680×4320@60FPS,就这也不敢称作为 “超清”,然而国内普遍将 1280×720@30FPS 甚至更低的分辨率,就叫 “超清”,这比前者低了将近 100 倍(从像素的信息量上来说)。而且,同等分辨率下的画质也是有很大区别的,使用越低的画质越能节约钱(中国大陆很多视频网站都是很有钱的,但为什么还要节约钱呢?这就不得而知了~)。

更高的视频分辨率,意味着能够在更高分辨率的显示器上获得更好的效果。4K、8K 的电视以及显示器已经有很多了,而且能够录制 4K、8K 视频的相机/手机也越来越多。这些高画质内容正在走向低端消费市场,所以视频网站还是有支持的必要的。

但之前说的这些,我并不指望国内的视频网站能做到,因为它们都还想多赚钱呢。但是为什么还要使用一个漏洞百出,并且官方已经停止维护,需要通过额外安装插件的方法来播放视频呢?我说的就是 Flash(当然有少数网站已经不用它了),仅仅播放一个视频,用 HTML5 完全就够了,根本不需要这么一个复杂的东西。 想要让它们不用 Flash,首先得让 Flash 彻底地从客户端消失(如果浏览器能够主动屏蔽 Flash 就更好了,例如 Safari),渐渐的,它们会发现有大量的用户流失(这件事正在发生),然后终于有一天就能使用上新的技术了。 在国外,就比中国领先的多。从很多年前开始,主流的视频网站默认都不再使用 Flash 了。

视频在互联网上的流播实际上还有很多很多可以讲的,本文仅仅是粗略的谈一下而已。

使用 srcset + sizes 属性与 w 标识符解决一切响应式图片问题

使用 srcset 属性可以解决一切响应式图片问题,但这里分为两种情况,确定和不确定宽度的照片。相比之下,不确定宽度的照片更复杂些。

确定宽度的照片

此处指的确定宽度照片,是指样式属性宽度设置为一个确定了多少 px 的照片。 随着越来越多的高设备像素比(指 device pixel ratio,下同)显示器出现,网站需要更高像素的照片来适配这些显示器。 比如有一张照片显示宽度为 200px,它在@1x(即设备像素比为 1 的显示器,下同) 的显示器上,是占了 200 个物理像素(即实际所占的像素,下同);它在 @2x 的显示器上,实际上是占了 400 个物理像素;在 @3x 的显示器上,实际上是占了 600 个物理像素;同样的,在 @4x 的显示器上就是占了 800 个物理像素。 如果这个照片只提供 200 像素的版本,那么在 @2x~@4x 的显示器上看起来就很模糊。如果只提供 800 像素的版本,那么在 @1x~@3x 的设备上会加载不必要的内容,也就意味着更长的不必要时间(尤其是在手机上)。 此时,就需要使用响应式图片这个方法,最简单高效的方式是使用 srcset 来解决。比如现在提供了 4 个宽度分别为 200、400、600、800 像素的照片,通过 srcset 属性,能够实现在 @1x~@4x 设备像素比的显示器上分别显示这 4 张照片,这样在每个显示器上都能显示最适合它的照片。这四张图片的文件名分别为 200px.png, 400px.png, 600px.png, 800px.png。 总结一下,就是确定宽度的照片所占物理的像素只与设备像素比有关,所以只需要 srcset 属性的 x 标识符。

<img src="200px.png" srcset="400px.png 2x, 600px.png 3x, 800px.png 4x">

这样加上 srcset 属性,浏览器就会根据自己的设备像素比来加载不同的照片,不支持 srcset 属性的浏览器就默认加载 200px.png 这张照片。这个方法是向下兼容的。

不确定宽度的照片

此处指的不确定宽度的照片,是指样式属性宽度设置为一个确定了多少百分比的照片。 如果是不确定宽度的照片的话(或者是手机上不确定图片宽度,电脑上确定宽度等),也可以通过 srcset 解决。由于像素是不能确定的,所以很难达到照片本身和照片所占物理像素是一样的,因为用户的窗口大小是不确定的。 如果只准备 5 张照片,宽度分别是 400~3200 像素,文件名是400px.png, 800px.png, 1600px, 2400px, 3200px。通常,如果没有对应图像所占的物理像素的照片的话,则会加载比所占的物理像素稍大一些的图片。例如照片占屏幕的 500 个物理像素,那么就应该选择加载 800px.png。这样加载稍大的照片仍然可以达到最佳的显示效果,同时最大限度的节省网络资源。 总结一下,就是不确定宽度的照片所占物理的像素与设备像素比和所占宽度有关,然而使用了 srcset 属性的 w 标识符后,你只需要指定宽度,浏览器会自动根据设备像素比来选择最优图片。 如果这个图片占整个窗口的 100% 大小(即 100vw 宽,下同),那么只需要这样设置 srcset 就能达到效果。 抛开设备像素比的概念吧,这个全新的 srcset 属性的 w 标识符能够让浏览器自己适应所要加载的照片。

<img src="800px.png" srcset="400px.png 400w, 800px.png 800w, 1600px.png 1600w, 2400px.png 2400w, 3200px.png 3200w" alt="" />

不过此时还有一个小问题,浏览器比较笨,它会认为图片宽度始终是整个窗口的 100% 宽,如果图片不是占整个窗口的 100%,两边留有边框怎么办呢?此时就需要使用 size 属性。 sizes 属性里制定图片的宽度,不能使用百分比单位,但可以使用 vw、px 等单位(100vw 就是整个窗口的宽度),也可以使用 calc 运算,同时支持媒体查询。要保证使用 sizes 里计算出来的宽度始终是图片所占屏幕宽度,剩下的事情都只需要浏览器完成了。 以下几种典型案例示范:

两边边框为百分比

如果图片两侧边框(指图片边缘到窗口边缘长度)始终是占屏幕的一定的百分比时,那么图片总会有一个相对于整个窗口的百分比。比如图片两侧边框始终是整个窗口的 10%,那么 size 属性就可以如下设置:

sizes="80vw"

如果图片已经在一个宽度是整个窗口的 70% 的元素下,而且元素内图片两侧还留有 10% 的边框,那么 size 属性就可以如下设置:

sizes="50vw"

两边边框为确定的像素

比如图片两侧边框始终是 10px,那么 size 属性就可以如下设置:

sizes="calc( 100vw - 10px )"

如果图片已经在一个宽度是整个窗口的 70% 的元素下,而且元素内图片两侧边框始终是 10p,那么 size 属性就可以如下设置:

sizes="calc( 70vw - 10px )"

图片有最大尺寸

比如图片两侧边框始终是 10px,但是图片最大只能是 1000px,那么 size 属性就可以如下设置:

sizes="(min-width: 1020px) 1000px, calc( 100vw - 10px )"

其中 (min-width: 1020px) 1000px 代表当屏幕像素宽度大于 1020px 时,图片宽度为 1000px。

实际效果

此处以我的网站为例,我的网站图片边框适中是 1.875rem,这个图片本身就是在一个元素下,这个元素默认宽度是整个窗口的 100%,但在屏幕宽度大于 40.063rem 的情况下,这个元素占整个窗口的 50%,并且这个元素有最大尺寸 500px,现在把 size 属性和用 w 标识符的 srcset 属性放在一起,就完美解决了。

<img src="800px.png" sizes="(min-width: 1000px) calc( 500px - 1.875rem ), (min-width: 40.063rem) calc( 50vw - 1.875rem ), calc( 100vw - 1.875rem )" srcset="400px.png 400w, 800px.png 800w, 1600px.png 1600w, 2400px.png 2400w, 3200px.png 3200w" alt="" />

然后你就可以在浏览器中测试了,达到了适配所有屏幕尺寸,所有设备像素比的效果。

关于兼容性

通常情况下,srcset 属性的 x 标识符支持的比 w 标识符更好,因为 w 标识符是一个新的属性。不过好消息是 Chrome 38+、Firefox 38+、Safari 9+、Opera 30+、Android 5.0+、iOS 9+(没错,IE/Edge、Windows Phone 截止到写文章之日都不支持) 都纷纷支持了 w 标识符,所以我认为现在很适合换用 srcset 属性的 w 标识符来做响应式图片了。而且这个方法是完美向下兼容的,即使不支持也没关系。 你也可以在 CanIUse.com 上查询关于 secset 属性的完整的兼容性列表

移动优先 – 速度

移动设备上的网速通常并不快,他们通常会有比宽带更慢的速度、更高的延迟,那么移动端的浏览器是如何加载页面的呢?这便是本文要介绍的内容。 其实无论是在移动端还是桌面端加载页面,都会需要以下几个步骤:

1. 解析域名

域名通常对应着一个 IP 地址,浏览器只有在知道了这个 IP 地址后才能与服务器通讯。为了解析域名,客户端会向 DNS 解析服务器发送一个关于这个域名的请求,就能查询到这个域名的 IP 地址。DNS 解析是存在 “TTL 生存时间” 的,也就是缓存时间,这个内容会被缓存在 DNS 解析服务器上、路由器和客户端上,按照缓存时间存储。DNS 解析服务器通常会由运营商提供,如果存在缓存,那么解析速度则是相当快的。(目前的 DNS 解析是以十分不安全的明文的方式传输的,任何中间人都能破坏或者是修改解析结果)

2. 发起请求

有了 IP 地址之后,浏览器会与服务器建立 TCP 链接,然后发出一个请求。请求中会包含需要访问网站的域名(得以使 IP 可以给多个域名提供不同内容)、请求方式、网址路径、所能接受的数据,压缩类型和编码方式、 Cookie 以及浏览器信息(能够判断你是否在手机上访问网站)。

3. 下载页面

浏览器开始下载页面本身,下载页面本身后又会开始下载页面中依赖的其他内容,包括 CSS 、JavaScript 、图片等。随着优先级不同,它们可能是在渲染之前或者之后下载。

4. 渲染页面

浏览器会等待加载了最重要的依赖内容(也就是在 head 部分中的 CSS 和 JavaScript,之后会重点提到)加载完毕后,进行渲染。因为渲染时要依赖这些内容, CSS 也就是我们常说的样式表,这些样式表十分重要,比如百度这个网页:

没有样式表的百度

没有 CSS 样式表后,整个网页的布局是 “失控” 的,页面不会以预期的方式被加载,这种体验十分糟糕。没有样式表与有样式表的网页通常是两种完全不同的样子。然而 JavaScript 通常与布局无关,可以放在之后加载,不影响页面样式。

提升速度

网页中的每一个内容都要经历这样的过程,以被客户端得到。只有到了渲染页面这一步时,用户才能够看到页面的内容,在此之前,页面是一片空白。要知道如果一个页面在 3 秒之内都是一片空白,那么多半用户会退出。 移动优先会考虑高延迟移动网络下的用户体验,然而当到了桌面端则也能享受到移动有限带来的速度提升。为了提升速度,有以下几种常用方法:

将不必要的 JavaScript 放在页面末尾

如果 JavaScript 放在末尾,可以在页面被渲染后再加载,大大减少了页面空白的时间。这是最简单,且提升效果最明显的一条。

使用 CDN

使用 CDN 的意思是尽可能把静态文件放在 CDN 服务器上,一个 CDN 包含很多个节点,用户下载 CDN 上的文件时会从物理位置最近或者是相同运营商的节点上下载,如果这个节点上已经有缓存则直接传给用户,如果没有缓存则从最近的数据中心下载并缓存,再传给用户。 我的做法是把网站上所有图片、视频、CSS 样式表和 JavaScript 放在 CDN 上,网站本身不在 CDN 上。这样做能大大减少加载时间,而且成本不大。无论是个人博客还是行业网站,使用 CDN 都很有必要。如果没有钱买自己的 CDN,没关系,你可以免费使用常用开源 CSS 和 JavaScript 的 CDN,官网地址地址:cdnjs.com

妥善利用缓存机制

将图片、CSS 样式表和 JavaScript 设置缓存。我习惯于设置相当长的时间(一周、一年等),有的人为了能够经常修改而把缓存期限调小。我在修改文件时会直接换文件目录,这样文件从能保持是最新的。利用好缓存,可以让用户在第二次访问时大大加速。

总结

对于网站速度,既要注重首次启动速度,还要利用缓存提升下一次访问速度。而且应该注意在提升速度的同时也要注重用户体验。

移动优先 – 起源

现有的桌面上的操作系统大多都有浏览器,浏览器可以让我们方便的浏览一个网页。 然而最初的手机浏览器访问网页是很困难的,因为它们分辨率不高、屏幕很小而且浏览器不支持众多前沿的 CSS 以及 JavaScript,使它们没有能力访问桌面版网站。那时的手机访问的网站都是专门为设备大幅度简化的版本,就像这样:

简化版本的移动网页

不过,直到 iPhone 的发布,这一切都得到了很大的改变。因为 iPhone 上的浏览器 —— Safari 是一个真正的且非常前沿的 Web 浏览器,而且它还支持 HTML5 标准。它配备有几乎和电脑一样的浏览器 —— 以至于它支持 CSS3 和 JavaScript。但是这一切,却是在一个宽度仅有 320 个像素的设备上显示。 然而当一个桌面浏览器宽度缩小到 320 个像素时,它就变的大不一样,当他访问一个没有对它优化的网页时,它就会将其缩放(宽度将缩放至 1/3),以百度新闻为例,若没有适配,则将现实电脑版网站,这样页面的字会很小,但是用户可以通过缩放就能够解决,所以并没有什么问题。然而,百度新闻做了一个专门为 iPhone 的版本,像这样:

百度新闻 iPhone 版

它能够更好的适应触摸屏,并且没有了字体小的问题,也不需要缩放。但是请要注意,这个页面已经不是刚才的那个页面了,也就意味着百度需要专门重新设计一个为 iPhone 版本的网页,成本是很高的。 然而在 CSS 的帮助下,不需要准备两个网页就可以同时适配电脑和手机,它们加载到的页面相同,但是应用的样式表并不同。例如没有针对移动而优化的 Wikipedia 导航页,iPhone 上的 Safari 访问时将会缩放显示,布局就如同电脑访问一样:

没有针对移动而优化的 Wikipedia 导航页

然而通过增加的 CSS 样式表(当然还包括 head 中的 viewport),这个页面在手机上就会完美的显示了,就像下图:

针对移动优化了的 Wikipedia 导航页

像 Wikipedia 导航页这样先根据电脑布局设计,再新增 CSS 使其支持移动设备的网页,就是桌面优先而不是移动优先。而移动优先则正好相反,页面根据手机而设计,然后再让它适配电脑,电脑与手机也是访问同一个页面,这就属于移动优先。由于移动优先的网站是根据手机设计,自 iPhone 发布之后,很多智能手机也使用了前沿的浏览器,所以移动优先的网站自然会使用 HTML5、CSS3 中的众多新特性,大大缩短了一个网页所需要的开发时间。而且移动优先的网站根据手机的高延迟网络而设计,减少了页面大小,无论身处什么网络环境,加载速度都有很大的提升。移动优先的优点众多,这就是导致其流行起来的原因。

❌