const Observer = function(data) { // 循环修改为每个属性添加get set for (let key in data) { reactive(data, key); } } const Dep = function() { const self = this; //这里可以用数组,就能实现同一个data,观察多个vue实例 this.depend = ()=>{ if (Dep.target) { this.sub = Dep.target } } this.notify = ()=>{ this.sub.update(); } } const Watcher = function(vm, fn) { //这样的写法是为了把target传到dep实例里面,当然也可以不挂载到dep,但要有一个地方暂存起来 Dep.target = this; this.update = function() { console.log('in watcher update'); fn(); } //初始化运行一次render函数 fn(); Dep.target = null; } const reactive = function(obj, key) { const dep = new Dep(); let val = obj[key]; Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { console.log('in get'); dep.depend(); return val; }, set(newVal) { if (newVal === val) { return; } val = newVal; dep.notify(); } }); } const Vue = function (options) { this._data = options.data.call(this); //设置data的get,set Observer(this._data); this.mount = (id)=>{ this.dom = document.querySelector(id); new Watcher(this, this.render); } //渲染函数,这里主要是触发get方法 this.render = ()=>{ //这里vue应该会有虚拟dom的算法实现 this.dom.innerHTML = `<div>${this._data.text}</div>`; } } const vue = new Vue({ data() { return { text: 'hello world' }; } }) vue.mount('#app'); vue._data.text = '123';
首先看一下主入口 Vue,包含 Observer, mount, render 三个方法
其中 Observer 的作用是为了获取data中的每一个get,set操作,以达到监听数据变化的手段(可用ES6的Proxy实现)
Watcher函数有2个作用,第1个是在程序首次运行的时候,用于触发Observer中的get方法,收集依赖,并把当前watcher的实例绑入到dep中。
第2个作用是,当数据被修改,也就是触发set操作的时候,通知对应的dep中的watch实例触发updata->render,达到页面重新渲染的目的。
render函数这里只是简单的一行代码,实际vue里面的实现应该是非常复杂的,包括 jsx,vue模版引擎,虚拟dom之类的实现
github地址: https://github.com/gaxking/vue-deeplearn