在掘金上买了一个关于解读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) === -) {
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 =
// 第一次调用返回的是第一个子组件的从父组件继承的数据(vue中props属性的值)
// 第二次调用返回的是第二个子组件的从父组件继承的数据(vue中props属性的值)
function getProps () {
times++
if (times == ) { 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 == ) { 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的双向绑定和依赖收集的更多相关文章

  1. 【学习笔记】剖析MVVM框架,简单实现Vue数据双向绑定

    前言: 学习前端也有半年多了,个人的学习欲望还比较强烈,很喜欢那种新知识在自己的演练下一点点实现的过程.最近一直在学vue框架,像网上大佬说的,入门容易深究难.不管是跟着开发文档学还是视频教程,按步骤 ...

  2. 撸一个vue的双向绑定

    1.前言 说起双向绑定可能大家都会说:Vue内部通过Object.defineProperty方法属性拦截的方式,把data对象里每个数据的读写转化成getter/setter,当数据变化时通知视图更 ...

  3. vue的双向绑定原理及实现

    前言 使用vue也好有一段时间了,虽然对其双向绑定原理也有了解个大概,但也没好好探究下其原理实现,所以这次特意花了几晚时间查阅资料和阅读相关源码,自己也实现一个简单版vue的双向绑定版本,先上个成果图 ...

  4. Vue数据双向绑定原理及简单实现

    嘿,Goodgirl and GoodBoy,点进来了就看完点个赞再go. Vue这个框架就不简单介绍了,它最大的特性就是数据的双向绑定以及虚拟dom.核心就是用数据来驱动视图层的改变.先看一段代码. ...

  5. vue的双向绑定原理解析(vue项目重构二)

    现在的前端框架 如果没有个数据的双向/单向绑定,都不好意思说是一个新的框架,至于为什么需要这个功能,从jq或者原生js开始做项目的前端工作者,应该是深有体会. 以下也是个人对vue的双向绑定原理的一些 ...

  6. vue 之 双向绑定原理

    一.实现双向绑定 详细版: 前端MVVM实现双向数据绑定的做法大致有如下三种: 1.发布者-订阅者模式(backbone.js) 思路:使用自定义的data属性在HTML代码中指明绑定.所有绑定起来的 ...

  7. 探讨vue的双向绑定原理及实现

    1.vue的实现原理 vue的双向绑定是由数据劫持结合发布者-订阅者模式实现的,那么什么是数据劫持?vue是如何进行数据劫持的?说白了就是通过Object.defineProperty()来劫持对象属 ...

  8. 【Vue】vue的双向绑定原理及实现

    vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的,那么vue是如果进行数据劫持的,我们可以先来看一下通过控制台输出一个定义在vue初始化数据上的对象是个什么东西. 代码: var ...

  9. 【Vue源码学习】依赖收集

    前面我们学习了vue的响应式原理,我们知道了vue2底层是通过Object.defineProperty来实现数据响应式的,但是单有这个还不够,我们在data中定义的数据可能没有用于模版渲染,修改这些 ...

随机推荐

  1. 完整开发vue后台管理系统小结

    最近业余帮朋友做两个vue项目,一个是面向用户纯展示系列的(后统称A项目),一个是后端管理系统类的(后统称B项目).两者在技术上都没难度,这里对开发过程遇到的问题.取舍等做一个小节. 关于项目搭建 目 ...

  2. 解决ubuntu的screen已经处于Attached状态,无法再打开窗口

    Attached表示该窗口已经被上一登录用户使用,需要重新登录 1. 先查询窗口id screen -ls 3606.node (11/26/2019 07:31:49 PM) (Attached) ...

  3. Codeforces Round #346 (Div. 2) B题

    B. Qualifying Contest Very soon Berland will hold a School Team Programming Olympiad. From each of t ...

  4. window上git bash运行错误记录

    错误现象:每次启动git bash报出如下错误gitbash  0 [main] bash 11928 fork: child -1 - CreateProcessW failed for 'D:\P ...

  5. 洛谷P2796 Facer的程序

    洛谷题目链接 动态规划 我们看题目后知道这是一棵无根树,要求出有多少子树 我们设$f[u][1]$表示选了当前节点$u$的方案数 相反的$f[u][0]$则为不选中$u$ 那么考虑状态转移如下: f[ ...

  6. Linux之GDB命令(二)

    gdb命令: 前提条件:可执行文件必须包含调试信息 gcc -g gdb 文件名 –启动gdb调试 查看代码命令   当前文件:     list 行号(函数名)   指定文件:     list 文 ...

  7. js反混淆

    var esprima = require('esprima') var escodegen = require('escodegen') content = "function _0x35 ...

  8. Content:"\2715",特殊字符和图标

    原文 项目中用到的一些特殊字符和图标 html代码 <div class="cross"></div> css代码 1 2 3 4 5 6 7 8 9 10 ...

  9. Linux Centos安装宝塔面板教程

    材料:阿里云服务器远程ip地址和购买服务的账号和密码 版本Centos 6.9版本 用Xshell远程登录安装宝塔 Xshell的界面是这样的,如图 点击左上角新建回话可以创建一个新的SSH连接,如图 ...

  10. docker安装并设置开机启动(CentOS7/8)

    CentOS7.2 docker分为CE和EE版本,EE版本收费,一般我们使用CE版本就满足要求了 docker安装及启动 docker安装很简单,直接使用如下命令安装即可,安装后的docker版本即 ...