手写Vue 对象劫持

Posted guangzhou11

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写Vue 对象劫持相关的知识,希望对你有一定的参考价值。

1.引入我们手写的Vue 拿到配置数据

import Vue from ../source/src/index;
let vm = new Vue({
  el: #app,
  data() {
    return {
      msg: hello,
      school: {
        name: zf,
        age: 10
      },
      arr: [1, 2, 3]
    }
  },
  computed: {

  },
  watch: {

  }
})
console.log(vm)
// setTimeout(() => {
//   vm.arr[0].push(100)
//   console.log(vm)
// }, 1000)

2. 新建文件    source/src/index  

在这个文件中 我们利用 ——init 这个方法 对 用户传入的数据进行初始化 传入配置信息

function Vue(options) {
  // console.log(options)
  // 初始化vue
  this._init(options)
}

3. 在_init 方法中 我们可以先将  options 挂载到实例  再进行初始化数据操作 

Vue.prototype._init = function (options) {
  // vue 的初始化
  let vm = this;
  // 将 options 挂载到实例
  vm.$options = options;
  //需要数据重新初始化
  initState(vm)
}

5. 新建 文件  observe/index 编写  initState  方法并且导出

export function initState(vm) {
  console.log(vm)
}

在这里我们可以拿到 vue 的整个实例方法 

技术图片

6. 在initState  方法中 我们进行初始化  比如初始化 数据 初始化 计算属性 初始化 watch 

 

export function initState(vm) {
  // 拿到option  存储起来
  let options = vm.$options;
  if (options.data) {
    // 初始化数据
    initData(vm)
  }
  if (options.computed) {
    // 初始化计算属性
    initComputed()
  }
  if (options.watch) {
    // 初始化watch
    initWatch()
  }
}

 

7. 初始化数据 在    initData  方法中 我们通过 传入实例的option 拿到数据 再判断 数据 是 函数 还是 对象 如果是函数 调用call 方法 拿到函数 返回值 如何不是直接返回数据 或者 空数据 

 let data = vm.$options.data
  // 判断是否是函数 取返回值
  data = vm._data = typeof data === function ? data.call(vm) : data || {}

 

8. 拿到数据后 我们要对数据进行 监听 编写   observe  方法 监听数据 、

 

function initData(vm) {
  let data = vm.$options.data
  // 判断是否是函数 取返回值
  data = vm._data = typeof data === function ? data.call(vm) : data || {}
  observe(vm._data)
}

 

10 在  observe 方法中 要进行判断 看看数据是不是对象或者为空 如果不是 直接返回 如果是 返回一个  Observe 对象 

export function observe(data) {
  // 不是对象或者是null
  if (typeof data !== object || data === null) {
    return
  }
  return new Observe(data)
}

 

11. 编写这个  Observe 对象   新建文件 Observe.js

这个文件里面最主要的是   Object.defineProperty 方法 里面传入 data , 还有key  key 代表属性 所以 我们需要遍历数据 拿到 所有的 key value 传入 

class Observe {
  constructor(data) {
    // 数组 重写push 方法
    if (Array.isArray()) {
    } else {
      // 对象
      // data 就是我们 定义的 vm._data 的数据
      this.walk(data)
    }
  }
  //将对象的数据使用 defineProperty 重新定义 
  walk(data) {
    let keys = Object.keys(data);
    for (let i = 0; i < keys.length; i++) {
      let key = keys[i];  // key
      let value = data[keys[i]];  // value
      defineReactive(data, key, value)
    }
  }
}
export function defineReactive(data, key, value) {
  Object.defineProperty(data, key, {
    get() {
      // 有值
      return value;
    },
    set(newValue) {
      if (newValue !== value) return;
      value = newValue
    }
  })
}
export default Observe;

 

11.  因为 数据里面 的对象 可能嵌套 一个对象 所以我们应该在 数据里面的对象再进行 深度监听 


import { observe } from ‘./index‘

export function defineReactive(data, key, value) { observe(value); Object.defineProperty(data, key, {
get() { // 有值 return value; }, set(newValue) { if (newValue !== value) return; value = newValue } }) }

技术图片

12. 测试代码 

import Vue from ../source/src/index;
let vm = new Vue({
  el: #app,
  data() {
    return {
      msg: hello,
      school: {
        name: zf,
        age: 10
      },
      arr: [1, 2, 3]
    }
  },
  computed: {

  },
  watch: {

  }
})
console.log(vm._data.msg)  // hello

打印出100 

13.  这里每次取值都需要 挂在 _data 上  很 不方便 比如 

vm._data.msg  我们简化为 vm.msg 
 
所以 我们可以 利用  proxy 代理 _data  代理应该在 初始化数据 的操作中 监听数据前
function initData(vm) {
  let data = vm.$options.data
  // 判断是否是函数 取返回值
  data = vm._data = typeof data === function ? data.call(vm) : data || {}
  for (let key in data) {
    proxy(vm, "_data", key)
  }
  observe(vm._data)
}

14, 编写 proxy  方法

function proxy(vm, source, key) {
  Object.defineProperty(vm, key, {
    get() {
      return vm[source][key]
    },
    set(newValue) {
      vm[source][key] = newValue
    }
  })
}

15。测试成功 

import Vue from ../source/src/index;
let vm = new Vue({
  el: #app,
  data() {
    return {
      msg: hello,
      school: {
        name: zf,
        age: 10
      },
      arr: [1, 2, 3]
    }
  },
  computed: {

  },
  watch: {

  }
})
console.log(vm.msg)  // hello

 

 

以上是关于手写Vue 对象劫持的主要内容,如果未能解决你的问题,请参考以下文章

vue3手写shallowReactive浅的劫持浅的监视浅的响应数据

vue3手写shallowRef浅的ref

手写实现vue的MVVM响应式原理

vue 数据劫持

Vue做数据和视图原理(数据劫持)

Proxy(vue响应式原理:数据侦测--数据劫持和数据代理)