<!DOCTYPE html>
<html>
<head>
<title>vue源码学习</title>
<meta charset = "utf-8">
</head>
<body>
<div id="app">
<input v-model="msg"/>
<p>{{msg}}</p>
<h1 v-html="msg"></h1>
<button @click="changeEvent">点击事件</button>
</div>
<script>
// 1.构造vue(构造函数/类)
class Vue{
constructor(options){
this.$el = document.querySelector(options.el);
this.$options = options;
this.$watchEvent = {}; //监听事件,按照key保存 this.$watchEvent[key] = [event1,event2,...]
// 代理数据(循环通过set和get实现代理)
this.proxyData(options);
// 劫持事件
this.observe();
//将视图的数据、事件绑定
this.compile(this.$el);
}
proxyData(options){
for(let key in options.data){
Object.defineProperty(this,key,{
configurable:false,
enumerable:true,
set(newValue){
options.data[key] = newValue;
},
get(){
return options.data[key];
}
})
}
}
observe(){
for(let key in this.$options.data){
let value = this.$options.data[key];
let _this = this;
Object.defineProperty(this.$options.data,key,{
configurable:false,
enumerable:true,
set(newValue){
value = newValue;
if(_this.$watchEvent[key]){
_this.$watchEvent[key].forEach(item=>{
item.update()
})
}
},
get(){
return value;
}
})
}
}
compile(PNode){
PNode.childNodes.forEach((node,index)=>{
if(node.nodeType == 1){
// console.log([node]) //可查看节点对象
// 元素类型
if(node.hasAttribute("v-html")){
let vmKey = node.getAttribute("v-html").trim();
node.innerHTML = this[vmKey];
let watcher = new Watch(this,vmKey,node,'innerHTML');
if(!this.$watchEvent[vmKey]){
this.$watchEvent[vmKey] = [];
}
this.$watchEvent[vmKey].push(watcher);
}
if(node.hasAttribute("v-model")){
let vmKey = node.getAttribute("v-model").trim();
if(this.hasOwnProperty(vmKey)){
node.value = this[vmKey]
}
let watcher = new Watch(this,vmKey,node,'value');
if(!this.$watchEvent[vmKey]){
this.$watchEvent[vmKey] = [];
}
this.$watchEvent[vmKey].push(watcher);
// 事件监听
node.addEventListener('input',(e)=>{
this[vmKey] = e.target.value;
})
}
//判断绑定@click事件
if(node.hasAttribute("@click")){
node.addEventListener('click',(e)=>{
let vmKey = node.getAttribute("@click").trim();
let fn = this.$options.methods[vmKey].bind(this);
fn(e);
})
}
if(node.childNodes.length>0){
this.compile(node)
}
}else if(node.nodeType == 3){
// 正则匹配文本类型
let reg = /\{\{(.*?)\}\}/g;
let text = node.textContent;
node.textContent = text.replace(reg,(match,vmKey)=>{
vmKey = vmKey.trim();
let watcher = new Watch(this,vmKey,node,'textContent');
if(!this.$watchEvent[vmKey]){
this.$watchEvent[vmKey] = [];
}
this.$watchEvent[vmKey].push(watcher);
return this[vmKey]
})
}
})
}
}
class Watch{
constructor(vm,key,node,attr,nType){
this.vm = vm; //实例化的app对象
this.key = key; //即绑定vm触发的属性
this.node = node; //vm[key]数据绑定的html的节点
this.attr = attr; //vm[key]数据绑定html节点属性名称
this.nType = nType;
}
update(){
this.node[this.attr] = this.vm[this.key]
}
}
</script>
<script>
let app = new Vue({
el:'#app',
data:{
msg:'hahaha'
},
methods:{
changeEvent:function(){
this.msg = "hello"
}
}
})
</script>
</body>
</html>
网友评论