首页 > 开发 > JS > 正文

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

2024-05-06 16:47:26
字体:
来源:转载
供稿:网友

在掘金上买了一个关于解读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的双向绑定和依赖收集遇到的坑,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对VeVb武林网网站的支持!


注:相关教程知识阅读请移步到JavaScript/Ajax教程频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表