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&param2=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


#面经##AfterShip#
全部评论
别慌,有面试评价就说明没挂,我挂了都是直接面试反馈结果滴,现在又被捞起来了
1 回复 分享
发布于 2021-09-15 21:13
通常QA问问面试评价都能知道个大概,顺便知道面试官还有什么疑惑的地方
点赞 回复 分享
发布于 2021-09-16 21:28

相关推荐

评论
6
20
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务