AfterShip 一面
花费 40 分钟
1. 自我介绍
2. 问实习经历(难点,收货之类的)。这个聊了挺久的。面试官觉得我这段实习经历没有什么亮点,平平无奇
3. 一道场景题:有一个组件,传入 props,当 props 发生改变的时候,执行一个函数
开始的时候,网络不好,一直卡,很久才听懂题目。我说使用在生命周期函数 componentDidUpdate 的时候触发。面试官问,在 react hook 中怎么实现。我的回答是将 props 作为 useCallback 的依赖项,面试官说不对。后来他说一种最简单的方法是将 props 使用 state 存起来,之后再 useEffect 的时候,比较后面传入的 props,进行触发。
4. 跨域是什么。怎么解决
### 3. 浏览器的跨域问题
为了防止网站遭到恶意攻击,导致信息被窃取,所以浏览器设计了同源策略。
#### 3.1 为什么浏览器会禁止跨域
浏览器是很开放的,只要在地址栏里面输入网址或者点击某个链接就可以访问了。正是因为这种开放的形态,才需要对浏览器做出限制,保护用户的信息安全。因此同源策略就是用来保护信息安全的。
#### 3.2 什么是同源策略
同源政策由 Netscape 公司引入浏览器。同源策略是一个安全策略。所谓的同源,指的是协议,域名,端口相同。
浏览器出于安全方面的考虑,只允许本域名下的接***互,不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 和 JS 对象无法获取
- Ajax请求发送不出去
#### 3.3 如何解决跨域问题
#### 3.3.1 `JSONP`
##### `JSONP` 实现跨域的原理
浏览器的同源策略限制不允许跨域请求;但页面中的 `script`、`img`、`iframe`标签是例外不受同源策略限制。
`Jsonp` 就是利用`script`标签跨域特性进行请求,通过 `<script>` 标签指向一个需要访问的地址并提供一个回调函数来接收数据。
思路:客户端事先准备一个接收数据的全局函数,之后客户端解析 script 标签发出请求。服务端接受到请求之后,返回函数的调用。客户端接收数据,执行回调。
假设JSONP请求如下:
```js
jsonp({
url: 'http://path/to/server/b',
params: {A: a, B: b},
success: function myCallback (response) {}
})
```
背后其实在进行:
拼接一个script标签,
```html
<script src="http://path/to/server/b?A=a&B=b&callback=myCallback"></script>
```
从而触发对指定地址的GET请求.
服务器端对这个GET请求进行处理,并返回字符串 "myCallback('response value')",前端script加载完之后,其实就是在script中执行 myCallback('response value'),就完成了跨域的请求,因此就是只能用GET。
JSONP 只能发 GET 请求,因为本质上 script 加载资源就是 GET。
##### 优缺点
优点:兼容性好,可以解决主流浏览器的跨域数据访问的问题。
缺点:只能进行 `GET` 请求,具有局限性,不安全。
```js
<script src="http://domain/api?param1=a¶m2=b&callback=jsonp"></script>
<script>
function jsonp(data) {
console.log(data)
}
</script>
```
```js
const jsonp = ({ url, params, callbackName }) => {
const generateUrl = () => {
let dataSrc = ''
for (let key in params) {
if (params.hasOwnProperty(key)) {
dataSrc += `${key}=${params[key]}&`
}
}
dataSrc += `callback=${callbackName}`
return `${url}?${dataSrc}`
}
return new Promise((resolve, reject) => {
const scriptEle = document.createElement('script')
scriptEle.src = generateUrl()
document.body.appendChild(scriptEle)
window[callbackName] = data => {
resolve(data)
document.removeChild(scriptEle)
}
})
}
```
#### 3.3.2 `CORS`
`CORS` 是 “跨域资源共享” (Cross-origin resource sharing ) 的缩写。它允许浏览器向跨源服务器,发出跨域请求,整个`CORS`通信过程都是浏览器自动完成的。
##### 设计思想
使用自定义 `HTTP` 头部让浏览器与服务器进行沟通,从而决定请求或响应是成功还是失败。
##### 两种请求方式
###### 简单请求
浏览器直接发出 `CORS` 请求。具体来说,就是在头信息之中,增加一个`Origin`字段。`Origin` 中会指出当前请求属于哪个域(协议+域名+端口),服务会根据这个值决定是否允许其跨域。
1. 请求方法是以下三种方法之一:
HEAD,GET,POST
2. HTTP的头信息不超出以下几种字段:
- `Accept`,`Accept-Language`,`Content-Language`,`Last-Event-ID`
`Content-Type`:只限于三个值 `application/x-www-form-urlencoded`、`multipart/form-data`、`text/plain`
如果服务器允许跨域,需要在返回的响应头中携带下面信息:
> Access-Control-Allow-Origin: http://manage.leyou.com
> Access-Control-Allow-Credentials: true
跨域请求要想操作cookie,需要满足3个条件:
1. 服务的响应头中需要携带Access-Control-Allow-Credentials并且为true。
2. 浏览器发起ajax需要指定withCredentials 为true
3. 响应头中的Access-Control-Allow-Origin一定不能为*,必须是指定的域名。
###### 高级请求
必须首先使用 `OPTIONS`方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。服务器对 `AJAX` 跨域请求设置限制条件。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
非简单请求会发出一次预检测请求,返回码是204,预检测通过才会真正发出请求,这才返回200。这里通过前端发请求的时候增加一个额外的headers来触发非简单请求。
与简单请求相比,除了Origin以外,多了两个头:
Access-Control-Request-Method:接下来会用到的请求方式,比如PUT
Access-Control-Request-Headers:会额外用到的头信息
预检请求的响应
服务的收到预检请求,如果许可跨域,会发出响应:
除了Access-Control-Allow-Origin和Access-Control-Allow-Credentials以外,这里又额外多出3个头:
Access-Control-Allow-Methods:允许访问的方式
Access-Control-Allow-Headers:允许携带的头
Access-Control-Max-Age:本次许可的有效时长,单位是秒,过期之前的ajax请求就无需再次进行预检了
如果浏览器得到上述响应,则认定为可以跨域,后续就跟简单请求的处理是一样的了。
#### 3.3.3 `webpack` 代理
通过 `webpack` 中的 `proxy` 进行代理,从而解决跨域的问题。
```json
module.exports = {
//...
devServer: {
proxy: {
'/api': {
target: 'http://www.baidu.com/',
pathRewrite: { '^/api': '' },
changeOrigin: true, // target是域名的话,需要这个参数,
secure: false, // 设置支持https协议的代理
},
'/api2': {
.....
}
}
}
};
```
`webpack proxy` ,就是 `webpack` 提供的解决跨域的方案。其基本行为是接受客户端发送的请求后转发给其他的服务器,目的是为了便于开发者在开发的模式下解决跨域的问题。要想实现代理必须要一个中间服务器, `webpack` 提供服务器的工具是 `webpack-dev-server`,只适用于开发阶段。
##### 原理
`proxy` 工作原理上市利用 `http-proxy-middleware` 这个 `http` 代理中间件,实现请求转发给其他的服务器。如下:在开发阶段,本地地址是 Http://loaclhost:3000 , 该浏览器发送一个前缀带有 /api 标识的向拂去器请求数据,但是这个服务器只是将这个请求转发给另一台服务器:
```js
const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();
app.use('/api', proxy({target: 'http://www.example.org', changeOrigin: true}));
app.listen(3000);
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
```
在开发阶段,`webpack-dev-server` 会自动启动一个本地开发服务器,所以我们的应用在开发阶段是独立运行在 `localhost` 的一个端口上的,而后端服务器又是运行在另一个地址上。所以在开发阶段中,由于浏览器的同源策略,当本地访问的时候就会出现跨域资源请求的问题,通过设置 `webpack proxy` 实现代理请求后,相当于浏览器和服务器之间添加了一个代理商。当本地发送请求的时候,中间服务器会接受这个情求,并将这个请求转发给目标服务器,目标服务器返回数据后,中间服务器又会将数据返回给浏览器,当中间服务器将数据返回给服务器的时候,它们两者是同源的,并不会存在跨域的问题。服务器和服务器之间是不会存在跨域资源的问题的。
为了防止网站遭到恶意攻击,导致信息被窃取,所以浏览器设计了同源策略。
#### 3.1 为什么浏览器会禁止跨域
浏览器是很开放的,只要在地址栏里面输入网址或者点击某个链接就可以访问了。正是因为这种开放的形态,才需要对浏览器做出限制,保护用户的信息安全。因此同源策略就是用来保护信息安全的。
#### 3.2 什么是同源策略
同源政策由 Netscape 公司引入浏览器。同源策略是一个安全策略。所谓的同源,指的是协议,域名,端口相同。
浏览器出于安全方面的考虑,只允许本域名下的接***互,不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 和 JS 对象无法获取
- Ajax请求发送不出去
#### 3.3 如何解决跨域问题
#### 3.3.1 `JSONP`
##### `JSONP` 实现跨域的原理
浏览器的同源策略限制不允许跨域请求;但页面中的 `script`、`img`、`iframe`标签是例外不受同源策略限制。
`Jsonp` 就是利用`script`标签跨域特性进行请求,通过 `<script>` 标签指向一个需要访问的地址并提供一个回调函数来接收数据。
思路:客户端事先准备一个接收数据的全局函数,之后客户端解析 script 标签发出请求。服务端接受到请求之后,返回函数的调用。客户端接收数据,执行回调。
假设JSONP请求如下:
```js
jsonp({
url: 'http://path/to/server/b',
params: {A: a, B: b},
success: function myCallback (response) {}
})
```
背后其实在进行:
拼接一个script标签,
```html
<script src="http://path/to/server/b?A=a&B=b&callback=myCallback"></script>
```
从而触发对指定地址的GET请求.
服务器端对这个GET请求进行处理,并返回字符串 "myCallback('response value')",前端script加载完之后,其实就是在script中执行 myCallback('response value'),就完成了跨域的请求,因此就是只能用GET。
JSONP 只能发 GET 请求,因为本质上 script 加载资源就是 GET。
##### 优缺点
优点:兼容性好,可以解决主流浏览器的跨域数据访问的问题。
缺点:只能进行 `GET` 请求,具有局限性,不安全。
```js
<script src="http://domain/api?param1=a¶m2=b&callback=jsonp"></script>
<script>
function jsonp(data) {
console.log(data)
}
</script>
```
```js
const jsonp = ({ url, params, callbackName }) => {
const generateUrl = () => {
let dataSrc = ''
for (let key in params) {
if (params.hasOwnProperty(key)) {
dataSrc += `${key}=${params[key]}&`
}
}
dataSrc += `callback=${callbackName}`
return `${url}?${dataSrc}`
}
return new Promise((resolve, reject) => {
const scriptEle = document.createElement('script')
scriptEle.src = generateUrl()
document.body.appendChild(scriptEle)
window[callbackName] = data => {
resolve(data)
document.removeChild(scriptEle)
}
})
}
```
#### 3.3.2 `CORS`
`CORS` 是 “跨域资源共享” (Cross-origin resource sharing ) 的缩写。它允许浏览器向跨源服务器,发出跨域请求,整个`CORS`通信过程都是浏览器自动完成的。
##### 设计思想
使用自定义 `HTTP` 头部让浏览器与服务器进行沟通,从而决定请求或响应是成功还是失败。
##### 两种请求方式
###### 简单请求
浏览器直接发出 `CORS` 请求。具体来说,就是在头信息之中,增加一个`Origin`字段。`Origin` 中会指出当前请求属于哪个域(协议+域名+端口),服务会根据这个值决定是否允许其跨域。
1. 请求方法是以下三种方法之一:
HEAD,GET,POST
2. HTTP的头信息不超出以下几种字段:
- `Accept`,`Accept-Language`,`Content-Language`,`Last-Event-ID`
`Content-Type`:只限于三个值 `application/x-www-form-urlencoded`、`multipart/form-data`、`text/plain`
如果服务器允许跨域,需要在返回的响应头中携带下面信息:
> Access-Control-Allow-Origin: http://manage.leyou.com
> Access-Control-Allow-Credentials: true
跨域请求要想操作cookie,需要满足3个条件:
1. 服务的响应头中需要携带Access-Control-Allow-Credentials并且为true。
2. 浏览器发起ajax需要指定withCredentials 为true
3. 响应头中的Access-Control-Allow-Origin一定不能为*,必须是指定的域名。
###### 高级请求
必须首先使用 `OPTIONS`方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。服务器对 `AJAX` 跨域请求设置限制条件。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
非简单请求会发出一次预检测请求,返回码是204,预检测通过才会真正发出请求,这才返回200。这里通过前端发请求的时候增加一个额外的headers来触发非简单请求。
与简单请求相比,除了Origin以外,多了两个头:
Access-Control-Request-Method:接下来会用到的请求方式,比如PUT
Access-Control-Request-Headers:会额外用到的头信息
预检请求的响应
服务的收到预检请求,如果许可跨域,会发出响应:
除了Access-Control-Allow-Origin和Access-Control-Allow-Credentials以外,这里又额外多出3个头:
Access-Control-Allow-Methods:允许访问的方式
Access-Control-Allow-Headers:允许携带的头
Access-Control-Max-Age:本次许可的有效时长,单位是秒,过期之前的ajax请求就无需再次进行预检了
如果浏览器得到上述响应,则认定为可以跨域,后续就跟简单请求的处理是一样的了。
#### 3.3.3 `webpack` 代理
通过 `webpack` 中的 `proxy` 进行代理,从而解决跨域的问题。
```json
module.exports = {
//...
devServer: {
proxy: {
'/api': {
target: 'http://www.baidu.com/',
pathRewrite: { '^/api': '' },
changeOrigin: true, // target是域名的话,需要这个参数,
secure: false, // 设置支持https协议的代理
},
'/api2': {
.....
}
}
}
};
```
`webpack proxy` ,就是 `webpack` 提供的解决跨域的方案。其基本行为是接受客户端发送的请求后转发给其他的服务器,目的是为了便于开发者在开发的模式下解决跨域的问题。要想实现代理必须要一个中间服务器, `webpack` 提供服务器的工具是 `webpack-dev-server`,只适用于开发阶段。
##### 原理
`proxy` 工作原理上市利用 `http-proxy-middleware` 这个 `http` 代理中间件,实现请求转发给其他的服务器。如下:在开发阶段,本地地址是 Http://loaclhost:3000 , 该浏览器发送一个前缀带有 /api 标识的向拂去器请求数据,但是这个服务器只是将这个请求转发给另一台服务器:
```js
const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();
app.use('/api', proxy({target: 'http://www.example.org', changeOrigin: true}));
app.listen(3000);
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
```
在开发阶段,`webpack-dev-server` 会自动启动一个本地开发服务器,所以我们的应用在开发阶段是独立运行在 `localhost` 的一个端口上的,而后端服务器又是运行在另一个地址上。所以在开发阶段中,由于浏览器的同源策略,当本地访问的时候就会出现跨域资源请求的问题,通过设置 `webpack proxy` 实现代理请求后,相当于浏览器和服务器之间添加了一个代理商。当本地发送请求的时候,中间服务器会接受这个情求,并将这个请求转发给目标服务器,目标服务器返回数据后,中间服务器又会将数据返回给浏览器,当中间服务器将数据返回给服务器的时候,它们两者是同源的,并不会存在跨域的问题。服务器和服务器之间是不会存在跨域资源的问题的。
下午4:00 开始面试的,晚上8:00 收到面试评价的邮件
老铁们,不会是凉了吧。别呀,这都半个月了,我才收到两家公司的面试。
哭了o(╥﹏╥)o
查看15道真题和解析