vue源码-双向绑定

905 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

前言

学习框架,仅仅会使用是不够的,想要更熟悉框架,需要对源码有一定的了解。本篇文章通过vue源码解析,实现vue的双向绑定。

介绍

单向绑定,是将Model绑定到View中,如果更新了Model,那么View中的数据也会跟着改变,此时就是单向绑定

双向绑定,在单向绑定的基础上,如果修改View中的数据,Model中数据也会跟着改变,此时就是双向绑定

双向绑定

  <div id="app">
    <h1>{{message}}</h1>
    <input type="text" v-model="message">
  </div>

  <script src="vue.min.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        message: 'helloWorld',
      }
    })
  </script>

实现

接下来,我们就来介绍一下vue中双向绑定v-model的原理,并实现

  • 创建双向绑定的DOM结构

    <input v-model="message">
    
  • vue中,循环每一个节点,找到包含v-model的节点

    通过hasAttribute('v-model')进行判断节点是否添加了 v-model

    • 如果节点添加了v-model,就获取属性值

      通过getAttribute('v-model')获取属性名为v-model的属性值

    • 还需要判断data中是否有v-model对应属性值

      通过hasOwnProperty(vmKey)判断data中是否有vmKey属性,这里的vmKeymessage

      if(this.hasOwnProperty(vmKey)){
      	console.log(this,this[vmKey])  
      }
      

      此时的thisvue对象,通过vue[vmKey]可以获取到属性对应的值

      问题:

      获取不到data中的message,只能获取到data$data下的message

      console.log(this,vmKey,this[vmKey],this.$data[vmKey])

      image-20220615095714106

      所以我就通过判断data中的$data是否有vmKey属性

      if(this.$data.hasOwnProperty(vmKey)){
                  console.log(this.$data[vmKey])            
                }
      

      image-20220615095936020

      • 如果有对应属性值,那么就把data中对应的值取出来,赋值给当前节点的value

        例如:v-model绑定了message,那么还需要判断data中是否有message属性,如果有,就取出datamessage对应的属性值,将对应属性值赋值给,v-model绑定的input框的value,从而实现Model数据传输到View

        还需要实现v-model绑定的input框中value值改变时,data中对应的属性也要跟着改变,这里需要利用前面说的数据劫持

         if(this.$data.hasOwnProperty(vmKey)){
                    // data中对应的值取出来,赋值给当前节点的value中
                    item.value = this.$data[vmKey]         
                  }
        

        image-20220615100116836

      • 为节点添加事件

        为节点添加事件,将input中的value赋值给data中的message,此时就能实现双向绑定了

                  item.addEventListener('input',e => {
                    this[vmKey] = item.value
                  })
        

        这里的前提是对数据进行劫持了,当我们把value值赋值给对象的属性中,observe()中的set就会监听到,进而调用update去更新视图

完整代码

        // 双向绑定,判断元素节点是否被包含v-model
        if(item.hasAttribute('v-model')){
          // 获取属性名为v-model对应的属性值,去除两边空格
          let vmKey = item.getAttribute('v-model').trim()
          // 判断当前节点中是否包含属性值(例:message),即data中是否有message属性,这里的this是vue对象
          // 问题:获取不到data中的message,只能获取到data中$data下的message ,这里不太理解
          // console.log(this,vmKey,this[vmKey],this.$data[vmKey])            
          if(this.$data.hasOwnProperty(vmKey)){
            // data中对应的值取出来,赋值给当前节点的value中
            item.value = this.$data[vmKey]         
          }
          // 为节点添加事件,将input中的value赋值给data中的message,此时就能实现双向绑定了
          item.addEventListener('input',e => {
            this.$data[vmKey] = item.value
          })
        }

效果

双向绑定2

总结

双向绑定是改变Model然后View也会改变,改变View然后Model也会改变,

Model->View 实际上是通过Object.defineProperty劫持数据的改变,如果数据改变了,就更新视图(数据变化,set监听到后调用update去更新视图)

View->Model 则是通过监听元素节点中包含 v-model 的节点,并添加事件,监听value变化。例如是input绑定了v-model,首先找到该input,然后就为input绑定事件,监听inputvalue的变化,只要value变化,就将值赋给vue对象中的data,此时data变化又触发Model->View