vue实现双向绑定和依赖收集遇到的坑

在掘金上买了一个关于解读vue源码的小册,因为是付费的,所以还比较放心

在小册里看到了关于vue双向绑定和依赖收集的部分,总感觉有些怪怪的,然后就自己跟着敲了一遍。 敲完后,发现完全无法运行,  坑啊,  写书人完全没有测试过。

然后自己完善代码, 越写越发现坑, 问题有些大。。。。。。

最后自己重新实现了一遍,代码较多。 用到观察订阅者模式实现依赖收集, Object.defineProperty() 实现双向绑定

/*
  自己写的代码, 实现vue的双向绑定和依赖收集
  场景: 多个子组件用到父组件data中的数据, 当父组件data中的此数据发生改变时,
  所有依赖它的 子组件全部更新
  通常子组件的从父组件中拿取的数据不允许发生改变
*/
  //订阅者 Dep
  //一个订阅者只管理一个数据
  class Dep {
    constructor () {
      this.subs = []  //存放vue组件
    }
    addSubs (sub) {
      this.subs.push(sub)
      console.log('add watcher: ', sub._name)
    }
    notify () {
      this.subs.forEach( sub => {  //通知vue组件更新
        sub.update()
      })
    }
  }
  //监听者
  //一个vue实例包含一个Watcher实例
  class Watcher {
    // 在实例化Watcher时, 将Dep的target指向此实例, 在依赖收集中使用
    // 因为依赖收集是在组件初始化时触发的, 而数据变更后视图相应变更是在初始化后
    // 所以让Dep.target指向此实例, 当此vue实例初始化完成后, 再指向下一个正在初始化的vue实例完成依赖收集
    constructor (name) {
      Dep.target = this
      this._name = name
    }
    update () {
      // 这里模拟视图更新
      // 其实还应该让子组件的props相应值与父组件更新的数据同步
      console.log("子组件视图更新了..." + this._name)
    }
  }
  //对data中的数据设置读写监听, 并且创建订阅者, 用于收集子组件的依赖和发布
  function defineReactive (obj, key, value) {
    // 对vue实例中data对象的每一个属性都 设置一个订阅者Dep
    let dep = new Dep()
    // 第二个vue实例的监听 覆盖了第一个vue实例的监听, 因为引用的obj是同一个
    Object.defineProperty(obj, key, {
      configurable: true,
      enumerable: true,
      get () {
      // 在读此属性时, 将当前 watcher 对象收集到此属性的 dep 对象中
      // 在实例化vue时将Dep.target指向当前Watcher
      // get()依赖收集的时候是vue组件初始化的时候, set()是在初始化后
        if (dep.subs.indexOf(Dep.target) === -1) {
          dep.addSubs(Dep.target)
        }
        //return obj[key]   此写法报错 提示栈溢出 原因是无限调用get()
        return value
      },
      set (newVal) {  // 此属性改变时, 通知所有视图更新
        if (newVal !== value) {
          value = newVal
          dep.notify()
        }
      }
    })
  }
  //接收一个对象作为参数, 将该对象的所有属性调用defineReactive设置读写监听
  function observer (obj) {
    if (!obj || (typeof obj !== 'object')) {
      return
    }
    Object.keys(obj).forEach( key => {
      defineReactive(obj, key, obj[key])
    })
  }
  // 构造函数, 监听 配置options中的data()方法返回的对象的所有属性 的读写
  class Vue {
    constructor (options) {
      this._name = options.name
      this._data = options.data
      // 每个vue组件都是一个vue实例, 在一个页面中有多个vue实例
      // 在初始化该vue实例时, new一个Watcher对象, 使Dep.target指向此实例
      new Watcher(options.name)
      // 给data中的数据挂载读写监听
      observer(this._data)
      //模拟vue解析template过程, 获取从父组件传递过来的props
      //在这里进行依赖收集
      this._props = options.props ? getProps() : {}
      // 实例化该组件的子组件
      this._children = options.render ? (options.render() || {}) : {}
    }
  }
  // 父组件数据
  let data = {
    first: "hello",
    second: 'world',
    third: ['啦啦啦']
  }
  let times = 0
  // 第一次调用返回的是第一个子组件的从父组件继承的数据(vue中props属性的值)
  // 第二次调用返回的是第二个子组件的从父组件继承的数据(vue中props属性的值)
  function getProps () {
    times++
    if (times == 1) {
      let obj = {first: "", second: ""}
      Object.keys(obj).forEach( key => {
        // 如果是对象, 则进行深拷贝
        // 这里使用到了父组件的数据, 触发依赖收集
        if (data[key] instanceof Object) {
          obj[key] = JSON.parse(JSON.stringify(data[key]))
        } else {
          obj[key] = data[key]
        }
      })
      return obj
    } else if (times == 2) {
      let obj = {first: "", third: ""}
      Object.keys(obj).forEach( key => {
        if (data[key] instanceof Object) {
          obj[key] = JSON.parse(JSON.stringify(data[key]))
        } else {
          obj[key] = data[key]
        }
      })
      return obj
    }
  }
   let vue_root = new Vue({
     name: 'vue_root',
     data,
     //模拟编译template和实例化vue的过程
     //在编译父组件 并且传递参数给子组件时, 将子组件的 watcher 添加进父组件的 dep
     render () {
       let vue_1 = new Vue({
         name: 'vue_1',
         data: {},
         props: true,
         render () {}
       })
       let vue_2 = new Vue({
         name: 'vue_2',
         data: {},
         props: true,
         render () {}
       })
       return {
         vue_1,
         vue_2
       }
     }
   })
  console.log(vue_root)
   vue_root._data.first = 'hello hello'  // vue_1 和 Vue_2 都依赖此数据, 都更新
   vue_root._data.third = "aaa"      // 只有 vue_2 依赖到了此数据, 更新

总结

以上所述是小编给大家介绍的vue的双向绑定和依赖收集遇到的坑,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Vue 实现双向绑定的四种方法

    1. v-model 指令 <input v-model="text" /> 上例不过是一个语法糖,展开来是: <input :value="text" @input="e => text = e.target.value" /> 2. .sync 修饰符 <my-dialog :visible.sync="dialogVisible" /> 这也是一个语法糖,剥开来是: <my

  • 详解Vue实战指南之依赖注入(provide/inject)

    案例 UI美眉说咱家的选项菜单太丑了,小哥哥能不能美化一下呀,洒家自然是说小意思啦~ 自定义一个select组件,so easy~ 简单粗暴型: <el-select v-model="favourite" :option="[]"></el-select> option作为数据进来就ok啦. 然后发现下列问题: key-value,不是所有的接口都是id-name option要disabled 怎么办? option存在几种情况怎么办?

  • 详解VueJS 数据驱动和依赖追踪分析

    之前关于 Vue 数据绑定原理的一点分析,最近需要回顾,就顺便发到随笔上了 在之前实现一个自己的Mvvm中,用 setter 来观测model,将界面上所有的 viewModel 绑定到 model 上. 当model改变,更新所有的viewModel,将新值渲染到界面上 .同时监听界面上通过v-model 绑定的所有 input,并通过 addEventListener事件将新值更新到 model 上,以此来完成双向绑定 . 但是那段程序除了用来理解 defineProperty,其它一文不值

  • Vue 2.0的数据依赖实现原理代码简析

    首先让我们从最简单的一个实例Vue入手: const app = new Vue({ // options 传入一个选项obj.这个obj即对于这个vue实例的初始化 }) 通过查阅文档,我们可以知道这个options可以接受: 选项/数据 data props propsData(方便测试使用) computed methods watch 选项 / DOM 选项 / 生命周期钩子 选项 / 资源 选项 / 杂项 具体未展开的内容请自行查阅相关文档,接下来让我们来看看传入的选项/数据是如何管理

  • 详解Vue 事件驱动和依赖追踪

    之前关于 Vue 数据绑定原理的一点分析,最近需要回顾,就顺便发到随笔上了 在之前实现一个自己的Mvvm中,用 setter 来观测model,将界面上所有的 viewModel 绑定到 model 上. 当model改变,更新所有的viewModel,将新值渲染到界面上 .同时监听界面上通过v-model 绑定的所有 input,并通过 addEventListener事件将新值更新到 model 上,以此来完成双向绑定 . 但是那段程序除了用来理解 defineProperty,其它一文不值

  • vue实现双向绑定和依赖收集遇到的坑

    在掘金上买了一个关于解读vue源码的小册,因为是付费的,所以还比较放心 在小册里看到了关于vue双向绑定和依赖收集的部分,总感觉有些怪怪的,然后就自己跟着敲了一遍. 敲完后,发现完全无法运行,  坑啊,  写书人完全没有测试过. 然后自己完善代码, 越写越发现坑, 问题有些大...... 最后自己重新实现了一遍,代码较多. 用到观察订阅者模式实现依赖收集, Object.defineProperty() 实现双向绑定 /* 自己写的代码, 实现vue的双向绑定和依赖收集 场景: 多个子组件用到父

  • vue MVVM双向绑定实例详解(数据劫持+发布者-订阅者模式)

    目录 实现过程 1.实现一个Observer 2.实现Watcher 3.实现Compile 总结 参考文献:https://www.jb51.net/article/160654.htm https://www.jb51.net/article/239554.htm MVVM拆开来即为Model-View-ViewModel,有View,ViewModel,Model三部分组成.View层代表的是视图.模版,负责将数据模型转化为UI展现出来.Model层代表的是模型.数据,可以在Model层中

  • 详解vue的双向绑定原理及实现

    前言 使用vue也好有一段时间了,虽然对其双向绑定原理也有了解个大概,但也没好好探究下其原理实现,所以这次特意花了几晚时间查阅资料和阅读相关源码,自己也实现一个简单版vue的双向绑定版本,先上个成果图来吸引各位: 代码: 效果图: 是不是看起来跟vue的使用方式差不多?接下来就来从原理到实现,从简到难一步一步来实现这个SelfVue.由于本文只是为了学习和分享,所以只是简单实现下原理,并没有考虑太多情况和设计,如果大家有什么建议,欢迎提出来. 本文主要介绍两大内容: 1. vue数据双向绑定的原

  • Vue数据双向绑定原理及简单实现方法

    Vue这个框架就不简单介绍了,它最大的特性就是数据的双向绑定以及虚拟dom.核心就是用数据来驱动视图层的改变.先看一段代码. 一.示例 var vm = new Vue({ data: { obj: { a: 1 } }, created: function () { console.log(this.obj); } }); 二.实现原理 vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的. 1)数据劫持.vue是通过Object.defineProperty()来实现数据劫持

  • Vue数据双向绑定原理实例解析

    Vue数据双向绑定原理是通过数据劫持结合发布者-订阅者模式的方式来实现的,首先是对数据进行监听,然后当监听的属性发生变化时则告诉订阅者是否要更新,若更新就会执行对应的更新函数从而更新视图 MVC模式 以往的MVC模式是单向绑定,即Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新 MVVM模式 MVVM模式就是Model–View–ViewModel模式.它实现了View的变动,自动反映在 ViewModel,反之亦然.对于双向绑定的理解,就是用户更

  • AngularJS双向绑定和依赖反转实例详解

    AngularJS双向绑定和依赖反转 一.双向绑定: UI<-->数据 数据->UI (数据改变UI跟着变) UI->数据 (UI改变数据跟着变) 数据改变->UI改变原理: 监听数据是否改变,如果改变更新UI数据. UI改变->数据改变原理: <html> <body> <input type="text" name="name" value="" id="text1&

  • Vue.js双向绑定实现原理详解

    Vue.js最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统.本文仅探究几乎所有Vue的开篇介绍都会提到的hello world双向绑定是怎样实现的.先讲涉及的知识点,再参考源码,用尽可能少的代码实现那个hello world开篇示例. 参考文章:http://www.jb51.net/article/100819.htm 一.访问器属性 访问器属性是对象中的一种特殊属性,它不能直接在对象中设置,而必须通过defineProperty()方法单独定义. var obj = { }; /

  • Angular和Vue双向数据绑定的实现原理(重点是vue的双向绑定)

    我在整理javascript高级程序设计的笔记的时候看到面向对象设计那章,讲到对象属性分为数据属性和访问器属性,我们平时用的js对象90%以上都只是用到数据属性;我们向来讲解下数据属性和访问器属性到底是什么? 数据属性:数据属性包含一个数据值的位置,在这个位置可以读取和写入值. 访问器属性:访问器属性不包含数据值;他们包含一对getter和setter函数在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值,在写入访问器属性时,会调用setter函数并传入新值. 这里介绍的重点是

  • 深入理解vue.js双向绑定的实现原理

    前言 大家都知道Vue.js最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统.本文仅探究几乎所有Vue的开篇介绍都会提到的hello world双向绑定是怎样实现的.先讲涉及的知识点,再参考源码,用尽可能少的代码实现那个hello world开篇示例. 一.访问器属性 访问器属性是对象中的一种特殊属性,它不能直接在对象中设置,而必须通过defineProperty()方法单独定义. var obj = { }; // 为obj定义一个名为hello的访问器属性 Object.defin

  • Vue.js双向绑定操作技巧(初级入门)

    首先在页面引入vue.js以及其他需要用到的或者可能要用到的插件(这里我多引用了bootstrap和jquery) 引用的时候需要注意文件的路径,准备工作这样基本就完成了,下面正式开始入门. vue.js最重要的一个特点就是双向数据绑定也就是我们常说的MVVM:Model-View-ViewModel.我们要实现双向绑定首先当然要有"双向",这里vue.js为我们提供了View层和Model层.View层就是在HTML中的代码,Model层则是Javascript代码. 下面是一个最基

随机推荐