精华内容
下载资源
问答
  • 主要介绍了Vue响应式原理-理解Observer、Dep、Watcher,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • vue响应式原理

    2021-09-07 15:02:25
    vue响应式原理 vue响应式也叫作数据双向绑定,大致原理阐述: ​ 首先我们需要通过Object.defineProperty()方法把数据(data)设置为getter和setter的访问形式,这样我们就可以在数据被修改时在setter方法设置监视...

    vue响应式原理
    vue响应式也叫作数据双向绑定,大致原理阐述:

    ​ 首先我们需要通过Object.defineProperty()方法把数据(data)设置为getter和setter的访问形式,这样我们就可以在数据被修改时在setter方法设置监视修改页面信息,也就是说每当数据被修改,就会触发对应的set方法,然后我们可以在set方法中去调用操作dom的方法。

    ​ 此外,如果页面有input用v-model绑定数据,我们需要在这种绑定了data的input元素上添加监听,添加input事件监听,每当input事件被触发时,就修改对应的data。

    vue实现数据响应式,是通过数据劫持侦测数据变化,发布订阅模式进行依赖收集与视图更新,换句话说是Observe,Watcher以及Compile三者相互配合,

    • Observe实现数据劫持,递归给对象属性,绑定setter和getter函数,属性改变时,通知订阅者
    • Compile解析模板,把模板中变量换成数据,绑定更新函数,添加订阅者,收到通知就执行更新函数
    • Watcher作为Observe和Compile中间的桥梁,订阅Observe属性变化的消息,触发Compile更新函数

     1.什么是响应式

    当price 发生变化的时候,Vue就知道自己需要做三件事情:

    • 更新页面上price的值

    • 计算表达式 price*quantity 的值,更新页面

    • 调用totalPriceWithTax 函数,更新页面

    数据发生变化后,会重新对页面渲染,这就是Vue响应式,那么这一切是怎么做到的呢?

    想完成这个过程,我们需要:

    • 侦测数据的变化

    • 收集视图依赖了哪些数据

    • 数据变化时,自动“通知”需要更新的视图部分,并进行更新

    对应专业俗语分别是:

    • 数据劫持 / 数据代理

    • 依赖收集

    • 发布订阅模式

    如何侦测数据的变化 

    ​​​​​​​首先有个问题,在Javascript中,如何侦测一个对象的变化?其实有两种办法可以侦测到变化:使用 Object.defineProperty和ES6的 Proxy,这就是进行数据劫持或数据代理。

    方法1.Object.defineProperty

    Vue通过设定对象属性的 setter/getter 方法来监听数据的变化,通过getter进行依赖收集,而每个setter方法就是一个观察者,在数据变更的时候通知订阅者更新视图。

    方法2.Proxy

    Proxy 是 JavaScript 2015 的一个新特性。 Proxy 的代理是针对整个对象的,而不是对象的某个属性,因此不同于 Object.defineProperty 的必须遍历对象每个属性, Proxy 只需要做一层代理就可以监听同级结构下的所有属性变化,当然对于深层结构,递归还是需要进行的。此外 Proxy支持代理数组的变化。

    为什么要收集依赖

    我们之所以要观察数据,其目的在于当数据的属性发生变化时,可以通知那些曾经使用了该数据的地方。比如第一例子中,模板中使用了price 数据,当它发生变化时,要向使用了它的地方发送通知。 

    订阅者 Dep

    1.为什么引入 Dep

    收集依赖需要为依赖找一个存储依赖的地方,为此我们创建了Dep,它用来收集依赖、删除依赖和向依赖发送消息等。

    于是我们先来实现一个订阅者 Dep 类,用于解耦属性的依赖收集和派发更新操作,说得具体点,它的主要作用是用来存放 Watcher 观察者对象。我们可以把Watcher理解成一个中介的角色,数据发生变化时通知它,然后它再通知其他地方。

    观察者 Watcher

    1.为什么引入Watcher

    Vue 中定义一个 Watcher 类来表示观察订阅依赖。至于为啥引入Watcher,《深入浅出vue.js》给出了很好的解释:

    当属性发生变化后,我们要通知用到数据的地方,而使用这个数据的地方有很多,而且类型还不一样,既有可能是模板,也有可能是用户写的一个watch,这时需要抽象出一个能集中处理这些情况的类。然后,我们在依赖收集阶段只收集这个封装好的类的实例进来,通知也只通知它一个,再由它负责通知其他地方。

    依赖收集的目的是将观察者 Watcher 对象存放到当前闭包中的订阅者 Dep 的 subs 中,形成如下所示的这样一个关系(图参考《剖析 Vue.js 内部运行机制》)。

    640?wx_fmt=other

     收集依赖

    所谓的依赖,其实就是Watcher。至于如何收集依赖,总结起来就一句话,在getter中收集依赖,在setter中触发依赖。先收集依赖,即把用到该数据的地方收集起来,然后等属性发生变化时,把之前收集好的依赖循环触发一遍就行了。

    具体来说,当外界通过Watcher读取数据时,便会触发getter从而将Watcher添加到依赖中,哪个Watcher触发了getter,就把哪个Watcher收集到Dep中。当数据发生变化时,会循环依赖列表,把所有的Watcher都通知一遍。

    最后我们对 defineReactive 函数进行改造,在自定义函数中添加依赖收集和派发更新相关的代码,实现了一个简易的数据响应式。


    • 总结: 

       

      640?wx_fmt=other

    • 在 newVue() 后, Vue 会调用 _init 函数进行初始化,也就是init 过程,在 这个过程Data通过Observer转换成了getter/setter的形式,来对数据追踪变化,当被设置的对象被读取的时候会执行 getter 函数,而在当被赋值的时候会执行 setter函数。

    • 当render function 执行的时候,因为会读取所需对象的值,所以会触发getter函数从而将Watcher添加到依赖中进行依赖收集。

    • 在修改对象的值的时候,会触发对应的 setter, setter通知之前依赖收集得到的 Dep 中的每一个 Watcher,告诉它们自己的值改变了,需要重新渲染视图。这时候这些 Watcher就会开始调用 update 来更新视图。

    展开全文
  • Vue响应式原理

    2021-01-07 13:48:56
    Vue响应式原理 // 源对象 let data = { name: hellow, pwd: world } // 委托对象 let $data = {}; Object.keys(data).forEach(key => { def($data, key); $data[key] = data[key]; }); // 监听属性 ...
  • 主要介绍了vue响应式原理的的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
  • coderwhy Vue面试题-vue响应式原理笔记

    Vue的响应式原理

    • 数据发生改变,界面跟着更新,并不是理所当然的,Vue内部是做了很多封装的

    依赖技术的分析和学习

    • 首先,来看一个最简单的Vue响应式例子:
     <div id="app">
            {{message}}
     </div>
       <script src='./js/vue.js'></script>
        <script>
            const app = new Vue({
                el: '#app',
                data: {
                    message: '哈哈哈哈',
                    name: 'coderYYY'
                }
            })
        </script>
    
    • 分析
      • 1.app.message修改数据,Vue内部是如何监听message数据的改变?
        • Vue2 -> object.defineProperty -> 监听对象属性的改变
        • Vue3 -> Proxy
      • 2.当数据发生改变,Vue是如何知道要通知哪些人,界面发生刷新?
        • 发布者订阅者模式
        const obj = {
                message: '哈哈哈哈',
                name: 'yyy'
            }
            // 1.监听值的改变 -> Object.defineProperty(属性所在的对象,要添加或者修改的对象属性,属性描述符)
            Object.keys(obj).forEach(key => {
                let value = obj[key]
    
                Object.defineProperty(obj, key, {
                    set(newValue) {
                        console.log('监听' + key + '改变');
                        // 告诉谁?谁用告诉谁?谁在用?
                        // 根据解析html代码,获取到哪些人有用属性
                        // 张三/李四/王五 在用
                        value = newValue
                        // dep.notify()// 通知
                    },
                    get() {
                        // 谁用一次就会调用一次get
                        // 张三: get -> update
                        // 李四: get -> update
                        // 王五: get -> update
                        console.log('获取' + key + '对应的值');
                        return value
                    }
                })
            })
            // 2.发布者订阅者模式
            // 发布者
            class Dep { // 存储所有对属性有依赖的东西
                constructor() {
                    this.subscribe = [] // subscribe 订阅 这个数组记录谁要订阅属性
                }
                addSub(watcher) {
                    this.subscribe.push(watcher)
                }
                notify(){
                    this.subscribe.forEach(item=>{
                        item.update()
                    })
                }
            }
            const dep = new Dep()
            // 订阅者
            class Watcher {
                constructor(name) {
                    this.name = name;
                }
                update() {
                    console.log(this.name + '发生update');
                }
            }
            const w1 = new Watcher('张三')
            dep.addSub(w1)
            const w2 = new Watcher('李四')
            dep.addSub(w2)
            const w3 = new Watcher('王五')
            dep.addSub(w3)
            dep.notify()
    

    在这里插入图片描述
    在这里插入图片描述

    通过图解理解过程

    在这里插入图片描述

    展开全文
  • 浅谈Vue响应式原理

    千次阅读 2021-02-02 21:22:39
    二、响应式的基本原理 1.通过Object.definePropert来实现监听数据的改变和读取(属性中的getter和setter方法) 实现数据劫持 2.观察者模式(发布者-订阅者) 观察者(订阅者) – Watcher: update():当事件发生时,...

    一、Vue中的响应式

    Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。

    二、响应式的基本原理

    1.通过Object.defineProperty来实现监听数据的改变和读取(属性中的getter和setter方法) 实现数据劫持

    2.观察者模式(发布者-订阅者)
    观察者(订阅者) – Watcher:
    update():当事件发生时,具体要做的事情

    目标(发布者) – Dep:
    ①subs 数组:存储所有的观察者
    ②addSub():添加观察者
    ③notify():当事件发生,调用所有观察者的 update() 方法

    3.当数据发生改变通过发布者订阅者模式来进行通知 进行界面刷新

    三、响应过程

    首先要对数据(data)进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器(发布者)Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。
    在这里插入图片描述

    Vue中的data中的每个属性都会被创建一个Dep对象,且解析el时进行视图的初始化如果html中有多个地方用到该属性,则每个地方都会将会生成一个Watcher的实例被放入到该属性对应Dep的实例中的subs数组中。当属性发生改变时,Observe监听到属性的改变,然后调用该属性对应的Dep实例的notify方法,然后notify方法会对Dep实例中的数组进行遍历然后同时调用遍历出的Watcher的实例进行update方法的调用进行视图的更新。伪代码如下:

        const obj = {}
        Object.keys(obj).forEach(key => {
          let value = obj[key]
          Object.defineProperty(obj, key, {
            get() {
              console.log('试图读取obj的a属性')
            },
            set(newValue) {
              console.log('试图改变obj的a属性')
              //属性变化时进行对Watcher实例进行通知
              dep.notify()
            }
          })
        })
    	//发布者
        class Dep{
          constructor(){
            this.subs = []
          }
          addSub(watch){
            this.subs.push(watch)
          }
          //属性变化发送通知函数
          notify(){
          //接受到通知后 调用update函数进行视图的更新
            this.subs.forEach(item => {
              item.update()
            })
          }
        }
        //订阅者
        class Watcher {
          constructor(name){
            this.name =name
          }
          update(){
            console.log(this.name+'发生update');
          }
        }
        //模拟创建一个发布者
        const dep = new Dep()
        //模拟创建一个订阅者
        const w1 = new Watcher('第一个实例')
        //将订阅者添加到发布者中subs的数组里进行管理
        dep.addSub(w1)
        const w2 = new Watcher('第二个实例')
        dep.addSub(w2)
        const w3 = new Watcher('第三个实例')
        dep.addSub(w3)
    

    因此我们可以执行以下3个步骤,实现数据的双向绑定:
    1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
    2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
    3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。

    参考:https://www.cnblogs.com/canfoo/p/6891868.html

    展开全文
  • Vue响应式原理之defineReactive defineReactive 不论如何,最终响应式数据都要通过defineReactive来实现,实际要借助ES5新增的Object.defineProperty。 defineReactive接受五个参数。obj是要添加响应式数据的对象;...

    Vue响应式原理之defineReactive

    defineReactive

    不论如何,最终响应式数据都要通过defineReactive来实现,实际要借助ES5新增的Object.defineProperty

    defineReactive接受五个参数。obj是要添加响应式数据的对象;key是属性名,val是属性名对应的取值;customSetter是用户自定义的setter;会在响应式数据的setter中执行,只有开发环境可用;通过shallow指定是否浅比较,默认深比较。

    
    export function defineReactive (
      obj: Object,
      key: string,
      val: any,
      customSetter?: ?Function,
      shallow?: boolean
    ) {
      const dep = new Dep()
    
      const property = Object.getOwnPropertyDescriptor(obj, key)
      if (property &amp;&amp; property.configurable === false) {
        return
      }
    
      const getter = property &amp;&amp; property.get
      if (!getter &amp;&amp; arguments.length === 2) {
        val = obj[key]
      }
      const setter = property &amp;&amp; property.set
    
      let childOb = !shallow &amp;&amp; observe(val)
      Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          const value = getter ? getter.call(obj) : val
          if (Dep.target) {
            dep.depend()
            if (childOb) {
              childOb.dep.depend()
              if (Array.isArray(value)) {
                dependArray(value)
              }
            }
          }
          return value
        },
        set: function reactiveSetter (newVal) {
          const value = getter ? getter.call(obj) : val
          /* eslint-disable no-self-compare */
          if (newVal === value || (newVal !== newVal &amp;&amp; value !== value)) {
            return
          }
          /* eslint-enable no-self-compare */
          if (process.env.NODE_ENV !== 'production' &amp;&amp; customSetter) {
            customSetter()
          }
          if (setter) {
            setter.call(obj, newVal)
          } else {
            val = newVal
          }
          childOb = !shallow &amp;&amp; observe(newVal)
          dep.notify()
        }
      })
    }
    

    在函数内,首先实例化一个Dep实例depdep会在稍后添加为响应式数据自定义的get/set中发挥作用。接着获取属性描述符,如果属性不可配置,则无法调用Object.defineProperty来修改setter/getter,所以返回。

    如果原来已设置过setter/getter,缓存起来。当未自定义getter且arguments长度为2(即只传入了objkey)时,可以直接用方括号求值,使用闭包变量val缓存初始值。

    如果不是浅复制,执行observe(val),为val添加__ob__属性并返回__ob__指向的Observer实例。(只有数组和对象才可能是响应式,才能返回Observer实例)。

    使用Object.definePropertyobj[key]设置getter和setter。

    get内,如果原来已设置过getter,则用缓存的getter求值,否则使用闭包变量val作为返回值;同时添加依赖。此处为两个Dep实例添加依赖。dep是闭包变量,在getter/setter中会使用到。另一个Dep实例是childOb.dep,只用调用set/delete更新响应式数据时,才会触发;如果value是数组,还会遍历元素,为存在__ob__属性的元素收集依赖。

    set内,先获取更新前的值(逻辑和get内第一步一样)。判断更新前后的值是否相等,相等时直接返回;不等时,如果有缓存的setter,调用缓存的setter更新,否则直接赋值。值得注意的是,NaN === NaN是不成立的,反而NaN !== NaN是成立的,后面的判断语句newVal !== newVal && value !== value就是为了避免newVal/val都是NaN。在更新后的值newVal上执行observe,更新闭包变量childOb,并调用notify。

    参考链接

    原文地址:https://segmentfault.com/a/1190000017216175

    展开全文
  • Vue响应式原理详解

    2020-11-29 18:10:19
    Vue 嘴显著的特性之一便是响应式系统(reactivity system),模型层(model)只是普通JavaScript对象,修改它则更新视图(view)。 Vue 响应式系统的底层细节 如何追踪变化 把一个普通的JavaScript对象传给Vue实例的...
  • vue响应式原理概述

    2021-04-21 15:00:55
    Vue 是怎么知道数据改变?(Object.defineProperty) Vue 在数据改变时,怎么知道通知哪些视图更新?(依赖收集) Vue 在数据改变时,视图怎么知道什么时候更新?(依赖更新)
  • 简述vue响应式原理

    2021-08-09 11:22:37
    响应式数据的最终目标,是当对象本身或对象属性发生变化时,将会运行一些函数,最常见的就是render函数。 在具体实现上,vue用到了几个核心部件: Observer Dep Watcher Scheduler Observer Observer要实现的目标...
  • Vue 内部使用了 Object.defineProperty() 来实现数据响应式,通过这个函数可以监听到 set 和 get 的事件。 var data = { name: 'yck' } observe(data) let name = data.name // -> get value data.name = 'yyy' /...
  • Vue源码--解读vue响应式原理

    千次阅读 2018-05-02 20:40:10
    原文链接:https://geniuspeng.github.io/2018/01/05/vue-reactivity/Vue的官方说明里有深入响应式原理这一节。在此官方也提到过:当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象...
  • vue响应式原理理解

    2019-11-09 10:53:52
    要理解响应式原理,首先要理解两个问题,数据改变后,Vue是如何监听的,要通知哪些人然后进行界面的刷新 首先来理解一下下面这这个监听数值改变与监听获取数据的函数,首先创建一个对象,然后这个对象就是用来被...
  • vue响应式原理的实现

    2020-04-20 23:01:19
    Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象...Vue的数据双向绑定,响应式原理,其实就是通过Object.defineProperty()结合发布者订阅者模式来实现的。 Observer 通过O...
  • 模拟 Vue 响应式原理。 准备工作 数据驱动 数据响应式 数据模型仅仅是普通的 JavaScript 对象,而当我们修改数据时,视图会进行更新,避免了繁琐的 DOM 操作,提高开发效率。 双向绑定 数据改变,视图改变;...
  • vue响应式原理.pdf

    2020-04-19 11:13:33
    响应式原理:在读取属性的时候收集依赖,在改变属性值的时候触发依赖的更新 实现一个observer,劫持对象属性的getter和setter 实现一个全局的订阅器Dep,可以追加订阅者,和通知依赖更新 在读取属性的时候追加当前...
  • vue响应式原理(数据双向绑定的原理)

    万次阅读 多人点赞 2018-04-01 09:35:49
    1、渐进框架 下面是摘自知乎的一个解答(个人认为讲述比较好的回答): 在我看来,渐进代表的含义是:主张最少。 每个框架都不可避免会有自己的一些特点,从而会对使用者有一定的要求,这些要求就是主张,主张...
  • vue 响应式原理

    2020-08-27 21:14:15
    Dep.target 全局变量指向的就是当前正在解析指令的Complie生成的 Watcher Compile是HTML指令解析器,对每个元素节点的指令进行扫描和解析,根据指令... 详解Vue响应式原理 Vue源码解读之Dep,Observer和Watcher ...
  • 我们都知道,vue 最独特的特性之一,是非侵入性的响应式系统,数据模型仅仅是普通的 JavaScript 对象,而当我们修改它们时,视图会进行更新,其实就是让组件重新渲染。那么 vue 是怎样追踪到数据的变化并重新渲染...
  • Vue 最显著的一个功能是响应系统 —— 模型只是普通对象,修改它则更新视图。下面这篇文章主要给大家深入讲解了关于Vue的响应式原理,以及Vue响应式的一些注意事项,需要的朋友下面随着小编来一起学习学习吧。
  • 本文介绍 Vue 响应式原理的实现过程,并试图以之为参照改造出一个便于移植的库。这里笔者把 Vue 的 observer 部分提出来独立地讲,读者不需要对 Vue 其他部分十分熟悉。 Vue 的响应式模型十分完善,实现地足够巧妙,...
  • 学过 vue 如果不了解响应式原理,怎么能说自己熟练使用 vue,要是没有写过一个简易版的 vue 怎么能说自己精通vue,这篇文章通过300多行代码,带你写一个简易版的 vue,主要实现 vue 数据响应式 (数据劫持结合发布...
  • 前端面试题:vue响应式原理 Vdom diff

    千次阅读 2020-08-22 16:30:14
    vue响应式原理,也算是面试中再常见不过的题目了,之前遇见这道题目只会说:利用的是Object.defineProperty进行的数据劫持,监听数据的变化,通知watcher进行的数据更新。总的来说这是没错的,但是只要面试官...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 20,817
精华内容 8,326
关键字:

vue响应式原理

vue 订阅