本身用vue也不久了,学习之初就看过vue实现的原理,当时看也是迷迷糊糊,能说出来最基本的,可是感受仍是理解的不深刻,最近找到了以前收藏的文章,跟着大神一步步敲了一下简易的实现,算是又加深了理解。javascript
原文连接html
<!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"> {{name}} <p v-if="isShow"> <span>{{name}}</span> </p> <input type="text" id="a" v-model="name"> </div> <script> function compile(node, vm) { var reg = /\{\{(.*)\}\}/; if (node.nodeType === 1) { var attr = Array.prototype.slice.call(node.attributes); //解析属性 for (var i = 0; i < attr.length; i++) { if (attr[i].nodeName == 'v-model') { var name = attr[i].nodeValue; node.addEventListener('input', function (e) { vm[name] = e.target.value; //eval(`vm.data.${name}=e.target.value`) console.log(vm) }) node.value = eval(`vm.${name}`); node.removeAttribute('v-model'); } if (attr[i].nodeName == 'v-if') {// 这里是我本身加的指令,真正确定不是这样玩的吧 var name = attr[i].nodeValue; var isInsert = eval(`vm.${name}`); if (!isInsert) { node = ''; return node; } else { node.removeAttribute('v-if'); } } } } if (node.nodeType === 3) { if (reg.test(node.nodeValue)) { var name = RegExp.$1; name = name.trim(); //node.nodeValue = eval(`vm.data.${name}`); new Watcher(vm, node, name)//这里给每一个属性文本节点生成一个Watcher对象,嗯,大体跟vue的原理类似 } } return node; } function nodeToFragment(node, vm) { var flag = document.createDocumentFragment(); var child; while (child = node.firstChild) { child = compile(child, vm) if (child !== "") { if (child.childNodes.length != 0) { child.append(nodeToFragment(child, vm)); } } else { node.removeChild(node.firstChild) } flag.append(child); } return flag; } function defineReactive(obj, key, val) { var dep = new Dep();//这里给每一个属性生成一个数据订阅中心,它能够存储订阅它的全部watcher, Object.defineProperty(obj, key, { get: function () { if (Dep.target) dep.addSub(Dep.target);//这里的Dep.target是对应的Watcher对象,这里是dep对象调用addSub,我看别人说的vue源码是在Watcher对象实现addSub操做的 return val; }, set: function (newVal) { if (newVal === val) return; console.log('修改了', key) val = newVal; dep.notify();//数据更新了,就通知全部的观察者实例 } }) } function observer(obj, vm) { Object.keys(obj).forEach(function (key) { defineReactive(vm, key, obj[key]); }) } function Watcher(vm, node, name) { Dep.target = this;//在实例化新的watcher对象时把Dep.target赋值为this,也就是每一个指令对应的那个watcher对象,这样在下面调用this.update,从而调用this.get时触发数据的get方法,从而触发dep.addSub(Dep.target),这样这个watcher就被添加进去 this.name = name; this.node = node; this.vm = vm; this.update(); Dep.target = null;//为了保证全局只有一个,在最后须要清空,为下一个指令作准备 } Watcher.prototype = { update: function () { this.get();//更新时调用get() this.node.nodeValue = this.value; }, get: function () { this.value = this.vm[this.name]; //会触发vm.data中属性的get方法,进而能够添加watcher到Dep中 } } function Dep() { this.subs = []; } Dep.prototype = { addSub: function (sub) { this.subs.push(sub); }, notify: function () { this.subs.forEach(function (sub) { sub.update(); }) } } function Vue(options) { this.data = options.data; var id = options.el; var data = this.data; observer(data, this) var dom = nodeToFragment(document.getElementById(id), this); document.getElementById(id).appendChild(dom); } var vm = new Vue({ el: 'app', data: { text: { name: 'jay' }, 'name': 'zxf', isShow: true } }) </script> </body> </html>
以上的备注是我看了文章后本身的一些理解,这个得简单实现跟vue的源码好像是有区别,好比前端
function defineReactive(obj, key, val) { var dep = new Dep() var childOb = Observer.create(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function metaGetter() { // 若是Dep.target存在,则进行依赖收集 if (Dep.target) { dep.depend() if (childOb) { childOb.dep.depend() } } return val }, set: function metaSetter(newVal) { if (newVal === val) return val = newVal childOb = Observer.create(newVal) dep.notify() } }) } Watcher.prototype.addDep = function (dep) { var id = dep.id if (!this.newDeps[id]) { this.newDeps[id] = dep if (!this.deps[id]) { this.deps[id] = dep dep.addSub(this) } } } 做者:百度外卖大前端技术团队 连接:https://juejin.im/post/5a44b15e51882538fe631406 来源:掘金 著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。
源码中在dep的get里面并无直接调用addsub,而是调用vue
dep.depend();
而dep.depend() 实际执行了 Watcher.addDep() 这样最终是在Watcher对象的方法里面实现了增长订阅者,感受好像是订阅者主动要求dep添加本身,而简易框架是,dep主动添加,感受都差很少吧。,可能原文做者为了方便,而vue可能处于更全面的考虑吧
另外我也懂了vue原理中watcher,dep,observer,compile等这几个对象的概念和他们之间的关系,observer是数据观察者,主要是劫持data数据,添加set,get,并观察数据的变更,若触发了set,就调用dep的notify方法,若触发了get,就会新增watcher到dep的存放观察者的数组中。java
另外这个图片也能够说明一切,终于看懂了,之前知道这么链接,只是不知道为何这么链接的,另外还有一篇文章是讲解关于这幅图的理解的那个是对源码进行解读的,比较详细node
vue源码解读,另外还有一篇文章也是大牛本身实现vue的简易框架的,与这个得实现思路略微有所不一样:vue简易框架2c#