内容简介:前段时间面试,MVVM原理成为了一道必考题。由于理解不够深,最近详细了解以结构图流程分析原理。
前段时间面试,MVVM原理成为了一道必考题。由于理解不够深,最近详细了解以结构图流程分析原理。
一张原详细的理图
使用MVVM双向绑定
- 定义双向绑定,传入元素,和数据。
var vm = new MVVM({ el: '#mvvm-app', data: { someStr: 'hello ', className: 'btn', htmlStr: '<span style="color: #f00;">red</span>', child: { someStr: 'World !' } } }); 复制代码
MVVM类
- 新建劫持数据
- 编译绑定数据
class MVVM { constructor(options) { this.$options = options || {}; var data = this._data = this.$options.data; Object.keys(data).forEach(key => { this._proxyData(key); }) //数据劫持 observe(data, this); //编译 this.$compile = new Compile(options.el || document.body, this); } _proxyData(key, setter, getter) { Object.defineProperty(this, key, { configurable: false, enumerable: true, get: function proxyGetter() { return this._data[key]; }, set: function proxySetter(newVal) { this._data[key] = newVal; } }) } } 复制代码
Compile类
- 将真实DOM移动到虚拟DOM中
- 解析元素中的指令
- 指令新建订阅传入更新函数
class Compile { constructor(el, vm) { this.$vm = vm; this.$el = this.isElementNode(el) ? el : document.querySelector(el); if (this.$el) { //生成文档碎片 this.$fragment = this.node2Fragment(this.$el); //编译 this.init() //文档碎片加回容器中 this.$el.appendChild(this.$fragment); } } node2Fragment(el) { var fragment = document.createDocumentFragment(), child; while (child = el.firstChild) { fragment.appendChild(child); }; return fragment; } init() { this.compileElement(this.$fragment); } compileElement(el) { var childNodes = el.childNodes; [].slice.call(childNodes).forEach(node => { var text = node.textContent; var reg = /\{\{(.*)\}\}/; if(this.isElementNode(node)){ //指令解析 this.compile(node); }else if(this.isTextNode(node) && reg.test(text)){ this.compileText(node, RegExp.$1) } if(node.childNodes && node.childNodes.length){ this.compileElement(node); } }) } compile(node){ var nodeAttrs = node.attributes; [].slice.call(nodeAttrs).forEach(attr => { var attrName = attr.name; if(this.isDirective(attrName)){ var exp = attr.value; var dir = attrName.substring(2); //事件指令 if(this.isEventDirective(dir)){ compileUtil.eventHandler(node, this.$vm, exp, dir); }else{ compileUtil[dir] && compileUtil[dir](node, this.$vm, exp); } node.removeAttribute(attrName); } }) } isDirective(attr){ return attr.indexOf('v-') === 0; } isEventDirective(attr){ return attr.indexOf('on') === 0; } isElementNode(node) { return node.nodeType == 1; } isTextNode(node) { return node.nodeType == 3; } compileText(node, exp) { compileUtil.text(node, this.$vm, exp); } } //指令处理集合 var compileUtil = { ... } var updater = { ... } 复制代码
Observer类
- 劫持数据
- 数据变化通知Watcher
//数据劫持 class Observer { constructor(data) { this.data = data; this.walk(data); } walk(data) { Object.keys(data).forEach(key => { this.convert(key, data[key]); }) } convert(key, val) { this.defineReactive(this.data, key, val); } //绑定数据,添加发布订阅,核心** defineReactive(data, key, val) { var dep = new Dep(); var childObj = observe(val); Object.defineProperty(data, key, { enumerable: true, //可枚举 configurable: false, //不能再define get: function(){ if(Dep.target){ console.log(Dep.target, 'Dep.target'); dep.depend(); } return val; }, set: function(newVal){ if(newVal === val){ return; } val = newVal; // 新的值object的话,进行监听 childObj = observe(newVal); console.log(newVal); //通知订阅者 dep.notify(); } }) } } function observe(value, vm) { if (!value || typeof value !== 'object') { return; } return new Observer(value); } 复制代码
Dep类
- 发布订阅类
var uid = 0; class Dep { constructor() { this.id == uid++; this.subs = []; } addSub(sub) { this.subs.push(sub); } depend() { Dep.target.addDep(this) } removeSub(sub) { this.subs.remove(sub); } notify() { this.subs.forEach(sub => { sub.update(); }) } } Dep.target = null; 复制代码
Watcher类
- 监控数据变化,发布消息,执行订阅函数。
class Watcher{ constructor(vm, expOrFn, cb){ this.cb = cb; this.vm = vm; this.expOrFn = expOrFn; this.depIds = {}; if(typeof expOrFn === 'function') { this.getter = expOrFn; }else{ this.getter = this.parseGetter(expOrFn); } this.value = this.get(); } update(){ this.run(); } run(){ var value = this.get(); var oldVal = this.value; if (value !== oldVal) { this.value = value; this.cb.call(this.vm, value, oldVal); } } get(){ Dep.target = this; var value = this.getter.call(this.vm, this.vm); Dep.target = null; return value; } parseGetter(exp){ if(/[^\w.$]/.test(exp)) return; var exps = exp.split(','); return function(obj) { for (let i = 0; i < exps.length; i++) { if(!obj) return; obj = obj[exps[i]]; } return obj; } } addDep(dep){ if (!this.depIds.hasOwnProperty(dep.id)) { dep.addSub(this); this.depIds[dep.id] = dep; } } } 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ordering Disorder
Khoi Vinh / New Riders Press / 2010-12-03 / USD 29.99
The grid has long been an invaluable tool for creating order out of chaos for designers of all kinds—from city planners to architects to typesetters and graphic artists. In recent years, web designers......一起来看看 《Ordering Disorder》 这本书的介绍吧!