汪图南
  • RAG

    • RAG
  • 快速入门
  • 高级技巧
前端面试之道
  • 打包工具

    • Webpack
    • Rollup
  • TypeScript

    • TypeScript基础
    • TypeScript类型挑战
  • CSS预编译器

    • SASS
  • 自动化测试

    • Vue应用测试
  • Vue2.0源码分析
  • Vue3.0源码分析
  • 数据结构和算法(基础)
  • LeetCode(刷题)
  • JavaScript书籍

    • 你不知道的JavaScript(上)
    • 你不知道的JavaScript(中下)
    • JavaScript数据结构和算法
    • JavaScript设计模式与开发实践
    • 深入理解ES6
  • Git书籍

    • 精通Git
Github
  • RAG

    • RAG
  • 快速入门
  • 高级技巧
前端面试之道
  • 打包工具

    • Webpack
    • Rollup
  • TypeScript

    • TypeScript基础
    • TypeScript类型挑战
  • CSS预编译器

    • SASS
  • 自动化测试

    • Vue应用测试
  • Vue2.0源码分析
  • Vue3.0源码分析
  • 数据结构和算法(基础)
  • LeetCode(刷题)
  • JavaScript书籍

    • 你不知道的JavaScript(上)
    • 你不知道的JavaScript(中下)
    • JavaScript数据结构和算法
    • JavaScript设计模式与开发实践
    • 深入理解ES6
  • Git书籍

    • 精通Git
Github
  • 介绍

    • 介绍和参考
  • 源码目录设计和架构设计

    • 设计
  • Rollup构建版本

    • Rollup基础知识
    • Vue中的Rollup构建
  • 从入口到构造函数整体流程

    • 整体流程
    • initGlobalAPI流程
    • initMixin流程
    • stateMixin流程
    • eventsMixin流程
    • lifecycleMixin流程
    • renderMixin流程
  • 响应式原理

    • 介绍
    • 前置核心概念
    • props处理
    • methods处理
    • data处理
    • computed处理
    • watch处理
    • 深入响应式原理
    • 依赖收集
    • 派发更新
    • nextTick实现原理
    • 变化侦测注意事项
    • 变化侦测API实现
  • 虚拟DOM和VNode

    • 虚拟DOM
    • VNode介绍
    • Diff算法
  • 组件化

    • 介绍
    • $mount方法
    • render和renderProxy
    • createElement
    • createComponent
    • 合并策略
    • update和patch
    • 组件生命周期
    • 组件注册
  • 编译原理

    • 介绍
    • compileToFunctions
    • parse模板解析
    • optimize优化
    • codegen代码生成
  • 扩展

    • 扩展
    • directive指令
    • filter过滤器
    • event事件处理
    • v-model
    • 插槽
    • Keep-Alive
    • Transition
    • Transition-Group
    • Vue.use插件机制
  • Vue-Router

    • 介绍
    • 路由安装
    • matcher介绍
    • 路由切换
    • 内置组件
    • 路由hooks钩子函数
  • Vuex

    • 介绍
    • Vuex安装
    • Vuex初始化
    • Vuex辅助API
    • Store实例API

前置核心概念

Object.defineProperty介绍

也许你已经从很多地方了解到,Vue.js利用了Object.defineProperty(obj, key, descriptor)方法来实现响应式,其中Object.defineProperty()方法的参数介绍如下:

  • obj:要定义其属性的对象。
  • key:要定义或修改属性的名称。
  • descriptor:要定义或修改属性的描述符。

其中descriptor有很多可选的键值, 然而对Vue响应式来说最重要的是get和set方法,它们分别会在获取属性值的时候触发getter,设置属性值的时候触发setter。在介绍原理之前,我们使用Object.defineProperty()来实现一个简单的响应式例子:

function defineReactive (obj, key, val) {
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      console.log('get msg')
      return val
    },
    set: function reactiveSetter (newVal) {
      console.log('set msg')
      val = newVal
    }
  })
}
const vm = {
  msg: 'hello, Vue.js'
}
let msg = ''
defineReactive(vm, 'msg', vm.msg)
msg = vm.msg          // get msg
vm.msg = 'Hello, Msg' // set msg
msg = vm.msg          // get msg

为了在别的地方方便的使用Object.defineProperty()方法,因此我们把其封装成一个defineReactive函数。

proxy代理

在开发过程中,我们经常会使用this.xxx的形式直接访问props或者data中的值,这是因为Vue为props和data默认做了proxy代理。关于什么是proxy代理,请先看一个简单的例子:

this._data = {
  name: 'AAA',
  age: 23
}
// 代理前
console.log(this._data.name) // AAA
proxy(vm, '_data', 'name')
// 代理后
console.log(this.name)       // AAA

接下来我们详细介绍proxy()方法是如何实现的,在instance/state.js文件中定义了proxy方法,它的代码也很简单:

const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}
export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

我们可以从上面的代码中发现,proxy方法主要是做了属性的get和set方法劫持。

const name = this.name
this.name = 'BBB'
// 等价于
const name = this._data.name
this._data.name = 'BBB'

$options属性

在之前的介绍中,我们知道当初始化Vue实例的时候,传递的options会根据不同的情况进行配置合并,关于详细的options合并策略我们会在之后的章节详细介绍,现阶段我们只需要知道$options可以拿到合并后的所有属性,例如props、methods以及data等等。

假设我们定义了如下实例:

const vm = new Vue({
  el: '#app',
  props: {
    msg: ''
  },
  data () {
    return {
      firstName: 'AAA',
      lastName: 'BBB',
      age: 23
    }
  },
  methods: {
    sayHello () {
      console.log('Hello, Vue.js')
    }
  },
  computed: {
    fullName () {
      return this.firstName + this.lastName
    }
  }
})

那么我们在之后可以通过下面的方式来取这些属性。

const opts = this.$options
const props = opts.props
const methods = opts.methods
const data = opts.data
const computed = opts.computed
const watch = opts.watch
// ...等等
最后更新时间: 2025/5/6 15:36
贡献者: wangtunan
Prev
介绍
Next
props处理