hgq's docs
主页
ES6-阮一峰 (opens new window)
Vue文档 (opens new window)
Axios文档 (opens new window)
Vue Router (opens new window)
Vuex文档 (opens new window)
面试题-Vue (opens new window)
面试题-JS (opens new window)

guoguoqiqi

漫不经心的向往
主页
ES6-阮一峰 (opens new window)
Vue文档 (opens new window)
Axios文档 (opens new window)
Vue Router (opens new window)
Vuex文档 (opens new window)
面试题-Vue (opens new window)
面试题-JS (opens new window)
  • Vue 生命周期
  • v-model 的原理
  • Vue 组件间通信有哪几种方式
  • v-for给每一项加个key的作用
  • v-if与v-show的区别
  • computed 和 watch 的区别
  • 对 SPA 单页面的理解,它的优缺点分别是什么
  • 为什么不建议v-if和v-for一起使用
  • Vue的内置指令
  • MVVM的理解
  • keep-alive 的了解
  • Vue身上set方法的实现原理,为什么能实现视图的更新
  • mixin的用法
  • Vuex 为什么要分模块并且加命名空间
  • vue-router 路由钩子函数是什么 执行顺序是什么
  • Vuex 页面刷新数据丢失怎么解决
  • vue-router的hash和history模式
  • vue-router 路由模式有几种
  • vue-router 中常用的 hash 和 history 路由模式实现原理
  • 动态绑定Class和Style
  • 服务端渲染SSR
  • 对Diff算法的认识
  • 对虚拟DOM的认识
  • 在组件上使用v-model
  • 路由有哪几种导航守卫
  • 在哪个生命周期内调用异步请求
  • 怎么理解Vue中的单向数据流
  • 组件中 data 为什么是一个函数
  • 通过数组下标或者长度无法触发更新
  • 路由懒加载是什么意思
  • 父组件监听子组件的生命周期
  • 父子组件生命周期钩子函数执行顺序
  • Vue项目的SEO优化
  • Vue项目的优化
  • Vue2的响应式原理的基本实现
  • Vue2和Vue3的响应式原理的区别
  • axios简单配置
  • Vuex的使用
  • 相关面试题集合
  • 对函数式组件的认识
  • Vue extend 作用和原理
  • 自定义指令的使用
  • 模板编译的原理
  • Vue是怎样进行依赖收集的
  • Vue生命周期钩子是如何实现的
  • 说明nextTick的原理
  • 请描述组件的渲染流程
  • Vue的普通Slot以及作用域Slot的区别
  • Vue use方法的作用及原理
  • Vue中的修饰符
  • Vue如何兼容ie的问题
  • Vue中style标签的scoped的作用及原理
  • Vue路由传参数
  • Vue事件绑定原理
  • 如何设计一个比较友好的Header组件
  • Vue-router守卫
  • scoped样式穿透
  • Vue是如何重写数组的方法的
  • scoped的情况下无法修改element的样式问题
  • 修改element ui的样式
  • 强制刷新组件的方式有哪些
  • vue组件的设计原则
  • 一个组件的渲染流程
  • 默认插槽、具名插槽和作用域插槽
  • Vue组件的name的作用
  • 为什么Vue被称为“渐进框架”
  • 怎么做Vue项目的SEO优化
  • vue推荐的风格指南
  • 如何重置data
  • vuex中actions和mutations有什么区别
  • 说说你对vue的错误处理的了解
  • 说一说递归组件
  • 对于vue双向数据绑定的理解
    • 说一说vue中template模板的编译过程
    • 说一说nextTick的作用
    • Vue为何采用异步渲染
    • vue-router有几种钩子函数?具体是什么及执行流程是怎样的
    • vue中mixin各个选项的合并策略
    • vue中一个组件模板为什么只能由一个根元素
    • vue中的watch和created哪个先执行
    • vue中编辑页面如果判断是否编辑过并在离开时提示保存
    • vue修饰符sync的作用及原理
    • vue中如何批量引入组件
    • vue使用axios如何取消请求
    • vue项目如何实现国际化(多语言)
    • vue中定义全局方法有哪几种方式
    • 跟keep-alive组件相关的生命周期钩子有哪些
    • 如何响应路由参数变化
    • vue中父子组件生命周期的执行顺序
    • 内置组件keep-alive的实现原理
    • mixins和extends的使用及区别
    • vue自定义指令
    • vue的事件绑定原理
    • vue权限控制一般怎么做
    • vue的动态路由怎么配置使用
    • vuex数据持久化怎么做
    • Vue
    guoguoqiqi
    2022-03-08

    对于vue双向数据绑定的理解

    # 实现原理

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

    vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。

    • Observer 对所有数据的属性进行监听
    • Compile 对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数
    • Watcher 作为连接Observer 跟 Compile 之间的桥梁, 能够订阅接收到每个属性变动的通知,执行相应的回调函数

    # 模拟实现

    const  Dep  =  function ()  {
      this.subs =  [];
    };
    Dep.prototype =  {
      addSub: function (sub)  {
        this.subs.push (sub);
      },
      notify: function () {
        this.subs.forEach(sub  =>  {
          sub.update();
        });
      },
    };
    
    const Watcher = function (vm, node, name) {
      Dep.target =  this;
      this.name = name;
      this.node = node;
      this.vm = vm;
      this.update();
      Dep.target =  null;
    };
    
    Watcher.prototype =  {
    
      update: function ()  {
        this.get();
        this.node.nodeValue =  this.value;
      },
    
      get: function ()  {
        this.value =  this.vm[this.name];
      },
    
    };
    
    const compile =  function  (node, vm)  {
    
      if  (node.nodeType ===  1)  {
    
        let attr = node.attributes;
    
        for  (let i =  0; i < attr.length; i++)  {
    
          if  (attr[i].nodeName ===  'v-model')  {
    
            let name = attr[i].nodeValue;
    
            node.addEventListener('input', e =>  {
              vm[name]  = node.value;
            });
    
            node.value = vm[name];
    
            node.removeAttribute ('v-model');
    
          }
    
        }
    
      }
    
      // Text 节点类型
      if  (node.nodeType ===  3)  {
    
        if  (/\{\{(.*)\}\}/.test (node.nodeValue))  {
    
          let name =  RegExp.$1;
    
          name = name.trim();
    
          node.nodeValue = vm[name];
    
          new  Watcher(vm, node, name);
    
        }
    
      }
    
    };
    
    const observe = data =>  {
    
      if  (!data ||  typeof data !==  'object')  return;
    
      Object.keys(data).forEach (key => defineReactive(data, key, data[key]));
    
    };
    
    const defineReactive =  (data, key, value)  =>  {
    
      const dep =  new  Dep();
    
      observe (value);
    
      Object.defineProperty (data, key,  {
    
        get:  function  () {
    
          if(Dep.target) dep.addSub(Dep.target);
    
          return value;
    
        },
    
        set:  function  (newValue)  {
    
          console.log(数据已发生变化,新的值为${newValue});
    
          value = newValue;
    
          dep.notify();
    
        },
    
      });
    
    };
    
    function nodeToFragment (node, vm)  {
    
      let flag = document.createDocumentFragment();
    
      let child;
    
      while((child = node.firstChild))  {
    
        compile(child, vm);
    
        flag.appendChild(child); 
    
      }
    
      return flag;
    
    }
    
    function  Vue(options)  {
    
      let data =  this.data = options.data;
    
      observe(data,  this);
    
      let id = options.el;
    
      let dom = nodeToFragment(document.getElementById(id), data);
    
      document.getElementById(id).appendChild(dom);
    
    }
    
    let vm =  new  Vue({
      el:  'app',
      data:  {
        text:  'example text',
      },
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157

    ← 说一说递归组件 说一说vue中template模板的编译过程→

    最近更新
    01
    vuex数据持久化怎么做
    05-22
    02
    vue的动态路由怎么配置使用
    05-22
    03
    vue权限控制一般怎么做
    05-22
    更多文章>
    Theme by Vdoing | Copyright © 2022-2022 Guoquoqiqi | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式