废话:上一篇https://www.cnblogs.com/adouwt/p/9928278.html

提到了vue实现的基本实现原理:Object.defineProperty() -数据劫持 和 发布订阅者模式(观察者),下面讲的就是数据劫持在代码中的具体实现。

1.先看如何调用

  

new一个对象,传入我们的参数,这个Myvue ,做了啥?

  

上面看到了在实例化一个Myvue 对象的时候,会执行init方法, init 方法做了两个事,调用了observer 方法,和 实例化调用了 compile 方法。 到这里我们就明白了,实例化一个Myvue后,我们要做的就是监听数据变化和编译模板 。

上面Object.key() 方法,实例化时传入的data里面对应的变量缓存到 Myvue 对象的 $prop上,这样方便在后续处理数据。怎么个方便法呢!...

2.observer 的实现

  observer ,模式里面的角色定位 他是一个发布者,也可以理解为是一个观察者

function observer (data) {
if(!data || typeof data !== 'object') {
return;
}
Object.keys(data).forEach(key => {
// 对每个属性监听处理
defineReactive(data, key, data[key]);
})
}

  defineReactive

function defineReactive (data,key,value) {
// 每次访问/修改属性的时候 实例化一个调度中心Dep
var dep = new Dep();
Object.defineProperty(data,key,{
get: function() {
// 添加到watcher 的Dep 调度中心
if (Dep.target) { // Dep.target 是个什么鬼? 转到watcher.js 它是某个订阅者 watcher
dep.addSub(Dep.target); //这个代码段的意思: 如果有订阅者(访问/修改属性的时候) 就将这个订阅者统一放进 Dep 调度中心中
}
// console.log(`${key}属性被访问了`)
return value
},
set: function (newValue) {
if (value != newValue) {
// console.log(`${key}属性被重置了`)
value = newValue
dep.notify(); //我这里有做改动了,通知调度中心的notify方法
}
}
})
// 递归调用,observe 这个value
observer(value)
}

  Dep: 这里是所有订阅者的一个调度中心,它不是直接监听 发布者的信息,发布者将要发布的信息 发布到 一个中介、调度中心(Dep),由这个Dep 来调度信息给哪个订阅者(Watcher)

// 统一管理watcher订阅者的Dep (调度中心)  Dispatch center
function Dep () {
// 所有的watcher 放进这里统一管理
this.subs = []
}
Dep.target = null;
// 通知视图更新dom的 notify的方法
Dep.prototype.notify = function () {
// this.subs 是上面订阅器watcher 的集合
this.subs.forEach(sub => {
// sub 是某个Watcher 具体调用某个Watcher的update 方法
sub.update()
})
} // 添加订阅者的方法
Dep.prototype.addSub = function (sub) {
this.subs.push(sub)
}

3.订阅器Watcher

// 具体的订阅器Watcher
// 传入一个vue 的实例, 监听的属性, 以及处理的回调函数
function Watcher (vm,prop,callback) {
this.vm = vm;
this.$prop = prop;
this.value = this.get();
this.callback = callback; // 具体watcher所具有的方法,不同的watcher 不同的回调函数,处理不同的业务逻辑
}
// 添加watcher 获得属性的get 方法,当有属性访问/设置 的时候,就产生订阅者 将这个订阅者放进调度中心
Watcher.prototype.get = function () {
Dep.target = this;
// 获得属性值
const value = this.vm.$data[this.$prop];
return value
}
// 添加watcher的更新视图的方法
Watcher.prototype.update = function () {
// 当属性值有变化的时候,执行方法,更新试图
const value = this.vm.$data[this.$prop];
const oldValue = this.value;
// update 执行的时候,先获取 vm 中data实时更新的属性值,this.value 是vm data中之前的老值
if (oldValue != value) {
// console.log('人家通知了,我要改变了')
// 把刚刚获取的更新值赋给之前vm data 中的值
this.value = value
// 执行回调函数 具体怎么处理这个,看实际调用时候 callback 的处理
this.callback(this.value)
}
}

4.模板编译

(为了直接看到页面数据变化的效果,在模板编译的核心数据处理上做了dom 操作,下一篇将讲模板编译的一些细节处理)

// dom模板编译 vm 就是我们最上面的Myvue 对象
function Compile (vm) {
this.vm = vm;
this.$el = vm.el;
// this.data = vm.data;
this.fragment = null; // 用作后面模板引擎 创建文档片段
this.init()
} Compile.prototype = {
// init 方法简单处理,直接做dom 操作,后面会用详细的模板引擎的学习
init: function () {
let value = this.vm.$data.name // 初始化获取到的值 放进dom节点中
document.querySelector('.form-control').value = value;
document.querySelector('.template').textContent = value
// 通知订阅者更新dom
new Watcher(this.vm,this.vm.$prop, (value) => {
document.querySelector('.form-control').value = value;
document.querySelector('.template').textContent = value
})
document.querySelector('.form-control').addEventListener('input',(e) => {
let targetValue = e.target.value
if(value !== targetValue) {
this.vm.$data.name = e.target.value // 将修改的值 更新到 vm的data中
document.querySelector('.form-control').value = targetValue; // 更新dom 节点
document.querySelector('.template').textContent = targetValue
} },false)
}
}

这样就可以看到 在表单中,数据的双向绑定了。

未完待续,错误之处,敬请指出,共同进步!

下一篇 vue 双向数据绑定的实现学习(三)- 模板编译

附:演示代码:

js:

function Myvue (options) {
this.$options = options
this.$el = document.querySelector(options.el);
this.$data = options.data;
Object.keys(this.$data).forEach(key => {
this.$prop = key;
})
this.init()
}
Myvue.prototype.init = function () {
// 监听数据变化
observer(this.$data);
// 获得值
// let value = this.$data[this.$prop];
// 不经过模板编译直接 通知订阅者更新dom
// new Watcher(this,this.$prop,value => {
// console.log(`watcher ${this.$prop}的改动,要有动静了`)
// this.$el.textContent = value
// })
//通知模板编译来执行页面上模板变量替换
new Compile(this)
} function observer (data) {
if(!data || typeof data !== 'object') {
return;
}
Object.keys(data).forEach(key => {
// 对每个属性监听处理
defineReactive(data, key, data[key]);
})
} function defineReactive (data,key,value) {
// 每次访问/修改属性的时候 实例化一个调度中心Dep
var dep = new Dep();
Object.defineProperty(data,key,{
get: function() {
// 添加到watcher 的Dep 调度中心
if (Dep.target) { // Dep.target 是个什么鬼? 转到watcher.js 它是某个订阅者 watcher
dep.addSub(Dep.target); //这个代码段的意思: 如果有订阅者(访问/修改属性的时候) 就将这个订阅者统一放进 Dep 调度中心中
}
// console.log(`${key}属性被访问了`)
return value
},
set: function (newValue) {
if (value != newValue) {
// console.log(`${key}属性被重置了`)
value = newValue
dep.notify(); //我这里有做改动了,通知调度中心的notify方法
}
}
})
// 递归调用,observe 这个value
observer(value)
} // 统一管理watcher订阅者的Dep (调度中心) Dispatch center
function Dep () {
// 所有的watcher 放进这里统一管理
this.subs = []
}
Dep.target = null;
// 通知视图更新dom的 notify的方法
Dep.prototype.notify = function () {
// this.subs 是上面订阅器watcher 的集合
this.subs.forEach(sub => {
// sub 是某个Watcher 具体调用某个Watcher的update 方法
sub.update()
})
} // 添加订阅者的方法
Dep.prototype.addSub = function (sub) {
this.subs.push(sub)
} // 具体的订阅器Watcher
// 传入一个vue 的示例, 监听的属性, 以及处理的回调函数
function Watcher (vm,prop,callback) {
this.vm = vm;
this.$prop = prop;
this.value = this.get();
this.callback = callback; // 具体watcher所具有的方法,不同的watcher 不同的回调函数,处理不同的业务逻辑
}
// 添加watcher 获得属性的get 方法,当有属性访问/设置 的时候,就产生订阅者 将这个订阅者放进调度中心
Watcher.prototype.get = function () {
Dep.target = this;
// 获得属性值
const value = this.vm.$data[this.$prop];
return value
}
// 添加watcher的更新视图的方法
Watcher.prototype.update = function () {
// 当属性值有变化的时候,执行方法,更新试图
const value = this.vm.$data[this.$prop];
const oldValue = this.value;
// update 执行的时候,先获取 vm 中data实时更新的属性值,this.value 是vm data中之前的老值
if (oldValue != value) {
// console.log('人家通知了,我要改变了')
// 把刚刚获取的更新值赋给之前vm data 中的值
this.value = value
// 执行回调函数 具体怎么处理这个,看实际调用时候 callback 的处理
this.callback(this.value)
}
} // dom模板编译 vm 就是我们最上面的Myvue 对象
function Compile (vm) {
this.vm = vm;
this.$el = vm.el;
// this.data = vm.data;
this.fragment = null; // 用作后面模板引擎 创建文档片段
this.init()
} Compile.prototype = {
// init 方法简单处理,直接做dom 操作,后面会用详细的模板引擎的学习
init: function () {
let value = this.vm.$data.name // 初始化获取到的值 放进dom节点中
document.querySelector('.form-control').value = value;
document.querySelector('.template').textContent = value
// 通知订阅者更新dom
new Watcher(this.vm,this.vm.$prop, (value) => {
document.querySelector('.form-control').value = value;
document.querySelector('.template').textContent = value
})
document.querySelector('.form-control').addEventListener('input',(e) => {
let targetValue = e.target.value
if(value !== targetValue) {
this.vm.$data.name = e.target.value // 将修改的值 更新到 vm的data中
document.querySelector('.form-control').value = targetValue; // 更新dom 节点
document.querySelector('.template').textContent = targetValue
} },false)
}
}

html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue双向绑定原理及实现</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
<style>
#app {
margin: 20px auto;
width: 400px;
padding: 50px;
text-align: center;
border: 2px solid #ddd;
}
</style>
</head> <body>
<div id="app">
<input class="form-control" v-model="name" type="text">
<h1 class="template">{{name}}</h1>
</div>
<script src="./js/index1.js"></script>
<script>
const vm = new Myvue({
el: "#app",
data: {
name: "vue 双向数据绑定test1"
}
});
</script>
</body>
</html>

vue 双向数据绑定的实现学习(二)- 监听器的实现的更多相关文章

  1. vue 双向数据绑定的实现学习(一)

    前言:本系列学习笔记从以下几个点展开 什么是双向数据绑定 双向数据绑定的好处 怎么实现双向数据绑定 实现双向数据数据绑定需要哪些知识点 数据劫持 发布订阅模式 先看看我们要实现的目标是什么,如下动图: ...

  2. vue双向数据绑定最最最最最简单直观的例子

    vue双向数据绑定最最最最最简单直观的例子 一.总结 一句话总结:双向绑定既不仅model可以影响view的数据,view也可以影响model的数据 view model 数据 1.vue双向数据绑定 ...

  3. vue双向数据绑定的简单实现

    vue双向数据绑定的简单实现 参考教程:链接 <!DOCTYPE html> <html lang="en"> <head> <meta ...

  4. Vue双向数据绑定原理分析(转)

    add by zhj: 目前组里使用的是前端技术是jQuery + Bootstrap,后端使用的Django,Flask等,模板是在后端渲染的.前后端没有分离,这种做法有几个缺点 1. 模板一般是由 ...

  5. angular和vue双向数据绑定

    angular和vue双向数据绑定的原理(重点是vue的双向绑定) 我在整理javascript高级程序设计的笔记的时候看到面向对象设计那章,讲到对象属性分为数据属性和访问器属性,我们平时用的js对象 ...

  6. React 事件对象、键盘事件、表单事件、ref获取dom节点、react实现类似Vue双向数据绑定

    1.案例实现代码 import React, { Component } from 'react'; /** * 事件对象.键盘事件.表单事件.ref获取dom节点.react实现类似Vue双向数据绑 ...

  7. 详解 vue 双向数据绑定的原理,并实现一组双向数据绑定

    1:vue 双向数据绑定的原理: Object.defineProperty是ES5新增的一个API,其作用是给对象的属性增加更多的控制Object.defineProperty(obj, prop, ...

  8. Vue双向数据绑定原理深度解析

    首先,什么是双向数据绑定?Vue是三大MVVM框架之一,数据绑定简单来说,就是当数据发生变化时,相应的视图会进行更新,当视图更新时,数据也会跟着变化. 在分析其原理和代码的时候,大家首先了解如下几个j ...

  9. Vue双向数据绑定简易实现

    一.vue中的双向数据绑定主要使用到了Object.defineProperty(新版的使用Proxy实现的)对Model层的数据进行getter和setter进行劫持,修改Model层数据的时候,在 ...

随机推荐

  1. C#创建安装、卸载部署程序

    分享3: 需求:对已经开发的应用程序进行安装封装操作,即创建安装.卸载部署程序: 分析:程序的开发是为了在不同的人在不同的机器上使用,为了使不同机器使用该软件就需要见程序安装包,并且保证安装包中必须包 ...

  2. 安装Cnario Player 3.8.1.156或其他版本时提示"Warning 4154. Adobe Flash Player 13 ...not correctly installed"

    错误提示 安装Cnario Player 3.8.1.156或其他版本时, 有时会出现如下提示: Warning 4154. Adobe Flash Player 13 ...not correctl ...

  3. Python两个栈实现一个队列

    牛客网原题: 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型.   实现这个算法的方式有很多种,这里就写一种比较简单易懂的:虽然可能算法和效率上不太出色,当大多数人 ...

  4. PHP二维数组去重(指定键名)

    本例对二维数组中某个指定键名进行去重,发现网上大多使用array_unique或是foreach遍历等方式去重,效果不佳且较为繁琐. 本文以二维数组去重为引,介绍array_column函数的三种用法 ...

  5. win 解压安装mysql步骤

    5  安装成功之后,启动mysql时报错: 系统错误2,找不到指定的文件.原因:有的系统安装过MySQL没有卸载干净,或者系统自带精简版的MySQL导致注册表关于MySQL的配置与实际安装路径不一致. ...

  6. idea安装成功后,设置字体、快捷键、配置jdk等操作

    设置字体 配置jdk 快捷键 复制当前一行: 设置自动提示,不区分大小写 关闭当前窗口 设置类头注释 自定义注释+注释快捷键 Lombok 插件安装  get.set方法报红cannot resolv ...

  7. 简单了解python使用正则表达式

    正则[Regular Expression]:正则表达式通常被用来检索.替换那些符合某个模式(规则)的文本. 正则是用来干啥的:正则就是用来匹配字符串的. Python中string的几个方法: fi ...

  8. Jetson TX1 install Opencv3

    https://jkjung-avt.github.io/opencv3-on-tx2/ 注意:在编译的时候会遇到内存空间不足的情况,可以插入U盘,将程序拷贝到U盘内编译,然后安装到Jetson上.U ...

  9. DNS 地址

    腾讯DNS:119.29.29.29百度DNS:  180.76.76.76  阿里DNS:223.5.5.5 223.6.6.6 成都电信: 61.139.2.69

  10. Flask 构建微电影视频网站(六)

    会员模块实现 会员注册 class RegistForm(FlaskForm): name = StringField( label="昵称", validators=[ Data ...