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

  • 相关阅读:
    牛客练习赛51 D题
    Educational Codeforces Round 72 (Rated for Div. 2) C题
    Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises) C题
    Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises) A题
    Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises) A题
    Educational Codeforces Round 72 (Rated for Div. 2) B题
    Educational Codeforces Round 72 (Rated for Div. 2) A题
    《DSP using MATLAB》Problem 7.2
    《DSP using MATLAB》Problem 7.1
    《DSP using MATLAB》Problem 6.24
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4709346.html
Copyright © 2011-2022 走看看