淘宝网性能优化之借鉴——webp及Bigpipe

23225次浏览

前言

说到性能优化,我博客前面文章讲了不少,其实性能优化做的好坏,直接体现前端开发人员的水平。因此,很多面试中基本上都会提及这一点。今天主要借鉴淘宝网性能优化方式之一webp及Bigpipe 进行简单的讲解。

webp

打开淘宝网,假如你是chrome浏览器,你会发现,所有图片都是webp结尾的,淘宝网图片运用了webp。假如你是safari浏览器,看到图片就是jpg或者png了,淘宝网自动判断浏览器支持不支持webp,假如支持,则输出相应的图片格式!

看下图:

enter image description here

淘宝网图片请求头。

淘宝网的流程应该也是如下的:

运用了bigpipe客户端服务器端同时渲染,图片全是异步请求。

1、判断浏览器是否支持webp

2、客户端发送请求头到服务器端,假如请求头带:

image/webp,

说明支持webp ,则服务器渲染webp格式图片,否则就是jpg或者png

如何检测平台是否支持webp格式

方法一:

function checkWebp() {
    try{
        return (document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0);
    }catch(err) {
        return  false;
    }
}

console.log(checkWebp());   // true or false

方法二:自官网的。

// check_webp_feature:
//   'feature' can be one of 'lossy', 'lossless', 'alpha' or 'animation'.
//   'callback(feature, result)' will be passed back the detection result (in an asynchronous way!)
function check_webp_feature(feature, callback) {
    var kTestImages = {
        lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA",
        lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",
        alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
        animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"
    };
    var img = new Image();
    img.onload = function () {
        var result = (img.width > 0) && (img.height > 0);
        callback(feature, result);
    };
    img.onerror = function () {
        callback(feature, false);
    };
    img.src = "data:image/webp;base64," + kTestImages[feature];
}

其实跟第一种方法差不多,这里提供了几种webp的图片模式,如果浏览器支持webp,那么图片的宽高会大于0,从而返回true,否则返回false.

使用方法:

第一个参数feature可以传 lossy,lossless,alpha,animation中的一个,第一个传个回调函数。获取他result。如果支持,返回ture,否则返回false。可以再谷歌和IE下试试,谷歌返回ture,IE返回false

check_webp_feature('lossless',function(feature,result){
    alert(result); //true or false
});

方法三:增加class

;(function(doc) {

    // 给html根节点加上webps类名
    function addRootTag() {
        doc.documentElement.className += "webps";
    }

    // 判断是否有webps=A这个cookie
    if (!/(^|;\s?)webps=A/.test(document.cookie)) {
        var image = new Image();

        // 图片加载完成时候的操作
        image.onload = function() {

            // 图片加载成功且宽度为1,那么就代表支持webp了,因为这张base64图是webp格式。如果不支持会触发image.error方法
            if (image.width == 1) {

                // html根节点添加class,并且埋入cookie
                addRootTag();
                document.cookie = "webps=A; max-age=31536000; domain=haorooms.com";
            }
        };

        // 一张支持alpha透明度的webp的图片,使用base64编码
        image.src = 'data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==';
    } else {
        addRootTag();
    }
}(document));

原理也是一样的,不过这个比较贴近实战,就是加载一张webp图片,如果可以加载出来,那么就是支持webp,否则就是不支持。如果支持webp,那么给html加一个class 叫 webps。

方法四:

var isSupportWebp = !![].map && document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0;

一行代码就可以判断浏览器支持不支持webp。

用法:

console.log(isSupportWebp);   // true or false

webp格式纯前端应用

这种方式只是纯前端的一种尝试,还是推荐和后端配合使用。

纯前端使用的话,可以如下方式:

在页面加载css之前,加载检测是否支持webp的js。如果支持,则html节点上里面有一名为webps的class。

背景图的话,我们就用写两套。一套是没有用webp的css,一套是用了webp图片的背景图。

.haorooms{
    background-image: url('../images/haorooms.jpg');
}

.webps .haorooms{
    background-image: url('../images/haorooms.webp');
}

img图片也是一样,用js输出相关路径。特别适用于异步加载。

Bigpipe 技术

也是比较早的前端渲染技术了,淘宝网用了,请看文章:http://taobaofed.org/blog/2016/03/25/seller-bigpipe-coding/

Bigpipe 技术是把网页分割成多个PageLet的小块,然后分段输出到浏览器,前后端并行处理。

BigPipe的原理

BigPipe的主要思想是实现浏览器和服务器的并发执行,实现页面的异步加载,从而提高页面的访问速度。 为了达到这个目的,它首先根据页面的功能或者位置,将页面分成若干个模块,这些模块的名字也被称为PageLet,并对这些分解的模块进行唯一的标识。然后通过Web服务器和浏览器之间建立管道,进行分段输出 (减少请求数)。

下面来看一个简单的例子:

我们来看下面的代码:layout.html

<!DOCTYPE html>
<html>
<head>
  <script>
    var BigPipe = {
      view: function(selector,temp) {
        document.querySelector(selector).innerHTML= temp;
      }
    }
  </script>
</head>
<body>
    <div id="moduleA"></div>
    <div id="moduleB"></div>
    <div id="moduleC"></div>

服务端代码,基于express

var express = require('express');
var app = express();
var fs = require('fs');

app.get('/', function (req, res) {
  var layoutHtml = fs.readFileSync(__dirname + "/layout.html").toString();
  res.write(layoutHtml);

  // setTimeout只是模拟异步返回
  setTimeout(function() {
    res.write('<script>BigPipe.view("#moduleA","moduleA");</script>');
  100);

  setTimeout(function() {
    res.write('<script>BigPipe.view("#moduleC","moduleC");</script>');
  },200);

  setTimeout(function() {
    res.write('<script>BigPipe.view("#moduleB","moduleB");</script>');
    res.write('</body></html>');
  },300);

  res.end();
});

app.listen(3000);

关于Bigpipe,淘宝前端团队有2篇文章,讲的很细致,推荐大家一看:

1、http://taobaofed.org/blog/2016/03/25/seller-bigpipe-coding/

2、http://taobaofed.org/blog/2015/12/17/seller-bigpipe/

Tags: webpBigpipe性能优化

相关文章:

  1. allen
    1
    由于是异步,res.end要在所有的res.write结束后再执行。 Promise.all(promises).then(datas => { res.end(); });