Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
confidence68 opened this issue Mar 27, 2018 · 0 comments
Open

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

confidence68 opened this issue Mar 27, 2018 · 0 comments

Comments

@confidence68
Copy link
Owner

前言

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

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/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant