vue双向绑定的原理

什么是双向数据绑定?Vue是一个MVVM框架,数据绑定简单来说,就是当数据发生变化时,相应的视图会进行更新,当视图更新时,数据也会跟着变化。

实现数据绑定的方式大致有以下几种:

- 1、发布者-订阅者模式(backbone.js)
- 2、脏值检查(angular.js)
- 3、数据劫持(vue.js)

发布者-订阅者模式

一般通过sub, pub的方式实现数据和视图的绑定监听,更新数据方式通常做法是 vm.set(‘property‘, value),有兴趣可参考这里

我们更希望可以通过 vm.property = value 这种方式进行数据更新,同时自动更新视图。

脏值检查

angular是通过脏值检查方式来对比数据是否变化,来决定是否更新视图,最常见的方式是通过setInterval()来监测数据变化,当然,只会在某些指定事件触发时下才进行脏值检查。大致如下:

- DOM事件,譬如用户输入文本,点击按钮等。( ng-click )
- XHR响应事件 ( $http )
- 浏览器Location变更事件 ( $location )
- Timer事件( $timeout , $interval )
- 执行 $digest() 或 $apply()

数据劫持

Vue.js则是通过数据劫持以及结合发布者-订阅者来实现的,数据劫持是利用ES5的Object.defineProperty(obj, key, val)来劫持各个属性的的setter以及getter,在数据变动时发布消息给订阅者,从而触发相应的回调来更新视图。

一、实现最基础的数据绑定

<input type="text" id="in"/>
    输入的值为:<span id="out"></span>

    <script>
        var int = document.getElementById(‘in‘);
        var out = document.getElementById(‘out‘);
        var obj = {};

        Object.defineProperty(obj, ‘msg‘, {
            enumerable: true,
            configurable: true,
            set (newVal) {
                out.innerHTML = newVal;
            }
        })

        int.addEventListener(‘input‘, function(e) {
            obj.msg = e.target.value;
        })
    </script>

Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性:里面有三个值,分别如下:

  1. obj 需要定义属性的当前对象
  2. prop 当前需要定义的属性名
  3. desc 属性描述符
  • 一般通过为对象的属性赋值的情况下,对象的属性可以修改也可以删除,但是通过Object.defineProperty()定义属性,通过描述符的设置可以进行更精准的控制对象属性。

有兴趣可以深入了解,这里就不多解释了=。=

二、双向数据绑定实现(此处用MVue替代)

上面的只是简单的使用了Object.defineProperty(),并不是我们最终想要的效果,最终想要的效果如下:

<div id="app">
        <input type="text" v-model="text">
        输入的值为:{{text}}
        <div>
            <input type="text" v-model="text">
        </div>
    </div>
    <script>
        var vm = new MVue({
            el: ‘#app‘,
            data: {
                text: ‘hello world‘
            }
        })
    </script>

实现思路:
1、输入框以及文本节点和data中的数据进行绑定
2、输入框内容变化时,data中的对应数据同步变化,即 view => model
3、data中数据变化时,对应的文本节点内容同步变化 即 model => view

上述流程如图所示:

1、实现一个数据监听器Obverser,对data中的数据进行监听,若有变化,通知相应的订阅者。
2、实现一个指令解析器Compile,对于每个元素上的指令进行解析,根据指令替换数据,更新视图。
3、实现一个Watcher,用来连接Obverser和Compile, 并为每个属性绑定相应的订阅者,当数据发生变化时,执行相应的回调函数,从而更新视图。
4、构造函数 (new MVue({}))

MVue构造函数

在初始化MVue实例时,对data中每个属性劫持监听,同时进行模板编译,指令解析,最后挂载到相应的DOM中。

function MVue (options) {
        this.$el = options.el;
        this.$data = options.data;

        // 初始化操作,后面会说
        // ...
    }

1、实现 view => model

DocumentFragment(文档片段)

vue进行编译时,将挂载目标的所有子节点劫持到DocumentFragment中,经过一份解析等处理后,再将DocumentFragment整体挂载到目标节点上。

function nodeToFragment (node, vm) {
        var flag = document.createDocumentFragment();
        var child;
        while (child = node.firstChild) {
            compile(child, vm);
             if (child.firstChild) {
                var dom = nodeToFragment(child, vm);
                child.appendChild(dom);
            }
            flag.appendChild(child);
        }
        return flag;
    }

模板编译(指令解析,事件绑定、初始化数据绑定)

编译过程图

代码如下:

function compile (node, vm) {
        let reg = /\{\{(.*)\}\}/;
        // 元素节点
        if (node.nodeType === 1) {
            var attrs = node.attributes;
            for (let attr of attrs) {
                if (attr.nodeName === ‘v-model‘) {
                    // 获取v-model指令绑定的data属性
                    var name = attr.nodeValue;
                    // 绑定事件
                    node.addEventListener(‘input‘, function(e) {
                        vm.$data[name] = e.target.value;
                    })
                    // 初始化数据绑定
                    node.value = vm.$data[name];
                    // 移除v-model 属性
                    node.removeAttribute(‘v-model‘)
                }
            }
        }

        // 文本节点
        if (node.nodeType === 3) {
            if (reg.test(node.nodeValue)) {
                var name = RegExp.$1 && (RegExp.$1.trim());
                // 绑定数据到文本节点中
                 node.nodeValue = node.nodeValue.replace(new RegExp(‘\\{\\{\\s*(‘ + name + ‘)\\s*\\}\\}‘), vm.$data[name]);
            }
        }
    }

现在,我们修改下MVue构造函数,增加模板编译,如下:

function MVue (options) {
        this.$el = options.el;
        this.$data = options.data;

        // 模板编译
        let elem = document.querySelector(this.$el);
        elem.appendChild(nodeToFragment(elem, this))
    }

那么,我们的view => model 已经实现了,包括初始化绑定默认值,只要修改了input中的值,data中对应的值相应变化,并触发了setter, 更新属性值等(可以自行在set方法中打印看效果,或者在控制台手动输入vm.$data.text也会看到效果)。
2、实现 model => view
上面可以看出,虽然我们实现了初始化数据绑定,以及输入框变化时,data中text也会变化,但是文本节点仍然没有任何变化,那么如果做到文本节点也同步变化呢,这里用的是发布者-订阅者模式。

原文地址:https://www.cnblogs.com/LWWTT/p/11111874.html

时间: 2024-11-07 00:50:15

vue双向绑定的原理的相关文章

vue双向绑定原理源码解析

当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理. 简易vue源码地址:https://github.com/maxlove123/simple-Vue.git 1.vue双向绑定原理 vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调.我们先来看Object.definePr

梳理vue双向绑定的实现原理

Vue 采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后进行相应的处理. 要实现mvvm的双向绑定,就必须要实现以下几点: Compile-指令解析系统,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数 Observer-数据监听系统,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者 Dep+Watche

关于vue的计算属性以及双向绑定的原理理解(vue2.x)以及vue3.0

vue的计算属性: 1.什么是计算属性? 计算属性的目的是用于对数据进行简单运算的,若在模板中放过多的计算逻辑会导致模板难以维护. 计算属性是基于它们的依赖进行缓存的.计算属性只有在它的相关依赖发生改变时才会重新求值. 2.计算属性如何使用? 1.在一个计算属性里可以完成各种复杂的逻辑,包括运算.函数调用等,只要最终返回一个结果就可以. computed: { reverseText: function(){ return app1.text.split('').reverse().join('

Vue双向绑定的实现原理系列(四):补充指令解析器compile

补充指令解析器compile github源码 补充下HTML节点类型的知识: 元素节点 Node.ELEMENT_NODE(1) 属性节点 Node.ATTRIBUTE_NODE(2) 文本节点 Node.TEXT_NODE(3) CDATA节点 Node.CDATA_SECTION_NODE(4) 实体引用名称节点 Node.ENTRY_REFERENCE_NODE(5) 实体名称节点 Node.ENTITY_NODE(6) 处理指令节点 Node.PROCESSING_INSTRUCTIO

Vue双向绑定的实现原理系列(三):监听器Observer和订阅者Watcher

监听器Observer和订阅者Watcher 实现简单版Vue的过程,主要实现{{}}.v-model和事件指令的功能 主要分为三个部分 github源码 1.数据监听器Observer,能够对数据对象的所有属性进行监听; 实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性 2.Watcher将数据监听器和指令解析器连接起来,数据的属性变动时,执行指令绑定的相应回调函数, 1.如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新

[Vue源码]一起来学Vue双向绑定原理-数据劫持和发布订阅

有一段时间没有更新技术博文了,因为这段时间埋下头来看Vue源码了.本文我们一起通过学习双向绑定原理来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vue模板编译原理(一)-Template生成AST 一起来学Vue模板编译原理(二)-AST生成Render字符串 一起来学Vue虚拟DOM解析-Virtual Dom实现和Dom-diff算法 这些文章统一放在我的git仓库:https://github.com/yzsun

Vue实现数据双向绑定的原理

Vue实现数据双向绑定的原理:Object.defineProperty() vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调.当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter.用户看不

vue双向绑定原理

vue双向绑定原理的核心 它的实现的核心是通过Object.defineProperty(),对data的每个属性进行了get.set的拦截. 其实只要Object.defineProperty()已经可以实现双向绑定,只是这样做效率非常低. 观察者模式 它在双向绑定当中是什么角色呢? 它其实是让双向绑定更有效率 为什么? 观察者模式,它是一对多的一种模式,在vue里面,"一"是改了某一data数据,"多"是页面上凡是用了这个数据的地方,都更新.这就是页面上的很多&

IOS自带输入法中文不触发KEYUP事件导致vue双向绑定错误问题

先上图: 可以看到输入框中的内容和弹出框的内容不一致, <input class="am-fr labRight" id="txcode" type="text" placeholder="请输入纳税人识别号" v-model="invBuyer.TaxCode" /> 文本框使用的是vue的v-model双向绑定,在android中是ok的,在IOS上不行, 导致问题出现的原因是IOS自带输入