个人对mvvm原理的一点理解,文章包括了mvvm中涉及的基本知识,还有mvvm实现步骤和代码注释,希望能对大家有所帮助,有问题的还请多多指教
源码中用到的知识点
一,声明一个类
es5 构造函数
funtion Watcher(){•••}
es6 class
class Compile{}
二,原型链 继承
三,Object.defineProperty(obj, descriptor)
作用:
定义对象某属性的描述对象,并返回
该对象
属性的Descriptor:
descriptor{
configurable 默认false
enumerable 默认false
value 默认undefined
writable 默认false
get 默认undefined
set 默认undefined
}
四,document.querySelector(a)
仅仅返回匹配指定选择器的第一个元素。
五,nodeType属性返回节点类型
1-元素节点
2-属性节点
3-文本节点
8-注释节点
六,document.createDocumentFragment()
作用
创建了一虚拟的节点对象,节点对象包含所有属性和方法
特征
1不属于文档树,继承的parentNode属性总是null
2把一个DocumentFragment节点插入文档树时,插入的不是DocumentFragment自身,而是它的所有子孙节点
3appendChid()将原dom树中的节点添加到DocumentFragment中时,会删除原来的节点。
提升性能
当需要添加多个dom元素时,如果先将这些元素添加到DocumentFragment中,再统一将DocumentFragment添加到页面,会减少页面渲染dom的次数,效率会明显提升
七,firstChild
返回被选节点的第一个子节点
没有子节点,返回 NULL
八,childNodes
返回指定节点的所有子节点,包括节点元素和文本元素
九,textContent
设置或者返回指定节点的文本内容。
十,attributes
返回指定节点的属性集合, length确定属性数量
十一,element.addEventListener(event, function, useCapture)
event
必须。字符串,指定事件名。
注意: 不要使用 “on” 前缀。 例如,使用 “click” ,而不是使用 “onclick”。
function
必须。指定要事件触发时执行的函数。useCapture
可选。布尔值,指定事件是否在捕获或冒泡阶段执行。
true - 事件句柄在捕获阶段执行
false- 默认。事件句柄在冒泡阶段执行
十二,RegExp.$1
RegExp的一个属性,指的是与正则表达式匹配的第一个 子匹配(以括号为标志)字符串,以此类推,RegExp.$2,RegExp.$3,…RegExp.$99总共可以有99个匹配
手写一个Mvvm框架
class Mvvm {
//option - vue组件中的属性,如 data methods props等等
construct(option){
this.option=option
this.
e
l
=
o
p
t
i
o
n
.
e
l
t
h
i
s
.
el=option.el this.
el=option.elthis.data=option.data
this._proxy()
this._observe(this.
d
a
t
a
)
t
h
i
s
.
data) this.
data)this.compile=new Compile(this.KaTeX parse error: Expected 'EOF', got '}' at position 32: …y,this) }̲ // 把data…data.a
_proxy(){
for(let key in this.KaTeX parse error: Expected '}', got 'EOF' at end of input: … return this.data[key]
}
set:function(newVal){
this.$data[key]=newVal
}
}
}
递归监听所有属性,添加getter setter
触发getter时,把dep.target添加到watcher管理器,
触发setter时,要执行Watchers的更新方法notify,通知更新
_observe(data){
if(!data&&typeof data!=‘object’) return
let dep=new Dep()
for(let key in data){
this._observe(data[key])
Object.defineProperty(data,key,{
configurable:true,
enumerable:false,
get:function(){
dep.target&&
dep.add(dep.target)
return data[key]
},
set:function(newVal){
if(data[key]==newVal) return
dep.notify()
data[key]=newVal
}
)
}
订阅者管理器
function Dep(){
this.Watchers=[]
}
Dep.prototype.add=function(watcher){
this.watchers.push(watcher)
}
Dep.prototype.notify=function(){
this.Watchers.forEach(watcher=>
watcher.update()
)
}
有订阅者管理器,就有订阅者
订阅者实现的
1订阅者通过get()实现订阅(获取属性值),同时要将Dep.target=this,
2执行更新update,实际上就是run(),run实现的是 更新newVal 并触发回调函数
vm- mvvm实例
exp- 指令绑定的变量
cb-节点内容更新的回调函数,updater
funtion Watcher(vm,exp,cb){
this.vm=vm
this.exp=exp
this.cb=cb
this.val=this.get()
}
Watcher.prototype.get=funtion(){
//告诉Dep 我要订阅
Dep.target=this,
//触发getter
let value=this.vm[this.exp]
//我订阅完了
Dep.target=null
return value
}
Watcher.prototype.update=function(){
this.run()
}
Watcher.prototype.run=function(){
let newVal = this.get()
let oldVal = this.val
if(newVal!=oldVal) {
this.val=newVal
this.cb.call(this.vm,newVal,oldVal)
}
}
编译器Compile
解析模板指令
将模板中的变量替换成数据,初始化渲染视图,
对每个指令对应的节点,绑定更新函数,添加监听数据的订阅者,一旦数据有变化,收到通知,更新视图
el – 选择器
vm-- Mvvm实例
funtion Compile(el,vm){
this.
v
m
=
t
h
i
s
.
v
m
t
h
i
s
.
vm=this.vm this.
vm=this.vmthis.el=el.nodeType=1? el:document.querySelector(el)
if(this.KaTeX parse error: Expected '}', got 'EOF' at end of input: el){ this.fragment=
this.createFragment(this.
e
l
)
t
h
i
s
.
p
a
r
s
e
T
e
m
p
l
a
t
e
(
t
h
i
s
.
el) this.parseTemplate(this.
el)this.parseTemplate(this.fragment)
this.
e
l
.
a
p
p
e
n
d
C
h
i
l
d
(
t
h
i
s
.
el.appendChild(this.
el.appendChild(this.fragment)
}
//创建节点片段,利用documentFrament做性能优化
Compile.prototype.createFragment=funtion(el){
const fragment=document.createDocumentFragment(el)
while(child=el.child){
fragment.appendChild(child)
}
return frament
}
//解析模板
Compile.prototype.parseTemplate=function(fragment){
const childNodes=fragment.childNodes
childNodes.slice().forEach(node=>
const text=node.textContent
const reg=/{{(.*)}}/
if(fragment.nodeType=1){
this.compileElement(node)
}else if(frament.nodeType===3&®.test(text)){
this.compileText(node,RegExp.KaTeX parse error: Expected 'EOF', got '}' at position 5: 1) }̲ if(node.child…vm,exp,dir)
}else{
parseUtil[dir]&&parseUtildir
}
}
Compile.prototype.compileText=funtion(node ,exp){
parseUtil.text(node,this.$vm,exp)
}
//编译工具v-text v-model v-bind 注册事件
const parseUtil={
text(node,vm,exp){
this.bind(node,vm,exp,‘text’)
},
model(node,vm,exp){
this.bind(node,vm,exp,‘model’)
node.addEventListener(‘input’,e=>{
vm.KaTeX parse error: Expected 'EOF', got '}' at position 35: …value }̲) }, b…options.methods
&&vm.$options.methods[exp]
if(eventType&&fn){
node.addEventListener(eventType,
fn.bind(vm))
}
}
}
//更新函数,也是watcher的cb
const updater={
textUpdater(node,value){
if(value){
node.textContent=value
},
modelUpdater(node,value){
if(value){
node.value=value
},
},
本文详细介绍了MVVM模式的基本原理,包括数据响应式、DOM操作和事件处理。通过一个简单的MVVM实现,阐述了如何使用原型链继承、Object.defineProperty进行数据劫持、以及如何通过观察者模式实现数据变化的实时更新。同时,讲解了文档碎片、节点类型、事件监听等DOM操作技巧,帮助读者深入理解MVVM的工作机制。
2141

被折叠的 条评论
为什么被折叠?



