1.vue的双向绑定原理
vue框架本身的一个重要特性就是数据的双向绑定,底层通过数据劫持的方式完成数据的收集和渲染,当数据发生变化时通过自己的发布订阅模式进行虚拟DOM树的更新,通过DIFF算法优化更新效率,执行差异化更新和提高视图加载性能。
2.vue的生命周期
创建前/后:beforeCreate和created
渲染前/后:beforeMount和mounted
数据更新前/后:beforeUpdate和updated
销毁前/后:beforeDestory和destoryed
3.vue2和vue3的区别
双向数据绑定的原理不同,vue2使用Object.defineProperty进行数据劫持,结合发布订阅者模式来实现,vue3使用proxy来实现。
vue2的组件只支持一个根节点,vue3支持多个根节点。vue3增加了组合型api,代码更简洁了。
生命周期这块vue3增加了setup,替代了beforeCreate和Created,其他生命周期的名字也发生了变化。
vue2和vue3的diff算法发生了变化,vue2会比较每一个虚拟节点,vue3会比较发生变化的虚拟节点。
v-if和v-for的优先级发生了变化,vue2中v-for的优先级高于v-if,vue3中v-if的优先级高于v-for
4.vue和react的区别
设计思想:react是函数式思想,使用jsx语法,all in js;vue是响应式思想,也是基于数据可变的,把html css js组合到一起。
渲染方式:react默认当状态改变时会重新渲染所有子组件,通过shouldComponentUpdate这个生命周期方法控制;vue在渲染过程中会跟踪每个组件的依赖关系,不需要渲染整个组件树。
性能:react适合大中型项目,vue中小型项目。
vue支持指令,react没有。
react是单向数据流,vue是双向数据绑定。
5.es6的新特性
let、const(let是声明变量、const是声明常量)
模板字符串
解构
...操作符
promise
import导入和export导出
6.Vue中key有什么作用
key的作用主要是为了高效的更新虚拟DOM,如果没有唯一key,页面上删除一条标签,由于不知道是哪一条,所有要把全部的虚拟dom重新渲染,如果知道key为x标签被删除掉,只需要把渲染的dom为x的标签去掉即可。
7.简单描述一下vue中的diff算法
用户在渲染视图时底层会将模板文件进行数据抽象,抽取成的抽象树加载到内存中形成虚拟DOM结构,然后通过抽象树ACT计算的哈希表数据完成视图的更新,最终决定更新的节点数据,完成差异化加载更新,提高视图的渲染效率。
8.call、apply、bind的异同
相同点:apply、call、bind三者都可以利用后续参数传参
不同点:首先call和bind的参数都是以逗号隔开的这样一种形式,然后apply的参数是以数组的形式存在。bind是返回对应函数,便于稍后调用;apply、call则是立即调用 。
9.js的循环机制
js是一门单线程语言,同一时间只能做一件事,单线程可能会出现阻塞的问题。所以js分了同步任务和异步任务,同步任务进入主线程,主执行栈会立即执行。
异步任务会进入浏览器进行管理,然后进入任务队列中,当主线程中所有的同步任务全部执行完,才会执行队列中的异步任务,以上的一个操作不断循环就叫做事件循环机制。
先执行顺序:同步代码 - 异步代码(微任务)promise.then(xxx) - 异步代码(宏任务)setTimeout(xxx)
10.promise是什么
promise是es6中的一种异步编程解决方案,它是一个链式调用,里面有.then .catch .all三个方法。.then的话里面包含两个参数,一个resolved(成功),一个rejected(失败),.then一般接收成功的回调 .catch就是捕捉失败的回调 .all就是都会回调
promise是同步函数,then方法本身是同步执行,then方法中的内容加入微任务异步执行
11.原型链是什么
原型链它本身就是一个列表,当你有一个函数的时候它会返回一个实例,这个实例会有一个proto属性指向它的原型,同时这个function会有一个prototype属性指向这个原型。
当你访问这个实例的属性的时候,如果它没有的话,它会顺着它这个proto指向的原型上找,如果原型上没有的话它会顺着proto再找原型的原型,一直指到大Object的实例,如果找到他会把这个值赋给你取的这个变量。
它是原型链特有的,所有对象都可以共享原型上面的方法,这个在java python都没,它的优势就是能够节省内存,非常方便。
应用场景:jQuery源码上,当我们使用$的时候就可以很方便的拿到一些属性。在vue开发的时候,我们的axios就放到了vue的实例上,我们通过$axios就可以在任何一个文件上使用这个方法。
12.闭包的理解
闭包可以理解成一个函数内部的函数。它有两个作用,第一个作用是保护,第二个作用就是保存。
保护就是函数之间会形成一个私有作用域,不受外部干扰,它很适合模块化开发。第二个就是保存,当一个函数返回另一个引用数据类型,被外界所接收了就会形成不销毁的作用域,它就会一直存在在堆内存里,它很容易造成内存泄漏。
ps:内存泄漏就是浏览器无法回收代码的内存
13.什么是深拷贝、什么是浅拷贝?
浅拷贝:创建一个新对象,这个对象对原始对象进行拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。
14.水平垂直居中
方法一:position 定位(利用子绝父相对定位的方式来实现)
.parent{ width: 500px; height: 500px; position:relative; }
.child{ width: 100px; height: 100px; position:absolute; top:50%; left:50%; margin-left:-50px margin-top:-50px }
方法二:position + transform(利用css3的transform)
.parent{ position:relative; }
.child{ position:absolute; top:50%; left:50%; transform: translate(-50%, -50%); }
方法三:flex 布局(子盒子有或没有宽高的时候都适用)
.parent { display:flex; algin-item:center; 垂直 justify-content:center; 水平 }
15.平时都是用什么实现跨域的?
跨域的解决方式有3种:JSONP技术、后端同事打开CORS功能、自己配置反向代理(同源策略:协议、域名、端口号相同)
JSONP:script标签的src发起一个请求,它不受浏览器的同源策略的一个限制,但是只支持get请求。
CORS:需要浏览器和服务器同时打开(后端同事打开),或者在请求头中添加Content Type:application/json属性和属性值。
反向代理:利用nginx创建一个代理服务器,这个服务器的域名跟浏览器要访问的域名一致,通过这个代理服务器修改cookie中的域名访问来http接口,通过反向代理实现跨域。
16.一个页面从输入URL到页面加载显示完成,这个过程中都发生了什么?
1.浏览器查找域名对应的IP地址
2.浏览器向web服务器发送一个HTTP请求(TCP三次握手)
3.服务器301重定向(从http://example.com重定向到http://www.example.com)
4.浏览器跟踪重定向地址,请求另一个带www的网址
5.服务器处理请求(通过路由读取资源)
6.服务器返回一个HTTP响应(报头中Content-type设置为text/html)
7.浏览器进DOM树构建
8.浏览器发送请求获取嵌在HTML中的资源(如图片、音频、CSS、JS等)
9.浏览器显示完成页面
17.后台管理系统中权限管理是怎么实现的?
登录:当用户填写完账号和密码后向服务器验证是否正确,验证通过之后,服务器会返回一个token,拿到token之后(我们会将这个token存储到cookie中,保证刷新页面后记住用户登录状态),前端会根据token再去拉取一个user_info的接口来获取用户的详细信息(如用户权限,用户名等等信息)
权限验证:通过token获取用户对应的权限,动态根据用户的权限算出其对应有权限的路由,通过router.addRoutes动态挂载这些路由。
18.大文件上传
分片:使用File对象的slice方法进行分片-分片成Blob格式的二进制流,传完之后再去组合分片就好了。
开发思路:
1.对文件做切片,将一个请求拆分成多个请求,每个请求的时间就会缩短,且如果某个请求失败,只需要重新发送一次请求即可,无需重头开始。
2.通知服务器合并切片,在上传切片后,前端通知服务器做合并切片操作。
3.控制多个请求的并发量,防止多个请求同时发送,造成浏览器内存溢出,导致页面卡死。
4.做断点续传,当多个请求中有请求发送失败,例如出现网络故障、页面关闭等,我们得对失败的请求做处理,让它们重复发送。
19.预渲染
项目中有一个比较核心的c端场景,然后这个页面的流量非常大,它本身就是一个偏平台的页面,它的页面接口非常多。虽然我们也做了一些接口的聚合,但他可能还是要发4到5个接口,一个是你的请求并发,而且这个数据必须是首屏的数据,你不管是用ssr也好,还是其他的这个性能压不下来。
然后我们的静态资源能离线化的全部离线化,能缓存的全部缓存了。然后它本身的js开销也不大,他整个页面的lcp大概是2.6到2.7左右,其实也不算高。但是主要60%~70%都是那个接口的耗时,所以这个时候我们要怎么办呢?
我们的做法就是:首先这个页面它的场景比较特殊,他是一个交互高度可预测的,可以理解成abcd,这个用户他一定在b或者c的时候,我们就能判断的出来,这个用户有非常高的概率会点击这个d,所以中间我们就推动客户端去做这样的一个事,就叫做预渲染。
可能他在c的时候,我们通过去配置一些白名单和策略,当他发现他的这个身份和这个页面地址符合,在白名单内的时候,客户端就会在后台直接起一个容器,当用户在点击的时候,这个容器就直接可见了。
20.性能优化
性能优化是一个比较大的话题,主要从3个方面进行优化:
1.网络层面:
减少http请求
在发生请求页面做一些判断,进行节流处理
压缩图片,页面使用懒加载
然后利用缓存机制,把一些不经常更新的资源放到缓存里面
2.代码层面:
html尽量用语义化的标签
css尽量把公共的代码提取出来,做成一个文件
js部分可以把公共的代码封装成一个函数、尽量少用全局变量、减少dom操作、还有减少事件的绑定次数
3.webpack打包:
开启HRM热模块替换
开启source-map
使用gzip压缩
使用babel缓存
开启多进程打包
使用懒加载和预加载