一、同源策略
用户浏览网站时难免需要将一些经常用到的信息,缓存在本地以提升交互体验,避免一些多余的操作。那么这些信息中难免有些就会涉及用户的隐私,怎么保证用户的信息不在多个站点之间共享,以达到可用的最小范围,这边引出了浏览器的同源策略。那么...
什么是同源策略?
同时满足以下三个条件的网页称为同源:
- 协议相同
- 域名相同
- 端口相同
非同源的网页将受到以下限制:
- Cookie、LocalStorage 和 IndexDB 无法读取。
- DOM 无法获得
- AJAX 请求不能发送
同源策略是必要的,但这些限制有时也会对一些合理的使用带来不便,这便引出了跨域通信的需求。
二、跨域通信
常见的跨域通信方式有如下集中:
- JSONP
- Hash
- Postmessage
- WebSocket
- CORS
2.1 JSONP
JSONP的原理是,首先客户端动态添加一个<script>
元素,向服务器请求JSON数据,在请求的URL中添加search字段,指定回调函数名;服务器在收到请求后,会将数据放在指定名字的回调中传回来。实现源码:
/**
* jsonp
* @param {[type]} url [description]
* @param {[type]} onsucess [description]
* @param {[type]} onerror [description]
* @param {[type]} charset [description]
* @return {[type]} [description]
*/
const jsonp = function(url, onsucess, onerror, chartset){
let callbackName = getName('tmp_jsonp')
window[callbackName] = function(){
if(onsucess && onsucess instanceof Function){
onsucess(arguments[0])
}
}
let script = createScript(url + '&callback=' + callbackName, charset)
script.onload = script.onreadystatechange = function () {
// script标签加载完成(即jsonp请求完成)后移除创建的临时标签
if(!script.readyState || /loaded|complete/.test(script.readyState)){
script.onload = script.onreadystatechange = null
// 移除该script的 DOM 对象
if (script.parentNode) {
script.parentNode.removeChild(script)
}
window[callbackName] = null
}
}
script.onerror = function () {
if (onerror && util.isFunction(onerror)) {
onerror();
}
}
document.getElementsByTagName('head')[0].appendChild(script)
}
/**
* 获取一个随机的5位字符串
* @param {string} prefix [字符前缀]
* @return {string} [字符集]
*/
const getName = function (prefix) {
return prefix + Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5)
}
/**
* 在页面中创建script
* @param {string} url [url]
* @param {string} charset [字符集]
* @return {string} [创建完成的script标签]
*/
const createScript = function (url, charset) {
let script = document.createElement('script')
script.setAttribute('type', 'text/javascript')
charset && script.setAttribute('charset', charset)
script.setAttribute('src', url)
script.async = true
return script
}
2.2 hash
hash方式主要用在内嵌iframe的父子窗口间的通信,原理是URL的#
后部分的改变不会使页面刷新。
(1)父窗口向子窗口发送数据
// 父窗口向子窗口发送数据
let src = orignURL + '#' + data
document.getElementById('myIframe').src = src
// 子窗口接收父窗口发送的数据
window.onhashchange = function(){
let message = window.location.hash
...
}
(2)子窗口向父窗口发送数据,也同理
parent.location.herf = target + '#' + hash
2.3 postmessage
首先创建目标窗口的window对象,然后通过该对象的postmessage方法传递数据,最后目标窗口通过监听message事件来接受数据。
// 发送数据
let bWindow = window.open('http://b.com', 'title')
bWindow.postmessage('hello message', 'http://b.com')
// 接受数据
window.addEventListener('message', function(e){
// e.source 发送消息窗口的引用
// e.origin 发送消息的网址
// e.data 发送消息的内容
})
使用postmessage时需要注意的问题:
- 接受方打开了对
message
事件的响应,接收时需要对发送方进行验证 - 发送消息时需要指定确定的接收方,不可指定为
*
进行群发
2.4 websocket
// ws和wss的区别就是加不加密
let ws = new WebSocket('ws://xx.com')
ws.onopen = function(e){}
ws.onmessage = function(e){}
ws.onclose = function(e){}
2.5 CORS
可以简单理解为跨域通信的ajax,需要浏览器和服务器同时支持,主要关键还是须在浏览器端进行配置。可参考CORS配置说明
三、前后端如何通信
除了跨域通信,前端最常接触的便是前后端通信,常见的前后端通信方式有以下三种:
- ajax/fetch
- websocket
- cors
四、如何创建ajax
4.1 Vue框架实现ajax
Vue是个没什么入侵性的框架,所以在ajax方面可以有很多选择:
- 使用原生js的XHR实现
- 引入jquery
- Vue2.0官方推荐axios.js
- fetch API
4.2 原生js实现ajax
/**
* 原生JS实现ajax
* @param {object} options [传入参数]
* @return {[type]} [description]
*/
const ajax = function(options){
var opt = {
url: '',
type: 'get',
data: {},
success: function () {},
error: function () {},
}
Object.assign(opt, options)
if(opt.url){
let xhr = XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject('Microsoft.XMLHTTP')
let data = opt.data,
url = opt.url,
type = opt.type.toUpperCase(),
dataArr = []
for(let k in data){
dataArr.push(k + '=' + data[k])
}
if(type == 'GET'){
url = url + '?' + dataArr.join('&')
xhr.open(type, url.replace(/\?$/.g, ''), true)
xhr.send()
}
if(type == 'POST'){
xhr.open(type, url, true)
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
xhr.send(dataArr.join('&'))
}
xhr.onload = () => {
if(xhr.status === 200 || xhr.status === 304 || xhr.status === 206){
let res
if(opt.success && opt.success instanceof Function){
res = xhr.responseText
if(typeof res === 'string'){
res = JSON.parse(res)
opt.success.call(xhr, res)
}
}
}else{
if(opt.error && opt.error instanceof Function){
opt.error.call(xhr, res)
}
}
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。