vue实现原理,实现数据的双向绑定
<!DOCTYPE html>
<!-- 数据的双向绑定 -->
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<div id="app">
<input type="text" v-model="msg">
<h1>{{msg}}</h1>
<!-- v-html绑定的时h1对象上的innerhtml-->
<h1 v-html="msg"></h1>
<button type="button" @click="changeEvent">修改MSG</button>
</div>
<script type="text/javascript">
class Vue{
constructor(options) {
//通过选择获取根对象
this.$el = document.querySelector(options.el);
//数据创造之前
if(typeof options.beforeCreate == 'function'){
options.beforeCreate.bind(this)()
}
this.$options= options
this.$watchEvent = {}
//this.$watchEvent[key] = [event,event,event]
//设置1个对象专门保存修改更新的事件
//代理options的data数据
this.proxyData()
//劫持设置事件
this.observe()
//数据创造之后
if(typeof options.createcd == 'function'){
options.createcd .bind(this)()
}
//console.log(options.data)
//挂载前
if(typeof options.beforeMount == 'function'){
options.beforeMount .bind(this)()
}
//把view的数据喝事件进行绑定
this.compile(this.$el)
//挂载后
if(typeof options.mounted == 'function'){
options.mounted.bind(this)()
}
}
proxyData(){
//循环通过set,get方法来实现代理数据
for(let key in this.$options.data){
//console.log(key)
Object.defineProperty(this,key,{
configurable:false,
enumerable:true,//迭代,默认false
//value:"定义值"
//writeable:true,false"是否能够更改"
get(){
//获取this[key]时,即返回options的data[]
return this.$options.data[key]
},
set(val){
this.$options.data[key] = val
},
})
}
}
observe(){
//劫持事件
for(let key in this.$options.data){
//获取此处value保存
let value = this.$options.data[key]
let that = this
Object.defineProperty(this.$options.data,key,{
configurable:false,
enumerable:true,//迭代,默认false
//value:"定义值"
//writeable:true,false"是否能够更改"
get(){
//获取this[key]时,即返回options的data[]
//console.log("触发获取内容事件")
return value
},
set(val){
value = val
//console.log("触发设置事件")
//触发以这个key值的更新事件
if(that.$watchEvent[key]){
//console/log(that.$watchEvent[key])
that.$watchEvent[key].forEach((item,index)=>{
item.updata()//通过updata更新
})
}
},
})
}
}
compile(cNode){
//console.log([this.$el])
cNode.childNodes.forEach((node,index)=>{
if(node.nodeType == 1){
//元素类型,getAttribute,hasAttribute
if(node.hasAttribute('v-html')){
//console.log(vmKey)
let vmKey = node.getAttribute('v-html').trim();
if(this.hasOwnProperty(vmKey)){
node.innerHTML = this[vmKey]
let watcher = new Watch(this,vmKey,node,'innerHTML')
if(this.$watchEvent[vmKey]){
this.$watchEvent[vmKey].push(watcher)
}else{
this.$watchEvent[vmKey] = [];
this.$watchEvent[vmKey].push(watcher)
}
//删除属性
node.removeAttribute('v-html')
}
}
//判断是否由v-model属性
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')v
if(this.$watchEvent[vmKey]){
this.$watchEvent[vmKey].push(watcher)
}else{
this.$watchEvent[vmKey] = [];
this.$watchEvent[vmKey].push(watcher)
}
}
//节点input事件监听
node.addEventListener('input',(event)=>{
this[vmKey] = node.value;
})
//删除属性
node.removeAttribute('v-model')
}
//判断是否有数据绑定@click属性
if(node.hasAttribute('@click')){
let vmKey = node.getAttribute('@click').trim();
node.addEventListener('click',(event)=>{
this.eventFn= this.$options.methods[vmKey].bind(this)
this.eventFn(event)
})
}
if(node.childNodes.length>0){
this.compile(node)//将节点放进去继续编译
}
}
if(node.nodeType == 3){
//文本类型
let reg = /\{\{(.*?)\}\}/g; //.*?就是需要匹配的内容,g全局配置
//console.log()
let text = node.textConent;
//console.log('xxxxx')
node.textContent = node.textContent.replace(reg,(match,vmKey)=>{
//console.log(match)
//console.log(vmKey)
//去除空格
vmKey = vmKey.trim()
if(this.hasOwnProperty(vmKey)){
node.value = this[vmKey];
let watcher = new Watch(this,vmKey,node,'textConent')
if(this.$watchEvent[vmKey]){
this.$watchEvent[vmKey].push(watcher)
}else{
this.$watchEvent[vmKey] = [];
this.$watchEvent[vmKey].push(watcher)
}
}
return this[vmKey]
}) //返回替换的字符串
}
})
}
}
class Watch{
constructor(vm,Key,node,attr,nType){
this.vm = vm;
//vm实例化的app对象
this.key = Key;
//key即绑定的vm触发的属性
this.node = node;
//node即,此vm[key]数据绑定的html节点
//this.property = property;
//property即vm数据所绑定的属性的名称
this.attr = attr;
}
updata(){
//更新之前
if(typeof options.beforeUpdata == 'function'){
options.beforeUpdata.bind(this)()
}
this.node[this.attr] = this.vm[this.key];
//数据更新之后
if(typeof options.updatad == 'function'){
options.updatad.bind(this)()
}
}
}
</script>
<script type="text/javascript">
let options = {
el:"#app",
data:{
msg:"hello vue",
username:"小明",
},
methods:{
changeEvent:function(){
this.msg="hello laoli"
}
},
beforeMount(){
//挂载前
console.log("挂载前")
},
mounted(){
console.log('挂载后')
}
}
let app = new Vue(options)
//app.msg == options.data
//app.msg = 'abc'
//options.data.msg = 'abc'
console.log(app)
</script>
</body>
</html>
实现代码视频链接:https://www.bilibili.com/video/BV1m741137Q5?p=32