1. VUE的基本原理
当一个Vue实例创建时,Vue会遍历data中的属性,用Object.defineProperty将它们转为getter/setter,并且在内部追踪相关依赖,在属性被访问和修改时通知变化。
每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而使它关联的组件得以更新
2. 响应式原理
vue的响应式原理就是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的getter和setter,在数据变动时发布消息给订阅者
3. MVVM
MVVM(Model-View-ViewModel)模式是一种基于前端开发的架构模式,其核心是提供对 View 和 Model 的双向数据绑定,即当 View 或 Model 其中任意一个发生变化时,都会通过 ViewModel 引起另外一个的改变。
MVVM的优点
1. 双向绑定。数据层和视图层中任何一层发生变化,都会通过ViewModel使另一层变化,时刻保持数据和视图的一致性;
2. 通过数据驱动视图更新,不再手动操作DOM元素,提高性能;
MVVM的缺点
1. 由于双向绑定的技术,产生bug后无法确定问题出现在数据层还是视图层;
2. 数据层过大会占用更多的内存资源,影响性能;
4. v-if与v-show的区别
原理
1. v-if会调用addIfCondition方法根据条件渲染,为false时在生成vnode的时候会忽略对应节点,render的时候就不会渲染
2. 添加v-show指令的元素一定会渲染,只是通过修改display属性的值来决定是否显示
切换
1. v-if切换时,DOM元素会重复生成和销毁,会执行生命周期钩子
2. v-show切换时不会执行生命周期钩子
应用场景
需要频繁切换DOM时,使用v-show;反之则使用v-if
5. 为什么避免v-for和v-if在一起使用?
Vue 处理指令时,v-for 比 v-if 具有更高的优先级,存在性能问题。 如果你有5个元素被v-for循环,,v-if也会分别执行5次
6. v-for 循环为什么一定要绑定key?
提升vue渲染性能
1. key 的作用主要是为了更高效的更新虚拟 DOM,因为它可以非常精确的找到相同节点,因此 patch 过程会非常高效
2. Vue 在 patch 过程中会判断两个节点是不是相同节点时,key 是一个必要条件。比如渲染列表时,如果不写 key,Vue 在比较的时候,就可能会导致频繁更新元素,使整个 patch 过程比较低效,影响性能
3. 应该避免使用数组下标作为 key,因为 key 值不是唯一的话可能会导致上面图中表示的 bug,使 Vue 无法区分它他,还有比如在使用相同标签元素过渡切换的时候,就会导致只替换其内部属性而不会触发过渡效果
4. Vue 判断两个节点是否相同时主要判断两者的元素类型和 key 等,如果不设置 key,就可能永远认为这两个是相同节点,只能去做更新操作,就造成大量不必要的 DOM 更新操作,明显是不可取的
7. 为什么不建议用index索引作为key?
使用index作为key和没写基本上没区别,因为不管数组的顺序怎么颠倒,index 都是 0, 1, 2...这样排列,导致 Vue 会复用错误的旧子节点,做很多无意义的额外工作
8. computed与watch的区别?
computed
1. 支持缓存,只有依赖的数据发生了变化,才会重新计算
2. 不支持异步,当computed中有异步操作时,无法监听数据的变化
3. computed的值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,即data中声明的数据或props传递的数据
4. 如果一个属性是由其他属性计算而来,这个属性依赖其它属性,一般会使用computed
5. 如果computed属性的属性值是函数,那么默认使用get方法,函数的返回值就是属性的属性值;在computed中,属性有一个get方法和一个set方法,当数据发生变化时,会调用set方法
watch
1. 不支持缓存,数据变化会执行相应操作
2. 支持异步监听
3. 监听的函数接收两个参数,第一个参数为更新后的值,第二个参数为更新前的值‘
4. 当一个属性发生变化时,就需要执行相应的操作
5. 监听数据必须是data中声明的或者父组件传递过来的props中的数据,当发生变化时,会触发其他操作,函数有两个的参数:
6. immediate:默认为false,为true时组件加载会立即触发
7. deep:默认为false,为true时开启深度监听。需要注意的是,deep无法监听到数组和对象内部的变化
当想要执行异步或者昂贵的操作以响应不断的变化时,就需要使用watch
总结computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。watch 侦听器 : 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。
9. 为什么data是一个函数而不是一个对象?
保证每个组件内数据的独立性,防止出现变量污染。对象为引用类型,当复用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题。
10. Vue-Router
对前端路由的理解
前端路由的核心,就在于改变视图的同时不会向后端发出请求;而是加载路由对应的组件。Vue-Router就是将组件映射到路由, 然后渲染出来的
1. 拦截用户的刷新操作,避免服务端盲目响应、返回不符合预期的资源内容。把刷新这个动作完全放到前端逻辑里消化调
2. 感知URL的变化,根据这些变化使用js生成不同的内容
什么是Vue-Router,有哪些组件
Vue-Router是Vue官方的路由管理器。它和Vue.js的核心深度集成,路径和组件的映射关系使得构建SPA(Single Page Application,单页面应用)变得易如反掌
1. router-link - 实质上最终会渲染成a链接
2. router-view - 子级路由显示
3. keep-alive - 包裹组件缓存
$route和$router
$route 是路由信息对象,包含了path、params、hash、query、fullPath、matched、name等路由信息参数
$router是路由的实例对象,包含了路由的跳转方法、钩子函数等内容
路由开发的优缺点
优点:
- 整体不刷新页面,用户体验更好
- 数据传递简单,开发效率高
缺点:
- 学习成本高
- 首次加载缓慢,不利于seo
使用方式
- 新建index.js路由入口文件
- 创建路由规则
- 创建路由对象
- 将路由对象挂载到Vue.use()中
- 将路由对象挂载到 Vue 实例上
Hash模式
基于浏览器的hashchange事件,当url发生变化时,通过 window.location.hash 获取地址上的hash值,并通过Router类,配置routes对象设置与hash值对应的组件内容
优点:
- hash值会出现在url中,但是不会被包含在http请求中,因此hash值改变不会重新加载页面
- hash改变会触发hashchange事件,能控制浏览器的前进后退
- 兼容性好
缺点:
- 地址中携带#,不美观
- 只可修改#后面的部分,因此只能设置与当前URL同文档的URL
- hash有体积限制,故只可以添加短字符串
- 设置的新值必须与原来不同才会触发hashchange事件,并将记录添加到栈中
- 每次URL的改变不属于一次http请求,所以不利于seo优化
History模式
基于H5新增的pushState()和replaceState()两个api,以及浏览器的popstate事件,地址变化时,通过 window.location.pathname 找到对应的组件,并通过构造Router类,配置routes对象设置pathname值与对应的组件内容
优点:
- 没有#,相对美观
- pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL
- pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中
- pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中
- pushState() 可额外设置 title 属性供后续使用
- 浏览器的进后退能触发浏览器的popstate事件,获取window.location.pathname来控制页面的变化
缺点:
- URL的改变属于http请求,借助history.pushState实现页面的无刷新跳转,因此会重新请求服务器。所以前端的 URL 必须和实际向后端发起请求的 URL 一致。如果用户输入的URL回车或者浏览器刷新或者分享出去某个页面路径,用户点击后,URL与后端配置的页面请求URL不一致,则匹配不到任何静态资源,就会返回404页面。所以需要后台配置支持,覆盖所有情况的候选资源,如果 URL 匹配不到任何静态资源,则应该返回app 依赖的页面或者应用首页
- 兼容性差,特定浏览器支持
路由hash模式和history模式的区别
hash 模式是一种把前端路由的路径用 # 拼接在真实 url 后面的模式。当 # 后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发 onhashchange 事件。
hash模式的特点:
hash变化会触发网页跳转,即浏览器的前进和后退hash可以改变url,但是不会触发页面重新加载(hash的改变是记录在window.history中),即不会刷新页面。也就是说,所有页面的跳转都是在客户端进行操作。因此,这并不算是一次http请求,所以这种模式不利于SEO优化。hash只能修改#后面的部分,所以只能跳转到与当前url同文档的urlhash通过触发hashchange事件,来监听hash的改变,借此实现无刷新跳转的功能hash永远不会提交到server端(可以理解为只在前端自生自灭)
history API 是 H5 提供的新特性,允许开发者直接更改前端路由,即更新浏览器 URL 地址而不重新发起请求
- 的
url可以是与当前url同源的任意url,也可以是与当前url一样的地址,但是这样会导致的一个问题是,会把重复的这一次操作记录到栈当中 - 通过
history.state,添加任意类型的数据到记录中 - 可以额外设置
title属性,以便后续使用 - 通过
pushState、replaceState来实现无刷新跳转的功能,需要后端配合
history模式下的404问题
- URL的改变属于http请求,借助history.pushState实现页面的无刷新跳转,因此会重新请求服务器
- 所以前端的 URL 必须和实际向后端发起请求的 URL 一致
11.Vue3.0性能提升主要是通过哪几方面体现的?
① 编译阶段优化
回顾Vue2,我们知道每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把用到的数据property记录为依赖,当依赖发生改变,触发setter,则会通知watcher,从而使关联的组件重新渲染。
因此,Vue3在编译阶段,做了进一步优化:
1. diff算法优化
vue3在diff算法中相比vue2增加了静态标记,其作用是为了会发生变化的地方添加一个flag标记,下次发生变化的时候直接找该地方进行比较。
2.静态提升
Vue3中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用。免去了重复的创建操作,优化内存。
没做静态提升之前,未参与更新的元素也在render函数内部,会重复创建阶段。
做了静态提升后,未参与更新的元素,被放置在render 函数外,每次渲染的时候只要取出即可。同时该元素会被打上静态标记值为-1,特殊标志是负整数表示永远不会用于 Diff。
3.事件监听缓存
默认情况下绑定事件行为会被视为动态绑定(没开启事件监听器缓存),所以每次都会去追踪它的变化。开启事件侦听器缓存后,没有了静态标记。也就是说下次diff算法的时候直接使用。
4.SSR优化
当静态内容大到一定量级时候,会用createStaticVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。
②源码体积
相比Vue2,Vue3整体体积变小了,除了移出一些不常用的API,最重要的是Tree shanking。
任何一个函数,如ref、reavtived、computed等,仅仅在用到的时候才打包,没用到的模块都被摇掉,打包的整体体积变小。
③响应式系统
vue2中采用 defineProperty来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter和setter,实现响应式。vue3采用proxy重写了响应式系统,因为proxy可以对整个对象进行监听,所以不需要深度遍历。- 可以监听动态属性的添加
- 可以监听到数组的索引和数组length属性
- 可以监听删除属性
12.Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?
1、vue2中采用 defineProperty来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter和setter,实现响应式。但是存在以下的问题:
- 检测不到对象属性的添加和删除
- 数组API方法无法监听到
- 需要对每个属性进行遍历监听,如果嵌套对象,需要深层监听,造成性能问题
2、proxy:监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作。
总结:
- Object.defineProperty只能遍历对象属性进行劫持
- Proxy直接可以劫持整个对象,并返回一个新对象,我们可以只操作新的对象达到响应式目的
- Proxy可以直接监听数组的变化(push、shift、splice)
- Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等,这是Object.defineProperty不具备的
13.Vue3.0响应式原理 ?
vue3 响应式是使用 ES6 的 proxy 和 Reflect 相互配合实现数据响应式,解决了 vue2 中视图不能自动更新的问题。
proxy 是深度监听,所以可以监听对象和数组内的任意元素,从而可以实现视图实时更新。
总结响应式大致分为三个阶段:
初始化阶段:初始化阶段通过组件初始化方法形成对应的proxy对象,然后形成一个负责渲染的effect。get依赖收集阶段:通过解析template,替换真实data属性,来触发get,然后通过stack方法,通过proxy对象和key形成对应的deps,将负责渲染的effect存入deps。(这个过程还有其他的effect,比如watchEffect存入deps中 )。set派发更新阶段:当我们this[key] = value改变属性的时候,首先通过trigger方法,通过proxy对象和key找到对应的deps,然后给deps分类分成computedRunners和effect,然后依次执行,如果需要调度的,直接放入调度。
Proxy只会代理对象的第⼀层,那么Vue3⼜是怎样处理这个问题的呢?
判断当前Reflect.get的返回值是否为Object,如果是则再通过 reactive ⽅法做代理, 这样就实现了深度观测。
监测数组的时候可能触发多次get/set,那么如何防⽌触发多次呢?
我们可以判断key是否为当前被代理对象target⾃身属性,也可以判断旧值与新值是否相等,只有满⾜以上两个条件之⼀时,才有可能执⾏trigger。
14.Vue3 新特性有哪些?
1、性能提升
- 响应式性能提升,由原来的
Object.defineProperty改为基于ES6的Proxy,使其速度更快 - 重写了
Vdom(diff算法优化,增加静态标志) - 进行模板编译优化(静态提升,不参与更新的元素只被创建一次)
- 更加高效的组件初始化
2、更好的支持 typeScript
Vue.js 2.x选用Flow做类型检查,来避免一些因类型问题导致的错误,但是 Flow 对于一些复杂场景类型的检查,支持得并不好。Vue.js 3.0抛弃了Flow,使用TypeScript重构了整个项目TypeScript提供了更好的类型检查,能支持复杂的类型推断
3、新增 Composition API
Composition API 是 vue3 新增的功能,比 mixin 更强大。它可以把各个功能模块独立开来,提高代码逻辑的可复用性,同时代码压缩性更强。
在 Vue3 中,定义 methods、watch、computed、data数据等都放在了 setup() 函数中。
setup()函数会在created()生命周期之前执行。执行顺序为:beforeCreate > setup > created
4、新增组件
Fragment不再限制template只有一个根节点。Teleport传送门,允许我们将控制的内容传送到任意的DOM中。Suspense等待异步组件时渲染一些额外的内容,让应用有更好的用户体验。
5、Tree-shaking:支持摇树优化
摇树优化后会将不需要的模块修剪掉,真正需要的模块打到包内。优化后的项目体积只有原来的一半,加载速度更快。
15.vue3 组合式API生命周期钩子函数有变化吗?
setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显示的定义它们。其他的钩子都可以编写到 setup 内。
值得注意的是组合式API中的钩子函数,通过在生命周期钩子前面加上 on 来访问组件的生命周期钩子。需要注册,并且只能在 setup 期间同步使用,因为它们依赖于内部的全局状态来定位当前组件实例。
更多生命周期内容见Vue3组合式 API:生命周期钩子总结 - 掘金 (juejin.cn)
16.v-if 和 v-for 的优先级哪个高?
在 vue2 中 v-for 的优先级更高,但是在 vue3 中优先级改变了。v-if 的优先级更高。
17.script setup 是干啥的?
scrtpt setup 是 vue3 的语法糖,简化了组合式 API 的写法,并且运行性能更好。使用 script setup 语法糖的特点:
- 属性和方法无需返回,可以直接使用。
- 引入
组件的时候,会自动注册,无需通过components手动注册。 - 使用
defineProps接收父组件传递的值。 useAttrs获取属性,useSlots获取插槽,defineEmits获取自定义事件。- 默认
不会对外暴露任何属性,如果有需要可使用defineExpose。
17.ref与reactive的区别?
ref与reactive 是 Vue3 新推出的主要 API 之一,它们主要用于响应式数据的创建。
template模板中使用的数据和方法,都需要通过setup函数return出去才可以被使用。ref函数创建的响应式数据,在模板中可以直接被使用,在JS中需要通过.value的形式才能使用。ref函数可以接收原始数据类型与引用数据类型。reactive函数只能接收引用数据类型。ref底层还是使用reactive来做,ref是在reactive上在进行了封装,增强了其能力,使它支持了对原始数据类型的处理。- 在
Vue3中reactive能做的,ref也能做,reactive不能做的,ref也能做。
18.多组件(父子组件)中生命周期的调用顺序说一下?
- 加载渲染过程:父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount- >子mounted->父mounted
- 子组件更新过程:父beforeUpdate->子beforeUpdate->子updated->父updated
- 父组件更新过程:父 beforeUpdate -> 父 updated
- 销毁过程:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
19.Vue 实现双向数据绑定原理是什么?
Vue2.x 采用数据劫持结合发布订阅模式(PubSub 模式)的方式,通过 Object.defineProperty 来劫持各个属性的 setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
Vue 的数据双向绑定整合了 Observer,Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 的数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化->视图更新,视图交互变化(例如 input 操作)->数据 model 变更的双向绑定效果。
Vue3.x 放弃了 Object.defineProperty ,使用 ES6 原生的 Proxy,来解决以前使用 Object.defineProperty 所存在的一些问题。
20.Vue3.x 响应式数据?
Vue3.x 响应式数据原理是什么?
在 Vue 2 中,响应式原理就是使用的 Object.defineProperty 来实现的。但是在 Vue 3.0 中采用了 Proxy,抛弃了 Object.defineProperty 方法。
究其原因,主要是以下几点:
- Object.defineProperty 无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应
- Object.defineProperty 只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy 可以劫持整个对象,并返回一个新的对象。
- Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
- Proxy 有多达 13 种拦截方法
- Proxy作为新标准将受到浏览器厂商重点持续的性能优化
Proxy 只会代理对象的第一层,那么 Vue3 又是怎样处理这个问题的呢?
判断当前 Reflect.get 的返回值是否为 Object,如果是则再通过 reactive 方法做代理, 这样就实现了深度观测。
监测数组的时候可能触发多次 get/set,那么如何防止触发多次呢?
我们可以判断 key 是否为当前被代理对象 target 自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行 trigger。
21.v-model 双向绑定的原理是什么?
v-model 本质就是 :value + input 方法的语法糖。可以通过 model 属性的 prop 和 event 属性来进行自定义。原生的 v-model,会根据标签的不同生成不同的事件和属性。
例如:
- text 和 textarea 元素使用 value 属性和 input 事件
- checkbox 和 radio 使用 checked 属性和 change 事件
- select 字段将 value 作为 prop 并将 change 作为事件
以输入框为例,当用户在输入框输入内容时,会触发 input 事件,从而更新 value。而 value 的改变同样会更新视图,这就是 vue 中的双向绑定。双向绑定的原理,其实现思路如下:
首先要对数据进行劫持监听,所以我们需要设置一个监听器 Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者 Watcher 看是否需要更新。
因为订阅者是有很多个,所以我们需要有一个消息订阅器 Dep 来专门收集这些订阅者,然后在监听器 Observer 和订阅者 Watcher 之间进行统一管理的。
接着,我们还需要有一个指令解析器 Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者 Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者 Watcher 接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。
因此接下去我们执行以下 3 个步骤,实现数据的双向绑定:
- 实现一个监听器 Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
- 实现一个订阅者 Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
- 实现一个解析器 Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。
22.vue2.x 和 vuex3.x 渲染器的 diff 算法分别说一下?
简单来说,diff 算法有以下过程
- 同级比较,再比较子节点
- 先判断一方有子节点一方没有子节点的情况(如果新的 children 没有子节点,将旧的子节点移除)
- 比较都有子节点的情况(核心 diff)
- 递归比较子节点
正常 Diff 两个树的时间复杂度是 O(n^3),但实际情况下我们很少会进行跨层级的移动 DOM,所以 Vue 将 Diff 进行了优化,从O(n^3) -> O(n),只有当新旧 children 都为多个子节点时才需要 用核心的 Diff 算法进行同层级比较。
Vue2 的核心 Diff 算法采用了双端比较的算法,同时从新旧 children 的两端开始进行比较,借助 key 值找到可复用的节点,再进行相关操作。相比 React 的 Diff 算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅。
Vue3.x 借鉴了 ivi 算法和 inferno 算法
在创建 VNode 时就确定其类型,以及在 mount/patch 的过程中采用位运算来判断一个 VNode 的类型,在这个基础之上再配合核心的 Diff 算法,使得性能上较 Vue2.x 有了提升。该算法中还运用了动态规划的思想求解最长递归子序列。
23.说一下 Vue SSR 的实现原理?
- app.js 作为客户端与服务端的公用入口,导出 Vue 根实例,供客户端 entry 与服务端 entry 使用。客户端 entry 主要作用挂载到 DOM 上,服务端 entry 除了创建和返回实例,还需要进行路由匹配与数据预获取。
- webpack 为客服端打包一个 ClientBundle,为服务端打包一个 ServerBundle。
- 服务器接收请求时,会根据 url,加载相应组件,获取和解析异步数据,创建一个读取 Server Bundle 的 BundleRenderer,然后生成 html 发送给客户端。
- 客户端混合,客户端收到从服务端传来的 DOM 与自己的生成的 DOM 进行对比,把不相同的 DOM 激活,使其可以能够响应后续变化,这个过程称为客户端激活(也就是转换为单页应用)。为确保混合成功,客户 端与服务器端需要共享同一套数据。在服务端,可以在渲染之前获取数据,填充到 store 里,这样,在客户端挂载到 DOM 之前,可以直接从 store 里取数据。首屏的动态数据通过 window.INITIAL_STATE 发送到客户端
- VueSSR 的原理,主要就是通过 vue-server-renderer 把 Vue 的组件输出成一个完整 HTML,输出到客户端,到达客户端后重新展开为一个单页应用。
24.Vue 组件的 data 为什么必须是函数?
25.Proxy 相比 defineProperty 的优势在哪里?
Vue3.x 改用 Proxy 替代 Object.defineProperty
原因在于 Object.defineProperty 本身存在的一些问题:
- Object.defineProperty 只能劫持对象属性的 getter 和 setter 方法。
- Object.definedProperty 不支持数组(可以监听数组,不过数组方法无法监听自己重写),更准确的说是不支持数组的各种 API(所以 Vue 重写了数组方法。
而相比 Object.defineProperty,Proxy 的优点在于:
- Proxy 是直接代理劫持整个对象。
- Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。
目前,Object.definedProperty 唯一比 Proxy 好的一点就是兼容性,不过 Proxy 新标准也受到浏览器厂商重点持续的性能优化当中。
本文总结出自
2023前端面试必备个人总结(持续更新中) - 知乎 (zhihu.com)