Vue 总结

proxy & Object.defineProperty 理解

Object.defineProperty 的主要三个问题:

  1. 不能监听数组变化 :无法监听到数组下标的变化,导致通过数组下标添加元素,不能实时响应
  2. 必须遍历对象的每个属性 :只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果属性值是对象,还需要深度遍历。
  3. 必须深层遍历嵌套对象

Proxy 的优势:

  1. 针对对象:针对整个对象,而不是对象的某个属性
  2. 支持数组:Proxy 不需要对数组的方法进行重载,省去了众多hack
  3. Proxy 的第二个参数可以有13种拦截方法:不限于applyownKeysdeletePropertyhas等等是Object.defineProperty不具备的
  4. Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改
  5. Proxy作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利

Object.defineProperty的优势:

兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平

defineProperty的属性值

Object.defineProperty(obj, prop, descriptor)

// obj 要定义属性的对象
// prop 要定义或修改的属性的名称
// descriptor 要定义或修改的属性描述符

Object.defineProperty(obj,"name",{
  value:"poetry", // 初始值
  writable:true, // 该属性是否可写入
  enumerable:true, // 该属性是否可被遍历得到(for...in, Object.keys等)
  configurable:true, // 定该属性是否可被删除,且除writable外的其他描述符是否可被修改
  get: function() {},
  set: function(newVal) {}
})

Proxy只会代理对象的第一层,那么Vue3又是怎样处理这个问题的呢?

判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理, 这样就实现了深度观测。

监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?

我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger

Vue中依赖收集

  • 每个属性都有自己的dep属性,存放他所依赖的watcher,当属性变化之后会通知自己对应的watcher去更新
  • 默认会在初始化时调用render函数,此时会触发属性依赖收集 dep.depend
  • 当属性发生修改时会触发watcher更新dep.notify()

Vue实例挂载的过程中发生了什么

  1. 挂载过程指的是app.mount()过程,这个过程中整体上做了两件事:初始化建立更新机制
  2. 初始化会创建组件实例、初始化组件状态,创建各种响应式数据
  3. 建立跟新机制这一步会立即执行一次更新函数,这会首次执行组件渲染函数并执行patch将前面获得vnode转换为dom;同时首次执行函数会创建它内部响应式数据之间和组件更新函数之间的依赖关系,这使得以后数据变化时会执行对应的更新函数

Vue 初始化主要就干了几件事情,合并配置初始化生命周期初始化事件中心初始化渲染初始化 datapropscomputedwatcher

watchwatchEffect区别

  • watch需要明确监听哪个属性
  • watchEffect会根据其中属性,自动监听其变化, 初始化时,一定会执行一次(收集要监听的数据)

setup中如何获取组件实例

通过getCurrentInstance获取当前实例

Vue3为何比Vue2快

  • Proxy响应式:深度监听,性能更好(获取到哪一层才触发响应式get,不是一次性递归)
  • PatchFlag动态节点做标志
  • HoisStatic将静态节点的定义,提升到父作用域,缓存起来。多个相邻的静态节点,会被合并起来
  • CacheHandler事件缓存
  • SSR优化:静态节点不走vdom逻辑,直接输出字符串,动态节点才走
  • Tree-shaking根据模板的内容动态import不同的内容,不需要就不import

什么是Patchflag

  • 模板编译时,动态节点做标记
  • 标记,分为不同类型,如TextPROPSCLASS
  • diff算法时,可区分静态节点,以及不同类型的动态节点

diff算法

什么是Hoist和CacheHandler

Hoist

  • 将静态节点的定义,提升到父作用域,缓存起来
  • 多个相邻静态节点,会被合并起来
  • 典型的拿空间换时间的优化策略

CacheHandler 缓存事件

Vite 为什么启动非常快

  • 开发环境使用Es6 Module,无需打包,运行时编译,非常快
  • 生产环境使用rollup,并不会快很多

Composition API 和 React Hooks 的对比

  • 前者setup(相当于createdbeforeCreate的合集)只会调用一次,而React Hooks函数在渲染过程中会被多次调用
  • Composition API无需使用useMemouseCallback,因为setup只会调用一次,在setup闭包中缓存了变量
  • Composition API无需顾虑调用顺序,而React Hooks需要保证hooks的顺序一致(比如不能放在循环、判断里面)
  • Composition APIrefreactiveuseState难理解