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

一、react 和 vue 有什么区别?

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

相同点:

  1. vuereact都借鉴了mvvm的概念思想。
  2. 都提供了响应式和组件化的视图组件。
  3. VueReact实现原理和流程基本一致,都是使用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-CLIVutur工具等。属于那种大包大揽,遇到某类通用问题,只需要使用官方给出的解决方案即可。

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组件),尽量减少副作用(减少thisthis会引起副作用)。

组件实现不同

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

区别

computed

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

watch

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

三、computed怎么实现的缓存?

整个流程为下面几步:

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

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

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