前端知识问答(二)
Brian Lv3

同源策略和跨域请求

同源策略是一种阻止不同域之间的内容进行交互,隔离潜在恶意文件的关键完全机制,当一个请求URL的协议,域名、端口三者之间只要有一个不同就是跨域了。同源策略的目的只为了保证用户信息安全,防止恶意的网站窃取用户数据。如果网页之间不满足”同源”的要求,他们将不能共享cookie、localStorage、IndexDB,不能获取DOM,Ajax请求不能发送。
关于请求,同源策略并不是浏览器不让请求发送出去、或者后端拒绝返回数据,而是请求正常发出去了,后端也响应了,只是数据到了浏览器被浏览器组织并且丢弃了。但是也有例外,想img src,link href,script src这些是允许跨域加载的。
由此产生的问题就是跨域问题。怎么解决呢?
目前主流的做法是JSONP和CORS。
先讲讲jsonp,上面提到script允许跨域加载,jsonp就是通过这个实现数据接口跨域请求的。
当发生请求时,在客户端动态添加一个script标签,他的地址指向第三方地址,让后约定好处理结果的函数也可以通过url参数传递给后端,比如:

1
https://api.djsdf.com/v1/userinfo?userId=123&cb=getUserInfoById

根据上面所说的是通过script,那么在客户端页面上将会增加

1
<script src="https://api.djsdf.com/v1/userinfo?userId=123&cb=getUserInfoById"></script>

服务端处理这个请求,然后返回

1
getUserInfoById({"name":"hdfsd","gender":"male"})

在客户端定义好getUserInfoById这个处理函数,当服务器返回上面的内容时,这个函数将会被调用,传入的参数就是要获取的内容。
jsonp的优点是兼容性好,缺点也很明显,只支持get请求。

接下来说说CORS
中文名叫跨站资源共享机制,这个是真正的跨域访问。它需要浏览器和服务器同时支持。除ie9及以下浏览器外,其他浏览器都支持这个功能。

CORS请求是浏览器自动完成的。对于开发者来说,和同源Ajax没什么区别,浏览器一旦发现ajax请求跨域,就会自动添加附加的头信息,因此实现CORS通信的关键是服务器,只要服务器CORS接口,即可跨域通信。

如果服务器实现了跨域,就会返回给浏览器CORS头(Access-Control-Allow-Origin),这是浏览器就会正常处理返回的数据,而不是因为同源策略将其丢弃。

需要注意的是,浏览器会将cors请求分为简单请求和非简单请求。
满足以下两大条件就是简单请求:
1、请求方法是head、get、post之一
2、请求只会包含Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(值只为application/x-www-form-urlencoded、multipart/form-data、text/plain)
除此之外,其余的都是非简单请求。
对于这两种请求,浏览器的处理会有所不同。
对于简单请求,浏览器是直接发出,在头信息中增加一个Origin字段,表示本次请求来自哪个源,服务器根据这个值决定是否同意这次请求。如果origin不在允许的范围,服务器会返回一个不包含以Access-Control为开头的信息头的http响应,这时浏览器会丢弃返回结果。如果origin在允许范围内,则服务器将会返回带有以下消息头的的响应:

1
2
3
4
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: AppInfo
Content-Type: text/html; charset=utf-8

其中,第一个是必须的,它表示允许请求的域,也可以设置为*,表示允许所有域的请求。

Access-Control-Allow-Credentials表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。它是可选的。

Access-Control-Expose-Headers。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('AppInfo')可以返回AppInfo字段的值。

有一个需要特别注意的是,上面提到Access-Control-Allow-Credentials表示是允许发送cookie,只是把这个设置为true还不行,还应在前端显示设置withCredentials为true:

1
2
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

否则,浏览器不会发送cookie。

还需要额外注意的是,如果要发送cookie,就不能把Access-Control-Allow-Origin设置为星号(*),他应该与发出请求一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨域)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

非简单请求,在第一次请求的时候会有一次预检验请求,用来检验当前请求源是否在允许范围内,如果预检验通过,以后每次请求就跟简单请求差不多,会自动在请求头里面加上origin,服务器返回信息中也会包含Access-Control-Allow-Origin这个消息头。预检验请求检验origin、请求方法method、还有自定义消息头是否在允许范围内,如果允许,就在响应头中增加cors头消息,接着浏览器发起真正的请求,否则,正常返回,浏览器报错,提示不允许跨域。

需要额外注意的是,预检验请求如果通过还可返回一个头消息:Access-Control-Max-Age,用来告诉浏览器多久之内不用再发一次预检验请求。

CORS部分参考http://www.ruanyifeng.com/blog/2016/04/cors.html

BFC的作用

参考https://www.jianshu.com/p/1f91e136b22d

cookie、session和token的区别?使用?

参考https://www.jianshu.com/p/3ec89874b196

echart自适应?

通过监听浏览器窗口的resize时间,然后调用echart图表实例的resize方法。

vue nextTick的作用?

官方定义:

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

vue中试图的更新是异步的,也就是说,修改数据,vue不会理解更新视图,而是将这次”更新”放到一个异步队列中,等到所有数据都已经更改完,对这个异步队列进行去重,然后逐一更新视图,视图更新完毕后,如果更改数据后面紧跟着有nextTick,将会去执行nextTick里面传入的操作。

参考https://www.jianshu.com/p/dce747bc708f

js的垃圾回收机制?

参考https://www.cnblogs.com/94pm/p/16026887.html

vuex的优缺点?

优点

1.集中管理共享的数据,易于开发和后期维护

2.数据是响应是的,状态更改,会触发视图的更新

3.以js原生对象形式存储对象,使用方便

4.以一种可预测的方式改变数据,避免了数据污染

缺点

1.浏览器刷新会导致所有数据变为初始状态

不过这个缺点可通过插件 vuex-persistedstate解决

keep-alive属性及生命周期

KeepAlive是vue内置组件,用来缓存组件

它的属性包括:

  • include包含的组件(可以为字符串,数组,以及正则表达式,只有匹配的组件会被缓存)

  • exclude排除的组件(以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存)

  • max缓存组件的最大值(类型为字符或者数字,可以控制缓存组件的个数)

它的生命周期有activated和deacitvated

执行顺序是created->mounted->activated,退出是执行deactivated,再次进入执行activated

electron如何自定义安装/卸载界面

参考https://blog.51cto.com/u_15127581/4318016

vue实现双向绑定的原理

参考:

https://www.jianshu.com/p/87bd5e6f6263

https://www.cnblogs.com/libin-1/p/6893712.html

vue父子组件的生命周期顺序

初次加载:

父beforeCreated->created->beforeMount->子beforeCreated->created->beforeMount->mounted->父mounted

此阶段就是父组件创建,子组件创建,子组件挂载,父组件挂载

需要特别注意的是,如果组件是异步引入,执行顺序是

父组件创建,父组件挂载,子组件创建,子组件挂载

更新

父beforeUpdate->子beforeUpdate->子updated->父updated

销毁

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed