vue2的数据响应式基础原理

253 阅读2分钟

1.defineProperty

defineProperty其实并不是核心的为一个对象做数据双向绑定,而是去给对象做属性标签,只不过属性里的get和set实现了响应式

属性:

  1. value             默认值:undefined         该属性对应的值
  2. get                默认值:undefined         取值的时候调用的函数
  3. set                 默认值:undefined         赋值的时候调用的函数
  4. writable         默认值:false                  是否可写,更改
  5. enumerable   默认值:true                   是否可枚举,可遍历
  6. configurable  默认值:true                   是否可配置(delete权限问题)


流程:




2.简单版实现

vue2.js:

function vue (){

    this.$data = {a:1}

    this.el = document.getElementById('app')

    this.virtualdom=""

    this.observer(this.$data)

    this.render()

}// 注册get 和 set


vue.prototype.observer = function (obj) {

    var value

    var self = this

    for (var key in obj) {

        value = obj[key]

        if (typeof value === 'object') {

            this.observer(value)

        } else {

            Object.defineProperty(this.$data,key,{

                get:function(){

                    // 依赖收集

                    return value

                },

                set:function (newvalue){

                    value = newvalue

                    self.render()

                }

            })

        }

    }

}
vue.prototype.render = function () {

    this.virtualdom = 'i am ' + this.$data.a

    this.el.innerHTML = this.virtualdom

}


index.html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <title>Document</title>

</head>

<body>

    <div id="app"></div>

</body>

<script src="vue2.js"></script>

<script>

    var vm = new vue()

    setTimeout(()=>{

        console.log('changes')

        console.log(vm.$data)

        vm.$data.a=123

    },2000)

</script>

</html>


运行index.html,两秒后随着数据的改变,页面对应的dom也会发生改变


3.思考:defineProperty get 和 set 都是对象的属性,那么数组怎么办?

装饰者模式:

var arrayPro = Array.prototype

var arrOb = Object.create(arrayPro)

var arr = ['push','pop','shift']

arr.forEach(function (method,index) {

    arrOb[method] = function () {

        arrayPro[method].apply(this,arguments)

        departFocus.notify

    }

})

将数组原型链上的一些方法进行重写


4.defineProperty 还可以做什么?

真正的私有变量

举例:vue-router中的$router对象不可被修改

Object.defineProperty(this,'$router',{
    get (){
        return this._root.router
    }
})

只设置get,不设置set,所以this.$router对象是不可被修改的!