2023年来啦,相信有不少同学都经历了“毕业潮”。哈哈不要灰心,新的一年,新的开始,希望本篇文章可以帮助各位同学找到自己满意的工作。
1、react、vue 这两个框架的异同以及各自的优点
vue和react的相同点:
- 都支持服务端渲染;
- 都是组件化思想,组件化开发;
- 都利用了虚拟dom(Virtual DOM),提高渲染速度;
- 都有独立的路由系统,以及独立的状态管理库;
- 都有适配的第三方组件库;
vue和react的不同点:
- vue的思想是响应式的,也就是基于数据是可变的,实现了数据的双向绑定,react整体是函数式的思想,是单向数据流,推崇结合immutable来实现数据不可变;
- vue 采用了template(template,script,style), react采用了jsx(把html和css全都写进js中,编写代码方便,简洁)这两个本质上都是模版;
- 在react中要想更新状态,必须调用setState方法,而在vue中只需要通过this的某种方式去更新state中的数据,这种方式更加方便;
- react 的性能优化需要手动去做,而vue的性能优化则是自动的,但是vue的响应式机制也有问题,就是当 state 特别多的时候,Watcher 会很多,会导致卡顿,所有更适合小型项目开发;
- react可以使用Create React App (CRA),而vue对应的则是Vue-cli。两个工具都能让你得到一个根据最佳实践设置的项目模板;
- 都有管理状态,react有redux,而vue有自己的vuex;
利用虚拟dom的区别:
在react中,当状态发⽣改变时,组件树就会⾃顶向下的全diff, 重新render⻚⾯,重新⽣成新的虚拟dom tree,新旧dom tree进⾏⽐较,进⾏patch打补丁⽅式,局部更新dom。 所以react为了避免⽗组件跟新⽽引起不必要的⼦组件更新,可以在shouldComponentUpdate做逻辑判断,减少没必要的render, 以及重新⽣成虚拟dom,做差量对⽐过程;
vue会跟踪每⼀个组件的依赖关系,局部渲染组件树⽽不需要重新渲染整个组件树。 通过Object.defineProperty()来劫持各个属性的setter/getter,在数据变动时发布消息给订阅者,触发相应监听回调。 当把⼀个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,Object.defineProperty 将它们转为 getter/setter。 ⽤户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
vue的优点:
- 轻量级的框架+指令: 它通过双向数据绑定把 View 层和 Model 层连接了起来.实际的 DOM 封装和输出;
- 双向数据绑定:当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化;
- 组件化开发:就是把页面拆分成多个组件,每个组件依赖的 CSS、JS、模板、图片等资源放在一起开发和维护;
- 更好的性能: 最小力度更新,vue 每次更新会进行虚拟 dom 和屏幕已有 dom 对比,只更新有变化的部分,性能更高;
- 单页面路由:单页是把原本的多个页面以组件的形式集成在一个页面中,页面跳转时由 vue 路由到目标页面,分别加载不同的组件,而页面不会刷新,路由在更新。
react的优点:
- 灵活性和响应性:它提供最大的灵活性和响应能力;
- 丰富的JavaScript库:来自世界各地的贡献者正在努力添加更多功能;
- 可扩展性:由于其灵活的结构和可扩展性,React已被证明对大型应用程序更好;
- 不断发展: React得到了Facebook专业开发人员的支持,他们不断寻找改进方法。
2、vue 数据响应-双向绑定的原理-v-model
vue采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,vue在初始化的时候,在initState方法中会调取initData方法初始化data数据,对data对象进行遍历,在这个方法中会调observe(监听器,观察者)对⽤户的数据进行监听,在observe中会对对象new Observe实例化创建监听,在observe中对数据进行判断,如果是数组执行observeArray深度进行监听,继续执行observe方法,如果当前传入的是对象则执行this.walk,对对象进行循环,重新定义对象的属性,这时使用的就是defineReactive,它是vue中的⼀个核心方法,用来定义响应式。在defineReactive方法中实例化了⼀个Dep(发布者),通过Object.defineProperty对数据进行拦截,把这些property 全部转为 getter/setter。get数据的时候,通过dep.depend触发Watcher(订阅者)的依赖收集,收集订阅者,如果数据是数组,执行dependArray,对数组中的每个值通过depend都添加到依赖。set时,会对数据进行比较,如果数据发⽣了变化会通过dep.notify发布通知,通知watcher,更新视图。
3、vue 无法检测数组和对象变化的原因
其实是可以检测到变化的,只是在 vue 的实现中,从性能/体验的性价比考虑,放弃了这个特性。当然,在 Vue3.0 中则对这方面做了很大程度的改进,采用 Proxy 替代了 Object.defineProperty 解决了这个问题。
4、 vue无法检测数组和对象变化的解决办法
监听数组的变化:
vue 能够监听数组变化的场景
- 通过赋值的形式改变正在被监听的数组;
- 通过 splice(index, num, val) 的形式改变正在被监听的数组;
- 通过数组的 push 的形式改变正在被监听的数组;
vue 无法监听数组变化的场景
- 通过数组索引改变数组元素的值;
- 改变数组的长度;
vue 无法监听数组变化的解决方案
- this.$set(arr, index, newVal);
- 通过 splice(index,num,val);
- 使⽤临时变量作为中转,重新赋值数组;
监听对象的变化:
vue 能够监听对象变化的场景
通过直接赋值的场景: e.g:watchObj = {name:"zyk"}
vue 无法监听对象变化的场景
对象的增加、删除、修改无法被vue监听到
vue 无法监听对象变化的解决方案
- 使用 this.set(object, key, value) (vue 无法监听 this.$set 修改原有属性)
- 使⽤ Object.assign(),直接赋值的原理;(深拷贝,推荐使用)
5、判断数据类型最常见的几种方式
typeof
可以判断数据类型,它返回表示数据类型的字符串(返回结果只能包括 number,boolean,string,function,object,undefined),可以使用typeof判断变量是否存在。
if(typeof a!="undefined"){...}
typeof 运算符的问题是无论引用的对象是什么类型它都返回object。
instanceof
原理:因为A instanceof B 可以判断A是不是B的实例,返回⼀个布尔值,由构造类型判断出数据类型。instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。
console.log(arr instanceof Array ); // true
Object.toString.call()
Object.prototype.toString.call();
console.log(toString.call(123)); //[object Number]
console.log(toString.call('123')); //[object String]
console.log(toString.call(undefined)); //[object Undefined]
6、如何实现跨域,三种⽅式
jsonp
jsonp 实现原理:主要是利⽤动态创建 script 标签请求后端接口地址,然后传递 callback 参数,后端接收callback,后端经过数据处理,返回 callback 函数调用的形式,callback 中的参数就是 json。
通过代理的方式(前端代理和后端代理)
前端代理我在 vue 中使用那个 vue.config.js 里面配置⼀个proxy,里面有个 target 属性指向跨域链接.修改完重启项目就可以了。实际上 就是启动了⼀个代理服务器.绕开同源策略,在请求的时候,通过代理服务器获取到数据再转给浏览器。
CORS
CORS 全称叫跨域资源共享,主要是后台工程师设置后端代码来达到前端跨域请求的。
注:现在主流框架都是用代理和 CORS 跨域实现的
7、computed 和 watch介绍和区别
watch
- watch是监听⼀个值的变化,然后执行对应的回调;
- watch中的函数不需要调用;
- watch有两个参数:
immediate:组件加载立即触发回调函数执行;
deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使⽤,例如数组中的对象内容的改变;
- watch中的函数名称必须要和data中的属性名⼀致,watch依赖于data中的属性,data中的属性发生变化的时候;
- watch中的函数就会发生变化;
- watch不支持缓存;
computed
- computed是计算属性,通过属性计算得来的属性;
- computed中的函数直接调用,不用;
- computed中的函数必须用return返回;
- computed是依赖data中的属性,data中属性发生改变的时候,当前函数才会执行,data中属性没有改变的时候,当前函数不会执行;
- computed中不能对data中的属性进行赋值操作,如果对data中的属性进行赋值,data中的属性发生变化,从而触发computed中的函数,就会形成死循环;
- computed属性的结果会被缓存,除非依赖的属性发生变化才会重新计算。
使用场景
watch的使用场景: 一个数据影响多个数据,需要在数据变化时执行异步操作或者开销较大的操作时使用。
例如:搜索数据、浏览器自适应、监控路由对象、监控自身属性变化。
computed的使用场景: ⼀个数据受多个数据影响,处理复杂的逻辑或多个属性影响⼀个属性的变化时使用。
例如:购物车商品结算,数量计算等。
8、虚拟 Dom 和真实 Dom 之间怎么转化
其实和深拷贝差不多,深度遍历真实DOM,遇到节点就转换为虚拟DOM,遍历标签,复制而已。
自定义指令注册使用场景
注册⼀个自定义指令有全局注册与局部注册
全局注册主要是通过Vue.directive 方法进行注册 Vue.directive 第⼀个参数是指令的名字(不需要写上v-前缀),第⼆个参数可以是对象数据,也可以是⼀个指令函数
// 注册⼀个全局⾃定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插⼊到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus() // ⻚⾯加载完成之后⾃动让输⼊框获取到焦点的⼩功能
}
})
局部注册通过在组件options 选项中设置directive 属性
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus() // ⻚⾯加载完成之后⾃动让输⼊框获取到焦点的⼩功能
}
}
}
然后你可以在模板中任何元素上使用新的 v-focus property,如下:
<input v-focus />
使用场景: 使用自定义指令可以满足我们日常⼀些场景,这里给出几个自定义指令的案例:
- 防抖
- 图片懒加载
- ⼀键Copy的功能
9、keep-alive 作⽤及使⽤场景
keep-alive:主要用于保存组件状态 或避免重新渲染
应用场景:点击商品列表进⼊详情页面 点击返回按钮 回到商品列表页
第一种方法:
<keep-alive>
<router-view></router-view>
</keep-alive>
被keep-alive标签包裹的路由都会被缓存起来 但是我们不需要缓存这么多组件
- include:字符串或正则表达式。条件匹配的都缓存。
- exclude:字符串或正则表达式。条件匹配都不会被缓存。
第二种方法:
在router路由中配置 在某个路由中加上⼀个meta属性
{
path:'/index',
component:()=>import('@/views/index'),
meta:{
keepAlice:true,//为true是缓存的意思
}
}