1.vue源码-模版解析
javascript"><!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>
javascript">class Vue{constructor(options){this.$el = document.querySelector(options.el)this.$data = options.datathis.compile(this.$el)}compile(node){node.childNodes.forEach((item.index) => {// 元素节点if(item.nodeType == 1){this.compile(item)}// 这里是文本节点,如果有{{}}就替换成数据if(item.nodeType == 3){// 正则匹配let reg = /\{\{(.*?)\}\}/glet text = item.textContent// 给节点赋值item.textContent = text.replacce(reg,(match,vmKey) => {vmKey = vmKey.trim()return this.$data[vmKey]})}})}}
2.vue源码-生命周期
javascript"><!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
javascript">class Vue{constructor(options){if(typeof options.beforeCreate == 'function'){options.beforeCreate.bind(this)()}// 这是datathis.$data = options.dataif(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 = /\{\{(.*?)\}\}/glet text = item.textContent// 给节点赋值item.textContent = text.replacce(reg,(match,vmKey) => {vmKey = vmKey.trim()return this.$data[vmKey]})}})}}
3.vue源码-添加事件
javascript"><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,所以还需要改变。
javascript">this.$options = optionscompile(node){node.childNodes.forEach((item.index) => {// 元素节点if(item.nodeType == 1){// 判断元素节点是否绑定了@clickif(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 = /\{\{(.*?)\}\}/glet text = item.textContent// 给节点赋值item.textContent = text.replacce(reg,(match,vmKey) => {vmKey = vmKey.trim()return this.$data[vmKey]})}}) }
4.vue源码-data劫持
javascript"><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) // 123this.str = 456console.log(this.str) // 456,但是视图并不会改变}} }) </script>
想要直接通过this.str获得数据,就要在vue外部添加数据
vue{
$data:{str:123,b:'456'}
$el
options
eventFn
str:123
b:'456'
}
注意:其中存在一个问题,就是修改其中的数据,视图并没有改变,若想视图发生改变,就需要使用innerHTML、innerText、textContent
javascript">class Vue{constructor(options){this.$options = optionsthis.proxyData()if(typeof options.beforeCreate == 'function'){options.beforeCreate.bind(this)()}// 这是datathis.$data = options.dataif(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,发现修改数据之后,会发生数据改变了,但是视图并没有改变
javascript">class Vue{constructor(options){this.$options = optionsthis.$watchEvent = {}if(typeof options.beforeCreate == 'function'){options.beforeCreate.bind(this)()}// 这是datathis.$data = options.datathis.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中的updateobserve(){for(let key in this.$data){let value = this.$data[key]let that = thisObject.defineProperty(this.$data,key,{get(){return value},set(val){value = valif(that.$watchEvent[key]){that.$watchEvent[key].forEach((item,index) => {item.update()})}}})}}compile(node){node.childNodes.forEach((item.index) => {// 元素节点if(item.nodeType == 1){// 判断元素节点是否绑定了@clickif(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 = /\{\{(.*?)\}\}/glet 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]}}