vue双向数据绑定

345 阅读3分钟

参考文献

1. 原理

vue的双向数据绑定主要是通过Object对象的defineProperty属性,重写data的set和get函数来实现。

vue是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调。

主要实现v-bindv-modelv-click

2. v-model

v-model原理其实就是给input事件绑定oninput事件 就会立刻调用底层对象对应的setter方法 改变data里的属性的值 从而实现双向数据绑定

比如一个单选框

<ul>
    <li>性别:
        <input type="radio" name="sex" id="sex1" value="male" v-model="userinfo.sex"> <label for="sex1">男</label>
        <input type="radio" name="sex" id="sex2" value="female" v-model="userinfo.sex"> <label for="sex2">女</label>
    </li>
</ul>
<li>
	<select v-model="user.infocity">
        <option v-for="(item, index) in userinfo.cityList" :key="index" :value="item">{{item}}</option>
    </select>
</li>
<button @click="doSubmit()" class="submit">Submit</button>
<script>
export default {
    data() {
        return {
            username: "kuli",
            userinfo: {
                username: "",
                age: "",
                sex: "male",
                cityList: ["Beijing", "Shanghai", "Shenzhen"],
                city: "Shenzhen",
            }
        }
    },
    methods: {
        doSubmit() {
            console.log(this.userinfo)
        }
    }
}
</script>

img

v-model自定义指令下包裹的语法是input的value属性、input事件

<input v-modle="inputV" />
// 等同于
<input :value="inputV" @input="inputV = $event.target.value"/>

比如checkbox:

// 看似执行了v-model一个指令
<input type="checkbox" v-model="checkedNames">
// 实际上
<input
  type="checkbox" 
  :value="checkedNames" 
  @change="checkedNames = $event.target.value" 
/>

v-model与v-bind的区别有两点:

v-model是双向绑定数据,也就是当父组件的传进去的数据发生改变时,子组件的数据也会发生改变,当子组件发生改变时,父组件也会发生改变

v-bind是单向绑定,当父组件的数据发生改变时,子组件的数据也会改变,但是当子组件发生改变时,父组件的数据不会发生改变

  1. v-model默认绑定value值

  2. v-bind的绑定值是可以自定义的

自定义组件的v-model

父组件标签写法

<my-component v-model="myData" />
复制代码

子组件写法,按照value值绑定和input事件更新值来拆解。

首先,需要在子组件接收一个value的prop(用于值绑定,或者设置初始值)

props:{
  value:{
    type:String,
    default:''
  }
}

然后,在组件内部更新值,在需要更新值的逻辑部分写入

this.$emit('input', newData);

这样就是在组件内部拿到了值,并且手动触发input事件更新了值,但在父组件只需要写入v-model指令即可,也算是父子组件传值的一种。

3. Proxy

Object.defineProperty的缺陷:

Vue2.0中,数据双向绑定就是通过Object.defineProperty去监听对象的每一个属性,然后在get,set方法中通过发布订阅者模式来实现的数据响应,但是存在一定的缺陷,比如只能监听已存在的属性,对于新增删除属性就无能为力了,同时无法监听数组的变化,所以在Vue3.0中将其换成了功能更强大的Proxy

了解Proxy

ProxyES6新推出的一个特性,可以用它去拦截js操作的方法,从而对这些方法进行代理操作。

console.log(typeof Proxy)//function

由上可以得知:

  1. Proxy是定义在window上的全局变量
  2. 它的类型是function
  3. Proxy在构造对象时接受两个参数:targethandler
  4. 两个参数的类型必须是object

所以,target表示的就是要拦截(代理)的目标对象;而handler是用来定制拦截行为

Vue3定义了一系列的响应式API,比如reactive、ref等等,它们的特点是:当时数据发生变化时,页面会对应更新UI,而底层用的就是Proxy!

数据对象obj通过reactive包装成了代理对象,当数据发生变化时,会调用set方法,在更新数据的同时,同时执行一些update的操作。

Proxy特点(important)

  • Proxy可以直接监听对象而非属性
  • Proxy可以直接监听数组变化
  • Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具备的。
  • Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改。
  • Proxy的劣势就是兼容性问题,而且无法用polyfill磨平