Vue2源码学习
感觉工作了几个月了,也该看一看源码了,然后就捣鼓出了个盗版vue.js
实现的功能有模板渲染,生命周期,数据劫持,双向绑定,添加点击事件等很基础的功能,下面是源码
每天看掘金上的源码理论讲解,什么compile和watcher之类的看麻了,实现一下之后就有很大的感悟
首先在Vue的类中将$el和$data挂载在实例对象上,方便后续的操作
模板的编译思想就是,设置正则匹配,获取根节点后,再去获取根节点的子节点,循环去匹配文本节点,利用字符串的replace方法结合正则表达式,匹配到设置了模板字符串的文本节点和键,再利用键从data中获取相应值去替换文本节点
生命周期简单些,就是传递几个函数,判断类型,如果是函数就执行,顺序不能随便放,执行顺序是固定的,后期需要通过bind改变this指向
这是html内容
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 
 | copyvye.html<!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">
 <h1>{{ str }}</h1>
 <h2>{{ b }}</h2>
 <input type="text" v-model="modelStr">
 <button @click="changeStr">change str</button>
 <h1>{{ modelStr }}</h1>
 </div>
 <script src="./copyVue.js"></script>
 <script>
 new Vue({
 el:"#app",
 data:{
 str:'hao hello',
 b:'好嗨欧',
 modelStr:'v-model关联字符'
 },
 methods: {
 changeStr(){
 this.str = '我终于更新了'
 }
 },
 beforeCreate() {
 console.log('beforeCreate')
 },
 created() {
 console.log('created')
 },
 beforeMount() {
 console.log('beforeMount')
 },
 mounted() {
 console.log('mounted')
 }
 })
 </script>
 </body>
 </html>
 
 | 
这是copyVue.js内容
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 
 | copyVue.jsclass Vue {
 constructor(options){
 if(typeof options.beforeCreate === 'function'){
 options.beforeCreate.bind(this)()
 }
 this.$options = options
 this.$data = options.data
 this.$watchEvent = {}
 this.proxyData()
 this.observe()
 if(typeof options.created === 'function'){
 options.created.bind(this)()
 }
 if(typeof options.beforeMount === 'function'){
 options.beforeMount.bind(this)()
 }
 this.$el = document.querySelector(options.el)
 if(typeof options.mounted === 'function'){
 options.mounted.bind(this)()
 }
 this.compile(this.$el)
 }
 // 劫持数据 此时并未更新视图
 proxyData(){
 for(let key in this.$data){
 Object.defineProperty(this,key,{
 get(){
 return this.$data[key]
 },
 set(val){
 this.$data[key] = val
 }
 })
 }
 }
 // 数据被修改但是视图并未更新
 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 => {
 item.update()
 })
 }
 }
 })
 }
 }
 compile(node){
 let reg = /\{\{(.*?)\}\}/g;
 node.childNodes.forEach(item => {
 // 首先需要判断节点类型
 if(item.nodeType === 1){
 if(item.hasAttribute('@click')){
 let vmKey = item.getAttribute('@click').trim()
 item.addEventListener('click',(event)=>{
 this.eventFn = this.$options.methods[vmKey].bind(this)
 this.eventFn(event)
 })
 }
 if(item.hasAttribute('v-model')){
 let vmKey = item.getAttribute('v-model').trim()
 if(this.hasOwnProperty(vmKey)){
 item.value = this[vmKey]
 }
 item.addEventListener('input',(event)=>{
 this[vmKey] = item.value
 })
 }
 if(item.childNodes.length > 0){
 this.compile(item)
 }
 }
 if(item.nodeType === 3){
 // 使用另外一个变量保存文本节点的内容
 let nodeContent = item.textContent
 item.textContent = nodeContent.replace(reg,(match,vmKey)=>{
 vmKey = vmKey.trim()
 if(this.hasOwnProperty(vmKey)){
 let watcher = new Watch(this,vmKey,item,'textContent')
 if(this.$watchEvent[vmKey]){
 this.$watchEvent[vmKey].push(watcher)
 }else{
 this.$watchEvent[vmKey] = []
 this.$watchEvent[vmKey].push(watcher)
 }
 }
 return this.$data[vmKey]
 })
 }
 })
 }
 }
 class Watch {
 constructor(vm,key,node,attr){
 this.vm = vm
 this.key = key
 this.node = node
 this.attr = attr
 }
 update(){
 this.node[this.attr] = this.vm[this.key]
 }
 }
 
 | 
loading……