• 双向数据绑定


    随着vue的兴起,vue的一大亮点--双向数据绑定,也被很多人所熟知,之所以成为一大亮点是针对之前的单向数据绑定而言的,接下来就做一个简单的对比分析:

    单向数据绑定:指的是我们先把模板写好,然后把模板和数据(可能来自于后台)整合到一起形成html代码,然后把这段html代码插入到文档流里面

    单向数据绑定的缺点:html代码一旦生成之后就没法儿改变啦,如果数据发生变化的话,之前生成的html就得去掉,再重新把模板和数据一起整合后插入到文档流中

    双向数据绑定:数据模型和视图层之间的双向绑定,用户在视图层的修改会自动同步到数据模型中去,同样的,数据模型中的值发生了变化,也会立即同步到视图层。

    双向数据绑定最常用的应用场景就是表单,目前前端实现双向数据绑定主要流行的框架就是Angular和Vue,具体实现原理如下:

    一,发布订阅模式(PubSub模式)

    比较传统的模式,通过在数据对象上定义get和set方法,调用时手动调用set和get数据,改变数据之后发出UI层的渲染操作;以视图驱动数据变化的场景主要于input,textarea,select等元素,当UI层发生变化的时候,通过监听dom的change,keypress,keyup等事件来改变数据层的数据,整个过程均通过函数调用来完成

    HTML
    <
    div class="wrapper"> <input ps-value= "value" id="edit" type="text"/> <div ps-text="value" id="show"></div> </div>
    JavaScript
    <script> var elements = [document.getElementById('edit'),document.getElementById('show')] var data = { value: 'hello' } var command = { text: function(str){ this.innerHTML = str }, value:function (str) { this.setAttribute('value',str) } } var scan =function() { for (var i=0;i<elements.length;i++) { var el = elements[i] for (var j=0;j<el.attributes.length;j++) { var attr = el.attributes[j] if (attr.nodeName.indexOf('ps') !== -1) { command[attr.nodeName.slice(3)].call(el, data[attr.nodeValue]) } } } } function set (key,value){ data[key] = value scan() } // 视图层的变化触发数据层的变化 elements[0].addEventListener('keyup',function(e){ set('value',e.target.value) }) scan() // 模拟数据的变化 setTimeout(function(){ set('value','world') },2000) </script>

    二,数据劫持
    具体思路是使用Object.defineProperty对数据对象做属性的get和set监听,当有数据读取和赋值操作时则调用节点的指令,这样使用最通用的=赋值就可以了。具体实现如下:

    HTML
    <div class="wrapper">
        <input ps-value= "value" id="edit" type="text"/>
        <div ps-text="value" id="show"></div>
    </div>
    JavaScript
    <script> var elements = [document.getElementById('edit'),document.getElementById('show')] var data = { value: 'hello' } var command = { text: function(str){ this.innerHTML = str }, value:function (str) { this.setAttribute('value',str) } } var scan =function() { for (var i=0;i<elements.length;i++) { var el = elements[i] for (var j=0;j<el.attributes.length;j++) { var attr = el.attributes[j] if (attr.nodeName.indexOf('ps') !== -1) { command[attr.nodeName.slice(3)].call(el, data[attr.nodeValue]) } } } } var beforeValue var defineGetandSet = function(obj,name) { try{ Object.defineProperty(obj,name,{ get:function(){
                
    return beforeValue }, set:function(newvalue){ beforeValue = newvalue scan() }, enumerable: true, configurable: true }) }catch(error){ console.log(error) } } // 初始化数据 scan() defineGetandSet(data,'value') // 视图层的改变 if (document.addEventListener) { elements[0].addEventListener('keyup',function(e){ data.value = e.target.value }) }else{ elements[0].attchEvent('keyup',function(e){ data.value = e.target.value })
    }
    // 模拟数据的变化 setTimeout(function(){ data.value = 'world' },2000) </script>

    三,脏检查机制
    以angularjs为代表,angular通过检查脏数据来进行UI层的操作更新。关于angular的脏检测,有几点需要了解:当数据发生变化的时候才进行脏检测,并不是实时或者是定时的开启检测。angular对常用的dom事件,xhr事件等做了封装, 在里面触发进入angular的digest流程。在digest流程里面, 会从rootscope开始遍历, 检查所有的watcher。脏检测的主要思路:通过设置的数据来需找与该数据相关的所有元素,然后再比较数据变化,如果变化则进行指令操作。

    HTML
    <
    div class="wrapper"> <input p-event = "value" ng-bind="value" type="text" id="edit"/> <div p-event = "text" ng-bind="value" id="show"></div> </div>
    JavaScript
    <script>
        var elements = [document.getElementById('edit'),document.getElementById('show')]
        var data = {
            value:'hello'
        }
        var command = {
            text:function(str){
                this.innerHTML = str
            },
            value:function(str){
                this.setAttribute('value',str)
            }
        }
        var scan = function(){
            elements.forEach((el)=>{
                el.command ={}
                for (var i=0;i<el.attributes.length;i++) {
                    var attr = el.attributes[i]
                    if (attr.nodeName.indexOf('p-event') !== -1) {
                        var datakey = el.getAttribute('ng-bind') || undefined
                        command[attr.nodeValue].call(el,data[datakey])    //数据初始化
                        el.command[attr.nodeValue] = data[datakey]
                    }
                }
            })
        }
        // 脏循环检查,从根部开始轮询,检测值是否有变化
        var digest = function(elements) {
            elements.forEach((el)=>{
                for (var i=0;i<el.attributes.length;i++) {
                    var attr = el.attributes[i]
                    if (attr.nodeName.indexOf('p-event') !== -1) {
                        var datakey = el.getAttribute('ng-bind') || undefined
                        if (el.command[attr.nodeValue] !== data[datakey]) {   //当数据有变化的时候,检查并且进行重新赋值
                            command[attr.nodeValue].call(el,data[datakey])
                            el.command[attr.nodeValue] = data[datakey]
                        }
                    }
                }
            })
        }
        scan()   // 数据初始化
        function $digest (value) {
            var list = document.querySelectorAll('[ng-bind='+value+']')
            digest(list)
        }
        // 输入框数据绑定监听
        if (document.addEventListener) {
            elements[0].addEventListener('keyup',function(e){
                data.value = e.target.value
                $digest(e.target.getAttribute('ng-bind'))
            },false)
        } else {
            elements[0].attachEvent('onkeyup',function(e){
                data.value = e.target.value
                $digest(e.target.getAttribute('ng-bind'))
            },false)
        }
        // 模式数据的变化
        setTimeout(function(){
            data.value = 'world'
            $digest('value') // 这里需要手动启动脏检查 ,数据改变的时候,视图层也跟着更新
        },2000)
        </script>
  • 相关阅读:
    vim python extension
    aws msk
    Install python3
    sns
    inventory
    批量添加监听端口
    template screen
    DNS name
    add jar and proxy repo
    模型诊断论文心得
  • 原文地址:https://www.cnblogs.com/cn-andy/p/8558431.html
Copyright © 2020-2023  润新知