手寫Vue (1) 對象劫持

1.引入咱們手寫的Vue 拿到配置數據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 這個方法 對 用戶傳入的數據進行初始化 傳入配置信息app

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 的整個實例方法 this

6. 在initState  方法中 咱們進行初始化  好比初始化 數據 初始化 計算屬性 初始化 watch spa

 

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 方法 拿到函數 返回值 如何不是直接返回數據 或者 空數據 prototype

 let data = vm.$options.data
  // 判斷是不是函數 取返回值
  data = vm._data = typeof data === 'function' ? data.call(vm) : data || {}

 

8. 拿到數據後 咱們要對數據進行 監聽 編寫   observe  方法 監聽數據 、3d

 

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
相關文章
相關標籤/搜索