zoukankan      html  css  js  c++  java
  • vue双向数据绑定

    vue双向绑定原理分析

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

    .vue双向绑定原理

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

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

    实现最简单的双向绑定

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

    demo1:
    var obj  = {};
    Object.defineProperty(obj, 'name', {
            get: function() {
                console.log('我被获取了')
                return val;
            },
            set: function (newVal) {
                console.log('我被设置了')
            }
    })  
    demo2:
      ```var Book = { name: 'HTML5从入门到放弃' }; // 默认情况下name 会有两个方法 get set
      console.log(Book.name) // HTML5从入门到放弃 --- 调用了get
      // Book.name = "《" + Book.name + "》"; // 相当于调用了set
      // console.log(Book.name) // 《HTML5从入门到放弃》
    
      var val = ""; // 
      Object.defineProperty(Book, 'name', {
        set (value) { // 劫持了name的set方法,处理数据
        // val = value;
          val = '《' +  value + '》';
        }, 
        get () {
          return val;
        }
      }) 
      Book.name = "JS从入门到放弃";
      console.log(Book.name)
    

    obj.name = 'fei';//在给obj设置name属性的时候,触发了set这个方法
    var val = obj.name;//在得到obj的name属性,会触发get方法

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

    <!doctype html>

    Document
    ``` ## vue代码实现 ` 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'); } } } } ``` ### 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
    }
    }

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

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

  • 相关阅读:
    Windows下Android开发环境搭建
    解决Win7下打开或关闭Windows功能空白一片
    C#中得到程序当前工作目录和执行目录的一些方法
    创业者必看的小故事
    Sql Server 2005外围应用应用配置器打不开了怎么办
    vs2005无法Web调试
    SQL Server 全局变量
    转 document.documentElement与document.body
    [转]使用SSMA将Oracle数据库转成SQL Server 2008
    Delphi 2009、C++Builder2009正式发布
  • 原文地址:https://www.cnblogs.com/wangjiahui/p/11359395.html
Copyright © 2011-2022 走看看