zoukankan      html  css  js  c++  java
  • avalon与双缓冲技术

    avalon与双缓冲技术

    avalon1.5一个重要技术升级是引进异步渲染。异步渲染在游戏界有一个更专业的名字,叫双缓冲。游戏界要刷新界面与我们刷新浏览器视图,面临的问题是一致的。视图是由许多存在套嵌关系的方块组成,它们每一个的改动,都可能引起reflow(其父节点,其父父节点的大小重新计算),这是造成性能问题的关键。

    双缓冲技术的主要原理是:当一个动画争先显示时,程序又在改变它,前面的画面还没显示完,程序又要求重新绘制,这样屏幕就会不停闪烁。为了避免闪烁,可以使用双缓冲技术,将要处理的图片都放在内存中处理好过后,再将其显示到屏幕上。这样出来的就是完整的图像,不会出现闪烁现象。

    MVVM框架带来一个革命性的优化是,用户只操作VM就行了,视图由框架来同步更新。因此这个同步过程,我们就可以加入优化。比如说angular,就是靠用户手动调用$apply来驱动脏检测,只有数据不一致的地方,才会操作DOM。于是没有了aaa.innerHTML = "xxx&"; aaa.innerHTML = "xxx"的愚蠢代码。

    可能用户会说我怎么可能会这样写呢,那是你因为用于jquery,看到它是在同一个循环中对某个节点执行多次相同的操作。

    再回过头来看avalon。avalon是基于事件驱动(通过Object.defineProperty劫持了对象的属性),当用户修改了某属性,就会立即同步视图。这种机制最大的好处是,方便与jQuery或其他操作DOM的库配合使用

    avalon.config.async = false
            var vm = avalon.define({
                $id: "test",
                a: 1
            })
           setTimeout(function(){
               vm.a = 2
               alert(document.getElementById("aaa").innerHTML) //2
           }, 1000)
    <div ms-controller="test">
         <div id="aaa">{{a}}</div> // 2
     </div>

    坏处是可能会造成性能问题。作为测试,我们在avalon的text指令加入这样一行日志:

    avalon.directive("text", {
        update: function (value) {
            console.log(value)
            var elem = this.element
            value = value == null ? "" : value //不在页面上显示undefined null
            if (elem.nodeType === 3) { //绑定在文本节点上
                try { //IE对游离于DOM树外的节点赋值会报错
                    elem.data = value
                } catch (e) {
                }
            } else { //绑定在特性节点上
                if ("textContent" in elem) {
                    elem.textContent = value
                } else {
                    elem.innerText = value
                }
            }
        }
    })

    ` 测试页面为:

    
    <html>
        <head>
            <title>TODO supply a title</title>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <script src="avalon.js"></script>
            <script>
                avalon.config.async = false
                var vm = avalon.define({
                    $id: "test",
                    a: 1
                })
                setTimeout(function () {
                    vm.a = 2
                    vm.a = 3
                    vm.a = 4
                    vm.a = 5
                    vm.a = 6
                })
            </script>
        </head>
        <body>
            <div ms-controller="test">
                <div id="aaa">{{a}}</div>
            </div>
        </body>
    </html>

    image

    在avalon1.5中,打开 avalon.config.async = true开关后,就只输出两次。第一次扫描肯定立即出来,以后就是异步了。

    image

    avalon做到这个非常简单,因为经过多次重构优化,从VM到V的同步,都要经过notifySubscribers这个方法。它是用来执行当前属性对应的订阅数组中的每个对象的update方法。

    function notifySubscribers(subs) {
        if (!subs)
            return
        if (new Date() - beginTime > 444 && typeof subs[0] === "object") {
            rejectDisposeQueue()  //如果这个绑定对象的节点已经被移出DOM树,那么需要对这sub对象进行GC回收处理
        }
        for (var i = 0, sub; sub = subs[i++]; ) {
                sub.update && sub.update() //最小化刷新DOM树
         }
    }

    ` 现在改成这样了

    function notifySubscribers(subs) {
        if (!subs)
            return
        if (new Date() - beginTime > 444 && typeof subs[0] === "object") {
            rejectDisposeQueue()//...
        }
        if (kernel.async) {
            buffer.render()
            for (var i = 0, sub; sub = subs[i++]; ) {
                if (sub.update) {
                    avalon.Array.ensure(buffer.queue,sub)//这里有去重处理
                }
            }
        } else {
            for (var i = 0, sub; sub = subs[i++]; ) {
                sub.update && sub.update()//最小化刷新DOM树
            }
        }
    }

    buffer对象很简单

    var buffer = {
        render: function () {
            if (!this.locked) {
                this.locked = 1
                avalon.nextTick(function () {
                    buffer.flush()
                })
            }
        },
        queue: [],
        flush: function () {
            for (var i = 0, sub; sub = this.queue[i++]; ) {
                sub.update()
            }
            this.queue.length = this.locked = 0
        }
    }

    这是历次重构,精简入口带来的好处。换言之,做一样的东西,只有一个函数负责。那么要对功能做扩展,就直接对 此函数进行处理就行了。

    只要减少了视图无效的刷新,那么avalon的性能就会立即上去,这对大表格的渲染刷新能立竿见影!

    avalon的官网地址: avalonjs.github.io 有关双缓冲的实现可以这里:avalon2.buffer

  • 相关阅读:
    debugs
    MySQL事务隔离级别详解(转)
    解决SQL Server管理器无法连接远程数据库的问题(转)
    jQuery中click(),bind(),live()的区别(转)
    各jQuery选择器的用法(转)
    使用JQuery获取对象的几种方式(转)
    HTML中元素的定位方式
    深入浅出REST(转载)
    jQuery-Selectors(选择器)的使用(二、层次篇)(转载)
    linux常用命令
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4709346.html
Copyright © 2011-2022 走看看