冷门知识点——proxy数据劫持优化引发的with语句特性思考

187 阅读3分钟

问题场景

  • qiankun微前端框架的沙箱特性采用的proxy数据劫持,来完成全局变量更新,proxy是一个元编程的工具,可以自定义对象特性触发副作用,因此对性能产生一定的影响,因此在微前端使用大数据量渲染下会产生一定的延迟。

引发思考

  • vue3也是使用proxy处理响应式数据变化,他对性能做了哪些优化?

vue3数据劫持的优化

  1. 缓存响应式对象。vue3会将响应式对象缓存起来,避免重复创建proxy对象,从而提供性能
  2. 懒执行响应式数据。vue3会将响应式数据的访问和修改操作延迟到实际需要时候才执行,避免不必要的性能开销
  3. 批量更新响应式数据。vue3会将多个响应式数据更新操作合并成一个批处理,减少更新次数提高性能

以上优化思路适用一切性能优化

探讨方案一

缓存响应式对象

纵观qiankun2做的缓存优化:

const windowProxy = new Proxy(window, traps);

with(windowProxy) {
  // 应用代码,通过 with 确保所有的全局变量的操作实际都是在操作 qiankun 提供的代理对象
  + // 提前将一些全局变量通过 赋值/取值 从 proxy 里缓存下来
  - var undefined = windowProxy.undefined; var Array = windowProxy.Array; var Promise = windowProxy.Promise;
  + const undefined = windowProxy.undefined; const Array = windowProxy.Array; const Promise = windowProxy.Promise;
  ${appCode}  
}
疑问

使用with语句扩展作用域链,直接访问对象属性并缓存,但此处把var定义改为const定义,是为什么?

  1. 首先按照常识,既然使用proxy了,那肯定也支持let、const块级作用域,按习惯直接上手const
  2. 其次,如果定义var应该也没问题,那为什么要删掉改成const

解决思路

  1. 首先,with 语句是 JavaScript 中的一个语法特性,用于扩展语句的作用域链,允许在语句块中直接访问指定对象的属性,而无需重复引用对象本身。
  2. 因此它本身是会修改作用域链顺序的,它拥有自己的作用域,所以用var定义会修改作用域的一个范围,出现意外

最后

 with 语句的问题

尽管 with 语句可以简化代码,但它存在以下问题:

(1)性能问题

  • with 语句会改变作用域链,导致 JavaScript 引擎无法在编译时优化变量查找,从而降低性能。

(2)代码可读性问题

  • with 语句使得代码的可读性和可维护性变差,因为很难确定变量是来自 with 对象还是外部作用域。

(3)潜在的错误

  • 如果 with 对象中没有某个属性,JavaScript 会继续在外部作用域中查找,可能导致意外的行为。

(4)严格模式禁用

  • 在严格模式(strict mode)下,with 语句是被禁用的,使用会报错。
替代方案

使用解构赋值、临时变量或函数参数等方式替代 with 语句,以提高代码的可读性和性能。