精华内容
下载资源
问答
  • 双向绑定原理

    千次阅读 2019-05-31 18:46:32
    希望了解双向绑定原理 从MVC、MVVM说起 参考阮一峰老师的文章:http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html 流程:View根据Model展示页面,当页面发生操作时(commander),View传递指令到Controller...

    适合读者:
    了解 MV* 架构模式
    希望了解双向绑定原理

    从MVC、MVVM说起

    参考阮一峰老师的文章:http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html

    MVC
    流程:View根据Model展示页面,当页面发生操作时(commander),View传递指令到Controller层,Controller层根据commander对Model做出修改,Model发生变化后,通知View重新渲染。
    在这里插入图片描述
    流程:Model传递到ViewModel层进行对应逻辑运算后同步到View上,同时View与ViewModel绑定,View的任何变动都反映在ViewModel上,ViewModel变更时,会通知Model做出变更。View与Model层不发生通信。

    单向绑定与双向绑定

    从上边MVC与MVVM模型图可以看出,MVC模型根据Model渲染View,Model向View单向通信,理解为单向绑定;
    MVVM模型View与ViewModel绑定,双方的任何变更均能反映到另一方,View的任何变更由ViewModel向Model通报,同时Model的变更向ViewModel通报,ViewModel与View同步。

    双向绑定的应用场景

    一句话概括: 双向绑定试用于所有UI控件,对非UI控件,当组件间通信时,可采用双向绑定,也可采用单向绑定。
    参考文章:单、双向绑定分析对比 https://www.wang1314.com/doc/topic-20437069-1.html

    模仿双向绑定

    <template>
      <div class="hello">
        <p> 双向绑定</p>
        <h1>{{price}}</h1>
        <h1>{{ tax }}</h1>
        <h1>{{ total }}</h1>
        <input v-model="price"/>
        <button @click = "clickBtn()">改变价格</button>
      </div>
    </template>
    <script>
    	export default {
    	  name: 'HelloWorld',
    	  data: function (){
    	    return {
    	      price: 10,
    	      count: 10
    	    }
    	  },
    	  computed: {
    	    total: function(){
    	      return this.price * this.count
    	    },
    	    tax: function(){
    	    	return pirce * (1+0.05)
    	    }
    	  },
    	  methods: {
    	    clickBtn: function() {
    	      this.price +=1;
    	    }
    	  }
    	}
    </script>
    
    

    问题1、修改price的值,为什么会更改total的值? 如何把这两个变量(属性)关联起来?

    计算属性:total
    total: function(){
          return this.price * this.count
        }
    total本质上是函数,在该函数调用过程中,会访问this.price和this.count的值
    在访问this.price属性的时候,可以将该方法保存起来,当this.price变更时,我们调用该方法,刷新total的值	
    

    具体实现分为两步:
    1、存储访问器属性对应的方法
    2、修改data中对应属性(依赖属性)时,调用存储的方法,刷新其他属性值

    存储访问器属性
    let price = 10
    let count = 10
    let total = price * count
    let target = null //全局属性, 用来替换访问器属性
    //第一步前奏
    class memoProperty {
    	constructor(){
    		this.subscribes = []
    	}
    	bind (){
    		target !== null && typeof target == 'function' && !this.subscribes.includes(target) ? 
    			this.subscribes.push(target) : null 
    	}
    	notify (){
    		this.subscribes.forEach(target => target())
    	}
    }
    console.log(total) 				// 100
    const memo = new memoProperty()
    //taget 表示计算属性对应的方法
    target = function(){
    	total = price * count
    }
    //第一步执行
    memo.bind()
    //第二步执行
    price = 15
    memo.notify()
    console.log(total)				//150
    price = 20
    memo.notify()
    console.log(total)				//300
    

    // 可以这样写的前提是我们知道total依赖price和count,是我们主动把total与price和count联系起来
    //怎么样才能做到当我们在执行function (){total = price * count }这句代码的时候,自动的把total与price和count联系起来?

    解决方案 Object.defineProperty

    /*简单介绍一下Object.defineProperty
    该方法可以对象的属性进行数据属性和访问器属性进行定义,不了解数据属性和访问器属性的同学参考MDN文档。
    对其访问器属性进行设置时,如下例*/
    Object.defineProperty(obj, 'name',{
        get: function(){
            console.log("name")
        },
        set: function(value){
            console.log("newValue: ", value)
        }
    })
    obj.name			//name
    obj.name = "hahah"	//newValue: hahah
    当获取或者更改该对象的属性时,调用对应的回调函数
    

    那么问题的具体解决思路为:
    1、主动计算访问器属性,即调用访问器属性方法target
    2、调用时,会访问price 和count变量,在get方法中存储调用方法target
    3、当price属性变更时,出发访问器set属性,此时调用对应的target,更新依赖于price的属性
    具体实现如下:

    let price = 10
    let count = 10
    let data = {price, count}
    let total = price * count
    let target = null //全局属性, 用来替换访问器属性
    //第一步前奏
    class memoProperty {
    	constructor(){
    		this.subscribes = []
    	}
    	bind (){
    		target !== null && typeof target == 'function' && !this.subscribes.includes(target) ? 
    			this.subscribes.push(target) : null 
    	}
    	notify (){
    		this.subscribes.forEach(target => target())
    	}
    }
    Object.keys(data).forEach( key =>{
    	//实例化存储类
    	const memo = new memoProperty()
    	let initValue = data[key]
    	Object.defineProperty(data, key,{
    		get: function(){
    			memo.bind()
                return initValue
    		},
    		set:function(value){
                initValue = value
    			// initValue !== value ? memo.notify() : null
                memo.notify()
            }
    	})
    })
    function watch(func){
    	//将计算属性对应方法赋值给target
    	target = func
    	//调用计算属性方法,此时会访问price和count变量,触发该变量的get方法,将target存储到队列中
    	target()
    	//将target重新指向null
    	target = null
    }
    function totalFun (){
    	data.total = data.price * data.count
    }
    watch(totalFun)
    
    console.log(data.total) 			//100
    data.price = 15
    console.log(data.total)				//150
    
    
    展开全文
  • 前端培训Vue 双向绑定原理几个要点:1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。2.实现一个订阅者Watcher,每一个Watcher都绑定一个更新函数,watcher可以收到属性的变化通知...

    v2-cb2e86159b81671e09fcd9276aedb2fe_1440w.jpg?source=172ae18b

    vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。

    前端培训Vue 双向绑定原理

    几个要点:

    1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。

    2.实现一个订阅者Watcher,每一个Watcher都绑定一个更新函数,watcher可以收到属性的变化通知并执行相应的函数,从而更新视图。

    3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令(v-model,v-on等指令),如果节点存在v-model,v-on等指令,则解析器Compile初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher)。

    前端培训Vue 双向绑定原理

    具体步骤:

    1. 需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter
      这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化
    2. compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
    3. Watcher就是一个订阅者。用于将Observer发来的update消息处理,执行Watcher绑定的更新函数。
    4. 可以说MVVM是Observer,Compile以及Watcher的“boss”了,他需要安排给Observer实现对MVVM自身model数据劫持,监听数据的属性变更,并在变动时进行notify,给Compile实现指令解析,初始化视图,并订阅数据变化,绑定好更新函数,给Watcher一方面接收Observer通过dep传递过来的数据变化,一方面通知Compile进行view update。最后,把这个MVVM抽象出来,就是vue中Vue的构造函数了,可以构造出一个vue实例。
    展开全文
  • 1.Vue2.X的数据双向绑定原理:采用的是Object.defineProperty()方法 <!-- 1.Vue2.x采用的数据双向绑定原理 --> <input type="text" v-model> <p v-bind></p> <script> // 1....

    1.Vue2.X的数据双向绑定原理:采用的是Object.defineProperty()方法

    <!-- 1.Vue2.x采用的数据双向绑定原理 -->
        <input type="text" v-model>
        <p v-bind></p>
        <script>
            // 1.获取行内属性[v-model]
            let txt = document.querySelector('[v-model]');
            // 2.input框实现输入值
            txt.oninput = function(){
                // 3.将获取到的值赋给obj.name
                obj.name = txt.value;
            }
    
            let obj = {};
            // 4.IE8以及以下浏览器不识别该用法,自带了get和set方法
            // get的得到值变化,set是设置值的变化
            Object.defineProperty(obj,'name', {
                get(){
                    console.log('get被调用了');
                },
                // set自带一个参数
                set(value){
                    console.log('set被调用了');
                    // 获取到p的属性
                    let p = document.querySelectorAll('[v-bind]')[0];
                    // 赋值
                    p.innerHTML = value;
                }
            });
    
            // obj.name = 'summer';  // 触发了set
            // console.log(obj.name); // 触发了get
        </script>

     

    2.Vue3.0实现数据的双向绑定原理:采用的是Proxy方法,proxy是个对象

    <input type="text" v-model>
        <p v-bind></p>
        <script>
            let txt = document.querySelector('[v-model]');
            txt.oninput = function(){
               p.obj = txt.value;
            }
    
    
            let obj = {};
            let p = new Proxy(obj,{
                // 1.目标对象
                // 2.被获取的属性值
                // 3.Proxy或继承Proxy
                get(data, property, receiver){
                    // console.log('get被调用了');
                },
                // 1.目标对象
                // 2.被获取的属性值
                // 3.被获取的value值
                // 4.最初被调用的对象Proxy
                set(data, property, value, receiver){
                    // console.log('set被调用了');
                    let p = document.querySelectorAll('[v-bind]')[0];
                    p.innerHTML = value;       
                }
            });
    
            // p.name = 'summer';  // set被调用了
            // console.log(p.name); // get被调用了
            
        </script>

    3.proxy比Object.defineProperty好在哪里?

    proxy优势:

    A.可以直接监听对象而非属性

    B.可以直接监听数组的变化

    C.Proxy有多达13种拦截方式,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具备的

    D.Proxy返回的是一个新对象,可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改

    Object.defineProperty的优势:

    A.兼容性好,支持IE9,而Proxy的存在浏览器兼容性问题,而且无法用polyfill磨平。

    展开全文
  • vue双向绑定原理

    2020-12-31 14:43:39
    vue双向绑定原理分析 当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理。 简易vue源码地址:https://github.com/jiangzhenfei/simple-Vue 1.vue双向...

    当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理。

    简易vue源码地址:https://github.com/jiangzhenfei/simple-Vue

    面试回答:
    vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属 性的 setter getter ,在数据变动时发布消息给订阅者,触发相应的监听回调。
    具体步骤:
    第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter getter 这样的话,给这个对象的某个值赋值,就会触发 setter ,那么就能监听到了数据变化
    第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指 令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
    第三步:Watcher订阅者是ObserverCompile之间通信的桥梁,主要做的事情是:
       1、在自身实例化时往属性订阅器(dep)里面添加自己
       2、自身必须有一个update()方法
       3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
    第四步:MVVM作为数据绑定的入口,整合ObserverCompileWatcher三者,通过Observer来监听 自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和 Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向 绑定效果。

     

     

    1.vue双向绑定原理

    vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调。我们先来看Object.defineProperty()这个方法:

    var obj  = {};
    Object.defineProperty(obj, 'name', {
            get: function() {
                console.log('我被获取了')
                return val;
            },
            set: function (newVal) {
                console.log('我被设置了')
            }
    })
    obj.name = 'fei';//在给obj设置name属性的时候,触发了set这个方法
    var val = obj.name;//在得到obj的name属性,会触发get方法

    已经了解到vue是通过数据劫持的方式来做数据绑定的,其中最核心的方法便是通过Object.defineProperty()来实现对属性的劫持,那么在设置或者获取的时候我们就可以在get或者set方法里假如其他的触发函数,达到监听数据变动的目的,无疑这个方法是本文中最重要、最基础的内容之一。

    2.实现最简单的双向绑定

    我们知道通过Object.defineProperty()可以实现数据劫持,是的属性在赋值的时候触发set方法,

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <div id="demo"></div>
        <input type="text" id="inp">
        <script>
            var obj  = {};
            var demo = document.querySelector('#demo')
            var inp = document.querySelector('#inp')
            Object.defineProperty(obj, 'name', {
                get: function() {
                    return val;
                },
                set: function (newVal) {//当该属性被赋值的时候触发
                    inp.value = newVal;
                    demo.innerHTML = newVal;
                }
            })
            inp.addEventListener('input', function(e) {
                // 给obj的name属性赋值,进而触发该属性的set方法
                obj.name = e.target.value;
            });
            obj.name = 'fei';//在给obj设置name属性的时候,触发了set这个方法
        </script>
    </body>
    </html>

    当然要是这么粗暴,肯定不行,性能会出很多的问题。

     

    3.讲解vue如何实现

    先看原理图

    3.1 observer用来实现对每个vue中的data中定义的属性循环用Object.defineProperty()实现数据劫持,以便利用其中的setter和getter,然后通知订阅者,订阅者会触发它的update方法,对视图进行更新。

    3.2 我们介绍为什么要订阅者,在vue中v-model,v-name,{{}}等都可以对数据进行显示,也就是说假如一个属性都通过这三个指令了,那么每当这个属性改变的时候,相应的这个三个指令的html视图也必须改变,于是vue中就是每当有这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者,其订阅者只是更新自己的指令对应的数据,也就是v-model='name'和{{name}}有两个对应的订阅者,各自管理自己的地方。每当属性的set方法触发,就循环更新Dep中的订阅者。

    4.vue代码实现

    4.1 observer实现,主要是给每个vue的属性用Object.defineProperty(),代码如下:

    function defineReactive (obj, key, val) {
        var dep = new Dep();
            Object.defineProperty(obj, key, {
                 get: function() {
                        //添加订阅者watcher到主题对象Dep
                        if(Dep.target) {
                            // JS的浏览器单线程特性,保证这个全局变量在同一时间内,只会有同一个监听器使用
                            dep.addSub(Dep.target);
                        }
                        return val;
                 },
                 set: function (newVal) {
                        if(newVal === val) return;
                        val = newVal;
                        console.log(val);
                        // 作为发布者发出通知
                        dep.notify();//通知后dep会循环调用各自的update方法更新视图
                 }
           })
    }
            function observe(obj, vm) {
                Object.keys(obj).forEach(function(key) {
                    defineReactive(vm, key, obj[key]);
                })
            }

    4.2实现compile:

    compile的目的就是解析各种指令称真正的html。

    function Compile(node, vm) {
        if(node) {
            this.$frag = this.nodeToFragment(node, vm);
            return this.$frag;
        }
    }
    Compile.prototype = {
        nodeToFragment: function(node, vm) {
            var self = this;
            var frag = document.createDocumentFragment();
            var child;
            while(child = node.firstChild) {
                console.log([child])
                self.compileElement(child, vm);
                frag.append(child); // 将所有子节点添加到fragment中
            }
            return frag;
        },
        compileElement: function(node, vm) {
            var reg = /\{\{(.*)\}\}/;
            //节点类型为元素(input元素这里)
            if(node.nodeType === 1) {
                var attr = node.attributes;
                // 解析属性
                for(var i = 0; i < attr.length; i++ ) {
                    if(attr[i].nodeName == 'v-model') {//遍历属性节点找到v-model的属性
                        var name = attr[i].nodeValue; // 获取v-model绑定的属性名
                        node.addEventListener('input', function(e) {
                            // 给相应的data属性赋值,进而触发该属性的set方法
                            vm[name]= e.target.value;
                        });
                        new Watcher(vm, node, name, 'value');//创建新的watcher,会触发函数向对应属性的dep数组中添加订阅者,
                    }
                };
            }
            //节点类型为text
            if(node.nodeType === 3) {
                if(reg.test(node.nodeValue)) {
                    var name = RegExp.$1; // 获取匹配到的字符串
                    name = name.trim();
                    new Watcher(vm, node, name, 'nodeValue');
                }
            }
        }
    }

    4.3 watcher实现

    function Watcher(vm, node, name, type) {
        Dep.target = this;
        this.name = name;
        this.node = node;
        this.vm = vm;
        this.type = type;
        this.update();
        Dep.target = null;
    }
    
    Watcher.prototype = {
        update: function() {
            this.get();
            this.node[this.type] = this.value; // 订阅者执行相应操作
        },
        // 获取data的属性值
        get: function() {
            console.log(1)
            this.value = this.vm[this.name]; //触发相应属性的get
        }
    }

    4.4 实现Dep来为每个属性添加订阅者

    function Dep() {
        this.subs = [];
    }
    Dep.prototype = {
        addSub: function(sub) {
            this.subs.push(sub);
        },
        notify: function() {
            this.subs.forEach(function(sub) {
            sub.update();
            })
        }
    }

    这样一来整个数据的双向绑定就完成了。

    5.梳理

    首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;然后在编译的时候在该属性的数组dep中添加订阅者,v-model会添加一个订阅者,{{}}也会,v-bind也会,只要用到该属性的指令理论上都会,接着为input会添加监听事件,修改值就会为该属性赋值,触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。

    转载: https://www.cnblogs.com/zhenfei-jiang/p/7542900.htm 

    展开全文
  • 主要介绍了Vue 的双向绑定原理与用法,结合实例形式总结分析了Vue 的双向绑定基本原理、功能、用法及注意事项,需要的朋友可以参考下
  • Vue数据双向绑定原理

    2021-04-02 20:33:27
    数据双向绑定原理3.简单版的数据双向绑定原理 1.数据双向绑定 v-bind属性绑定值 是单向数据绑定(只修改前台数据,后台不变) v-model 表单数据绑定 是数据双向绑定 <template> <!-- 表单元素的vlaue绑定 ...
  • Vue数据双向绑定原理是通过数据劫持结合发布者-订阅者模式的方式来实现的,首先是对数据进行监听,然后当监听的属性发生变化时则告诉订阅者是否要更新,若更新就会执行对应的更新函数从而更新视图 MVC模式 以往的...
  • 主要介绍了JS数据双向绑定原理与用法,结合实例形式分析了JavaScript数据双向绑定相关原理、实现技巧与操作注意事项,需要的朋友可以参考下
  • vue双向绑定原理分析

    2019-09-18 06:27:54
    vue双向绑定原理分析 当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理。 简易vue源码地址:https://github.com/jiangzhenfei/simple-Vue 1.vue双向...
  • Vue双向绑定原理初学

    2019-07-18 15:31:14
    Vue双向绑定原理入门双向绑定概念数据可观测依赖收集完整示例总结 从开始学习前端到现在走在进入中高级前端开发的路上,觉得上手容易又简单的就是Vue框架,包含其相关的生态系统。一直只是简单了解双向绑定的原理,...
  • vue,mvvm双向绑定原理和实现 1.实现compile,进行模板的编译。指令和watcher更新函数; 2.实现Observe,监听所有的数据,并对变化数据发布通知; 3.实现watcher,作为一个中枢,接收到observe发来的通知,并执行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,222
精华内容 1,688
关键字:

双向绑定原理