1.vue源码-模版解析
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <div id='app'> <h1> {{ str }} </h1> {{ str }} </div> </body> <script type="text/javascript" src='vue.js'></script> <script type="text/javascript"> new Vue({ el:'#app', data: { str:'您好' } }) </script> </html>
class Vue{ constructor(options){ this.$el = document.querySelector(options.el) this.$data = options.data this.compile(this.$el) } compile(node){ node.childNodes.forEach((item.index) => { // 元素节点 if(item.nodeType == 1){ this.compile(item) } // 这里是文本节点,如果有{{}}就替换成数据 if(item.nodeType == 3){ // 正则匹配 let reg = /\{\{(.*?)\}\}/g let text = item.textContent // 给节点赋值 item.textContent = text.replacce(reg,(match,vmKey) => { vmKey = vmKey.trim() return this.$data[vmKey] }) } }) } }
2.vue源码-生命周期
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <div id='app'> <h1> {{ str }} </h1> {{ str }} </div> </body> <script type="text/javascript" src='vue.js'></script> <script type="text/javascript"> new Vue({ el:'#app', data: { str:'您好' }, created(){ console.log('created',this.$el, this.$data) } beforeCreate(){ console.log('beforeCreate',this.$el, this.$data) } mounted(){ console.log('mounted',this.$el, this.$data) } beforeMount(){ console.log('beforeMount',this.$el, this.$data) } }) </script> </html>
得到的结果应该是:
beforeCreate undefined undefined
created undefined data
beforeMount undefined data
mounted el data
class Vue{ constructor(options){ if(typeof options.beforeCreate == 'function'){ options.beforeCreate.bind(this)() } // 这是data this.$data = options.data if(typeof options.created == 'function'){ options.beforeCreate.bind(this)() } if(typeof options.beforeMount == 'function'){ options.beforeCreate.bind(this)() } // 这是节点 this.$el = document.querySelector(options.el) // 模板渲染 this.compile(this.$el) if(typeof options.mounted == 'function'){ options.beforeCreate.bind(this)() } } compile(node){ node.childNodes.forEach((item.index) => { // 元素节点 if(item.nodeType == 1){ this.compile(item) } // 这里是文本节点,如果有{{}}就替换成数据 if(item.nodeType == 3){ // 正则匹配 let reg = /\{\{(.*?)\}\}/g let text = item.textContent // 给节点赋值 item.textContent = text.replacce(reg,(match,vmKey) => { vmKey = vmKey.trim() return this.$data[vmKey] }) } }) } }
3.vue源码-添加事件
<body> <h1>{{str}}</h1> <p>{{str}}</p> <button @click='btn'>按钮</button> </body> <script type="text/javascript"> new Vue({ el: '#app', data:{ str:'您好' }, methods:{ btn(){ console.log(this.str) // undefined } } }) </script>
注意这里点击按钮,并不能得到str的数据,还是undefined,因为如下图,其中并没有str,要得到str的数据,应该执行this.$data.str,但是对于实际应用中,执行的是this.str,所以还需要改变。
this.$options = options compile(node){ node.childNodes.forEach((item.index) => { // 元素节点 if(item.nodeType == 1){ // 判断元素节点是否绑定了@click if(item.hasAttribute('@click')){ // @click后绑定的属性值 let vmKey = item.getAttribute('@click').trim() item.addEventListener('click',(event) => { this.eventFn = this.$options.methods[vmKey].bind(this) this.eventFn(event) }) } if(item.childNodes.length>0){ this.compile(item) } } // 这里是文本节点,如果有{{}}就替换成数据 if(item.nodeType == 3){ // 正则匹配 let reg = /\{\{(.*?)\}\}/g let text = item.textContent // 给节点赋值 item.textContent = text.replacce(reg,(match,vmKey) => { vmKey = vmKey.trim() return this.$data[vmKey] }) } }) }
4.vue源码-data劫持
<body> <h1>{{str}}</h1> <p>{{str}}</p> <button @click='btn'>按钮</button> </body> <script type="text/javascript"> new Vue({ el: '#app', data:{ str:'您好' }, methods:{ btn(){ console.log(this.str) // 123 this.str = 456 console.log(this.str) // 456,但是视图并不会改变 } } }) </script>
想要直接通过this.str获得数据,就要在vue外部添加数据
vue{
$data:{str:123,b:'456'}
$el
options
eventFn
str:123
b:'456'
}
注意:其中存在一个问题,就是修改其中的数据,视图并没有改变,若想视图发生改变,就需要使用innerHTML、innerText、textContent
class Vue{ constructor(options){ this.$options = options this.proxyData() if(typeof options.beforeCreate == 'function'){ options.beforeCreate.bind(this)() } // 这是data this.$data = options.data if(typeof options.created == 'function'){ options.beforeCreate.bind(this)() } if(typeof options.beforeMount == 'function'){ options.beforeCreate.bind(this)() } // 这是节点 this.$el = document.querySelector(options.el) // 模板渲染 this.compile(this.$el) if(typeof options.mounted == 'function'){ options.beforeCreate.bind(this)() } } // 1.给vue大对象赋属性,来自于data中 // 2.data中的属性值和vue大对象的属性双向(劫持) proxyData(){ for(let key in this.$data){ Object.defineProperty(this,key,{ get(){ return this.$data[key] }, set(val){ this.$data[key] = val } }) } } }
5.vue源码-更新视图
经过劫持data,发现修改数据之后,会发生数据改变了,但是视图并没有改变
class Vue{ constructor(options){ this.$options = options this.$watchEvent = {} if(typeof options.beforeCreate == 'function'){ options.beforeCreate.bind(this)() } // 这是data this.$data = options.data this.proxyData() this.observe() if(typeof options.created == 'function'){ options.beforeCreate.bind(this)() } if(typeof options.beforeMount == 'function'){ options.beforeCreate.bind(this)() } // 这是节点 this.$el = document.querySelector(options.el) // 模板渲染 this.compile(this.$el) if(typeof options.mounted == 'function'){ options.beforeCreate.bind(this)() } } // 1.给vue大对象赋属性,来自于data中 // 2.data中的属性值和vue大对象的属性双向(劫持) proxyData(){ for(let key in this.$data){ Object.defineProperty(this,key,{ get(){ return this.$data[key] }, set(val){ this.$data[key] = val } }) } } // 触发data中的数据发生变化来执行watch中的update observe(){ for(let key in this.$data){ let value = this.$data[key] let that = this Object.defineProperty(this.$data,key,{ get(){ return value }, set(val){ value = val if(that.$watchEvent[key]){ that.$watchEvent[key].forEach((item,index) => { item.update() }) } } }) } } compile(node){ node.childNodes.forEach((item.index) => { // 元素节点 if(item.nodeType == 1){ // 判断元素节点是否绑定了@click if(item.hasAttribute('@click')){ // @click后绑定的属性值 let vmKey = item.getAttribute('@click').trim() item.addEventListener('click',(event) => { this.eventFn = this.$options.methods[vmKey].bind(this) this.eventFn(event) }) } if(item.childNodes.length>0){ this.compile(item) } } // 这里是文本节点,如果有{{}}就替换成数据 if(item.nodeType == 3){ // 正则匹配 let reg = /\{\{(.*?)\}\}/g let text = item.textContent // 给节点赋值 item.textContent = text.replacce(reg,(match,vmKey) => { vmKey = vmKey.trim() if(this.hasOwnProperty(vmKey)){ let watch = new Watch(this,vmKey,item,'textContent') if(this.$watchEvent[vmKey]){ this.$watchEvent[vmKey].push(watch) }else{ this.$watchEvent[vmKey] = [] this.$watchEvent[vmKey].push(watch) } } return this.$data[vmKey] }) } }) } } class Watch{ constructor(vm,key,node,attr){ // 对象 this.vm = vm // 属性名称 this.key = key // 节点 this.node = node // 改变文本节点内容的字符串 this.attr = attr } //执行改变(update)操作 update(){ this.node[this.attr] = this.vm[this.key] } }