该文章简单模拟Vue响应式原理(MVVM),笔者还只是个大三的学生,文章有不少不到之处欢迎各位大佬指出错误,多多交流。html
官方文档的东西对于初学者来讲都有点晦涩难懂,本文就将以一种简单的方式来聊聊Vue的响应式原理。前端
Vuejs是数据驱动型,数据发生改变界面也会刷新改变。但这并非理所固然,在其内部作了不少复杂的操做。vue
思路react
首先要搞懂Vue内部是如何监听数据的改变?bash
经过Object.definePropety这个方法来监听数据的改变。app
这个方法的第三个参数的 Setters 和 Getters是关键。详情ide
已经监听了数据的改变,Vue是如何知道要通知哪些元素界面发生刷新呢?函数
经过发布订阅者模式。ui
Observer会监听全部的data属性而且给他们建立一一对应的Dep对象.this
Compile 会解析模板中指令(同时也会将界面初始化)。 一个指令就建立一个Watcher对象, 而后添加到相对应的Dep对象的订阅中绑定更新函数。若是一个属性的value发生改变Observer就会通知所对应的Dep对象调用notify()通知全部的订阅者 更新界面。
详情看下图
PS:画图技术渣渣,见谅。
笔者只是简单模拟,尚未达到能够剖析源码的程度。
//render.js
var render = function(template, data) {
const reg = /\{\{(\w+)\}\}/; //不作贪婪匹配
if (reg.test(template)) { //退出条件 false
//是否须要编译
// vue源码模板编译用的正则方法
const key = reg.exec(template)[1]
// console.log(key)
template = template.replace(reg, data[key]);
return render(template, data) //递归渲染
}
// template.replace(/{{(.)+/)
return template
}
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
<script src="./render.js"></script>
<script>
const obj = { //模拟数据
name: '张三',
age: '18',
sex: '男'
}
// 发布订阅者模式
class Dep {
constructor() {
this.subs = []
}
addSub(watch) { //添加订阅方法
this.subs.push(watch)
}
notify() { //通知订阅者更新
this.subs.forEach(item => {
item.update();
})
}
}
class Watch {
constructor(name) {
this.name = name;
}
update() {
console.log(this.name + 'update')
//更新视图
document.getElementById('app').innerHTML = render(template,obj)
}
}
Object.keys(obj).forEach(key => { //遍历obj对象
let dep = new Dep; //建立发布者对象
let value = obj[key];
Object.defineProperty(obj, key, {//监听数据
set: function(newValue) { //数据改变
console.log( '数据' + key + '改变了')
// 更新数据
value = newValue
dep.notify() //通知订阅者更新
},
get: function() { //获取数据
console.log('数据' + key + '加入了响应式系统')
let w = new Watch(value)
dep.addSub(w);
return value
}
})
})
obj.message = "没用的"
//待编译的模板
var template = '我是{{name}}, {{name}} 年龄 {{age}},性别 {{sex}} 。 {{message}} '
document.getElementById('app').innerHTML = render(template,obj);
</script>
</body>
</html>
复制代码
obj.message = "没用的"
这个数据没在初始化中,全部没有打印。即不是响应式属性。
Vue 不容许动态添加根级响应式属性,因此你必须在初始化实例前声明全部根级响应式属性。详情看文档声明响应式属性。
笔者是一个入门前端不久的小白,这篇文章挺浅显的,可能也有一些错误,但愿大佬们多多给点建议。若是这篇文章对你有那么一点点小帮助的话,不妨点个赞吧。