本篇面经按照对应的考察点进行分类
1. CSS
1.1 实现响应式布局的几种方式
- 百分比布局
- 媒体查询布局
- rem响应式布局
- vw响应式布局
- flex弹性布局
1.2 flex布局的实现
flex主要包含两部分的内容:轴和项目。
- 轴:分为主轴(main axis)和交叉轴(cross axis)
- 容器:父容器和子容器,子容器又可以被叫做项目;当其父元素为
display:flex时,子元素就可以称之为项目
注意:容器具有这样的特点:父容器可以统一设置子容器的排列方式,子容器也可以单独设置自身的排列方式,如果两者同时设置,以子容器的设置为准。
容器,容器中常用属性主要为4个,父容器和子容器各两个。 父容器中可以使用的属性为:
- justify-content:设置主轴上子元素的对齐方式
- flex-start:起始端对齐,类似于word中的左对齐
- flex-end:末尾端对齐,类似于word中的右对齐
- center:居中对齐,类似于word中的居中对齐
- space-around:子容器沿主轴均匀分布,位于首尾两端的子容器到父容器的距离是子容器间距的一半。
- space-between:子容器沿主轴均匀分布,位于首尾两端的子容器与父容器相切,类似于word中的两端对齐
- align-items:设置交叉轴上子元素的对齐方式
- flex-start:起始端对齐
- flex-end:末尾端对齐
- center:居中对齐
- baseline:baseline是基线对齐,是指首行文字
- stretch:子容器沿交叉轴方向的尺寸拉伸至与父容器一致,当子元素不设置高度的时候产生
子容器中可以使用的属性为:
- flex flex是一个复合属性,包含flex-grow flex-shink flex-basis这三个属性。
- flex-grow:
flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大 - flex-shink:
flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。 - flex-bisis:浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为
auto,即项目的本来大小。
- align-self 单独设置子容器如何沿交叉轴排列:align-self,同父级元素的align-items属性 关于轴的介绍,可以看MDN文档的介绍,以上只介绍了部分
1.3 CSS中实现定位的方式
1.4 为什么有时候用translate来改变位置而不是定位
改变transform或opacity不会触发浏览器重新布局(reflow)或重绘(repaint),只会触发合成(compositions)。而改变绝对定位会触发重新布局,进而触发重绘和复合。transform使浏览器为元素创建一个 GPU 图层,但改变绝对定位会使用到 CPU。 因此translate()更高效,可以缩短平滑动画的绘制时间。
能够触发合成的情况:
- 3D transforms: translate3d, translateZ 等;
- video, canvas, iframe 等元素;
- 通过 Element.animate() 实现的 opacity 动画转换;
- 通过 СSS 动画实现的 opacity 动画转换;
- position: fixed; 。。。
1.5 重绘和重排的理解
2. JS
2.1 Promise 和async/await有什么区别
定义在原型上面的方法是给实例对象使用的
-
Promise的出现解决了传统callback函数导致的“地域回调”问题,但它的语法导致了它向纵向发展行成了一个回调链,遇到复杂的业务场景,这样的语法显然也是不美观的。
而async await代码看起来会简洁些,使得异步代码看起来像同步代码,await的本质是可以提供等同于”同步效果“的等待异步返回能力的语法糖,只有这一句代码执行完,才会执行下一句。await又是相当于新建一个Promise,然后await后面的代码,相当于Promise中的.then调用链。
-
async await与Promise一样,是非阻塞的。
-
async await是基于Promise实现的,可以说是改良版的Promise,它不能用于普通的回调函数。
3. 前端性能优化
3.1 怎么做前端性能优化
4. 其他
4.1 实现即时通讯(Instant Messaging)有哪几种方式
即时通讯是指客户端无需向服务器端主动发起请求,就可以拿到服务器端的数据。
场景:比如QQ邮箱,在我们没有刷新页面,没有主动请求的情况下,当有新的消息发送过来的时候,邮箱的右下角就会出现对应的提示。即时通信业经常用来实现通知功能。
(1)实现方式
- 轮询(短轮询)
- 长轮询(comet)
- 长连接(SSE)
- WebSocket
(2)具体实现
1. 短轮询
短轮询仍然是传统的客户端与服务器端的通信方式,通过"一发一收"来进行数据的访问。基本思路是浏览器每隔一段时间去向服务器请求数据。当数据请求完毕的时候,关闭这个TCP连接。当下一次请求的时候,再次建立TCP连接。缺点也很明显,就是由于需要不断的建立http连接,严重浪费了服务器端和客户端的资源。
// 请求数据方法
const request = () => {}
// 每隔5秒发送一次请求
setInterval(request(), 5000)
2. 长轮询
当服务器收到客户端发来的请求后,不会立即响应,而是先将请求挂起,然后判断服务器端数据是否有更新。 如果有更新,则进行响应,如果一直没有数据,如果到达一定的时间限制(服务器端设置)才返回。客户端在处理完服务器返回的信息后,再次发出请求,重新建立连接。
客户端发送请求后服务器端不会立即返回数据,服务器端会阻塞请求连接不会立即断开,直到服务器端有数据更新或者是连接超时才返回,客户端才再次发出请求新建连接、如此反复从而获取最新数据。
// 请求数据方法
const request = () => {}
// 当数据请求返回结果后发起下一次请求
request().then(res => {
request();
})
以上的长短轮询都是按照“一发一收”的客户端,服务器端的通信方式进行交互的。短轮讯是每隔一段时间就有客户端主动发起请求;长轮询则是对于客户端发起的请求,只有服务器端的数据变更后,才会给予响应
3. 长连接
SSE是HTML5新增的功能,允许服务器推送数据到客户端,利用的协议仍然是HTTP,最大的特点是不需要客户端发送请求,可以实现只要服务器端数据有更新,就可以马上发送到客户端。
SSE 的客户端 API 部署在EventSource对象上。具体的实现方式为
//上面的`url`可以与当前网址同域,也可以跨域。跨域时,可以指定第二
//参数,打开`withCredentials`属性,表示是否一起发送 Cookie。
var source = new EventSource(url);
var source = new EventSource(url, { withCredentials: true });
// 连接一旦建立,就会触发onopen事件
source.onopen = function (event) {
// ...
};
//客户端收到服务器发来的数据,就会触发`message`事件,
source.onmessage = function (event) {
var data = event.data;
// handle message
};
// 上面代码中,事件对象的`data`属性就是服务器端传回的数据(文本格式)。
EventSource实例的readyState属性,表明连接的当前状态。该属性只读,可以取以下值。
- 0:相当于常量
EventSource.CONNECTING,表示连接还未建立,或者断线正在重连。- 1:相当于常量
EventSource.OPEN,表示连接已经建立,可以接受数据。- 2:相当于常量
EventSource.CLOSED,表示连接已断,且不会重连
4. webSocket
WebSocket是Html5定义的一个新协议,与传统的http协议不同,该协议可以实现服务器与客户端之间全双工通信。简单来说,首先需要在客户端和服务器端建立起一个连接,这部分需要http。连接一旦建立,客户端和服务器端就处于平等的地位,可以相互发送数据,不存在请求和响应的区别。
5. Vue
5.1 如果直接给Vue中的数组项赋值的话,能够监听到变化吗
5.2 $set的使用方法
-
参数:
{Object | Array} target{string | number} propertyName/index{any} value
-
返回值:设置的值。
-
用法:
这是全局
Vue.set的别名。向响应式对象中添加一个property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如this.myObject.newProperty = 'hi')
注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
参考:Vue.set
5.3 vue2能监听到数组的变化吗
5.4 $nextTick的作用,以及实现方式,$nextTick是宏任务还是微任务
在Vue中,一个tick包括同步任务、异步队列(数据的改变会被缓冲到这个异步队列中)的更新、UI渲染。所以当我们想要在当前的tick中访问到更新后的DOM节点时,是无法完成的。因为UI的渲染是在事件循环的最后进行的。具体的步骤是:
- 因为Vue中首先是同步代码在执行,在同步代码阶段还没有涉及到DOM的更新
- 接下来, Vue会开启一个异步队列,缓冲在该tick下发生的所有数据改变。在这个过程中,如果同一个 watcher 被多次触发,只会被推入到队列中一次。(这里其实相当于js在执行Promise的时候,会将
then中的回调函数添加到微任务队列中) - 同步任务执行完毕,开始执行异步 watcher 队列的任务,更新 DOM 。Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MessageChannel 方法,如果执行环境不支持,会采用 setTimeout(fn, 0) 代替。
- 在该tick中,通过
Vue.nextTick获取到改变后的 DOM 。通过 setTimeout(fn, 0) 也可以同样获取到。因为在下一个事件循环中DOM节点已经完成了更新
DOM是在事件循环中的哪个时间段进行更新的呢?
5.5 vue的双向绑定管理
vue的双向绑定是基于MVVM模型的:
- 数据层Modal:应用的数据以及业务逻辑
- 视图层view:应用的展示效果,各类的UI组件
- 业务逻辑层viewModel:负责将数据和视图连接起来 双向绑定就是当数据变化是更新视图,当视图变化时更新数据。 总体来说,主要包含两个部分:
- 监听器:observer:对所有的数据进行响应式监听
- 解析器:compiler:将每个元素节点的指令进行扫描和解析,根据指令去替换数据,绑定对应的更新函数
具体的实现原理是
new Vue()执行初始化,对data中的数据通过Object.defineProperty()进行响应式处理,这个过程发生在observer中,每个属性都会有一个dep实例来存储watcher实例数组- 对模板进行编译,v-开头的关键词作为指令解析,找到动态绑定的数据,从data中获取数据并初始化,这个过程发生在
compiler中。如果说遇到的指令是v-model,就会监听input时间,更新data中的数据 - 在解析指令的过程中,会定义一个更新函数和
watcher,之后对应的数据变化时,watcher会调用更新函数,new watcher的过程中会去读取data中的key,触发依赖收集,将相对应的watcher添加到dep中 - 将来data中的数据发生变化的情况下,会首先找到
dep,通知所有的watcher执行更新函数。
function defineReactive(data, key, val)
{
observe(val);
var dep=new Dep();
Object.defineProperty(data,key,{
get(){
if(是否需要添加订阅者)
{
dep.addSub(watcher);
}
return val;
},
set(newValue){
if(val==newValue){
return;
}
val=newValue;
console.log('属性' + key + '已经被监听了,现在值为:“' + newValue.toString() + '”')
dep.notify();//如果数据变化,通知所有的订阅者,去执行更新视图的函数
}
})
}
function Dep () {
this.subs = [];
}
Dep.prototype={
addSub:function(sub){
this.subs.push(sub)
},
notify:function(){
this.subs.forEach(function(sub){
//所有的watcher都要调用更新函数
sub.update();
})
}
}
function observe(data) {
if (!data || typeof data !== 'object') {
return;
}
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key]);
});
};
5.6 Vue的响应式原理
Vue的响应式原理是使用Object.defineProporty()对new Vew()中的data的数据的getter和setter进行数据劫持,数据劫持其实也可以理解为重新定义setter和getter的过程,就是让我这个对象失去了对数据直接操作的权利。 答出上面的部分只能说明是我们有所了解,更具体来说,我们应该明白响应式是什么?
响应式其实就是我们能够监听到数据的变化,在数据变化以后,会产生相应的反映。 Vue中的响应式涉及到3个对象:Observe Watcher Dep,对这3个对象进行简单的说明:
- Observe:Observe其实就是一个数据的响应式处理工具,当Vue实例化的时候,会对data中的所有属性使用
Object.defineProporty()进行数据劫持,主要是对data中的每一个属性的getter以及setter进行重写。 - Dep:是一个依赖收集的池子,data中的每一个属性都有对应的一个Dep,即针对性地对每一个data中的数据进行观测。
- Watcher:watcher是在编译模板的时候创建的,每一个组件都有一个对应的watcher。 以上信息的逻辑关系图为:
讲解:其中利用发布-订阅者模式的是Dep和watcher两者。Dep可以理解为一个专门负责买杂志的一个小报亭,watcher是来这个小报亭买报纸的人。当watcher来Dep中买报纸以后,Dep为了扩大自己的生意,会记录watcher的电话。这个过程就相当于添加订阅者的过程;后续如果报亭有新的报纸时,Dep就可以通知所有的watchers,有新的报纸到了,可以来购买了。发布-订阅者模式更像是一种一对多的通信方式。
5.6.1 对象的响应式处理
对象必须在data中完成初始化工作,才能进行响应式处理。如果通过obj.proporty='XXX'当方式添加属性,那么是监听不到数据变化的,这个时候需要用到全局的$set方法来完成属性的响应式处理。
5.6.2 数组的响应式处理
在Vue中,对响应式处理利用的是Object.defineProperty对数据进行拦截,而这个方法并不能监听到数组内部变化,数组长度变化,数组的截取变化等,所以需要对这些操作进行hack,让Vue能监听到其中的变化。Vue对原生的数组方法进行了重写,代码如下:
const arrayProto = Array.prototype;
// 实现 arrayMethods.__proto__ === Array.prototype
export const arrayMethods = Object.create(arrayProto);
"push",
"pop",
"shift",
"unshift",
"splice",
"sort",
"reverse"
];
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function(method) {
// 缓存原生数组方法
const original = arrayProto[method];
def(arrayMethods, method, function mutator(...args) {
// 执行并缓存原生数组功能
const result = original.apply(this, args);
// 响应式处理
const ob = this.__ob__;
let inserted;
switch (method) {
// push、unshift会新增索引,所以要手动observer
case "push":
case "unshift":
inserted = args;
break;
case "splice":
inserted = args.slice(2);
break;
}
ob.dep.notify();
return result;
});
});
6. React
6.1 React Hooks包括哪些,常用的钩子函数是什么
6.2 useEffect有什么了解
7. 浏览器
7.1 对于浏览器的事件循环谈一谈
7.2
7.3
7.4
8. 计算机网络
8.1 常见的HTTP状态码
- 2XX 2XX的状态码表示请求被服务器端成功的处理了。
- 200 OK 表示客户端发来的请求被服务器端正常处理了
- 204 No content 表示客户端发来的请求被服务器端正常处理了,但是没有响应的内容,一般是用在客户端向服务器端发送消息,服务器端不需要进行返回的情况下。
- 206 Partial COntent 表示客户端进行了范围请求,服务器端执行了这部分的GET请求。响应报文中包含由 Content-Range 指定范围的实体内容。
- 3XX 300多用来表示重定向。
- 301 表示永久的重定向,即访问的页面被永久性地迁移到了新的URI,我们需要通过新的URI才能请求到页面。新的URI会在HTTP响应头的LOcation首部进行指定。
- 302
表示临时重定向,多用于页面的重定向。一般的应用场景是:
未登录的用户会跳转到登录页进行登录 - 304 表示请求的资源在服务端没有被修改,当命中协商缓存的时候,请求到的资源会返回该状态码。
- 4XX 通常用来表示客户端的错误。
- 400 表示客户端的请求报文中有错误,当错误发生时,需要修改当前的请求内容。
- 401 表示当前请求的资源需要进行认证才能访问,比如登录失败的情况下,当我们访问后台系统的时候,会以这个状态码进行标识
- 403
表示请求的资源被客户端拒绝了,服务器端不会给予详细的理由。典型的场景是:
普通用户在登录状态的时候,去访问后台管理系统中的页面时,就会出现403状态码 - 404 请求的资源在服务器端不存在
- 5XX 通常用来表示服务器端发生了错误
- 500 500表示当前发起的请求,引起了服务器端的错误
- 502 该状态码表明扮演网关或代理角色的服务器,从上游服务器中接收到的响应是无效的。
- 503 该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。