<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-model="v">{{ v }}
</div>
</body>
</html>
<script>
/*
*Vue响应式原理
*在创建Vue对象的时候,将options.data里面变量通过Object.defineProperty代理到Vue实例里面,即可通过this.xxx获取变量
*然后遍历所有文本节点,根据正则找到mustach语法里面的变量名,然后将Vue实例对应的变量名的值赋值到节点当中
*双向绑定:遍历监听所有input节点,找到将对应的v-mode属性的变量名,将input框内的值赋值给Vue实例的对应变量名
*
*/
class Vue{
constructor(options){
this.$options=options
this.$data=options.data
this.$el=options.el
//将data添加到响应式系统中
new Observer(this.$data)
//代理this.$data数据
Object.keys(this.$data).forEach(key=>{
this._proxy(key)
})
//处理el
new Compiler(this.$el,this)
}
_proxy(key){
//Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,所以可以通过this.xxx获取Vue实例里面的data的值,详见底部demo
Object.defineProperty(this,key,{
configurable : true,
enumerable : true,
set(newValue){
this.$data[key]=newValue
console.log(newValue);
},
get(){
console.log(this.$data[key]);
return this.$data[key]
}
})
}
}
class Observer{
constructor(data){
this.data=data
Object.keys(data).forEach(key=>{
this.defineReactive(this.data,key,data[key])
})
}
defineReactive(data,key,val){
const dep=new Dep()
Object.defineProperty(data,key,{
configurable : true,
enumerable : true,
get(){
if(Dep.target){
dep.addSub(Dep.target)
}
return val
},
set(newVal){
if(newVal==val){
return
}
val=newVal
dep.notify()
}
})
}
}
class Dep{
constructor(){
this.subs=[]
}
addSub(sub){
this.subs.push(sub)
}
notify(){
this.subs.forEach(sub=>{
sub.update()
})
}
}
class Watcher{
constructor(node,name,vm){
this.node=node
this.name=name
this.vm=vm
Dep.target=this
this.update()
Dep.target=null
}
update(){
this.node.nodeValue=this.vm[this.name]
}
}
const reg=/\{\{(.+)\}\}/
class Compiler{
constructor(el,vm){
this.el=document.querySelector(el)
this.vm=vm
this.frag=this._createFragment()
this.el.appendChild(this.frag)
}
_createFragment(){
//创建片段对象
const frag=document.createDocumentFragment()
let child
while(child=this.el.firstChild){
this._compile(child)
frag.appendChild(child)
}
return frag
}
_compile(node){
//遍历所有节点
if(node.nodeType===1){ //标签节点
const attrs=node.attributes
if(attrs.hasOwnProperty("v-model")){
const name=attrs['v-model'].nodeValue
node.addEventListener("input",e=>{
//如果是标签节点,监听标签节点的值,赋值给Vue实例对应的值,
//赋值时触发Observer.defineReactive 内部的set方法,更新所有节点的内容
this.vm[name]=e.target.value
})
}
}
if(node.nodeType===3){ //文本节点
if(reg.test(node.nodeValue)){
const name=RegExp.$1.trim()
//如果是文本节点,获取文本节点的变量名,将Vue实例的变量名对应的值赋值到文本节点,
new Watcher(node,name,this.vm)
}
}
}
}
const app=new Vue({
el : "#app",
data : {
v : "aa",
v2 : "ccc"
}
})
//Object.defineProperty()demo
const obj2={
name : "aaa"
}
console.log(obj2);
Object.defineProperty(obj2,"player",{
get(){
return "李连杰"
}
})
console.log(obj2);
</script>
vue响应式原理
最新推荐文章于 2024-05-27 10:01:44 发布