Vue 源码分析之proxy代理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013243347/article/details/83276715

Vue 源码分析之proxy代理

当我们在使用Vue进行数据设置时,通常初始化格式为:

let data = {
    age: 12,
    name: 'yang'
}
// 实例化Vue对象
let vm = new Vue({
    data
})

// 输出
vm.age // 12
vm.name // 'yang'
vm.data.age // throw error

我们发现,我们访问vm实例的对象属性时,是直接通过 vm.age 访问,而通过vm.data.age访问则会报错。

处于好奇,初窥Vue的源码,才了解一二,这里就与大家一同分享一下:

在这里插入图片描述

实现步骤

看完源码大致梳理一下流程,主要是下面三点:

  1. 实例化Vue
  2. 挂载data
  3. 代理data

那么我们来简单的实例一个Viwe(仿Vue)类,来实现文章开头展示的功能。

1. 实例化Vue

即创建一个vm实例对象,给View类传入data对象

// 创建一个View类
const View = function (options) {
    console.log(this, options)
}
// 实例化View
const vm = new View({
    data: {
        age: 12,
        name: 'yang'
    }
})

// 此时,无法访问data中的属性
vm // {}
vm.age // undefined
vm.name // undefined

2. 挂载data

紧接上一步,我们开始为vm挂载_data对象(私有对象以’_'起始),那么,我们就要准备改造我们的View类了。


const vm = function (options) {
    // 取出data对象
    const data = options.data || {}
    // 将data 指向this._data
    this._data = data
}

// 实例化View
const vm = new View({
    data: {
        age: 12,
        name: 'yang'
    }
})

// 此时,无法访问data中的属性
vm // {_data:{...}}
vm._data // {age:12,name:'yang'}
vm._data.age // 12
vm._data.name // 'yang'
vm.age // undefined
vm.name // undefined

3. 代理data

在挂载data的时候,可以通过vm._data访问data中的对象属性了,但是这和我们的预期还是差一点。

这里,我们使用Object.defineProperty来代理vm._data中的对象属性。

关于Object.defineProperty使用教程,请参考MDN文档

下面我们继续改造我们的View类


// 定义一个空函数
const noop = function () {}
// 定义一个默认属性配置
const defineProperty = {
    enumerable: true,
    configurable: true,
    get: noop,
    set: noop
}

const View = function (options) {
    
    this._data = options.data || {}
    
    // 获取this._data中的属性名
    Object.keys(this._data).forEach(key => {
        // 代理
        defineProperty.get = function dataGetter() {
            return this._data[key]
        }
        defineProperty.set = function dataSetter (val) {
            this._data[key] = val
        }
        // 关键点,代理this._data中的属性
        Object.defineProperty(this,key,defineProperty)
    
    })
}

const vm = new View({
    age: 12,
    name: 'yang'
})

vm // {_data:{...},age:12,name:'yang'}
vm._data // {age:12,name:'yang'}
vm.age // 12
vm.name // 'yang'

改到这里,一个基本的代理实现就完成啦~,下面,我们看看是否能够使用其他方式达到相同的改造效果呢?

二、使用ES6原生 Proxy实现

先来了解一下Proxy的简单用法:

例子一:


/**
* 定义:
* target type: Object
* handler type: Object
 **/
let proxy = new Proxy(target,handler)

// 用法
let target = {}
let proxy = new Proxy(target,{
    get: function () {
        return 'yang'
    },
    set: function () {
        return 12
    }
})

proxy.a // 'yang'
proxy.xxx // 'yang'
proxy.a = 1 // 12

例子二:

const target = {
  data: {
    age: 12,
    name: 'yang'
  }
}
const proxy = new Proxy(target, {
  get: function dataGetter (target, key) {
    return target.data[key] || {}
  },
  set: function dataSetter (target, key, val) {
    target.data[key] = val
    return true
  }
})
proxy.age = 1
console.log('p', proxy.age)

好了,掌握了基本语法之后,那么开始进行构建:

// 定义View类
const View = function (options) {
  // 挂载_data
  this._data = options.data || {}
  // 返回一个Proxy对象
  return new Proxy(this, {
    // get: function (target, key) {
    //   return target._data[key]
    // },
    // set: function (target, key, val) {
    //   target._data[key] = val
    //   return true
    // }
    // 简化写法
    get: (target, key) => this._data[key],
    set: (target, key, val) => this._data[key] = val // eslint-disable-line
  })
}

let vm = new View({
  data: {
    age: 12,
    name: 'yang'
  }
})
vm // Proxy {...}
vm.age += 1
console.log('vm', vm.age) // 13



好了,基本的内容讲解完了。如果有兴趣可以一起探讨~多谢支持

猜你喜欢

转载自blog.csdn.net/u013243347/article/details/83276715