zoukankan      html  css  js  c++  java
  • Vue的双向绑定原理

    Vue的构造函数分析

    vm就是MVVM中的View Model
    var vm = new Vue({
    el: '#app',
    data: {
    message: 'Hello Vue!'
    }
    })
    
    /*
    相关于Vue的构造函数
     */
    function Vue(options) {
      // 将选项对象保存到vm
      this.$options = options;
      // 将data对象保存到vm和data变量中
      var data = this._data = this.$options.data;
      //将vm保存在me变量中
      var me = this;
      // 遍历data中所有属性
      Object.keys(data).forEach(function (key) { // 属性名: name
        // 对指定属性实现代理: 通过Object.defineProperty(obj,attr,{配置项, get:func, set:func})方法,将data中遍历的属性添加到vm实例中实现代理,使得原本 vm._data[attr]访问方式变为vm.attr
        me._proxy(key);
      }); 
    
      // 对data进行监视,在observe中创建与当前属性对应的dep对象,当调用data中的属性值时,建立dep与watcher的关系;当更新data中的值时,通过dep.notify()方法通知当前dep对应的所有的watcher对界面数据进行更新
      observe(data, this);
    
      // 创建一个用来编译模板的compile对象
      this.$compile = new Compile(options.el || document.body, this)
    }
    
    1. 数据绑定
      • 初始化显示: 页面(表达式/指令)能从data读取数据显示 (编译/解析)
      • 更新显示: 更新data中的属性数据==>页面更新

    Dep和Watcher对象

    Dep

    与data中的属性一一对应

    { id: 0, subs[对应的Watcher实例对象数组] }
    

    Watcher

    与模板中一般指令(v-html、v-model、v-class...)/大括号表达式({{ }})一一对应

    {
    cb: 当对应的属性值发生了变化时, 自动调用该回调函数, 更新对应的节点,
    vm: Vue实例对象,
    exp: data属性表达式,例如:a.b.c,
    depIds:  用于存储对应的dep对象 {0: d0, 1: d1, 2: d2},
    value: 当前watcher中的属性值
    }
    
    1. 什么时候一个dep中关联多个watcher?
      多个指令或表达式用到了当前同一个属性 {{name}} {{name}}
    2. 什么时候一个watcher中关联多个dep?
      多层表达式的watcher对应多个dep {{a.b.c}}

    整体过程

    Vue是通过数据劫持的方式来做数据绑定的,最核心的方法是通过Object.defineProperty()方法来实现对属性的劫持,达到监听数据变动的目的。以MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
    原理图如下:

    Vue实例化一个对象的过程如下:

    1. mvvm入口函数,整合以下三者,创建一个实例后,遍历data中的所有属性实现数据代理,实现 vm.xxx 代替 vm._data.xxx
    2. 实现一个数据监听器Observer,利用Obeject.defineProperty()来监听数据对象的所有属性变动,并对应所有属性一一创建相应的dep对象,给dep对象添加订阅者(watcher);如Observer监测到有变动,set函数调用,触发Dep的notify()向对应的订阅者Watcher通知变化,Watcher调用update方法更新界面。
    3. 实现一个指令解析器Compile,主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图,由于遍历解析的过程有多次操作dom节点,为提高性能和效率,会先将跟节点el转换成文档碎片fragment进行解析编译操作,解析完成,再将fragment添加回原来的真实dom节点中
    4. 实现一个Watcher

    1、在自身实例化时往属性订阅器(dep)里面添加自己
    2、自身必须有一个update()方法
    3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调。


    文章学习重要来源:https://segmentfault.com/a/1190000006599500
  • 相关阅读:
    PHP-会话控制
    PHP-文件上传
    PHP-文档目录
    PHP-正则表达式
    PHP-数组类型
    PHP-函数编程
    PHP-基础知识
    $_FILES系统函数
    话说 MAX_FILE_SIZE
    Hello~! 我的blog
  • 原文地址:https://www.cnblogs.com/vikeykuo/p/11664387.html
Copyright © 2011-2022 走看看