1、了解MVVM框架吗?
2、谈谈你对MVVM的认识?
3、双向绑定是什么原理,可以写出来吗?
4、使用了什么设计模式?
5、生命周期是什么?
6、有看过源码吗?
1、了解MVVM框架吗?
vue,react,angular都是用的MVVM框架,vue开源协议是MIT,react闹过一出,所以用vue的人多。
2、谈谈你对MVVM的认识?
这个问题没有标准答案,可以先聊MVC,再聊MVVM,MVVM是MVC(model view control)延伸过来的
MVVM(model view viewModel),vuejs处理的就是view和model之间的viewModel这个核心枢纽,view里面内容改变了,viewModel就改了,viewModel改了,view就自动改了,viewModel数据改变了,怎么改变model,就是ajax请求,传递到服务器端
对比mvvm和mvc
3、双向绑定是什么原理,可以写出来吗?
双向绑定是mvvm最核心的问题,大大减轻了开发
view <-> data
data到view 数据驱动页面(之前都是这种模式)
view到data 比如input框,改变了值,通过v-model自动同时改变view,data的值
如何实现的?从服务端拿到了数据,赋值给data,data的数据变了,那么view自动就变了,那么view怎么知道data变了。比如有个变量a为1,这个时候a改成了2,之前是获取dom,自己赋值,展示。这里面是怎么实现的,Object.defineProperty,react,vue是基于这个来做的。这个api能监听到data变化,里面有个回调函数,里面写了view与data对数据的操作。
Object.defineProperty
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
就是说你有一个对象,这也就是大家为什么说vue里面的data是一个对象那个,可以定义一个属性,然后返回这个对象
里面有个get,set,读的时候会触发get操作,改变的时候会触发set操作,赋值的时候触发了set,这里会检测值有没有改变,如果改变了,就会触犯回调函数
所以,双向绑定的基石就是这个Object.defineProperty
4、使用了什么设计模式?
观察者模式,什么是观察者模式呢?第一个要有一个监听者,叫observer,他是来监听data发生的变化,,他发生变化以后,会通知所有的观察者列表,什么是观察者列表,比如我data中有一个a,a=1,然后有好多观察者列表在关注这个值,observer底层就是Object.defineProperty,他观察到变化,会对观察者列表对触发,比如观察者列表有,a,b,c,d,告诉observer,如果a发生了变化告诉我,然后observer监听到变化,就会触发观察者列表,这个列表中会有一个更新函数,通知了列表,列表会自动调用update函数,完了更新完就会更新view了。observer只是负责监听,他怎么知道谁是a,b,c,d,这个任务交给我了订阅watcher
5、生命周期是什么?
https://www.cnblogs.com/wzndkj/p/9612647.html
6、有看过源码吗?
源码里面东西太多,只要把里面最核心的东西,思路理清楚就可以了,下面有一段伪代码
index.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> <style> #app { text-align: center; } </style> <body> <div id="app"> <h2>{{title}}</h2> <input v-model="name"> <h1>{{name}}</h1> <button v-on:click="clickMe">click me</button> </div> </body> <script src="js/observer.js"></script> <script src="js/watcher.js"></script> <script src="js/compile.js"></script> <script src="js/index.js"></script> <script type="text/javascript"> new Vue({ el: '#app', data: { title: 'vue code', name: 'imooc' }, methods: { clickMe: function() { this.title = 'vue code click'; } }, mounted: function () { window.setTimeout(() => { this.title = 'timout 1000'; }, 1000); } }) </script> </html>
index.js
function Vue (options) { var self = this; this.data = options.data; this.methods = options.methods; Object.keys(this.data).forEach(function(key){ self.proxyKeys(key); }) observe(this.data); // 实现了data的监听 new Compile(options.el, this); options.mounted.call(this); } Vue.prototype = { proxyKeys: function(key) { var self = this; Object.defineProperty(this, key, { enumerable: false, configurable: true, get: function() { return self.data[key]; }, set: function(newVal) { self.data[key] = newVal; } }) } }
observer.js
/** * * @param {*} data * Observer是vue实例化的时候调用的 */ function Observer (data) { this.data = data; this.walk(data); } Observer.prototype = { walk: function (data) { var self = this; Object.keys(data).forEach(function (key) { self.defineReactive(data, key, data[key]); }); }, defineReactive: function (data, key, val) { var dep = new Dep(); // Dep对象,维护的是观察者列表 var childObj = observe(val); Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function getter() { if (Dep.target) { dep.addSub(Dep.target); } return val; }, set: function setter (newVal) { // 数据变了,通知观察者 if (newVal === val) { return; } val = newVal; dep.notify(); } }) } } function observe (value, vm) { if (!value || typeof value !== 'object') { return; } return new Observer(value); } function Dep () { this.subs = []; } Dep.prototype = { addSub: function (sub) { this.subs.push(sub); }, notify: function() { this.subs.forEach(function (sub) { sub.update(); }) } } Dep.target = null;
watcher.js
function Watcher (vm, exp, cb) { this.cb = cb; this.vm = vm; this.exp = exp; this.value = this.get(); // 将自己添加到订阅器的操作 } Watcher.prototype = { update: function() { this.run; }, run: function() { var value = this.vm.data[this.exp]; var oldVal = this.value; if (value !== oldVal) { this.value = value; this.cb.call(this.vm, value, oldVal); } }, get: function(){ Dep.target = this; // 缓存自己 var value = this.vm.data[this.exp]; // 强制执行监听器里的get函数 Dep.target = null; // 释放自己 return value; } }
compile.js
// 外层容器的对象 function Compile (el, vm) { this.vm = vm; this.el = document.querySelector(el); this.fragment = null; this.init(); } Compile.prototype = { init: function() { if (this.el) { this.fragment = this.nodeToFragment(this.el); this.compileElement(this.fragment); this.el.appendChild(this.fragment); } else { console.log('Dom元素不存在'); } }, nodeToFragment: function(el) { var fragment = document.createDocumentFragment(); var child = el.firstChild; while (child) { // 将Dom元素移入fragment中 fragment.appendChild(child); child = el.firstChild; } return fragment; }, compileElement: function (el) { /** * 把所有子节点遍历出来 */ var childNodes = el.childNodes; var self = this; [].slice.call(childNodes).forEach(function (node) { var reg = /{{(.*)}}/; var text = node.textContent; if (self.isElementNode(node)) { self.Compile(node); } else if (self.isTextNode(node) && reg.test(text)) { self.compileText(node, reg.exec(text)[1]); } if (node.childNodes && node.childNodes.length) { self.compileElement(node); } }); }, compile: function (node) { var nodeAttrs = node.attributes; var self = this; Array.prototype.forEach.call(nodeAttrs, function(attr) { var attrName = attr.name; if (self.isDirective(attrName)) { var exp = attr.value; var dir = attrName.substring(2); if (self.isEventDirective(dir)) { // 事件指令 self.compileEvent(node, self.vm, exp, dir); } else { // v-model指令 self.compileModel(node, self.vm, exp, dir); } node.removeAttribute(attrName); } }) }, compileText: function(node, exp) { var self = this; var initText = this.vm[exp]; this.updateText(node, initText); new Watcher(this.vm, exp, function(value) { self.updateText(node, value); }) }, compileEvent: function (node, vm, exp, dir) { var eventType = dir.split(':')[1]; var cb = vm.methods && vm.methods[exp]; if (eventType && cb) { node.addEventListener(eventType, cb.bind(vm), false); } }, compileModel: function (node, vm, exp, dir) { var self = this; var val = this.vm[exp]; this.modelUpdater(node, val); new Watcher(this.vm, exp, function (value) { self.modelUpdater(node, value); }); // 实现双向绑定的 node.addEventListener('input', function (e) { var newValue = e.target.value; if (val === newValue) { return; } self.vm[exp] = newValue; val = newValue; }) }, updateText: function (node, value) { node.textContent = typeof value === 'undefined' ? '' : value; }, modelUpdater: function (node, value, oldValue) { node.value = typeof value === 'undefined' ? '' : value; }, isDirective: function (attr) { return attr.indexOf('v-') == 0; }, isEventDirective: function (dir) { return dir.indexOf('on:') === 0; } }