微信公众号通过链接实现关注(盗用广告主流量主)

我们想要用户关注我们的微信公众号,大多数是通过二维码,或者直接告诉微信号,又或者是发一篇文章,通过文章的蓝色字进入关注页面。

其实我们在网页开发的时候总是会想要是能有个按钮,点击就能关注就好了,但是这个目前还真没有办法,至少我不知道 @_@
但退而求其次,要是有个链接一点就跳转到关注页面也是极好的。
于是我就发现了这个 鸡肋 的方法(好吧 有点标题党,而且还真挺鸡肋,但是要看你敢不敢想,肯不肯做 └(^o^)┘)
转载请注明出处:http://blog.csdn.net/m0sh1/article/details/46843163
微信有了 JSSDK 对于开发的好处自然不用说,但是也有不好地方,其中一点就是,js的执行相对页面加载是有些延后的,这就意味着我们往往想例如隐藏复制链接等功能,但是如果你点进页面就迅速点右上角的三个点,就会出现所有的功能,这个时候链接也就曝露出来了。(真的就这点坑害了多少,通过网页授权开发的应用,尤其是那些含有敏感GET参数的)
下面看一个通过链接直接跳转到公众号介绍的页面的例子:
弊端是没有关注按钮,但是可以展示历史文章。
链接如下:
http://mp.weixin.qq.com/mp/ad_biz_info?__biz=MjM5NDAwMTA2MA==#wechat_redirect
打开 如图:
这里写图片描述

其实这样也挺好的了,你的文章能抓住人性,自然会引起关注的。当然不够完美啦,想要关注得打开文章等操作去关注。这里面其实你只要更改参数 __biz 后面的值为你的公众号对应的值就可以了。这个值怎么获取我就卖个关子不讲了,如果你平时细心应该会知道的。因为下面的一些操作可能会有利用?的嫌疑,所以还是不要人人都随随便便就会才好。
什么?!当然是 base64编码,但是知道了什么编码并没有什么卵用。细心观察你会找到自己公众号的标识的。

没用过广告主和流量主功能,所以以下说错的地方希望见谅并指出。

那么言归正传,你平时阅读文章,有没有发现图文消息最下面的推广有的时候是个推荐的公众号,没错这就是广告主投放的公众号他直接会有个关注的按钮,如果你点开这个广告,还会有类似上图的样子,即公众号的介绍,但是不同的是还有关注按钮!!!
如果我们能换成我们自己的公众号,别人点击链接跳到这个页面就好了。
于是利用同样的方法(上面委婉的提到的方法)复制链接一看
大致样子为:
这里写图片描述

其实涂红的参数并不重要即不需要你修改,但是我还是给涂掉了 o(∩_∩)o
重要的参数是
__biz 的值 没错就这么简单,换成你的就可以了。
后面还有个参数 appuin 他的值就是 流量主的公众号标识。其实你也可以换成自己的。
然后在微信客户端中打开你构造的链接试试。哈哈是不是 有关注按钮了。 但是不要高兴的太早,因为你过一会再过来点开链接会发现没有关注按钮了
我猜测,要么就是过了流量主设定的投放时间,要么就是超出了流量主设定的额度。
这就是鸡肋的地方喽~

但是试想,如果你抓住时机,把构造链接发到群里,朋友圈或者其它地方,再通过历史文章抓住人性(比如 色 欲 #^_^#)应该会有不少人默默关注你的。
转载请注明出处:http://blog.csdn.net/m0sh1/article/details/46843163

 

此文章通过 python 爬虫创建,原文是自己的csdn 地址: 微信公众号通过链接实现关注(盗用广告主流量主)

 

magento getCarriers 分析

完整的设置订单追踪信息的时候我们可能会用到它,在后台中他在这里设置:
magento_get_carriers

有的时候我们想要设置自定义的 carrier 例如 顺丰 申通 圆通 。。等等
我们可以先从 magento api 入手分析
我们调用 magento api —-> order_shipment.getCarriers
可以去查找 api 函数 在 appcodecoreMageSalesModelOrderShipmentApi.php

    public function getCarriers($orderIncrementId)
    {
        $order = Mage::getModel('sales/order')->loadByIncrementId($orderIncrementId);

        /**
          * Check order existing
          */
        if (!$order->getId()) {
            $this->_fault('order_not_exists');
        }

        return $this->_getCarriers($order);
    }

追踪 _getCarriers 方法

 protected function _getCarriers($object)
    {
        $carriers = array();
        $carrierInstances = Mage::getSingleton('shipping/config')->getAllCarriers(
            $object->getStoreId()
        );

        $carriers['custom'] = Mage::helper('sales')->__('Custom Value');
        foreach ($carrierInstances as $code => $carrier) {
            if ($carrier->isTrackingAvailable()) {
                $carriers[$code] = $carrier->getConfigData('title');
            }
        }

        return $carriers;
    }

即 都会有个默认的 custom
之后的都是通过 Mage::getSingleton(‘shipping/config’)->getAllCarriers() 获取的
追踪方法 getAllCarriers() 其实就是循环config中的 carriers 例如这里面就有magento 自带的freeshipping
和 flatrate 等方法。 但是 要想显示到返回的 $carriers 中需要 isTrackingAvailable 为true
我们其实可以在上面代码中打印你每一个 $carrier 的类名 找到调用的 isTrackingAvailable 方法,
经分析 发现默认的例如 flatrate 他的 model中方法 isTrackingAvailable 是继承父类为false 的。
那么如果此时你想在后台 carrier中有 flatrate 可以设置他的 isTrackingAvailable 方法返回true就行了
同样适用于你自定义的运送方式。

扩展的说下:
还有一点,我们在前台结账的时候到 选择运送方式的时候例如有 flatrate 那么此时他的input value 你可以看到是
flatrate_flatrate 我们继续追踪代码,发现他在代码中显示的 是 getCarrier().’_’.getMethod() 通常我们会把这两个值在
设置的时候设置为一样的(在flatrate的model中)所以显示的就是这个样子了

但是这里要说的就是对接 Odoo 的时候默认的magento连接件 是通过这个订单的shipping method ,即运送方式
你可以在数据表 sale_flat_order 找到 例如就是 flatrate_flatrate 。 然而此时你在 Odoo 中设置tracking number的时候
可能报错,是因为提示没有 flatrate_flatrate
这时如果magento 设置 flatrate 的isTrackingAvailable 为true 你把Odoo中这个code改为 flatrate 就可以了
如果没有,可以直接设置code 为 custom 也是可以的,因为你可以在magento api中找到 默认都有个为 custom的code

$(function () {
$('pre.prettyprint code').each(function () {
var lines = $(this).text().split('n').length;
var $numbering = $('

    ').addClass('pre-numbering').hide();
    $(this).addClass('has-numbering').parent().append($numbering);
    for (i = 1; i <= lines; i++) {
    $numbering.append($('
  • ').text(i));
    };
    $numbering.fadeIn(1700);
    });
    });

此文章通过 python 爬虫创建,原文是自己的csdn 地址: magento getCarriers 分析

表单防止重复提交

首先介绍两种和简单的方法:
1. disable 提交按钮

$("#submit_button").attr("disabled",true);
  1. 设定锁定标志:
var lock = true;
var vvv = 'vvvvalue';
function clickButton(){
   if(lock){
        lock = false;
        $.ajax({
             url:"2.php",
             type:"post",
             data: "vvv="+vvv,
             success:function(data){
                  console.log(data);
                  lock = true;
             }
        });
   }
}

下面完整的介绍参考自
链接: http://www.zhihu.com/question/19805411
不推荐用外部变量锁定或修改按钮状态的方式,因为那样比较难:

* 要考虑并理解 success, complete, error, timeout 这些事件的区别,并注册正确的事件,一旦失误,功能将不再可用;

* 不可避免地比普通流程要要多注册一个 complete 事件;

* 恢复状态的代码很容易和不相干的代码混合在一起;

我推荐用主动查询状态的方式(A、B,jQuery 为例)或工具函数的方式(C、D)来去除重复操作,并提供一些例子作为参考:

A. 独占型提交
只允许同时存在一次提交操作,并且直到本次提交完成才能进行下一次提交。

module.submit = function() { 
    if (this.promise_.state() === 'pending') { return } 
    return this.promise_ = $.post('/api/save')
    }

B. 贪婪型提交
无限制的提交,但是以最后一次操作为准;亦即需要尽快给出最后一次操作的反馈,而前面的操作结果并不重要。

module.submit = function() {
  if (this.promise_.state() === 'pending') {
    this.promise_.abort()
  }
  // todo}

比如某些应用的条目中,有一些进行类似「喜欢」或「不喜欢」操作的二态按钮。如果按下后不立即给出反馈,用户的目 光焦点就可能在那个按钮上停顿许久;如果按下时即时切换按钮的状态,再在程序上用 abort 来实现积极的提交,这样既能提高用户体验,还能降低服务器压力,皆大欢喜。

C. 节制型提交
无论提交如何频繁,任意两次有效提交的间隔时间必定会大于或等于某一时间间隔;即以一定频率提交。

module.submit = throttle(150, function() {
  // todo})

如果客户发送每隔100毫秒发送过来10次请求,此模块将只接收其中6个(每个在时间线上距离为150毫秒)进行处理。
这也是解决查询冲突的一种可选手段,比如以知乎草稿举例,仔细观察可以发现:
编辑器的 blur 事件会立即触发保存;
保存按钮的 click 事件也会立即触发保存;
但是存在一种情况会使这两个事件在数毫秒内连续发生——当焦点在编辑器内部,并且直接去点击保存按钮——这时用 throttle 来处理是可行的。
另外还有一些事件处理会很频繁地使用 throttle,如: resize、scroll、mousemove。

D. 懒惰型提交
任意两次提交的间隔时间,必须大于一个指定时间,才会促成有效提交;即不给休息不干活。

module.submit = debounce(150, function() {
  // todo})

还是以知乎草稿举例,当在编辑器内按下 ctrl + s 时,可以手动保存草稿;如果你连按,程序会表示不理解为什么你要连按,只有等你放弃连按,它才会继续。

============
更多记忆中的例子

方式 C 和 方式 D 有时更加通用,比如这些情况:

* 游戏中你捡到一把威力强大的高速武器,为了防止你的子弹在屏幕上打成一条直线,可以 throttle 来控制频率;

* 在弹幕型游戏里,为了防止你把射击键夹住来进行无脑游戏,可以用 debounce 来控制频率;

* 在编译任务里,守护进程监视了某一文件夹里所有的文件(如任一文件的改变都可以触发重新编译,一次执行就需要2秒),但某种操作能够瞬间造成大量文件改变(如 git checkout),这时一个简单的 debounce 可以使编译任务只执行一次。

而方式 C 甚至可以和方式 B 组合使用,比如自动完成组件(Google 首页的搜索就是):

* 当用户快速输入文本时(特别是打字能手),可以 throttle keypress 事件处理函数,以指定时间间隔来提取文本域的值,然后立即进行新的查询;

* 当新的查询需要发送,但上一个查询还没返回结果时,可以 abort 未完成的查询,并立即发送新查询;

E. 记忆型

var scrape = memoize(function(url) {
  return $.post('/scraper', { 'url': url })})

对于同样的参数,其返回始终结果是恒等的——每次都将返回同一对象。
应用例子有编辑器,如粘贴内容时抓取其中的链接信息,memoize 用以保证同样的链接不会抓取两次。

F. 累积型
前几天处理自动完成事件时得到这个函数,发现也可以用在处理连续事件上,它能够把连续的多次提交合并为一个提交,比如:

var request = makePile(5, function() {
    $.post('/', { list: JSON.stringify([].slice.call(arguments)) })})

// 连续发送五次 request({a:1}), request({a:2}), request({a:3}), request({a:4}), request({a:5})/* post =>list:[{"a":1},{"a":2},{"a":3},{"a":4},{"a":5}] */

样例实现:

var makePile = function(count, onfilter, onvalue) { var values = [], id = function(value) { return value } return function(value) { values.push((onvalue || id).apply(this, arguments)) if (values.length === count) { onfilter.apply(this, values) values = [] } }}

另一种累积是按时间而不是次数,比如应用在行为统计上,可能在瞬间收集到数十上百类似的行为,这时可以用上面 pile 的结构加上 debounce 来防止大批重复请求(但又不丢失任何统计):

var trackFactory = function(delay, action) { var params = [], slice = [].slice var touch = debounce(delay, function() { if (params.length) { action(params) params = [] } }) return function() { params.push(slice.call(arguments)) touch() }} var track = trackFactory(550, function(params) { // send tracking request})

G. 采样型
这是最近重构时联想到的,一种和上面都不同的去重操作,可以应用在自动加载(timeline)行为控制上:

autoload.listen(feeds, 'next', sample(3, function() { this.enable() }))

如果 sample 是固化的选择函数(n 选 1),它这实际上会这样工作:
O-O-X-O-O-X

但「自动加载」的应用可能想要的是(两次自动,一次手动):
X-X-O-X-X-O

对于这种情况,可以定义作为配置的选择函数来实现控制:
options { sample: (n) => n % 3 !== 0 }

即每个下一次加载完成之后, 每三次有两次对下一次加载实行自动加载。

$(function () {
$('pre.prettyprint code').each(function () {
var lines = $(this).text().split('n').length;
var $numbering = $('

    ').addClass('pre-numbering').hide();
    $(this).addClass('has-numbering').parent().append($numbering);
    for (i = 1; i <= lines; i++) {
    $numbering.append($('
  • ').text(i));
    };
    $numbering.fadeIn(1700);
    });
    });

此文章通过 python 爬虫创建,原文是自己的csdn 地址: 表单防止重复提交

php curl , fsockopen 函数

下面直接给出 使用 curl 和 fsockopen 的例子,你可以当成使用实例,也可以直接当作封装好的函数直接使用。

curl函数使用代码

public function xcurl($url,$ref=null,$post=array(),$ua="Mozilla/5.0 (X11; Linux x86_64; rv:2.2a1pre) Gecko/20110324 Firefox/4.2a1pre",$print=false) {
     $ch = curl_init();
     curl_setopt($ch, CURLOPT_AUTOREFERER, true);
     if(!empty($ref)) {
          curl_setopt($ch, CURLOPT_REFERER, $ref);
     }
     curl_setopt($ch, CURLOPT_URL, $url);
     curl_setopt($ch, CURLOPT_HEADER, 0);
     curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
     curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
     if(!empty($ua)) {
          curl_setopt($ch, CURLOPT_USERAGENT, $ua);
     }
     if(count($post) > 0){
          curl_setopt($ch, CURLOPT_POST, 1);
          curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
     }
     $output = curl_exec($ch);
     curl_close($ch);
     if($print) {
          print($output);
     } else {
          return $output;
     }
}

fsockopen 函数使用代码:

public function curl_request_async($url, $params, $type='GET')
    {
    // set referer
        $referer = $_SERVER['HTTP_HOST'];
        foreach ($params as $key => &$val) {
            if (is_array($val)) $val = implode(',', $val);
            $post_params[] = $key.'='.urlencode($val);
        }
        $post_string = implode('&', $post_params);

        $parts=parse_url($url);

        @$fp = fsockopen($parts['host'], isset($parts['port'])?$parts['port']:80, $errno, $errstr, 2);

        if(!$fp){
            echo "$errstr ($errno)<br />n";
        }else{
            if('GET' == $type) $parts['path'] .= '?'.$post_string;

            $out = "$type ".$parts['path']." HTTP/1.1rn";
            $out.= "Host: ".$parts['host']."rn";
            $out.= "Referer: ".$referer."rn";
            $out.= "Content-Type: application/x-www-form-urlencodedrn";
            $out.= "Connection: Closernrn";
            // Data goes in the request body for a POST request
            if ('POST' == $type && isset($post_string)) $out.= $post_string;
            fwrite($fp, $out);

            fclose($fp);
        }
    }

仅仅 从如上的函数,使用上的区别其中重要的一点就是:
curl 请求某个Url 需要等待结果返回,而 fsockopen 不需要返回,发送请求后就继续执行代码。除非你使用 fsockopen 需要打印请求后返回的结果。

$(function () {
$('pre.prettyprint code').each(function () {
var lines = $(this).text().split('n').length;
var $numbering = $('

    ').addClass('pre-numbering').hide();
    $(this).addClass('has-numbering').parent().append($numbering);
    for (i = 1; i <= lines; i++) {
    $numbering.append($('
  • ').text(i));
    };
    $numbering.fadeIn(1700);
    });
    });

此文章通过 python 爬虫创建,原文是自己的csdn 地址: php curl , fsockopen 函数

网页性能管理详解

你遇到过性能很差的网页吗?

这种网页响应非常缓慢,占用大量的CPU和内存,浏览起来常常有卡顿,页面的动画效果也不流畅。
这里写图片描述
你会有什么反应?我猜想,大多数用户会关闭这个页面,改为访问其他网站。作为一个开发者,肯定不愿意看到这种情况,那么怎样才能提高性能呢?

本文将详细介绍性能问题的出现原因,以及解决方法。
一、网页生成的过程

要理解网页性能为什么不好,就要了解网页是怎么生成的。
这里写图片描述
网页的生成过程,大致可以分成五步。

    HTML代码转化成DOM
    CSS代码转化成CSSOM(CSS Object Model)
    结合DOM和CSSOM,生成一棵渲染树(包含每个节点的视觉信息)
    生成布局(layout),即将所有渲染树的所有节点进行平面合成
    将布局绘制(paint)在屏幕上

这五步里面,第一步到第三步都非常快,耗时的是第四步和第五步。

“生成布局”(flow)和”绘制”(paint)这两步,合称为”渲染”(render)。
这里写图片描述
二、重排和重绘

网页生成的时候,至少会渲染一次。用户访问的过程中,还会不断重新渲染。

以下三种情况,会导致网页重新渲染。

    修改DOM
    修改样式表
    用户事件(比如鼠标悬停、页面滚动、输入框键入文字、改变窗口大小等等)

重新渲染,就需要重新生成布局和重新绘制。前者叫做”重排”(reflow),后者叫做”重绘”(repaint)。

需要注意的是,”重绘”不一定需要”重排”,比如改变某个网页元素的颜色,就只会触发”重绘”,不会触发”重排”,因为布局没有改变。但是,”重排”必然导致”重绘”,比如改变一个网页元素的位置,就会同时触发”重排”和”重绘”,因为布局改变了。
三、对于性能的影响

重排和重绘会不断触发,这是不可避免的。但是,它们非常耗费资源,是导致网页性能低下的根本原因。

提高网页性能,就是要降低”重排”和”重绘”的频率和成本,尽量少触发重新渲染。

前面提到,DOM变动和样式变动,都会触发重新渲染。但是,浏览器已经很智能了,会尽量把所有的变动集中在一起,排成一个队列,然后一次性执行,尽量避免多次重新渲染。

div.style.color = 'blue';
div.style.marginTop = '30px';

上面代码中,div元素有两个样式变动,但是浏览器只会触发一次重排和重绘。

如果写得不好,就会触发两次重排和重绘。

div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';

上面代码对div元素设置背景色以后,第二行要求浏览器给出该元素的位置,所以浏览器不得不立即重排。

一般来说,样式的写操作之后,如果有下面这些属性的读操作,都会引发浏览器立即重新渲染。

    offsetTop/offsetLeft/offsetWidth/offsetHeight
    scrollTop/scrollLeft/scrollWidth/scrollHeight
    clientTop/clientLeft/clientWidth/clientHeight
    getComputedStyle()

所以,从性能角度考虑,尽量不要把读操作和写操作,放在一个语句里面。

// bad
div.style.left = div.offsetLeft + 10 + "px";
div.style.top = div.offsetTop + 10 + "px";

// good
var left = div.offsetLeft;
var top  = div.offsetTop;
div.style.left = left + 10 + "px";
div.style.top = top + 10 + "px";

一般的规则是:

    样式表越简单,重排和重绘就越快。
    重排和重绘的DOM元素层级越高,成本就越高。
    table元素的重排和重绘成本,要高于div元素

四、提高性能的九个技巧

有一些技巧,可以降低浏览器重新渲染的频率和成本。

第一条是上一节说到的,DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作。

第二条,如果某个样式是通过重排得到的,那么最好缓存结果。避免下一次用到的时候,浏览器又要重排。

第三条,不要一条条地改变样式,而要通过改变class,或者csstext属性,一次性地改变样式。

// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";

// good 
el.className += " theclassname";

// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

第四条,尽量使用离线DOM,而不是真实的网面DOM,来改变元素样式。比如,操作Document Fragment对象,完成后再把这个对象加入DOM。再比如,使用 cloneNode() 方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。

第五条,先将元素设为 display: none (需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。这样一来,你就用两次重新渲染,取代了可能高达100次的重新渲染。

第六条,position属性为absolute或fixed的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。

第七条,只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden 的元素只对重排有影响,不影响重绘。

第八条,使用虚拟DOM的脚本库,比如React等。

第九条,使用 window.requestAnimationFrame()、window.requestIdleCallback() 这两个方法调节重新渲染(详见后文)。
五、刷新率

很多时候,密集的重新渲染是无法避免的,比如scroll事件的回调函数和网页动画。

网页动画的每一帧(frame)都是一次重新渲染。每秒低于24帧的动画,人眼就能感受到停顿。一般的网页动画,需要达到每秒30帧到60帧的频率,才能比较流畅。如果能达到每秒70帧甚至80帧,就会极其流畅。
这里写图片描述
大多数显示器的刷新频率是60Hz,为了与系统一致,以及节省电力,浏览器会自动按照这个频率,刷新动画(如果可以做到的话)。
这里写图片描述
所以,如果网页动画能够做到每秒60帧,就会跟显示器同步刷新,达到最佳的视觉效果。这意味着,一秒之内进行60次重新渲染,每次重新渲染的时间不能超过16.66毫秒。
这里写图片描述
一秒之间能够完成多少次重新渲染,这个指标就被称为”刷新率”,英文为FPS(frame per second)。60次重新渲染,就是60FPS。
六、开发者工具的Timeline面板

Chrome浏览器开发者工具的Timeline面板,是查看”刷新率”的最佳工具。这一节介绍如何使用这个工具。

首先,按下 F12 打开”开发者工具”,切换到Timeline面板。
这里写图片描述
左上角有一个灰色的圆点,这是录制按钮,按下它会变成红色。然后,在网页上进行一些操作,再按一次按钮完成录制。

Timeline面板提供两种查看方式:横条的是”事件模式”(Event Mode),显示重新渲染的各种事件所耗费的时间;竖条的是”帧模式”(Frame Mode),显示每一帧的时间耗费在哪里。

先看”事件模式”,你可以从中判断,性能问题发生在哪个环节,是JavaScript的执行,还是渲染?
这里写图片描述
不同的颜色表示不同的事件。
这里写图片描述
蓝色:网络通信和HTML解析
黄色:JavaScript执行
紫色:样式计算和布局,即重排
绿色:重绘

哪种色块比较多,就说明性能耗费在那里。色块越长,问题越大。
这里写图片描述
这里写图片描述
帧模式(Frames mode)用来查看单个帧的耗时情况。每帧的色柱高度越低越好,表示耗时少。
这里写图片描述
你可以看到,帧模式有两条水平的参考线。
这里写图片描述
下面的一条是60FPS,低于这条线,可以达到每秒60帧;上面的一条是30FPS,低于这条线,可以达到每秒30次渲染。如果色柱都超过30FPS,这个网页就有性能问题了。

此外,还可以查看某个区间的耗时情况。
这里写图片描述
或者点击每一帧,查看该帧的时间构成。
这里写图片描述
七、window.requestAnimationFrame()

有一些JavaScript方法可以调节重新渲染,大幅提高网页性能。

其中最重要的,就是 window.requestAnimationFrame() 方法。它可以将某些代码放到下一次重新渲染时执行。

function doubleHeight(element) {
  var currentHeight = element.clientHeight;
  element.style.height = (currentHeight * 2) + 'px';
}
elements.forEach(doubleHeight);

上面的代码使用循环操作,将每个元素的高度都增加一倍。可是,每次循环都是,读操作后面跟着一个写操作。这会在短时间内触发大量的重新渲染,显然对于网页性能很不利。

我们可以使用window.requestAnimationFrame(),让读操作和写操作分离,把所有的写操作放到下一次重新渲染。

function doubleHeight(element) {
  var currentHeight = element.clientHeight;
  window.requestAnimationFrame(function () {
    element.style.height = (currentHeight * 2) + 'px';
  });
}
elements.forEach(doubleHeight);

页面滚动事件(scroll)的监听函数,就很适合用 window.requestAnimationFrame() ,推迟到下一次重新渲染。

$(window).on('scroll', function() {
   window.requestAnimationFrame(scrollHandler);
});

当然,最适用的场合还是网页动画。下面是一个旋转动画的例子,元素每一帧旋转1度。

var rAF = window.requestAnimationFrame;

var degrees = 0;
function update() {
  div.style.transform = "rotate(" + degrees + "deg)";
  console.log('updated to degrees ' + degrees);
  degrees = degrees + 1;
  rAF(update);
}
rAF(update);

八、window.requestIdleCallback()

还有一个函数window.requestIdleCallback(),也可以用来调节重新渲染。

它指定只有当一帧的末尾有空闲时间,才会执行回调函数。

requestIdleCallback(fn);

上面代码中,只有当前帧的运行时间小于16.66ms时,函数fn才会执行。否则,就推迟到下一帧,如果下一帧也没有空闲时间,就推迟到下下一帧,以此类推。

它还可以接受第二个参数,表示指定的毫秒数。如果在指定 的这段时间之内,每一帧都没有空闲时间,那么函数fn将会强制执行。

requestIdleCallback(fn, 5000);

上面的代码表示,函数fn最迟会在5000毫秒之后执行。

函数 fn 可以接受一个 deadline 对象作为参数。

requestIdleCallback(function someHeavyComputation(deadline) {
  while(deadline.timeRemaining() > 0) {
    doWorkIfNeeded();
  }

  if(thereIsMoreWorkToDo) {
    requestIdleCallback(someHeavyComputation);
  }
});

上面代码中,回调函数 someHeavyComputation 的参数是一个 deadline 对象。

deadline对象有一个方法和一个属性:timeRemaining() 和 didTimeout。

(1)timeRemaining() 方法

timeRemaining() 方法返回当前帧还剩余的毫秒。这个方法只能读,不能写,而且会动态更新。因此可以不断检查这个属性,如果还有剩余时间的话,就不断执行某些任务。一旦这个属性等于0,就把任务分配到下一轮requestIdleCallback。

前面的示例代码之中,只要当前帧还有空闲时间,就不断调用doWorkIfNeeded方法。一旦没有空闲时间,但是任务还没有全执行,就分配到下一轮requestIdleCallback。

(2)didTimeout属性

deadline对象的 didTimeout 属性会返回一个布尔值,表示指定的时间是否过期。这意味着,如果回调函数由于指定时间过期而触发,那么你会得到两个结果。

    timeRemaining方法返回0
    didTimeout 属性等于 true

因此,如果回调函数执行了,无非是两种原因:当前帧有空闲时间,或者指定时间到了。

function myNonEssentialWork (deadline) {
  while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && tasks.length > 0)
    doWorkIfNeeded();

  if (tasks.length > 0)
    requestIdleCallback(myNonEssentialWork);
}

requestIdleCallback(myNonEssentialWork, 5000);

上面代码确保了,doWorkIfNeeded 函数一定会在将来某个比较空闲的时间(或者在指定时间过期后)得到反复执行。

requestIdleCallback 是一个很新的函数,刚刚引入标准,目前只有Chrome支持。
九、参考链接

Domenico De Felice, How browsers work
Stoyan Stefanov, Rendering: repaint, reflow/relayout, restyle
Addy Osmani, Improving Web App Performance With the Chrome DevTools Timeline and Profiles
Tom Wiltzius, Jank Busting for Better Rendering Performance
Paul Lewis, Using requestIdleCallback

作者:阮一峰
转自:http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html

$(function () {
$('pre.prettyprint code').each(function () {
var lines = $(this).text().split('n').length;
var $numbering = $('

    ').addClass('pre-numbering').hide();
    $(this).addClass('has-numbering').parent().append($numbering);
    for (i = 1; i <= lines; i++) {
    $numbering.append($('
  • ').text(i));
    };
    $numbering.fadeIn(1700);
    });
    });

此文章通过 python 爬虫创建,原文是自己的csdn 地址: 网页性能管理详解