首先说下什么是mvvm?或者你是怎样理解mvvm的?
首先我说下MVC: Model-View-Controller,字面意思其实就是3种东西,以前搞过前后不分离的时候特别有感受,m是数据,(控制器c)里面获取到了(数据m )后,写入需要渲染的(视图view)模板,然后视图就显示了。
接着到mvvm 其实还是3种东西,前面2个不变,数据、视图,变得最后一个就是vm(视图数据)
来个形象点的比喻:vue的单文件是以template、script style 三层分开的。
<script >就可以表示为m数据 <template>表示为vm, 界面肯定就是v了
因此:在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
不需要手动操作DOM, 不需要关注数据状态的同步问题
m<->vm<->v
什么是双向绑定?
道理你们都懂,我就不再打字了。无非就是数据在view,model同步改变了。
代码非常简单,非常容易理解。vue的双向绑定也是这个原理。
不过人家那个是使用了 发布-订阅模式结合defineProperty进行循环遍历对象key属性值进行监听,下面这个demo只是把对象key值写死而已。
在new Vue的时候 先进行数据转换+执行new Wacher监听
Vue响应系统,其核心有三点:observe、watcher、dep:
observe:遍历data中的属性,使用 Object.defineProperty 的get/set方法对其进行数据劫持,
顺便在get加入了dep依赖收集,在set里面加入了触发dep依赖更新
dep:每个属性拥有自己的消息订阅器dep,用于存放所有订阅了该属性的观察者对象
watcher:观察者(对象),通过dep实现对响应属性的监听,监听到结果后,
主动触发自己的回调进行响应 (执行observe的时候会初始化这个观察者)
这里描述下new Vue过程:
当new Vue的时候会先执行:observe ,
使用 Object.defineProperty 的get/set方法对其进行数据劫持,
顺便在get加入了dep依赖收集其实就是新增订阅者(watcher的观察者),在set里面加入了触发dep的依赖更新(就是观察者的更新函数),
最后顺便会 Dep.target = null; 清空观察者
第二步:然后new watcher
Dep.target = this; // 缓存自己 上面的依赖收集的就是这个
let value = this.vm.data[this.exp] // 强制执行监听器里的get函数
Dep.target = null; // 释放自己
这里会又执行一遍上面的get函数
<script src="observer.js"></script>
<script src="watcher.js"></script>
<script>
function myVue (data, el, exp) {
this.data = data;
observable(data); //将数据变的可观测
el.innerHTML = this.data[exp]; // 初始化模板数据的值
//在new Vue的时候 先进行数据转换+执行new Wacher监听
new Watcher(this, exp, function (value) {
el.innerHTML = value;
});
return this;
}
var ele = document.querySelector('#name');
var input = document.querySelector('input');
var myVue = new myVue({
name: 'hello world'
}, ele, 'name');
//改变输入框内容
input.oninput = function (e) {
myVue.data.name = e.target.value
}
//改变data内容
function changeInput(){
myVue.data.name = "难凉热血"
}
</script>
observer.js (把data属性遍历转化成可监测的,里面包含了Dep依赖收集器)
/**
* 把一个对象的每一项都转化成可观测对象
* @param { Object } obj 对象
*/
function observable (obj) {
if (!obj || typeof obj !== 'object') {
return;
}
let keys = Object.keys(obj);
keys.forEach((key) =>{
defineReactive(obj,key,obj[key])
})
return obj;
}
/**
* 使一个对象转化成可观测对象
* @param { Object } obj 对象
* @param { String } key 对象的key
* @param { Any } val 对象的某个key的值
*/
function defineReactive (obj,key,val) {
let dep = new Dep();
Object.defineProperty(obj, key, {
get(){
dep.depend();
console.log(`${key}属性被读取了`);
return val;
},
set(newVal){
val = newVal;
console.log(`${key}属性被修改了`);
dep.notify() //数据变化通知所有订阅者
}
})
}
class Dep {
constructor(){
this.subs = []
}
//增加订阅者
addSub(sub){
this.subs.push(sub);
}
//判断是否增加订阅者
depend () {
if (Dep.target) {
this.addSub(Dep.target)
}
}
//通知订阅者更新
notify(){
this.subs.forEach((sub) =>{
sub.update()
})
}
}
Dep.target = null;
watcher.js
class Watcher {
constructor(vm,exp,cb){
this.vm = vm;
this.exp = exp;
this.cb = cb;
this.value = this.get(); // 将自己添加到订阅器的操作
}
get(){
Dep.target = this; // 缓存自己
let value = this.vm.data[this.exp] // 强制执行监听器里的get函数
Dep.target = null; // 释放自己
return value;
}
update(){
let value = this.vm.data[this.exp];
let oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal);
}
}
}