轻松搞懂前端面试题系列(vue篇三)

一、react 和 vue 有什么区别?

React 是由Facebook创建的JavaScript UI框架,React推广了 Virtual DOM( 虚拟 DOM )并创造了 JSX 语法。JSX 语法的出现允许我们在 javascript 中书写 HTML 代码。
VUE 是由尤雨溪开发的,VUE 使用了模板系统而不是JSX,因其实模板系统都是用的普通的 HTML,所以对应用的升级更方便、更容易,而不需要整体重构。

相同点:

  1. vue和react都借鉴了mvvm的概念思想。
  2. 都提供了响应式和组件化的视图组件。
  3. Vue和React实现原理和流程基本一致,都是使用Virtual DOM + Diff算法。
  4. 都将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关库。

不同点主要体现在以下几个方面:

核心思想不同

Vue早期定位是尽可能的降低前端开发的门槛(这跟Vue作者是独立开发者也有关系)。所以Vue推崇灵活易用(渐进式开发体验),数据可变,双向数据绑定(依赖收集)。

React早期口号是Rethinking Best Practices。背靠大公司Facebook的React,从开始起就不缺关注和用户,而且React想要做的是用更好的方式去颠覆前端开发方式(事实上跟早期jquery称霸前端,的确是颠覆了)。所以React推崇函数式编程(纯组件),数据不可变以及单向数据流。函数式编程最大的好处是其稳定性(无副作用)和可测试性(输入相同,输出一定相同),所以通常大家说的React适合大型应用,根本原因还是在于其函数式编程。

由于两者核心思想的不同,所以导致Vue和React许多外在表现不同:

核心思想不同导致写法差异

Vue推崇template(简单易懂,从传统前端转过来易于理解)、单文件vue。而且虽然Vue2.0以后使用了Virtual DOM,使得Vue也可以使用JSX(bebel工具转换支持),但Vue官方依然首先推荐template,这跟Vue的核心思想和定位有一定关系。

React推崇JSX、HOC、all in js。

核心思想不同导致api差异

Vue定位简单易上手,基于template模板 + options API,所以不可避免的有较多的概念和api。比如template模板中需要理解slot、filter、指令等概念和api,options API中需要理解watch、computed(依赖收集)等概念和api。

React本质上核心只有一个Virtual DOM + Diff算法,所以API非常少,知道setState就能开始开发了。

核心思想不同导致社区差异

由于Vue定义简单易上手,能快速解决问题,所以很多常见的解决方案,是Vue官方主导开发和维护。比如状态管理库Vuex、路由库Vue-Router、脚手架Vue-CLI、Vutur工具等。属于那种大包大揽,遇到某类通用问题,只需要使用官方给出的解决方案即可。

React只关注底层,上层应用解决方案基本不插手,连最基础的状态管理早期也只是给出flow单向数据流思想,大部分都丢给社区去解决。比如状态管理库方面,有redux、mobx、redux-sage、dva等一大堆(选择困难症犯了),所以这也造就了React社区非常繁荣。同时由于有社区做上层应用解决方案,所以React团队有更多时间专注于底层升级,比如花了近2年时间把底层架构改为Fiber架构,以及创造出React Hooks来替换HOC。

核心思想不同导致未来升级方向不同

核心思想不同,决定了Vue和React未来不管怎么升级变化,Vue和React考虑的基本盘不变。

Vue依然会定位简单易上手(渐进式开发),依然是考虑通过依赖收集来实现数据可变。这点从Vue3核心更新内容可以看到:template语法基本不变、options api只增加了setup选项(composition api)、基于依赖收集(Proxy)的数据可变。

React的函数式编程这个基本盘不会变。React核心思想,是把UI作为Basic Type,比如String、Array类型,然后经过render处理,转换为另外一个value(纯函数)。从React Hooks可以看出,React团队致力于组件函数式编程,(纯组件,无class组件),尽量减少副作用(减少this,this会引起副作用)。

组件实现不同

Vue源码实现是把options挂载到Vue核心类上,然后再new Vue({options})拿到实例(vue组件的script导出的是一个挂满options的纯对象而已)。所以options api中的this指向内部Vue实例,对用户是不透明的,所以需要文档去说明this.slot、this.slot、this.slot、this.xxx这些api。另外Vue插件都是基于Vue原型类基础之上建立的,这也是Vue插件使用Vue.install的原因,因为要确保第三方库的Vue和当前应用的Vue对象是同一个。

React内部实现比较简单,直接定义render函数以生成VNode,而React内部使用了四大组件类包装VNode,不同类型的VNode使用相应的组件类处理,职责划分清晰明了(后面的Diff算法也非常清晰)。React类组件都是继承自React.Component类,其this指向用户自定义的类,对用户来说是透明的。

响应式原理不同

Vue

  • Vue依赖收集,自动优化,数据可变。
  • Vue递归监听data的所有属性,直接修改。
  • 当数据改变时,自动找到引用组件重新渲染。

React

  • React基于状态机,手动优化,数据不可变,需要setState驱动新的State替换老的State。
  • 当数据改变时,以组件为根目录,默认全部重新渲染

diff算法不同

Vue基于snabbdom库,它有较好的速度以及模块机制。Vue Diff使用双向指针,边对比,边更新DOM。

React主要使用diff队列保存需要更新哪些DOM,得到patch树,再统一操作批量更新DOM。

事件机制不同

Vue

  • Vue原生事件使用标准Web事件。
  • Vue组件自定义事件机制,是父子组件通信基础。
  • Vue合理利用了snabbdom库的模块插件。

React

  • React原生事件被包装,所有事件都冒泡到顶层document监听,然后在这里合成事件下发。基于这套,可以跨端使用事件机制,而不是和Web DOM强绑定。
  • React组件上无事件,父子组件通信使用props。

二、​vue中computed和watch区别

watch和computed都是以Vue的依赖追踪机制为基础的,当某一个依赖型数据(依赖型数据:简单理解即放在 data 等对象下的实例数据)发生变化的时候,所有依赖这个数据的相关数据会自动发生变化,即自动调用相关的函数,来实现数据的变动。

当依赖的值变化时,在watch中,是可以做一些复杂的操作的,而computed中的依赖,仅仅是一个值依赖于另一个值,是值上的依赖。

应用场景

computed:用于处理复杂的逻辑运算;一个数据受一个或多个数据影响;用来处理watch和methods无法处理的,或处理起来不方便的情况。例如处理模板中的复杂表达式、购物车里面的商品数量和总金额之间的变化关系等。
watch:用来处理当一个属性发生变化时,需要执行某些具体的业务逻辑操作,或要在数据变化时执行异步或开销较大的操作;一个数据改变影响多个数据。例如用来监控路由、input 输入框值的特殊处理等。

区别

computed

  • 初始化显示或者相关的 data、props 等属性数据发生变化的时候调用;
  • 计算属性不在 data 中,它是基于data 或 props 中的数据通过计算得到的一个新值,这个新值根据已知值的变化而变化;
  • 在 computed 属性对象中定义计算属性的方法,和取data对象里的数据属性一样,以属性访问的形式调用;
  • 如果 computed 属性值是函数,那么默认会走 get 方法,必须要有一个返回值,函数的返回值就是属性的属性值;
  • computed 属性值默认会缓存计算结果,在重复的调用中,只要依赖数据不变,直接取缓存中的计算结果,只有依赖型数据发生改变,computed 才会重新计算;
  • 在computed中的,属性都有一个 get 和一个 set 方法,当数据变化时,调用 set 方法。

watch

  • 主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作,可以看作是 computed 和 methods 的结合体;
  • 可以监听的数据来源:data,props,computed内的数据;
  • watch支持异步;
  • 不支持缓存,监听的数据改变,直接会触发相应的操作;
  • 监听函数有两个参数,第一个参数是最新的值,第二个参数是输入之前的值,顺序一定是新值,旧值。

三、computed怎么实现的缓存?

整个流程为下面几步:

  1. 当组件初始化的时候,computed 和 data 会分别建立各自的响应系统,Observer遍历 data 中每个属性设置 get/set 数据拦截
  2. 初始化 computed 会调用 initComputed 函数
  • 注册一个 watcher 实例,并在内实例化一个 Dep 消息订阅器用作后续收集依赖(比如渲染函数的 watcher 或者其他观察该计算属性变化的 watcher )
  • 调用计算属性时会触发其Object.defineProperty的get访问器函数
  • 调用 watcher.depend() 方法向自身的消息订阅器 dep 的 subs 中添加其他属性的 watcher
  • 调用 watcher 的 evaluate 方法(进而调用 watcher 的 get 方法)让自身成为其他 watcher 的消息订阅器的订阅者,首先将 watcher 赋给 Dep.target,然后执行 getter 求值函数,当访问求值函数里面的属性(比如来自 data、props 或其他computed)时,会同样触发它们的 get 访问器函数从而将该计算属性的 watcher 添加到求值函数中属性的 watcher 的消息订阅器 dep 中,当这些操作完成,最后关闭 Dep.target 赋为 null 并返回求值函数结果。
  1. 当某个属性发生变化,触发 set 拦截函数,然后调用自身消息订阅器 dep 的 notify 方法,遍历当前 dep 中保存着所有订阅者 wathcer 的 subs 数组,并逐个调用 watcher 的 update 方法,完成响应更新。

这里面涉及到源码的阅读,有打破砂锅问到底精神的朋友可以自行查询资料了解细节。
推荐文章:Vue computed是如何实现的?

posted @ 2022-08-08 13:59  来亦何哀  阅读(148)  评论(0)    收藏  举报