面试题

22 阅读5分钟

vue react 区别

react使用单项是数据流,vue使用双向数据流

react使用不可变数据,vue使用可变的数据

let [num,setNum]=useState(0)

组件通信,react使用的回调函数的方式,vue使用事件 @emit

diff算法,react通过diff算法,找到差异存储为dom序列,统一进行更新。vue双指针,边找差异边更新dom

vue 数据驱动模型 MVVM

M 模型层,负责业务逻辑已经服务器交互 V 视图层, 负责将视图模型转换为html页面 VM 视图模型,M和V的桥梁

组件化

易于维护,易于调试,降低耦合度

几乎没用 (SPA单页面 MPA多页面 SPA 由一个主页面和多个片段页面组成, MPA多个主页面构成)

v-if v-show

都是控制元素的显示与隐藏

v-if 通过dom的添加和删除控制,v-show通过css中的display:none控制 v-if会重新触发事件的监听已经子组件渲染。v-show只是css的切换 频繁进行切换使用v-show v-if会重新触发生命周期

v-if和v-for

不能一起使用,每次循环都会进行v-if的判断,浪费性能,v-for的优先级大于v-if

可以在外侧的template中进行v-if的判断,条件在数据中,可以提前使用computed使用filter进行过滤

为什么data是函数

一个组件被复用多次就会创建多个实例,本质上这些实例是同一个构造函数,对象属于引用数据类型,会造成数据污染

数据更新页面不刷新

vue2使用的是object.defineProperty方法实现的数据更新,当读取或修改一个对象的属性会触发setter哥getter方法, 而新增是不会触发的,

可以使用this.$set新增一个属性。使用object.assign新创建一个对象,将原对象和新增的属性进行混入(可以添加多个属性)。最不行使用强制刷新

组件通信方式

父子组件,props,this.emit,this.emit,this.refs

兄弟组件:eventBus,创建一个事件总线,兄弟组件通过emit去创建一个自定义事件,第二次参数用来传递数据,另一个兄弟组件通过emit去创建一个自定义事件,第二次参数用来传递数据,另一个兄弟组件通过on接受

祖先:在祖先组件中通过provide属性去传递数据,在孙组件中通过inject去接受 复杂通信,vuex,作为公共容器,state存储数据,getter,操作state中的数据,muations用来操作state中的方法,actions也是用来操作方法,在muations的基础上进行异步操作

this.$nextTick 是在下次更新结束之后,执行延时回调,在修改之后执行,会获取到更新后的dom,

将回调函数放入callBack中等待执行,在将执行函数放入宏任务或者微任务,当循环到了宏任务获取微任务中,在执行callBack中的回调函数

vue2与vue3的区别

响应式:vue2采用object.definePropty方法对每个属性劫持。vue3采用proxy 代理的方法,可以直接监听每个属性,提高性能。

vue2采用选项式api,对于逻辑负责的代码不易于处理,vue3采用组合式api,对于逻辑相同的代码统一处理

vue2的diff算法会对比每一个属性,vue3对于相同的属性,会进行静态标记,再更改时直接使用

vue2必须需要根节点包裹,vue3不需要的,生成虚拟节点,提升性能

vue3更小包的体积,打包速度提升,运行速度更快

vue3对ts的支持

treeShaking 的支持更好的剔除未使用的代码

生命周期的不同

Suspense 用于处理异步组件加载时的用户体验。接受两个插槽#default(展示的内容) #fallback(加载时显示的内容) teleport 多用于弹框,接受一个to属性,指定目标元素的选择器,可以使用disabled属性禁用

展示的内容

toRef与toRefs

都是用来处理响应式数据的工具函数

let person=reactive({ name:'张三', age:18 })

let {name,age}=toRefs(person)

let nameRef=toRef(person,'name')

toRefs可以使整个对象的属性结构为响应式,toRef将对象中的单个结构为响应式

深浅拷贝

浅拷贝新建一个对象,对原对象进行拷贝,基本数据类型拷贝值,引用数据类型拷贝内存地址,共享一个内存地址,修改新对象的值,原对象也会发生改变

object.assign slice contant 扩展运算符

深拷贝不共享内存地址,修改不影响原对象。

deepClone json.parse(json.string())

循环拷贝 function deepClone(obj, hash = new WeakMap()) { if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作 if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); // 可能是对象或者普通的值 如果是函数的话是不需要深拷贝 if (typeof obj !== "object") return obj; // 是对象的话就要进行深拷贝 if (hash.get(obj)) return hash.get(obj); let cloneObj = new obj.constructor(); // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身 hash.set(obj, cloneObj); for (let key in obj) { if (obj.hasOwnProperty(key)) { // 实现一个递归拷贝 cloneObj[key] = deepClone(obj[key], hash); } } return cloneObj; }

闭包理解

内层函数可以访问到外层函数的作用域

私有变量,延长生命周期,科里化函数(避免频繁重复调用相同参数的函数,又可以轻松的重用)

作用域理解

全局作用域,局部作用域,块级作用域(在大括号中使用let和const声明的变量存在于块级作用域中。在大括号之外不能访问这些变量)

词法作用域,又叫静态作用域,变量被创建时就确定好了,而非执行阶段确定的。也就是说我们写好代码时它的作用域就确定了,JavaScript 遵循的就是词法作用域

作用域链 当在Javascript中使用一个变量的时候,首先Javascript引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域

如果在全局作用域里仍然找不到该变量,它就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错

原型链理解

每个对象拥有一个原型对象,当试图访问一个对象的属性时,不仅会在该对象上寻找,还会搜索该对象的原型,以及原型的原型,直接找到一个名字匹配的属性或到达原型链的末端

原型链

原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法