前端八股文之葵花宝典

面试造火箭,入职拧螺丝,不管是前端实习生还是前端老鸟,面试时候会遇到各种前端八股文。本人因此收集广大求职者被面试官问到的前端八股文,从JS基础,CSS,Vue,React,甚至浏览器的底层原理等叼钻问题,去繁从简,言简意赅,适合前端面试用,以下问题答案随机排列,仅供参考。

1. attribute 和 property 的区别是什么?

Attribute自定义属性,prop是内置属性。

2. ES6 中数组结构赋值都有哪几种操作方式?

正常赋值,解构赋值,扩展运算符

3. Vue 组件通讯有哪些方法?

有6种方式

  1. Props 和 Events。通过 props,父组件向子组件传递数据,子组件通过 events 构造函数向父组件发送数据
  2. $parent 和 $children。$parent 属性指向父组件,$children 属性指向子组件。通过 $parent 和 $children 属性,组件可以直接访问父组件和子组件的数据和方法。
  3. $refs。$refs 属性是一个对象,包含了在组件中使用 ref 属性命名的子组件或 DOM 元素的引用。通过 $refs 属性,组件之间可以相互引用和调用。
  4. Event Bus。Event Bus 是一个简单的 Vue 实例,可以通过 $emit 方法向其他 Vue 组件发送事件,也可以通过 $on 方法接收其他 Vue 组件发送的事件。
  5. Provide 和 Inject。Provide 和 Inject 允许父组件将数据传递给所有后代组件,而不是只传递给直接子组件。Provide 和 Inject 是一种不同于 props、events 和 $parent/$children 的组件通信形式,是一种更加灵活和封装性更强的通信方式。

4. ES6 Map 和 WeakMap 有什么区别?

weakMap 只接受 对象作为key,该对象的引用为弱引用,只要该对象的其他引用被删除,该对象就会被垃圾回收,因此weakmap成员数量不稳定,没有size属性,没有clear方法,也不能遍历

5. 简述浏览器渲染过程?

1、解析html文档构建dom树 2、构建渲染树 3、布局和绘制渲染树

6. promise 中常用的方法有哪些?

then回调,resolve成功回调,reject失败回调,all并发请求任意一失败即返回,all selected并发请求全部结束返回,race任意一个任意一个成功或者失败返回

7. 为什么 typeof null 是 Object?

因为typeof 是根据前四位的bit来判断类型,null和object都是0000

8. 谈谈你对深拷贝和浅拷贝的理解?

就是内存空间的问题吧,浅拷贝就是复制内存地址,拷贝了n份,有一份改了,其他都会变,深拷贝就是重新去分配一块内存进行复制,每个拷贝的值都是不同的内存地址,现有一些前端工具库有实现深拷贝方案,平常层级不深,没有特殊格式(例如函数,时间等等)可以直接序列化处理,层级较深可以递归处理

9. Vue 常用的修饰符有哪些应用场景?

.stop:阻止事件冒泡 .native:绑定原生事件 .once:事件只执行一次 .self :将事件绑定在自身身上,相当于阻止事件冒泡 .prevent:阻止默认事件 .caption:用于事件捕获 .once:只触发一次 .keyCode:监听特定键盘按下 .right:右键

10. ES6 数据结构中 Set 和 Map 有什么区别?

Set可以用于数组去重,因为Set值唯一,而Map不唯一 Map存储的是键值对,可以通过get方法取值而Set不行,Set存储的是值

11. 浏览器是怎样解析 CSS 选择器的?

从右到左解析,根据选择器遍历dom树,将样式加到对应的dom元素上。

12. Vue 父子组件生命周期执行顺序是什么?

1.当父组件执行完beforeMount挂载开始后,会依次执行子组件中的钩子,直到全部子组件mounted挂载到实例上,父组件才会进入mounted钩子 2.子级触发事件,会先触发父级beforeUpdate钩子,再去触发子级beforeUpdate钩子,下面又是先执行子级updated钩子,后执行父级updated钩子 总结:父组件先于子组件created,而子组件先于父组件mounted

13. JS 中 BOM 和 DOM 有什么区别?

JS Bom 和Dom的区别 1、1.BOM是浏览器对象模型 2、DOM是文档对象模型 2、BOM没有相关标准。 DOM是W3C的标准。 3、BOM的最根本对象是window。DOM最根本对象是document

14. Vue3.0 性能提升主要是通过哪几个方面体现的?

1、响应式实现优化,vue2使用数据劫持需要递归去做响应式处理,vue3使用代理则一步到位; 2、diff算法优化,vue2同层全量对比,vue3使用了静态标记、静态提升、函数缓存等去优化。

15. Vue 中组件的 data 为什么是一个函数?而 new Vue 实例里,data 可以直接是一个对象?

因为组件可能在应用中多次被使用而被多次实例化,组件的是个函数可以确保每个实例化后的实例有独立的作用域,从而确保组件实例化后的data中的数据不会相互污染。而new Vue生成的是根应用实例,只有一个。

16. 箭头函数和普通函数之间有什么区别?

  1.  箭头函数不绑定this 使用上层作用域的this
  2. 箭头函数没有arguments,使用上层的arguments
  3. 箭头函数不可new 因为没有this,new要把函数的prototype赋值给this的__proto__
  4. 箭头函数不会函数提升
  5. 不能用call和apply绑定

17. 页面导入样式时,使用 link 和 @import 有什么区别?

  1. link是由html提供的,@import是由css提供的。
  2. link会与页面加载时同步加载,@import在页面加载完成才会加载。
  3. link没有兼容性问题,@import存在兼容性问题,只有i5以上才能识别。
  4. link的权重会更高一些

18. JS 实现继承的方式有哪些

原型链,组合继承,寄生组合继承,类继承

19. ES6 中的 Module 和 CommonJS 模块有什么区别?

Module使用导出是export,导入是import是静态的,不立即执行 Commonjs使用的导出是module.exports方式,导入是require是动态的,立即执行的

20. JS 引用数据类型有哪些?

function,object,array

21. Vue3.0 里为什么要用 Proxy API 替代 defineProperty API?

proxy监听对象,defineproperty监听属性,当defineproperty监听对象时需要递归绑定监听,而proxy就不需要。而且defineproperty监听的对象添加属性时会监听不到。

22. JS 中 bind、call、apply 之间有什么区别?

都是用来改变this指向的。 bind返回的是一个函数,需要主动调用。 call,apply为立执行,apply传递数组,call散列传参(可分多次传入)

23. for 循环和 map 循环有什么区别?

for循环性能比map循环性能高,map循环更便于维护;map返回一个新数组,map是基于数组的循环的语法糖,而for是原生js语法;在计算上说,map是基于回调的,for是直接执行的。

24. JS 中的 Array.splice() 和 Array.slice() 方法有什么区别?

splice()返回符合条件的新数组,且会改变原数组;slice()返回符合条件的数组,并不会改变原数组

25. Vue 中组件和插件有什么区别?

组件 (Component) 是用来构成你的 App 的业务模块,它的目标是 App.vue。 插件 (Plugin) 是用来增强你的技术栈的功能模块,它的目标是 Vue 本身。

26. computed有哪些特性

  1. 支持缓存,只有依赖数据发生改变,才会重新进行计算
  2. 不支持异步,当 computed 内有异步操作时无效,无法监听数据的变化

27. http缓存有哪些?

  1. 强缓存:发送过得请求强行缓存,有效期内直接使用,不用重发请求
  2. 协商缓存:如果缓存过期,缓存的数据没有发生改变,服务器返回304,不返回内容。数据就能继续用了。

28. 什么是防抖与节流?应用场景举例

  • 防抖:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间 应用场景: 提交按钮、用户注册时候的手机号验证、邮箱验证。比如函数设置一个传入参数,内部声明一个timeout变量,然后返回一个函数,在回调函数内部设置一个setTimeout方法作为定时器,通过apply改变函数的this,再赋值给变量timeout,最后通过clearTimout方法清除timeout变量,也就是清除了定时器
  • 节流:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。 应用场景: window对象的resize、scroll事件 拖拽时候的mousemove 射击游戏中的mousedown、keydown事件 文字输入、自动完成的keyup事件,节流是通过闭包和setTimeout定时器来处理,在函数内声明一个变量time值为true和设置一个传入参数,然后在回调函数内部判断变量是否true,如果不是那就是return,然后再设置一个setTimeout定时器函数,定时器函数内部用apply改变传入参数的this的指向,并且定时器函数重新设置time变量为true

29. CSS 中实现元素水平垂直居中的方式有哪些?

  1. 设置绝对定位,然后上下左右值设为0,margin为auto。 ​
  2. 使用flex布局,justify-content:center,align-items:center。
  3. ​绝对定位,设置left,top为50%,transform:translate(-50%,-50%) ​
  4. 子元素display:inline-block,再在父元素上text-aligen:center ​
  5. 设置文本的话,行高+text-align

30. Vue 中 computed 和 watch 有什么区别?

computed是计算属性,依赖其他属性缓存的,不支持异步操作。 watch是监听器,不支持缓存,可以异步操作

31. ES6的新特性有哪些

  1. let定义变量,const定义常量
  2. Object.keys() 对键名的遍历,Object.values() 对键值的遍历,Object.entries()对键值对的遍历
  3. promise 优雅处理异步请求
  4. async/await 更好解决回调地狱
  5. 箭头函数
  6. class类
  7. symbol新类型。在某些情况下,我们需要确保属性名是唯一的,这时候就可以使用Symbol类型来创建一个全局唯一的字符,避免属性名或者方法名发生冲突。
  8. set集合
  9. 导入inport,导出export default
  10. for of 遍历键值对的值,for in 遍历键值对的键
  11. 模块字符串反引号  ``
  12.  数组对象的结构赋值 . ..
  13. 结构 {} 比如通过`${变量} `对变量进行拼接处理
  14. 函数传参可以有默认值
  15. spread操作符,被用于迭代器中
  16. rest操作符,用于获取函数的多余参数,这样就不需要使用arguments对象了

32. TCP 和 UDP 的区别是什么?并分别举例他们的应用场景有哪些?

个人认为最主要的区别是安全性,tcp通过三次握手不会出现丢包现象,udp只管发送。而且udp可以用来进行udp打洞,不需要端口映射就能与外网ip进行通信,p2p就是利用这种原理。音视频的的实时通讯也是用的udp因为传输量大,但是对数据的安全性要求不高,丢失一些像素并不影响整体,所以多采用udp。对于一些需要准确度较高的信息,比如QQ消息需要采用tcp通讯,QQ视频等可以使用udp。

如果面试官继续追问三次握手有哪些,或者问四次挥手是什么,可以简单回答。三次握手和四次挥手是TCP协议中用来建立和终止TCP连接的过程。三次握手是建立一个TCP连接的过程,需要三个步骤:客户端发送连接请求报文段,服务器收到请求并发送确认报文段,客户端收到确认并发送确认报文段。四次挥手是终止一个TCP连接的过程,需要四个步骤:客户端发送终止请求报文段,服务器收到请求并发送确认报文段,客户端收到确认并发送确认报文段,服务器收到确认并终止连接。

33. vue的双向绑定原理是什么?里面的关键点在哪里?

Vue数据双向绑定原理是通过数据劫持结合发布者-订阅者模式的方式来实现的,首先是对数据进行监听,然后当监听的属性发生变化时则告诉订阅者是否要更新,若更新就会执行对应的更新函数从而更新视图。通过Object.defineProperty()来劫持各个属性的setter, getter,在数据发生变动时通知Vue实例,触发相应的getter和setter回调函数。

关于VUE双向数据绑定,其核心是 Object.defineProperty() 方法
Object.defineProperty(obj, prop, descriptor)

这个方法内有三个参数,分别为 obj(要定义其上属性的对象)、prop(要定义或修改的属性)、descriptor(具体的改变方法)

简单来说,就是用这个方法定义一个值,当调用时我们使用了它里面的get方法,当我们给这个属性赋值时,同时又调用了里面的set方法

let obj = {}
Object.defineProperty(obj,'youjia',{
    get:function(){
        console.log('调用了get方法')
    },
    set:function(){
        console.log('调用了set方法')
    }
})
obj.youjia;
obj.youjia = 'jiayou'

34. 单向绑定与双向绑定的区别,适合的场景?

  • 单向绑定:单向绑定的优点是相应的可以带来单向数据流,这样做的好处是所有状态变化都可以被记录、跟踪,同时组件数据只有唯一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性。缺点则是代码量会相应的上升,数据的流转过程变长,从而出现很多类似的样板代码。同时由于对应用状态独立管理的严格要求(单一的全局store),在处理局部状态较多的场景时(如用户输入交互较多的“富表单型”应用),会显得繁琐。
  • 双向绑定:优点是在表单交互较多的场景下,会简化大量业务无关的代码。缺点就追踪局部状态的变化比较麻烦,潜在的行为太多也增加了出错时 debug 的难度。同时由于组件数据变化来源入口变得可能不止一个,造成数据追溯比较麻烦,不过一些浏览器可以通过安装插件来查看数据流动

35. 常用伪元素有哪一些?

  • a标签::hover  :visited ::before ::after 
  • p标签::first-child ::first-letter  ::first-line 
  • li标签 :first-child :last-child ::before ::after
  • input :focus  ::placeholder

36. 移动端如何适配不同屏幕尺寸?

  1. 媒体查询:使用CSS3的媒体查询功能,根据不同的屏幕尺寸和方向,应用不同的样式规则。通过设置不同的样式,可以适配不同的设备。个人认为是一种相对比较麻烦的方案,需要在每个页面上写媒体查询的代码。
  2. 百分比布局:使用百分比单位设置元素的宽度、高度和边距等。这样可以根据父元素的尺寸自动调整子元素的大小,实现一定程度的自适应效果。
  3. 因为不同属性的百分比值,相对的可能是不同的参照物,所以百分比往往很难统一,在移动端适配中使用是非常少的。
  4. 弹性盒子布局:使用CSS3弹性盒子布局模型,通过设置弹性容器和弹性项目的属性,实现灵活的布局。弹性盒子布局可以根据容器的尺寸和内容的大小,自动调整项目的 布局和排列。
  5. rem单位 + 动态的font-size:使用rem(根元素字体大小的倍数)作为单位来设置元素的尺寸。通过设置根元素的字体大小,可以控制整个页面的缩放比例,从而实现不同设备的适配。
  6. Viewport单位:使用viewport单位(例如vw、vh、vmin、vmax)来设置元素的尺寸。vw是窗口宽度的1%,vh窗口高度的1%,viewport单位是相对于浏览器窗口的尺寸来计算的,可以根据窗口的大小进行自适应布局。
  7. CSS预处理器的mixin和函数:使用CSS预处理器(如Sass或Less)的mixin和函数功能,可以根据不同的设备尺寸生成对应的样式代码。通过定义不同的mixin或函数,可以根据需要生成不同的样式规则。

37. less与sass的区别

共同点:less与sass都是css的预处理器,增加了变量、嵌套、函数、语句、继承等概念,基本思想都是用编程的思路编写css代码。

区别:

  • sass通过ruby编译,需要Ruby环境; less是通过js编译
  • Sass 变量符号是 $, Less 变量符号是 @
  • sass支持条件语句,可以使用if{}else{}、for{}循环等,less不支持
  • sass引用的外部文件命名必须以_开头, less引用外部文件和css中的@import相同
  • sass的混入是使用@mixin(){}–@include(), less的混入是使用.属性名(){}–.属性名()
  • sass的继承使用@extend, less的继承使用&:extend()

38. 本地存储有哪些,都有什么区别?

  1. sessionStorage。sessionStorage并不持久化,在窗口关闭时会被清除

  2. localStorage。localStorage是持久化存储,窗口关闭时不会被清除,只能手动清除,数据保存更多,可以达到5Mb

  3. session。session是基于cookie的,不过session保存在服务器端,客户端通过sessionId,读取到相对应的数据

39. Let、const、var三者有什么区别?

使用 var 时,变量会自动提升到函数作用域的顶部

let声明的范围是块作用域,不会在作用域变量提升,不能在同一个作用域内重复声明同一个变量,比如let声明的变量作用域仅限于or循环块内部

const与let基本相同,不过const要初始化变量,const变量的引用的是一个对象,那么修改这个对象内部的属性并不违反const的限制

40. 数组去重有哪些办法?

  1. Set()方法。Set本身是一个构造函数,用来生成Set数据结构,它类似于数组,但里面的成员是唯一的,不重复的。
  2. indexOf()。先for循环数组,然后在循环内部通过indexOf方法返回一个整数值,指出 string对象内子字符串的开始位置。如果没有找到该字符串则返回-1
  3. includes方法。  先for循环数组,然后在循环内部通过includes方法查询字符串中是否包含某一个元素   返回的是一个布尔值。
  4. slice方法。  Array.slice(num1,num2) 截取数组,将截取到的数组返回,不改变原数组。有两个参数,第一个是开始截取数组的位置,第二个是停止截取数组的位置
  5. 双层for循环结合splice方法。外层循环元素,内层循环时比较值如果有相同的值则跳过,内层循环时用的是splice方法去重,不相同的push进数组
  6. 数组递归去重。函数内部调用自身函数,先排序,然后从最后开始比较,遇到相同,则删除
  7. sort() 方法。sort() 方法可以对数组进行排序,然后从最后通过for循环开始比较,不相同则push进数组

41. Vuex有几个属性及作用?

  • state:vuex的基本数据,用来存储变量
  • geeter:从基本数据(state)派生的数据,相当于state的计算属性
  • mutation:提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
  • action:Action 提交的是 mutation,而不是直接变更状态,Action 可以包含任意异步操作。
  • modules::模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。

42. 判断一个变量是否是数组,有哪些办法?

  1. 使用 Array.isArray() 方法,如果是则返回true,否则返回false
  2. instanceof 运算符。用于检测某个对象是否是指定类(构造函数)的实例,因此可以使用该运算符判断变量是否为数组。如果是则返回true,否则返回false
  3. 通过原型链判断是否具有和数组同一原型链,如果是则返回true,否则返回false。变量.proto === Array.prototype。变量.constructor === Array。

43. 哪些遍历方式会改变原数组?

for,for of,for in,forEach

44. 什么是BFC

BFC,即“块级格式化上下文”(Block Formatting Context),是 CSS 中一个重要的概念,它指的是一个独立的渲染区域,让块级盒子在布局时遵循一些特定的规则。

  • 根元素或包含它的元素

  • 浮动元素(元素的 float 不是 none)

  • 绝对定位元素(元素的 position 为 absolute 或 fixed)

  • 行内块元素(元素的 display 为 inline-block)

  • 表格单元格(元素的 display 为 table-cell)

  • 表格标题(元素的 display 为 table-caption)

  • overflow 值不为 visible 的块元素

应用场景:清除浮动,避免 margin 重叠,实现多栏布局,防止浮动元素遮盖

45. Flex:1 包含哪三种属性

flex:1 是三个属性的连写:flex-grow(属性定义的是项目的方法比例,默认为 0)、flex-shrink(属性定义了项目的缩小比例)、flex-basis(属性定义了在分配多余空间之前,项目占据的主轴空间)

46. nextTick 的作用是什么?他的实现原理是什么?

作用:vue 更新 DOM 是异步更新的,数据变化,DOM 的更新不会马上完成,nextTick 的回调是在下次 DOM 更新循环结束之后执行的延迟回调。

实现原理:nextTick 主要使用了宏任务和微任务。

47. 宏任务和微任务

宏任务:整体代码script、setTimeout、setInterval、setImmediate、i/o操作(输入输出,比如读取文件操作、网络请求)、ui render(dom渲染,即更改代码重新渲染dom的过程)、异步ajax等

微任务:Promise(then、catch、finally)、async/await、process.nextTick、Object.observe(⽤来实时监测js中对象的变化)、 MutationObserver(监听DOM树的变化)

执行顺序:在 JavaScript 中,微任务会优先于宏任务执行。这意味着在当前任务执行结束后,所有微任务都会被立即执行,而宏任务只有在所有微任务执行完毕后才会执行。这意味着可以将一些耗时的操作放入宏任务队列中,从而避免阻塞当前任务的执行。

48.React hooks原理

hooks 的实现就是基于 fiber 的,会在 fiber 节点上放一个链表,每个节点的 memorizedState 属性上存放了对应的数据,然后不同的 hooks api 使用对应的数据来完成不同的功能。

49. Vue的渲染原理

vue是一个MVVM渐进式框架,vue框架中数据会自动驱动视图。

在MVVM架构下,ViewModel之间并没有直接的联系,而是通过ViewMode进行交互,Model和ViewModel之间的交互是双向的,因此View数据的变化会同步到Model中,而Model数据的变化也会立即反应到View上。

Vue 的响应式原理是核心是通过 ES5 的保护对象的 Object.defindeProperty中的访问器属性中的 get和 set方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM 树上。

50. 什么是虚拟dom?vue如何把虚拟dom转化成dom?

虚拟dom就是一段js形式的html代码,也就是jsx,jsx有tag标签,props属性(包括id,className,style,事件等),children子元素,数组或者字符串,vue通过diff算法,也就是通过新旧的虚拟dom对比,计算出更小的更新范围   核心算法就是:同层级比较,不做跨级比较;tag不相同则直接删除重建,不做深度比较;tag和key相同,则认为是相同节点,不做深度比较

vue源码中做diff算法的函数是patch函数           

51. vue2 如何监听数组的变化

在Vue 2中,底层是通过重写数组的原型方法来实现对数组变化的监听。具体来说,Vue 2使用了一个名为Observer的类来劫持数组的原型方法,使其在调用这些方法时能够触发相应的变化通知。

当Vue 2初始化一个响应式对象时,如果对象是一个数组,Vue会将数组的原型指向一个经过改造的Array原型对象。这个改造后的原型对象中重写了数组的一些常用方法,如push、pop、shift、unshift、splice、sort和reverse等。当调用这些方法时,Vue会在内部进行一系列的操作,包括触发变化通知、更新视图等。

52. react18 新特性

1. 默认情况下,setState 方法会以异步方式进行更新。

2. React 18 提供了 flushSync 方法,可以强制执行同步更新。


import { flushSync } from 'react-dom';

// 同步更新

flushSync(() => {

  this.setState({ count: this.state.count + 1 });

});

3. 使用 useTransition。useTransition 是 startTransition 的 hook 版本。提供的优先级控制来平衡不同任务之间的更新,延迟操作时控制异步更新的优先级。

4. startTransition  延迟更新组件状态,异步请求可以在后台执行,不会影响应用程序的交互性能。

5. createRoot  createRoot 是一个新的入口函数,用于创建根 React 组件。它可以替代原先的 ReactDOM.render 方法,使得开发者可以将多个根节点渲染到一个页面上。

6. useMutableSource   用于在多个组件之间共享数据状态。

7. useDeferredValue    用于组件状态的更新推迟到未来的帧中。这对于处理与用户输入相关的操作非常有用,可以避免在频繁输入时产生连续的重渲染。

8.  React.StrictMode   

React 严格模式主要包含以下几个方面的检查和提示:

  1. 识别不安全的生命周期方法,提示开发者修改,这些方法可能会导致意外的副作用或错误。
  2. 检测意外的副作用,例如:多余的重新渲染、不符合预期的函数调用等。
  3. 检测某些过时的 API 使用,提供更好的替代方案。
  4. 检测警告信息,使其更加明显和易于发现。

9. 并发模式 React Concurrent Mode   在传统的 React 中,更新组件树时会阻塞用户界面的响应,可能导致卡顿和延迟。利用并发模式,React 可以将渲染过程分解为多个小任务,并根据优先级来动态调整任务执行的顺序。这样,在浏览器空闲时间或网络请求等异步操作期间,React 可以暂停当前任务,执行其他具有更高优先级的任务,以实现更爽快的用户交互体验。

53. 说说 Vue 和 React 的区别

数据渲染:

  • Vue 双向绑定,修改数据自动更新视图,而 React 单向数据流,需要手动 setState
  • Vue template 结构表现分离,React 用 jsx 结构表现融合
  • Vue2 利用基本都是 MixinReact 可以用高阶函数、自定义 hook 实现
  • 都支持服务端渲染,都有虚拟 DOM,数据驱动,组件化开发,响应式,组件通信,生命周期,Diff,都有状态管理 Vuex/PiniaRedux/Mobx
  • React hook 是根据调用顺序来确定下一次重新渲染时的 state 是来源于哪个,所以有一些限制,比如不能在循环/条件判断/嵌套函数里使用,而且必须在函数最顶层调用 hook 等
  • Vue3 hook 是基于响应式实现的,它是声明在 setup 里,一次组件实例化只调用一次 setup,而 React 每次重新渲染都要重新调用,而且可以在循环/条件判断/嵌套函数里使用,并且正因为是基于响应式实现的,还自动实现了依赖收集,而 React需要手动传入依赖等
  • Vue2 响应式的特点就是依赖收集,数据可变,自动派发更新,初始化时通过 Object.defineProperty 递归劫持 data 所有属性添加 getter/setter,触发 getter 的时候进行依赖收集,修改时触发 etter 自动派发更新找到引用组件重新渲染

技术选项:

  • Vue2 响应式的特点就是依赖收集,数据可变,自动派发更新,初始化时通过 Object.defineProperty 递归劫持 data 所有属性添加 getter/setter,触发 getter 的时候进行依赖收集,修改时触发 etter 自动派发更新找到引用组件重新渲染
  • Vue3 响应式使用原生 Proxy 重构了响应式,一是 proxy 不存在响应式存在的缺陷,二是性能更好,不仅支持更多的数据结构,而且不再一开始递归劫持对象属性,而是代理第一层对象本身。运行时才递归,用到才代理,用 effect 副作用来代替 Vue2 里的 watcher,用一个依赖管理中心 trackMap 来统一管理依赖代替 Vue2 中的 Dep,这样也不需要维护特别多的依赖关系,性能上取得很大进步
  • 相比 Vue 的自动化,react 则是基于状态,单向数据流,数据不可变,需要手动 setState 来更新,而且当数据改变时会以组件根为目录,默认全部重新渲染整个组件树,只能额外用 pureComponent/shouldComponentUpdate/useMemo/useCallback 等方法来进行控制,更新粒度更大一些

Diff 算法:

  • Vue2 是同层比较新老 vnode,新的不存在老的存在就删除,新的存在老的不存在就创建,子节点采用双指针头对尾两端对比的方式,全量diff,然后移动节点时通过 splice 进行数组操作
  • Vue3 是采用 Map 数据结构以及动静结合的方式,在编译阶段提前标记静态节点,Diff 过程中直接跳过有静态标记的节点,并且子节点对比会使用一个 source 数组来记录节点位置及最长递增子序列算法优化了对比流程,快速 Diff,需要处理的边际条件会更少
  • React 是递归同层比较,标识差异点保存到 Diff 队列保存,得到 patch 树,再统一操作批量更新 DOMDiff 总共就是移动、删除、增加三个操作,如果结构发生改变就直接卸载重新创建,如果没有则将节点在新集合中的位置和老集合中的 lastIndex 进行比较是否需要移动,如果遍历过程中发现新集合没有,但老集合有就删除

性能优化:

React 迭代是增加了一个个避免刷新的钩子函数或者 API 还有采用 Fiber 的架构来做时间分片也是来优化渲染的性能。而 Vue1/Vue2/Vue3 每个版本虽然改的东西多,但核心都是围绕响应式来优化的,所以我觉得这是这两框架之间最重要的区别。

React hook 底层是基于链表实现的,每次组件被 render 的时候都会按顺序执行所有 hooks,而且正因为底层是链表,每个 hook 的 next 是指向下一个 hook 的,所以我们写代码是不能在不同的 hooks 调用里使用条件判断/函数嵌套之类的,因为这会导致执行顺序不对,从而出错。而 Vue hook 只会被注册调用一次,因为它是声明在 setup 里,一次组件实例化只调用一次 setupVue 之所以能避开这些问题,主要还是得益于数据响应式,不需要链表对 hooks 进行记录,而是直接对数据代理观察,但它也有困扰的地方,就是不得不返回一个包装对象,通过 .value 获取。面试官问为什么会有这样的问题?答:因为在 JS 里基础类型只有值,没有引用,或者说只存在栈里,使用完就回收了,无法追踪后续变化,自然做不到数据的代理和拦截,这算是这个设计的一个缺点吧。

再比如编译优化的问题,Vue 能够做到数据劫持,再到 Vue3 动静结合的 Diff 思想也得益于它的模板语法实现了静态编译。就是能做到预编译优化,可以静态分析,在解析模板时能根据解析到的不同的标签、文本等分别执行对应的回调函数来构造 AST,而 React 虽然 JSX 语法更加灵活,可也正是因为这样导致可以优化的地方不足,重新渲染时就是一堆递归调用 React.createElement,无法从模板层面进行静态分析,也就做不到双向绑定,即使是很厉害的 fiber,也是因为伤害已经造成,所以通过时间分片的优化来弥补伤害吧,因为已经无法在编译阶段进行优化了,这也是这个设计所带来的问题吧。

两者的项目选型

从加载速度,运行时性能来说,我觉得这两个框架综合各种场景应该是没什么质的差别的。硬要说的话,Vue 在更新时性能优化方面需要的心智负担可能会少那么一点,特别是 Vue3,而 React 如果不注意,容易导致一些组件无用的 Diff,但其实实际项目中真正能遇到这种性能瓶颈的也是极少数,所以(这里有两种说法):

(如果公司主要用 Vue 技术栈的话):所以总的来说我觉得 Vue 性能上会更有优势一点,特别是 Vue3 更加灵活,有很好的可扩展性,同时有更快的渲染速度和更小的打包体积。从 mixins 到 HOC 到 render props 再到 hooksReact 基本已经废掉了过去很多基于组件的逻辑抽象模式,抹掉了 JSX 对比模板的一个优势,Vue3 中现在也都能做到,所以我会偏向 Vue3

(如果公司主要用 React 技术栈的话):所以总的来说我觉得要是一些不大的系统或者 H5 就用 Vue,因为不管是上手还是开发难度上都很简单,开发效率也高嘛,而且它有更小的打包体积,毕竟在移动端网络差异大的情况下,资源体积是非常重要的。但像是一些中后台系统,或者一些大点的项目,会越做越大的,多人协作开发的,就用 React,因为它的函数式编程有更加灵活的结构和可扩展性,丰富的生态圈和工具链,解决方案多,后期也更方便迭代与维护,还适用原生 APP,所以我会偏向 React

什么是冒泡排序呢?

就是将数组的前一项元素与后一项元素进行比较,将大的一项往后移动一位,通过循环比较相邻的两位大小,不断将大的后移,实现数组元素的大小排序。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值