vue 双向数据绑定的实现学习之监听器的实现方法

提到了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 双向数据绑定的实现学习之监听器的实现方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

以上是 vue 双向数据绑定的实现学习之监听器的实现方法 的全部内容, 来源链接: utcz.com/z/339588.html

回到顶部