持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情
前言
学习框架,仅仅会使用是不够的,想要更熟悉框架,需要对源码有一定的了解。本篇文章通过vue源码解析,实现vue的视图更新。
介绍
在vue
中,我们如果修改了vm
对象中的data
属性,那么视图也会跟着修改,这就是vue
的响应式,也是vue
最独特的特性之一。上篇文章,讲到如何劫持data
,并将data
中数据同步更新到vm
对象中,但是对于视图响应式还没有实现,本篇文章就来实现视图的响应式。
实现
首先需要拿到$data
中的所有属性,将它们存储到对象里面,看到底哪一个改变,改变的属性就通过update
更新
-
创建对象
this.$watchEvent = {}
-
将
data
中属性存储到对象中-
判断对象中是否有视图中文本对应属性
-
如果没有,则
push
一个对象 -
如果有,那么则清空后
push
一个对象这里
push
的对象是通过定义一个Watch
构造函数,该构造函数是用于执行改变操作的// 文本节点 if (item.nodeType == 3) { let reg = /\{\{(.*?)\}\}/g let text = item.textContent item.textContent = text.replace(reg, (match, vmKey) => { vmKey = vmKey.trim() // 判断视图中是否有vmKey,vmKey就是message、name这些 if(this.hasOwnProperty(vmKey)){ // 判断对象中是否有视图中文本对应属性 if(this.$watchEvent[vmKey]){ this.$watchEvent[vmKey].push() }else{ this.$watchEvent[vmKey] = [] this.$watchEvent[vmKey].push() } } return this.$data[vmKey] }) }
-
-
-
创建
Watch
构造函数-
构造方法接收四个参数:当前对象
vm
、data
中的属性名、node
节点、属性名,并将四个参数赋值给当前构造函数let watch = new Watch(this, vmKey, item, 'textContent')
-
执行改变操作
-
找到对象
vm
中的data属性名,赋值给node
节点中的属性名例如:
data
中的message:'HelloWorld'
,修改后为message:'Hello'
,修改后找到node
节点中属性名为message
,将值更新为Hello
。从而实现赋值现在,已经有了执行改变操作的方法,但是如何让Watch知道对象
vm
中的data
属性,发生变化,需要调用update
来更新视图了呢?这里我们需要一个方法,用于劫持
data
中的数据变化。// 创建Watch构造函数 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] } }
-
-
-
劫持
data
中属性变化- 循环每一个
data
属性,获取到data
中的值 - 劫持
data
中的属性,在get
中直接返回原来的值,如果值改变了,即set
触发,那么获取到劫持的值,并调用update
,执行改变操作
constructor(options) { ... // 劫持data中的数据 this.observe() } // 劫持data中的数据,当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) { console.log('改变了'); value = val if (that.$watchEvent[key]) { that.$watchEvent[key].forEach(item => { item.update() }) } } }) } }
- 循环每一个
总结
更新视图,实际上就是通过劫持data
属性的变化,如何获取到修改后的值,再调用update
去修改模板中对应的值,实现视图的更新。