SOURCE

console 命令行工具 X clear

                    
>
console
function Vue(options) {
    this._init(options);
}

Vue.prototype._init = function(options) {
    this.$options = options;
    initData(this);
}

function initData(vm) {
    // 获取 data 选项
  let { data } = vm.$options
  // 设置 vm._data 选项,保证它的值肯定是一个对象
  if (!data) {
    vm._data = {}
  } else {
    vm._data = typeof data === 'function' ? data() : data
  }
  // 代理,将 data 对象上的的各个属性代理到 Vue 实例上,支持 通过 this.xx 的方式访问
  for (let key in vm._data) {
    proxy(vm, '_data', key)
  }
  // 设置响应式
  observe(vm._data);
}

function proxy(target, sourceKey, key) {
  Object.defineProperty(target, key, {
    // target.key 的读取操作实际上返回的是 target.sourceKey.key
    get() {
      return target[sourceKey][key]
    },
    // target.key 的赋值操作实际上是 target.sourceKey.key = newV
    set(newV) {
      target[sourceKey][key] = newV
    }
  })
}

function observe(value) {
    // 避免无限递归
    // 当 value 不是对象直接结束递归
    if (typeof value !== 'object') return

    // value.__ob__ 是 Observer 实例
    // 如果 value.__ob__ 属性已经存在,说明 value 对象已经具备响应式能力,直接返回已有的响应式对象
    if (value.__ob__) return value.__ob__

    // 返回 Observer 实例
    return new Observer(value)
}

function Observer(value) {
    // 为对象设置 __ob__ 属性,值为 this,标识当前对象已经是一个响应式对象了
    Object.defineProperty(value, '__ob__', {
        value: this,
        // 设置为 false,禁止被枚举,
        // 1、可以在递归设置数据响应式的时候跳过 __ob__ 
        // 2、将响应式对象字符串化时也不限显示 __ob__ 对象
        enumerable: false,
        writable: true,
        configurable: true
    })
    if (Array.isArray(value)) {
        // 数组响应式
        protoArgument(value)
        this.observeArray(value)
    } else {
        // 对象响应式
        this.walk(value)
    }
}


Observer.prototype.walk = function (obj) {
    for (let key in obj) {
        defindReactive(obj, key, obj[key]);
    }
}
Observer.prototype.observeArray = function (arr) {
    for(let item of arr) {
        observe(item);
    }
}

function defindReactive(obj, key, val) {
    observe(val);
    Object.defineProperty(obj, key, {
        get() {
            ctrLog(`getter: key = ${key}`);
            return val;
        },
        set(newValue) {
            if (newValue === val) return;
            val = newValue;
            // observe(val);
        }
    })
}


const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
const arrayToPatch = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];

arrayToPatch.forEach(method=>{
    Object.defineProperty(arrayMethods, method, {
        value: function(...args) {
            const ret = arrayProto[method].apply(this, args);
            ctrLog(`array reactive`)
            return ret;
        },
        enumerable: true,
        writable: true,
        configurable: true
    });
});
function protoArgument(arr) {
    arr.__proto__ = arrayMethods
}


const ins = new Vue({
    el: '#app',
    data() {
    return {
        // 原始值和对象的响应式原理
        t: 't value',
        t1: {
            tt1: 'tt1 value'
        },
        // 数组的响应式原理
        arr: [1, 2, 3],
        // 响应式更新
        counter: 0,
        // v-bind
        title: '看我',
        // v-model
        inputVal: 'test',
        isChecked: true,
        selectValue: 2
    }
    },
    // methods + 事件 + 数据响应式更新 原理
    methods: {
        handleAdd() {
            this.counter++
        },
        handleMinus() {
            this.counter--
        }
    },
})
// 数据响应式拦截
setTimeout(() => {
    ctrLog('********** 属性值为原始值时的 getter、setter ************')
    ctrLog(ins.t)
    ins.t = 'change t value'
    ctrLog(ins.t)
}, 1000)

setTimeout(() => {
    ctrLog('********** 属性的新值为对象的情况 ************')
    ins.t = {
        tt: 'tt value'
    }
    ctrLog(ins.t.tt)
}, 2000)

setTimeout(() => {
    ctrLog('********** 验证对深层属性的 getter、setter 拦截 ************')
    ins.t1.tt1 = 'change tt1 value'
    ctrLog(ins.t1.tt1)
}, 3000)

setTimeout(() => {
    ctrLog('********** 将值为对象的属性更新为原始值 ************')
    ctrLog(ins.t1)
    ins.t1 = 't1 value'
    ctrLog(ins.t1)
}, 4000)

setTimeout(() => {
    ctrLog('********** 数组操作方法的拦截 ************')
    ctrLog(ins.arr)
    ins.arr.push(4)
    ctrLog(ins.arr)
}, 5000)

function ctrLog(info) {
    const isShowLog = true;
    if (isShowLog) {
        console.log(info);
    }
}
<div id="app">
    <h3>数据响应式更新 原理</h3>
    <div>{{ t }}</div>
    <div>{{ t1 }}</div>
    <div>{{ arr }}</div>
    <h3>methods + 事件 + 数据响应式更新 原理</h3>
    <div>
        <p>{{ counter }}</p>
        <button v-on:click="handleAdd"> Add </button>
        <button v-on:click="handleMinus"> Minus </button>
    </div>
    <h3>v-bind</h3>
    <span v-bind:title="title">右键审查元素查看我的 title 属性</span>
    <h3>v-model 原理</h3>
    <div>
        <input type="text" v-model="inputVal" />
        <div>{{ inputVal }}</div>
    </div>
    <div>
        <input type="checkbox" v-model="isChecked">
        <div>{{ isChecked }}</div>
    </div>
    <div>
        <select v-model="selectValue">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        </select>
        <div>{{ selectValue }}</div>
    </div>
</div>