随着自己在前端越来越深入,也越来越喜欢前端。自己的路还有很长,希望能和大家一起努力进步。 本人不太习惯写博客,但正在尝试习惯。每增加一个阅读量,感觉就很受到鼓励。 这一次自己摸索出vue双向数据绑定的原理,不足的地方希望大家给予指正
class Mvvm {
constructor({el,data}){
this.$el=document.querySelector(el);//获取dom元素
this.data=data
this.arr=[] //存放每一个监听函数
this.init()
this.initDom()
}
init(){
this.intercept(this.data)
}
intercept(data){//数据劫持
let _this=this
if(!data || typeof(data)!=='object'){
return
}
Object.keys(data).forEach(item=>{ //将对象或数组 KEY 取出 遍历
let val=data[item] //定义每一索引对应的val
Object.defineProperty(data,item,{ //添加劫持
get(){
return val //默认返回每一次遍历取到的数据
},
set(newVal){
if(val===newVal){return} //数据没有变化不执行
val=newVal
_this.arr.forEach(item=>{item()})//每一次触发get 函数都会触发一次获取函数
}
})
this.intercept(val)
})
}
initDom(){
let createObj=this.createElement();
this.findNode(createObj)
this.$el.appendChild(createObj)
}
createElement(){//创建文档碎片
let fragment=document.createDocumentFragment();
let childrens;
while(childrens=this.$el.firstChild){ //将元素里的节点都放到文档碎片
fragment.appendChild(childrens)
}
return fragment
}
findNode(node){//节点处理 在文档碎片里处理数据 不会引起浏览器的重排和重绘
let _this=this;
if(node.nodeType===1){//节点类型为1
let attr=[...node.attributes] //获取节点属性 为一个数组
attr.forEach(item=>{
if(item.nodeName==='v-model'){
node.value=_this.getdata(this.data,item.nodeValue) //获取数据函数
node.addEventListener("input",(e)=>{//input 添加事件
this.data ,
_this.getval(_this.data,item.nodeValue,e.target.value) //input=>value
},false)
}
})
}else if(node.nodeType===3){
let content=node.textContent
if(content.indexOf("{{")>-1){
let str= content.split("{{")[1].split("}}")[0]
node.textContent=this.getdata(this.data,str) //{{}}里的文本替换为数据
//创建watcher 方法[()=>{}]
str && _this.arr.push(_this.watch(this.data,str,(newVal)=>{ //每一个{{}}的文本节点 都会绑定一个 watcher方法=>里面存储着当前文本节点的数据
node.textContent=newVal})) //重点为回调函数 对应watch方法里的callback 每一次触发defineProperty函数的set方法都会触发监听函数 监听函数会返回一个获取到的最新的修改后的val
}
}
if(node.childNodes &&node.childNodes.length>0){//递归 确保每一级节点都能遍历到
[...node.childNodes].forEach(item=>{
this.findNode(item)
})
}
}
getdata(data,key){//解析{{}} 和v-model
if(key.indexOf(".")>-1){
key.split(".").forEach(item=>{
data=data[item]
})
return data
}else{
return data[key]
}
}
getval(data,key,newVal){//input输入事件 将value值赋给数据
if(key.indexOf(".")>-1){
let arr=key.split(".")
for(let i=0;i<arr.length-1;i++){
data=data[arr[i]] //{}/}
}
data[arr[arr.length-1]]=newVal
}else{
data[key]=newVal
}
}
watch=(data,key,callback)=>()=>{//监听函数
let newVal=this.getdata(data,key)
callback(newVal) //此处为回调函数调用的参数 传递给 定义的位置
}
}
new Mvvm({
el:".box",
data:{
val:"我帅不帅",
person:{
a:{
b:"当然"
}
},
son:{
a:{
b:{
c:{
d:"1123"
}
}
}
}
}
})