Vue 的数据响应式原理是怎么实现的
Vue 数据响应式原理
什么是数据响应式
数据响应式即数据双向绑定,将
Model
绑定到view
, 当我们用 Javascript代码更新Model
时,View
就会自动更新;如果用户更新了View
,Model
也会自动更新。
数据响应式原理
- 在Vue中主要是利用了
Object.defineProperty()
这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现的。 - 在Vue 3.0 中采用 ES6 中的
Proxy
对象来实现。
数据响应式实现
- 以下采用 Object.defineProperty() 来实现。
- 根据订阅发布者模式实现一个整体架构,包括
MVVM
类,Vue
类,Watcher
类。
//发布者
class Vue {
constructor (options) {
this.options = options;
this.$data = options.data; //获取数据
this.$el = document.querySelector(options.el); //获取挂载对象
}
//劫持监听所有属性
Observer () {
}
//解析指令
Compile () {
}
}
- 实现
MVVM
中的由 M 到 V,将模型中的数据绑定到视图。
//发布者
class Vue {
constructor (options) {
this.options = options;
this.$data = options.data; //获取数据
this.$el = document.querySelector(options.el); //获取挂载对象
this._directive = {}; //创建容器存放订阅者
this.Observer(this.$data);
this.Compile(this.$el);
}
//劫持监听所有属性
Observer (data) {
for (let key in data) {
this._directive[key] = [];
}
}
//解析指令
Compile (el) {
let Vnodes = el.children;
for (Vnode of Vnodes) {
//递归查找子元素
if (Vnode.children.length) {
this.Compile(Vnode);
}
//判断是否含有对应的属性,做出相应的处理
if (Vnode.hasAttribute('v-text')) {
let attrVal = node.getAttribute('v-text');
//向订阅者容器中添加对应的订阅者
this._directive[attrVal].push(new Watcher(node, this, attrVal, 'innerHTML'));
}
if (Vnode.hasAttribute('v-model')) {
let attrVal = node.getAttribute('v-model');
this._directive[attrVal].push(new Watcher(node, this, attrVal, 'value'));
}
}
}
//订阅者,更新视图
class Watcher {
constructor (el, vm, exp, attr) {
this.el = el;
this.vm = vm;
this.exp = exp;
this.attr = attr;
this.uppublished_at(); //初始化视图
}
uppublished_at () {
this.el[this.attr] = this.vm.$data[this.exp];
}
}
}
- 最后实现 V 到 M,当视图变化的时候,触发更新模型中的数据。
//发布者
class Vue {
constructor (options) {
this.options = options;
this.$data = options.data; //获取数据
this.$el = document.querySelector(options.el); //获取挂载对象
this._directive = {}; //创建容器存放订阅者
this.Observer(this.$data);
this.Compile(this.$el);
}
//劫持监听所有属性
Observer (data) {
for (let key in data) {
this._directive[key] = [];
}
}
//解析指令
Compile (el) {
let Vnodes = el.children;
for (Vnode of Vnodes) {
//递归查找子元素
if (Vnode.children.length) {
this.Compile(Vnode);
}
//判断是否含有对应的属性,做出相应的处理
if (Vnode.hasAttribute('v-text')) {
let attrVal = node.getAttribute('v-text');
//向订阅者容器中添加对应的订阅者
this._directive[attrVal].push(new Watcher(node, this, attrVal, 'innerHTML'));
}
if (Vnode.hasAttribute('v-model')) {
let attrVal = node.getAttribute('v-model');
this._directive[attrVal].push(new Watcher(node, this, attrVal, 'value'));
node.addEventListener('input', (function () {
return function () {
this.$data[attrVal] = node.value; //这里更新了 ~
}
})().bind(this)); //bind不会立即执行
}
}
}
//订阅者,更新视图
class Watcher {
constructor (el, vm, exp, attr) {
this.el = el;
this.vm = vm;
this.exp = exp;
this.attr = attr;
this.uppublished_at(); //初始化视图
}
uppublished_at () {
this.el[this.attr] = this.vm.$data[this.exp];
}
}
}
- 同时更新相应视图。
//发布者
class Vue {
constructor (options) {
this.options = options;
this.$data = options.data; //获取数据
this.$el = document.querySelector(options.el); //获取挂载对象
this._directive = {}; //创建容器存放订阅者
this.Observer(this.$data);
this.Compile(this.$el);
}
//劫持监听所有属性
Observer (data) {
for (let key in data) {
this._directive[key] = [];
let val = data[key];
let watch = this._directive[key];
//这里是核心 !!!
Object.defineProperty(this.$data, key, {
get: function () {
return val;
},
set: function (newVal) {
if (newVal !== val) {
val = newVal;
watch.forEach(ele => { //ele为订阅者实例对象
ele.uppublished_at();
})
}
}
})
}
}
//解析指令
Compile (el) {
let Vnodes = el.children;
for (Vnode of Vnodes) {
//递归查找子元素
if (Vnode.children.length) {
this.Compile(Vnode);
}
//判断是否含有对应的属性,做出相应的处理
if (Vnode.hasAttribute('v-text')) {
let attrVal = node.getAttribute('v-text');
//向订阅者容器中添加对应的订阅者
this._directive[attrVal].push(new Watcher(node, this, attrVal, 'innerHTML'));
}
if (Vnode.hasAttribute('v-model')) {
let attrVal = node.getAttribute('v-model');
this._directive[attrVal].push(new Watcher(node, this, attrVal, 'value'));
node.addEventListener('input', (function () {
return function () {
this.$data[attrVal] = node.value;
}
})().bind(this)); //bind不会立即执行
}
}
}
//订阅者,更新视图
class Watcher {
constructor (el, vm, exp, attr) {
this.el = el;
this.vm = vm;
this.exp = exp;
this.attr = attr;
this.uppublished_at(); //初始化视图
}
uppublished_at () {
this.el[this.attr] = this.vm.$data[this.exp];
}
}
}