vue源码-更新视图

231 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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构造函数

    • 构造方法接收四个参数:当前对象vmdata中的属性名、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去修改模板中对应的值,实现视图的更新。