Wang's blog Wang's blog
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Wang Mings

跟随大神,成为大神!
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Python爬虫

  • 前端教程

    • 团队规范

    • Project

    • JS

    • NodeJS

    • Vue

      • 个人理解Vue和React区别
      • Vue高级用法
      • Vue2.x源码分析 - 框架结构
      • Vue2.x源码分析 - 模版编译以及挂载
      • 虚拟dom算法库 - snabbdom
      • Vue2.x源码分析 - Virtual DOM实现
      • Vue2.x源码分析 - 事件系统
      • Vue2.x源码分析 - 组件系统
      • Vue2.x源码分析 - Vue.nextTick
      • Vue2.x源码分析 - inject provide
      • Vue2.x源码分析 - 解析Template模板
      • Vue2.x源码分析 - 响应式原理
      • Vue2.x源码分析 - v-model
      • Vue CLI3 插件系统原理
      • Vue Loader v15 源码解析
      • Vue3 设计思想
      • Vue3 RFCS导读
      • Vue3 响应式原理 - Ref Reactive Effect源码分析
      • Vue3 API 源码解析
      • 为何Vue3 Proxy 更快
        • [#](#vue2-x-object-defineproperty) Vue2.x Object.defineProperty
        • [#](#vue3-proxy处理) Vue3 Proxy处理
      • Vue核心原理 - 笔记
    • React

    • 效率工具

    • 读书笔记

  • 教程
  • 前端教程
  • Vue
wangmings
2022-07-19
目录

为何Vue3 Proxy 更快

# # 为何Vue3 Proxy 更快

相比于Vue2.x Object.defineProperty的响应式原理,Vue3 Proxy的优势在哪里呢。以下我们从两者源码角度分析下使用Proxy的优势。

Proxy优势:

  1. ES6原生Proxy语法,更快的初始化,懒加载,不用递归的定义Object.defineProperty
  2. 支持动态的添加object新属性
  3. 支持原生array数组操作

假设有如下的响应式对象时:

    data() {
        return { a: { b: { c: { d: { e: 11 } } } } }
    }
    //  以上等价于以下代码
    const data = { a: { b: { c: { d: { e: 11 } } } } }
    // Vue2.x
    Vue.observe(data)
    // Vue3:
    reactive(data)
    
1
2
3
4
5
6
7
8
9
10

# # Vue2.x Object.defineProperty

vue2初始化时,会递归的调用Object.defineProperty。当第一层对象属性定义后,再会递归调用下一层属性的Object.defineProperty(为了是依赖收集)。所以在初始化时Vue2.x需要更多时间,去同步递归定义Object.defineProperty操作。

另外一个缺点也可以看出,初始化时把data的属性递归遍历收集了,当data在业务运行过程中,动态新增属性该怎么办?在Vue2.x中由于不是懒加载,所以需要用户主动告诉Vue框架,告诉哪些新增属性是要去依赖收集的,这就是Vue.set (opens new window) (opens new window) API的来由。同理Vue.delete (opens new window) (opens new window)。

以下是精简主流程的源码,Vue2.x中定义响应式对象API是:Vue.observe(data)

    // https://github.com/vuejs/vue/blob/dev/src/core/observer/index.js
    export function observe (value: any, asRootData: ?boolean): Observer | void {
      let ob = new Observer(value)
      return ob
    }
    
    export class Observer {
      constructor (value: any) {
          this.walk(value)
      }
    
      walk (obj: Object) {
        const keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) {
          defineReactive(obj, keys[i])
        }
      }
    }
    
    export function defineReactive (
      obj: Object,
      key: string,
      val: any,
      customSetter?: ?Function,
      shallow?: boolean // default: false
    ) {
      const dep = new Dep()
      const getter = property && property.get
      const setter = property && property.setter
    
      // 同步实时:递归子层级
      let childOb = !shallow && observe(val)
      
      Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          const value = getter ? getter.call(obj) : val
          // ... dep依赖收集
          return value
        },
        set: function reactiveSetter (newVal) {
          setter.call(obj, newVal)
          // ... dep触发更新
        }
      })
    }
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# # Vue3 Proxy处理

Vue3中定义响应式对象API是:reactive(data)。

Proxy提供了对JS的元数据编程,即可以使用JS提供的语法特性,创建新的JS语法。

可以看到Vue3中定义响应式非常简单,即原生的 new Proxy(target, handlers)。此时这里没有递归调用初始化,即可看成是懒加载去依赖收集(用到才去依赖收集)。

再看看代理操作baseHandlers中get定义是如何的。

    // https://github.com/vuejs/vue-next/blob/master/packages/reactivity/src/reactive.ts
    export function reactive(target: object) {
      return createReactiveObject(target)
    }
    
    function createReactiveObject(target: Target) {
      const observed = new Proxy(
        target,
        baseHandlers // {get, set, deleteProperty}
      )
      return observed
    }
    
1
2
3
4
5
6
7
8
9
10
11
12
13

get定义,主要用来依赖收集,同时如果属性的value为Object对象时,自定进行Proxy代理。

    // https://github.com/vuejs/vue-next/blob/master/packages/reactivity/src/baseHandlers.ts
    const get = createGetter()
    
    function createGetter(isReadonly = false, shallow = false) {
      return function get(target: Target, key: string | symbol, receiver: object) {
        const res = Reflect.get(target, key, receiver)
    
        // ...依赖收集
    
        // 懒加载,当访问响应式对象时,再去构造下一个Proxy(res, { getter })
        if (isObject(res)) {
          return reactive(res)
        }
    
        return res
      }
    }
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
编辑 (opens new window)
Vue3 API 源码解析
Vue核心原理 - 笔记

← Vue3 API 源码解析 Vue核心原理 - 笔记→

最近更新
01
theme-vdoing-blog博客静态编译问题
09-16
02
搜索引擎
07-19
03
友情链接
07-19
更多文章>
Theme by Vdoing | Copyright © 2019-2022 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式