面试总结(字节,喜马,网易,趣头条面试记录)

561 阅读17分钟

HTTP

HTTP是什么

HTTP(The HyperText Transfer Protocol,超文本传输协议)是用于在Web上传输超媒体文件的底层协议,最典型的场景就是浏览器和服务器之间传递的数据。 HTTP是基于文本的(所有的通信都以纯文本的形式进行)以及无状态的(当前通信状态不会发现以前的通信状态)。

HTTP流

当客户端和服务端进行信息交互时,主要几个步骤:

  1. 打开一个TCP连接: TCP连接被用来发送一条或多条请求,以及接收响应信息。
  2. 发送一个HTTP报文:HTTP报文(在HTTP/2之前)是语义可读的。在HTTP/2中,这些简单的消息被封装在了帧中,这将使得报文不能被直接读取,但原理相同。
  3. 读取服务端返回的报文信息。
  4. 关闭连接或则为后续请求重用连接。

HTTP/1,1.1和2

HTTP/2

HTTP/2是HTTP的一个重要版本。主要实现:

  1. 通过启用完整的请求和响应多路复用(复用了TCP连接)来减少延迟
  2. 通过有效的压缩HTTP标头字段来最小化协议开销
  3. 增加对请求优先级和服务器推送的支持

UDP TCP

HTTP/3是一个即将到来的修订版,将用新UDP协议代替TCP协议。

UDP(User Data Protocol,用户数据报系协议)是一个非连接协议,传输数据时发送端和接收端不建立连接。在发送端UDP传送数据的速度仅仅是受应用生成数据的速度,计算机的能力和传输带宽的限制。在接收端,UDP把每个消息放在队列中,应用程序每次从队列中读一个消息段。

TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议,在收发数据前必须和对方建立可靠的连接。也就是会有三次握手阶段。

HTTPS

HTTPS(安全的HTTP)是HTTP协议的加密版本。它通常使用SSL或则TLS来加密客户端和服务端之间的所有的通信。这安全的链接允许客户端与服务器安全地交换敏感的数据,例如网上银行或者在线商城等涉及金钱的操作。

强缓存和协商缓存

juejin.cn/post/684490…

expires和cache-control有什么区别

cache-control通过指令来实现缓存机制。

如果在cache-control响应头设置了max-age和s-max-age指令,那么exprires头会被忽略。

expires使用常见案例

expires响应头包含时间,即在此之后,响应过期。

expires: Sat, 23 Jan 2021 04:10:08 GMT

cache-control缓存请求指令

Cache-Control: max-age=<seconds>
Cache-Control: max-stale[=<seconds>]
Cache-Control: min-fresh=<seconds>
Cache-control: no-cache
Cache-control: no-store
Cache-control: no-transform
Cache-control: only-if-cached

control缓存响应指令

Cache-control: must-revalidate
Cache-control: no-cache
Cache-control: no-store
Cache-control: no-transform
Cache-control: public
Cache-control: private
Cache-control: proxy-revalidate
Cache-Control: max-age=<seconds>
Cache-control: s-maxage=<seconds>

常见示例:

  1. 禁止缓存
Cache-Control: no-store
  1. 缓存静态资源 对于应用中不会改变的文件,静态文件(图像,css文件以及js文件)
Cache-Control:public, max-age=31536000
  1. 需要重新验证 指定no-cache或则max-age=0表示客户端可以缓存资源,每次使用缓存资源都必须重新校验其有效性。即每次都会发起HTTP请求,当缓存内容仍有效时可以跳过响应下载。
Cache-Control: no-cache
Cache-Control: max-age=0

ctrl + f5 强制刷新,为什么缓存就没了

ctrl+f5这个操作可以把INTERNET临时文件夹删除再重新从服务器下载,也就是重新彻底刷新页面。

etag 和 if-none-match 区别

etag能够解决last-modified的一些缺点,但是etag每次服务端生成都需要进行读写操作,而last-modified只需要做读取操作,从这方面来看etag的消耗更大的。

http 301 302 403代表着什么

301 表示资源永久重定向,302 表示资源临时重定向,403表示 表示资源不可用。

CSS

如何使用animation写一个动画

@keyframes bounce {
 from {
   left: 0px;
 }
 to {
   left: 200px;
 }
}

.box {
  animation: bounce 1s inifinite linear 1.5s;
}

position属性有哪些分别应用的场景

  • 定位元素: 除了static以外的任何元素 指定元素使用正常的布局行为。

  • 相对定位元素:relative 在不改变页面布局的情况下调整元素的位置

  • 绝对定位元素:absolute和fixed absolute元素会被移出正常文档流,并不会为元素预留空间,指定元素相对于最近的非static元素祖先元素来偏移。

fixed元素会被移除正常文档流,并不会为元素预留空间,指定元素相对于屏幕视口的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。

  • 粘性定位元素:sticky

最新属性值sticky,可以理解为相对relative定位和固定fixed定位的混合,

#one { position: sticky; top: 10px; }

当viewport元素滚动到元素top距离小于10px之前,元素是相对定位。 之后元素将固定在与顶部距离10px的位置,直到viewport视口回滚。

** sticky存在兼容的问题,ie目前不支持。edge是完美支持。 如果需要sticky属性的功能,通过js去模拟就好。 **

块级元素,内联元素

<div class="box">
  <span class="pox">测试</span>
</div>

.box {
	width: 100px;
	.pox {
		margin: 10%;
    }
}

pox上下左右偏移多少? 答: 仅仅左右的margin:10px;上下的margin计算出来的结果是0

.pox {
	display: block;
}

此时pox上下左右偏移多少? 答:上右下左的margin都是10px。

img元素是什么元素?

img是替换元素,是一个内联块状元素。

webp知道是什么?

webp是一种图片的格式,体积非常小,但依然可以保持jpg同样的画质,google最先支持的一种图片格式,会有兼容性问题,但是支持的相当的好了。

link和@import有什么区别

link通过引入一个外部样式表来加载一个css文件,不仅可以加载文件还可以定义rel,rev等属性。而@import比较单一只是表示了导入样式表。

重点:link会和html同时加载,@import引入的css将在页面加载完毕后被加载

<link rel="stylesheet" rev="stylesheet" href="myCss.css" type="text/css" > 
<style type="text/css" >   
  @import url("./myCss.css");   
</style> 

盒子模型

box-sizing:border-box/content-box;

说说重排和重绘

重排: 对DOM操作修改引发页面结构的变化,渲染树需要重新计算。 重绘: 只有visibilty,outline,color,backgound-color等属性的变化导致样式的变化,是浏览器根据属性值进行新的绘制。不会影响页面结构,仅仅变化的是外观。

重绘不一定会触发重排,重排一定会触发重绘制。

直通车: juejin.cn/post/685457…

JS

script标签上的async和defer

defer:延迟脚本。

<script src="index1.js" defer />
<script src="index2.js" defer />

不会按照顺序执行,所以页面中最好只包含一个延迟脚本。

async:异步脚本

<script src="index1.js" async />
<script src="index2.js" async />

index2可能会在index1前执行,同样无法保证它们的执行顺序。 指定async的目的是不让页面等待,从而异步加载页面其他内容。

script标签上crossorigin

一些html元素上提供了对CORS的支持,例如audio, img, link, script和video均有一个跨域属性,它允许你配置元素获取数据的CORS请求。

  • anonymous: 对此元素的 CORS 请求将不设置凭据标志。
  • use-credentials: 对此元素的CORS请求将设置凭证标志;这意味着请求将提供凭据。

常见案例:

使用下面的script元素告诉浏览器执行来自 example.com/example-fra… 的脚本且不发送用户凭据

<script src="https://example.com/example-framework.js" crossorigin="anonymous"></script>

跨域(说说你知道的跨域方式)

CORS(跨域资源共享)

简单请求(get, post, head)

简单请求浏览器会直接发出CORS请求,其实就是在头信息中增加一个origin字段。 origin字段用来说明请求来自哪个源

origin: http://127.0.0.1:3000

对于服务端来说:

  • Access-Control-Allow-Origin:

  • Access-Control-Allow-Credentials:

    • true: 允许发送cookie等凭证

非简单请求(put, pull, patch, delete)

对于非简单请求的步骤主要分为两步。

  1. 预检请求和回应 会在正式请求之前,增加一个http验证机制。称为预检请求。

相当于会在put请求前先发送一个OPTIONS请求。

  1. 正常请求 预检请求通过后,后面的请求就会正常发送,和简单请求一样。

传送门: juejin.cn/post/684490…

图像Ping

网页可以从任何网页中加载图像,不存在跨域不跨域的问题。但通过图像Ping,浏览器得不到任何具体的数据。它仅仅能知道响应是什么时候接收到的。

let img = new Image();
img.onload = img.onerror = () => { // done }
img.src = 'http://www.juejin.cn?name=mangoyi'

两个主要缺点:

  • 只能发送GET请求
  • 无法访问服务器的响应文本

图像Ping只能用于浏览器和服务器间的单向通信。

JSONP

JSONP通过动态script元素来使用,可以为src属性指定一个跨域URL。

const handleResponse = res => {
	// res
}

let script = document.createElement('script');
script.src = 'http://www.juejin.cn?callback=handleResponse';
document.body.insertBefore(script, document.body.firstChild);

相比较图像Ping的方式,优点是能够直接访问响应文本,支持浏览器与服务器双向通信。

缺点: 如果其他域不安全,响应中夹带着一些恶意代码,就比较糟糕。

ngnix反向代理

通过对nginx的配置将接口转发到对应的服务端接口

es6中的promise简单说说,async异步函数处理异常?

promise(期约)对象表示一个异步操作的最终完成(兑现或则拒绝)的结果值

传送门: es6.ruanyifeng.com/#docs/promi…

async异步函数优雅的处理异常:

const to = (promise) => {
  return promise
    .then(data => ([data, undefined]))
    .catch(error => Promise.resolve([undefined, error]));
}

 async function userProfile () {
  let [user, userErr] = await to(getUser());

  if(userErr) throw new Error('Could not fetch user details');
}

event loop

js的并发模型是基于事件循环的。

传送门: juejin.cn/post/684490…

setTimeout & promise异步请求

Promise.resolve().then(() => {
  console.log('a')
})

setTimeout(() => {
	Promise.resolve().then(() => {
      console.log('1')
      setTimeout(() => {
      	console.log('2')
      })
    }).then(() => {
	  console.log('3')    
    })
}, 0)

Promise.resolve().then(() => {
  console.log('b')
})

// 输出结果a b 1 3 2

这里一定要记住关键点: setTimeout是一个宏任务,promise是一个微任务。

setTimeout是宿主环境提供的api,所以它是宏任务。 而在浏览器的执行中在当前任务中微任务一定是先于宏任务的。

XSS 是什么?

跨站脚本攻击(Cross-site scripting, XSS)是一种安全漏洞,攻击者可以利用这种漏洞在网站上注入恶意的客户端代码。

2种常见情况:

  1. 数据从一个不可靠的链接进入到一个web应用程序
  2. 没有过滤掉恶意代码的动态内容被发送给web用户

在react中dangerouslySetInnerHTML是react为浏览器DOM提供innerHTML的替代方案。解决了可能触发DOM型XSS攻击。

function createMarkup() {
  return {__html: 'First &middot; Second'};
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}

CSRF

CSRF(Cross-site request forgery) 跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。

主要的特点是伪造,冒充了登陆凭证去想服务端发起请求。

解决方式:

  1. 验证 HTTP Referer 字段,服务端可以通过解析这个字段的值,确定请求的来源。
  2. 在HTTP请求中增加token字段,服务端通过校验请求是否携带正确的token来把正常的请求和攻击分开。

Vue & React

说说class组件和函数组件hook区别

  1. 首先函数组件的出现动机,是在class组件之间复用状态逻辑很难。在class组件中最常见的方案HOC高阶组件和render props。
  2. hook使你在无需修改组件结构的情况下复用逻辑。
  3. 复杂组件难以理解,在class组件中componentDidMout和componentDidUpdate两个生命周期中常常需要获取数据,并在componentWillUnmount中清楚。而实际上两个相关关联的内容被拆分到了两个地方书写,如此容易产生bug
  4. class组件难以理解,比如其中的this
  5. hook其实就是一个函数,拥抱函数,函数式编程也是react的宗旨。

说说vue和react

  • vue和react都是组件开发,内部实现都有虚拟dom
  • 都支持props进行组件件通信
  • 都支持服务端渲染
  • vue实现了数据的双向绑定,而react是单向的
  • vue更像是模板语法在写html,react更拥抱js像是在写js
  • vue在api设计上更加简单,学习成本更低
  • react更加灵活,将更多的功能交给开发者去实现而不是直接提供api

说说你写过的通用组件

移动端form组件的封装和维护。

为什么在本地开发环境中配置proxy?

在常见SPA单页应用的开发中,实际上是启用了一个node服务,而通过node服务去访问服务端的接口是不存在跨域的问题。服务端去访问服务端没有跨域的问题。

多层组件的props解决方案

  • 在hook组件可以直接通过context来实现多层组件间的透传

说说storage和cookie

http cookie通常直接叫做cookie,最初实在客户端用于存储会话信息的。服务端和客户端都可以向当前域下写cookie。

  1. cookie是严格限制在当前域下的
  2. cookie的数据量最大是4kb

服务端如何设置可以让客户端无法读取cookie

在服务端设置http-only:true,这样客户端通过document.cookie是不能访问当前域下的cookie

sessionStorage & localStorage

sessingStorage存储会话窗口期间,会在关闭当前会话窗口消失。 localStorage:持久保存客户端数据,即便浏览器窗口关闭也不会消失。

storage的数据范围是在2.5M~5M。

用过nodejs写过服务吗?相关的框架呢?中间件是什么?

express框架。

express中一个非常重要的概念就是中间件。在express框架中,允许通过中间件的使用来调用各种第三方类库,可以让开发更加方便,同时也使得开发出更强大的应用程序。 一个中间件是一个用户处理客户端请求的函数。而一个http服务中可能会使用到各种中间件,

function middleware(req, res, next) {}

调用中间件

app.use([path], middleware)

了解过nuxtjs服务端渲染(SSR)?

nuxtjs是一个基于vue的为了去实现服务端渲染的框架。 使用nuxt.js主要可以解决vue项目中SEO优化以及首屏加载时间长出现白屏的问题。

react生命周期

在class组件中,

class Clock extends React.Component {
  
  componentDidMount() {
  	// 组件挂载时
  }
  
  componentWillUnMount(){
  	// 组件卸载时
  }
}

componentDidMount()方法会在组件已经被渲染到DOM中后运行。 componentWillUnMount()方法会在组件被卸载时及从DOM中移除时运行。

在函数组件中,


useEffect(() => {
	// 可以理解为componentDidMount & componentDidUpdate
    
    return () => {
    	// 在这里就是componentWillUnMount
    }
}, [])

componentDidUpdate()方法会在更新后被立即调用。首次渲染不会执行该方法。 同理也就是在组件内部状态更新后,

主要是先执行render() -> componentDidUpdate()

memo是用来做什么的?

可以理解memo是函数组件中用来实现PureComponent同样功能的hook,不过不完全相同有差异。

const MyComponet = React.memo(function MyComponent(props) {})

当你的组件在相同props的情况下渲染相同的结果,那么你可以通过将其包装在React.memo中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。 意味着在这种情况下,React将跳过渲染组件的操作并直接复用最近一次渲染的结果。

注: momo仅检查props变更。如果函数组件被memo包裹,且其实现中拥有useSate或则useContext的hook,当context发生变化时,它仍然重新渲染。

默认情况下,memo只会对复杂对象做浅层比较,如果想控制对比,可以自定义比较函数作为第二个参数传入。

function MyComponent(props) {
	// 使用props渲染
}


function areEqual(prevProps, nextProps) {}

export default React.memo(MyComponent, areEqual)

PureComponent React.PureComponent 中以浅层对比prop和state的方式来实现了该函数。主要解决子组件需要重新渲染的时候才去渲染。注意: 内部实现是浅层比较,不能传递复杂数据类型比如数组,对象。

如果赋予组件相同的props和state,render()函数会渲染相同的内容,那么在某些情况下使用React.PureComponent可提高性能。

了解react fiber?

fiber: 一种基于浏览器的单线程调度算法。

出现fiber的原因如下:

  • 组件足够复杂,组件更新时计算和渲染压力大
  • 同时有DOM操作需求(动画,鼠标拖拽等)将会卡顿
  • 因为JS单线程,所以计算和操作DOM都是在一个线程里面

fiber解决方案:

  1. 将reconciliation阶段进行任务拆分
  2. DOM需要渲染时暂停,空闲时恢复
  3. window.requestIdleCallback去判断DOM是否需要渲染

传送门: github.com/acdlite/rea…

为什么不能在循环,条件判断或则子函数中使用hook?

因为条件判断等会导致hook的调用顺序不一致。

当在单个组件中有多个state的时候,react是如何知道哪个state对应着哪个setState呢? 答案是:react靠的是Hook调用的顺序。

传送门: zh-hans.reactjs.org/docs/hooks-…

ES6和CommonJS的理解?

  • CommonJS是node的模块化方案,ES6是浏览器端模块化方案
  • CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用
  • CommonJS模块是运行时加载,ES6模块是编译时输出接口

传送门: juejin.cn/post/684490…

webpack

webpack性能优化?

主要从两方面去分析,打包体积和打包速度。 体积分析: webpack-bundle-analyzer插件,速度分析: speed-measure-webpack-plugin插件

如何将图片打包成base64的格式?

loader采用url-loader,但实际场景中只会将小于5kb的图片打包成base64格式直接插入到代码中。主要的目的是减少对http的请求消耗。

loader和plugin区别?

  • loader是文件加载器,能够加载资源文件并对这些文件进行特定的处理,然后打包到指定的文件中。loder的执行顺序和代码书写的顺序是相反的,即最后一个loader先执行。
  • webpack本身是只能打包commonjs规范的js文件,对于css,图片等是无法加载的,这就需要loader将资源转化,加载进来。
  • plugin用于扩展webpack的功能,直接作用于webpack,扩展了它的功能。它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务。

传送门: juejin.cn/post/684490…

TreeShaking是什么?

TreeShaking-树木摇晃(将没用的叶子摇落)。 TreeShaking主要是移除ES6模块规范当中的上下文未使用的代码(dead-code)。

重点:之所以可以实现TreeShaking主要还是依赖于ES6模块的静态编译的特性,而对于Commonjs模块的执行时加载是完全不适用的。

传送门: juejin.cn/post/688044…

如何开启热更新?

var HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin');

plugins: [
  new HotModuleReplacementPlugin()
],
devServer: {
  hot: true
}

// src/util.js 开启热更新
// 以下代码仅仅在math.js文件范围内有热更新
if (module.hot) {
  module.hot.accept(['./math'], () => {})
}

scope hosting是什么?

在webpack打包的时候,将函数之间的调用合并成一个函数。代码体积更小,创建函数作用域更少,代码可读性更好。

使用方式:

var ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');

resolve: {
// 针对于第三方模块优先采用jsnext:main中指向的es6模块文件
  mainFields: ['jsnext:main', 'browser', 'main']
},
plugins: [
  new ModuleConcatenationPlugin()
]

使用结果:

// 启用前
[({function() {}}), (function() {})]

// 启用后
[(function() {})]

webpack是如何实现异步加载的?

组件动态导入的方式:

  • es6通过import的方式
const App = () => import('./app');
  • webpack中的require.ensuire()
const App = r => require.ensure([], () => r(require('./pages/app')), 'app')

原理:函数会去判断chunkId是否被加载过,没有的话就会创建一个promise,在promise中创建script标签,设置src路径,该路径就是被webpack代码拆分打包的组件路径。然后再去通过jsonp的方式加载js也就是异步加载。