星星博客 »  > 

20210308某区块链公司前端面试总结

目录

  1. 讲下数组的一些方法
  2. 跨域的处理方式
  3. 讲下你最近的项目
  4. setState到底是异步还是同步
  5. 讲下hooks
  6. 讲下react的性能优化
  7. 前端的通信方式
  8. 讲下webpack的构建流程

1. 讲下数组的一些方法

比如:concat,fill,filter,map,pop,push,shift,slice,some,sort,includes等。

2. 跨域的处理方式

跨域主要分3部分:

  1. 协议相同
  2. 域名相同
  3. 端口相同

只要有一个不同,那么就是跨域


  // 地址
  http://www.baidu.com

  协议:http://
  域名:www.baidu.com
  端口:8080(http) 443(https) 默认端口省略

  http://www.baidu.com/login.html // 同源
  http://www.baidu2.com/login.html // 不同源,域名不同
  http://www.baidu.com:81/login.html // 不同源,端口不同

同源的目的:

目的是为了保护用户信息的安全,防止恶意网站窃取数据,否则Cookie可以共享。有的网站一般会把一些重要信息存放在cookie或者LocalStorage中,这时如果别的网站能够获取获取到这个数据,可想而知,这样就没有什么安全可言了。

限制范围:

  • Cookie、LocalStorage和IndexDB 无法读取
  • DOM无法获得
  • AJAX 请求不能发送

主要这3种方式不行。

方案1:CORS

比较常见的就是nodejs配置CORS允许跨域。

  1. Access-Control-Allow-Origin
    • 字段必传,为*表示允许任意域名的请求。当有cookie需要传递时,需要指定域名。
  2. Access-Control-Allow-Credentials
    • 字段可选,默认为false,表示是否允许发送cookie。若允许,通知浏览器也要开启cookie值的传递。
  3. Access-Control-Expose-Headers
    • 字段可选。如果想要浏览器拿到getResponesHeader()其他字段,就在这里指定。
  4. Access-Control-Request-Method
    • 必须字段,非简单请求时设置的字段,例如PUT请求。
  5. Access-Control-Request-Headers
    • 指定额外的发送头信息,以逗号分割字符串。
module.exports = {
  //=>WEB服务端口号
  PORT: 3001,
  //=>CROS跨域相关信息
  CROS: {
    ALLOW_ORIGIN: 'http://127.0.0.1:5500',
    ALLOW_METHODS: 'PUT,POST,GET,DELETE,OPTIONS,HEAD',
    HEADERS: 'Content-Type,Content-Length,Authorization, Accept,X-Requested-With',
    CREDENTIALS: true
  }
};

app.use((req, res, next) => {
  const {
    ALLOW_ORIGIN,
    CREDENTIALS,
    HEADERS,
    ALLOW_METHODS
  } = CONFIG.CROS;
  res.header("Access-Control-Allow-Origin", ALLOW_ORIGIN);
  res.header("Access-Control-Allow-Credentials", CREDENTIALS);
  res.header("Access-Control-Allow-Headers", HEADERS);
  res.header("Access-Control-Allow-Methods", ALLOW_METHODS);
  req.method === 'OPTIONS' ? res.send('CURRENT SERVICES SUPPORT CROSS DOMAIN REQUESTS!') : next();
});
方案2:Proxy

现在主流三大框架,react,vue,argular都使用了webpack进行工程化。在本地开发最常见的就是proxy代理,解决跨域。

主要原理是:客户端像服务器请求数据。webpack-dev-server会再本地创建一个web服务,这个服务会和客户端同源。本地服务实际上是一个node服务,它作为一个中间层会帮客户端去像服务端请求数据,然后把数据返回给客户端。

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'production',
  entry: './src/main.js',
  output: {
    filename: 'main.[hash].min.js',
    path: path.resolve(__dirname, 'build')
  },
  devServer: {
    port: '3000',
    compress: true,
    open: true,
    hot: true,
    proxy: {
      '/': {
        target: 'http://127.0.0.1:3001',
        changeOrigin: true
      }
    }
  },
  // 配置WEBPACK的插件
  plugins: [
    new HtmlWebpackPlugin({
      template: `./public/index.html`,
      filename: `index.html`
    })
  ]
};
方案3:JSONP

主要原理:link,script这种是不会跨域的。所以,前端代码写一个script src = http://localhoost:80/list?callback=func,把这个链接发送给服务端。但是传递给服务端的函数必须是一个全局的函数。服务端接受到请求后,会把callback这个值,返回给客户端。客户端获取到服务端返回的指定格式字符串。发现其实就是本地的func全局函数执行,并且把数据传递给这个函数。

但是这种方式有一个弊端,那就是只能get请求,而且不安全,只要服务端支持,谁都可以调用。

下面手写一个JSONP的实现

function jsonp(url = "", callback) {
  let script;

  // 把传递的回调函数挂载到全局上
  let name = `jsonp${new Date().getTime()}`;
  window[name] = data => {
    // 从服务器获取到了结果
    document.body.removeChild(script);
    delete window[name];
    callback && callback(data);
  };

  // 处理URL
  url += `${url.includes('?') ? '&' : '?'}callback=${name}`;

  // 发送请求
  script = document.createElement('script');
  script.src = url;
  document.body.appendChild(script);
}

jsonp('http://127.0.0.1:1001/list?lx=1', result => {
  console.log(result);
});

jsonp('https://matchweb.sports.qq.com/matchUnion/cateColumns?from=pc', result => {
  console.log(result);
});
方案4:nginx反向代理

这是后端需要做的,其实我也不是很熟悉,大致配置方式。

server {
  listen 80;
  server_name 192.168.161.189;
  #charset koi8-r;
  #access_log logs/host.access.log main ;
  location  {
    proxy_pass http: // 192.168.161.189:8070;
    root html;
    index index.html index.html;
  }
}

什么是代理?

既然是代理跨域,那么代理(Proxy Server)就是一个很重要的点,这里的代理说的服务器代理,是一种很重要的服务器安全功能,也是一种很常见的设计模式,来隔绝不同的模块,解耦模块。

为什么代理是反理?

nginx就能够把用户的请求分发到空闲的服务器上,然后服务器返回自己的服务到负载均衡设备上,然后负载均衡的设备会讲服务器的服务返回给用户,所以我们并不知道为什么服务的是哪一台服务器发送出来的,这就很好的隐藏了服务器。有一句精辟的话是这么说的:“反向代理就是流量发散状,代理是流量汇聚状。”

方案5:POST MESSAGE

A.html

<iframe id="iframe" src="http://127.0.0.1:1002/B.html" frameborder="0" style="display: none;"></iframe>

iframe.onload = function () {
  iframe.contentWindow.postMessage('消息', 'http://127.0.0.1:1002/');
}

//=>监听B传递的信息
window.onmessage = function (ev) {
  console.log(ev.data);
}

B.html

window.onmessage = function (ev) {
  // console.log(ev.data);

  //=>ev.source:A
  ev.source.postMessage(ev.data + '@@@', '*');
}
方案6:基于iframe的跨域解决方案1——locaction.hash

原理:也是利用iframe可以在不同域中传值的特点,而location.hash正好可以携带参数,所以利用iframe作为这个不同域之间的桥梁。

A域名页面

var iframe = document.createElement('iframe')
iframe.src = 'http://www.B.com:80/hash.html'
document.body.appendChild(iframe)

window.onhashchange = function () {
  //处理hash
  console.log(location.hash)
}

B域名页面

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    var res = JSON.parse(xhr.responseText)
    console.log(res.msg)
    parent.location.href = `http://www.A.com:80/a.html#msg=${res.msg}`
  }
}
xhr.open('GET', 'http://www.B.com:80/json', true)
xhr.send(null)

缺点

  1. iframe虽然能解决问题,但是安全风险还是比较重要的。
  2. hash传参处理起来比较麻烦。
方案7:基于iframe的跨域解决方案2——window.name

原理其实是和上面的方法一样,区别在于window.name能够传递2MB以上的数据。

A域名页面

var iframe = document.createElement('iframe')
iframe.src = 'http://www.B.com:80/name.html'
document.body.appendChild(iframe)
var times = 0
iframe.onload = function () {
  if (times === 1) {
    console.log(JSON.parse(iframe.contentWindow.name))
    destoryFrame()
  } else if (times === 0) {
    times = 1
  }
}

// 获取数据以后销毁这个iframe,释放内存;
function destoryFrame() {
  document.body.removeChild(iframe);
}

B域名页面

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    window.name = xhr.responseText
    location.href = 'http://www.A.com:80/a.html'
  }
}
xhr.open('GET', 'http://www.B.com:80/json', true)
xhr.send(null)

等等其他处理方式

3. 讲下你最近的项目

我最近一个项目是一个金融的交易系统。项目团队前端10人,后端50多人。前端技术栈是多技术栈,新版本用的是React,老版本用的是Jquery。在团队中我是主力开发,主要是负责项目的组件开发,还有交易的业务开发。项目中采用的是一个类似low coding的解决方案。因为,我们需求变化不大,所以这样的一个解决方案,会提高开发效率,也会减少bug率,而且后期维护,也会方便很多。项目中使用的是json话配置,是因为,可以服务端进行动态的页面配置。

4. setState到底是异步还是同步

  • setState只在合成事件和钩⼦函数中是“异步”的,在原⽣事件和setTimeout中都是同步的;
  • setState的“异步”并不是说内部由异步代码实现,其实本身执⾏的过程和代码都是同步的,只是合成事件和钩⼦函数的调⽤顺序在更新之前,导致在合成事件和钩⼦函数中没法⽴⻢拿到更新后的值,形成了所谓的“异步”,当然可以通过第⼆个参数setState(partialState, callback)中的callback拿到更新后的结果;
  • setState的批量更新优化也是建⽴在“异步”(合成事件、钩⼦函数)之上的,在原⽣事件和setTimeout中不会批量更新,在“异步”中如果对同⼀个值进⾏多次 setState,setState的批量更新策略会对其进⾏覆盖,取最后⼀次的执⾏,如果是同时setState多个不同的值,在更新时会对其进⾏合并批量更新。

5. 讲下hooks

useState、useReducer、useCallback,useEffect,useLayoutEffect、自定义hook、memo等

6. 讲下react的性能优化

  1. render里面尽量减少新建变量和bind函数,传递参数是尽量减少传递参数的数量
  2. 定制shouldComponentUpdate函数
  3. 使用Immutable.js
  4. 多个react组件性能优化,key的优化
  5. 使用useCallback,useMemo

7. 前端的通信方式

  1. 设置缓存,也就是A页面设置缓存,B页面取值
  2. 通过url传参的方式
  3. 通过路由的state模式传参
  4. 通过发布订阅模式
  5. 使用props传参
  6. 使用状态管理工具

8. 讲下webpack的构建流程

Webpack的运⾏流程是⼀个串⾏的过程,从启动到结束会依次执⾏以下流程:

  1. 初始化参数:从配置⽂件和Shell语句中读取与合并参数,得出最终的参数;
  2. 开始编译:⽤上⼀步得到的参数初始化Compiler对象,加载所有配置的插件,执⾏对象的run⽅法开始执⾏编译;
  3. 确定⼊⼝:根据配置中的entry找出所有的⼊⼝⽂件;
  4. 编译模块:从⼊⼝⽂件出发,调⽤所有配置的Loader对模块进⾏翻译,再找出该模块依赖的模块,再递归本步骤直到所有⼊⼝依赖的⽂件都经过了本步骤的处理;
  5. 完成模块编译:在经过第4步使⽤Loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
  6. 输出资源:根据⼊⼝和模块之间的依赖关系,组装成⼀个个包含多个模块的Chunk,再把每个Chunk转换成⼀个单独的⽂件加⼊到输出列表,这步是可以修改输出内容的最后机会;
  7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和⽂件名,把⽂件内容写⼊到⽂件系统。

在以上过程中,Webpack会在特定的时间点⼴播出特定的事件,插件在监听到感兴趣的事件后会执⾏特定的逻辑,并且插件可以调⽤Webpack提供的API改变Webpack的运⾏结果。

相关文章