1. 同源策略
概念
如果有两个url的 协议、域名、端口号完全一致,那么这两个url就是同源的在浏览器里打开页面则默认遵循同源策略。
源的概念
协议(HTTP/HTTPS)+端口+域名
限制
不是同一个源,不能权限操作另一个源的数据;不同源的页面间,不需互相访问数据。
优缺点
- 优点:保证用户的隐私安全和数据安全
- 缺点:当前端需要访问另一个域名的后端接口时,会被浏览器阻止其获取相应。
2. 前后端通信
Ajax:只适合同源通信WebScoket:不受同源策略限制CORS:支持同源通通信支持跨域通信Fetch API:只适合同源通信
3. Ajax
原理
简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用JavaScript来操作DOM而更新页面。
实现过程
实现 Ajax异步交互需要服务器逻辑进行配合,需要完成以下步骤:
- 创建
Ajax的核心对象XMLHttpRequest对象 - 通过
XMLHttpRequest对象的open()方法与服务端建立连接 - 构建请求所需的数据内容,并通过
XMLHttpRequest对象的send()方法发送给服务器端 - 通过
XMLHttpRequest对象提供的onreadystatechange事件监听服务器端你的通信状态 - 接受并处理服务端向客户端响应的数据结果
- 将处理结果更新到
HTML页面中
实例
- 初始化XMLHttpRequest实例对象
const xhr = new XMLHttpRequest();
- 通过
XMLHttpRequest对象的open()方法与服务器建立连接
xhr.open(method, url, [async][user][password])
| 参数 | 说明 |
|---|---|
method | 表示当前的请求方式,常见的有GET、POST |
url | 服务端地址 |
async | 布尔值,表示是否异步执行操作,默认为true |
user | 可选的用户名用于认证用途;默认为null |
password | 可选的用户名用于认证用途;默认为null |
- 通过
send()方法,将客户端页面的数据发送给服务端
xhr.send([body])
body: 在 XHR 请求中要发送的数据体,如果不传递数据则为 null
如果使用GET请求发送数据的时候,需要注意如下:
- 将请求数据添加到
open()方法中的url地址中 - 发送请求数据中的
send()方法中参数设置为null
- 绑定onreadystatechange事件
onreadystatechange事件用于监听服务器端的通信状态,主要监听的属性为XMLHttpRequest.readyState- 只要
readyState属性值一变化,就会触发一次readystatechange事件 XMLHttpRequest.responseText属性用于接收服务器端的响应结果
const request = new XMLHttpRequest();
request.open('GET', 'https://xxx');
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status >= 200 && request.status <= 300 || request.status === 304) {
console.log(request.responseText) // 服务端返回的结果
} else {
console.log("错误信息:" + request.status)
}
}
};
request.send('{"xxx":"xxx"}');
封装
//封装一个ajax请求
function ajax(options) {
//创建XMLHttpRequest对象
const xhr = new XMLHttpRequest()
//初始化参数的内容
options = options || {}
options.type = (options.type || 'GET').toUpperCase()
options.dataType = options.dataType || 'json'
const params = options.data
//发送请求
if (options.type === 'GET') {
xhr.open('GET', options.url + '?' + params, true)
xhr.send(null)
} else if (options.type === 'POST') {
xhr.open('POST', options.url, true)
xhr.send(params)
//接收请求
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
let status = xhr.status
if (status >= 200 && status < 300) {
options.success && options.success(xhr.responseText, xhr.responseXML)
} else {
options.fail && options.fail(status)
}
}
}
}
使用方式:
ajax({
type: 'post',
dataType: 'json',
data: {},
url: 'https://xxxx',
success: function(text,xml){ //请求成功后的回调函数
console.log(text)
},
fail: function(status){ //请求失败后的回调函数
console.log(status)
}
})
4. 跨域的解决方法
1. JSONP
- 甲站点利用
script标签可以跨域/异步加载的特性,向乙站点发送GET请求 - 乙站点后端改造 JS 文件内容,将数据传进回调函数
- 甲站点通过回调函数拿到乙站点数据
var jsonp = function() {
// 创建script标签
var script = document.createElement('script');
// 给script添加属性,如src,在src中约定callback函数。
script.src = 'http:/www.test.com/?name=zhangsan&callback=jsonp'
// script加载完的回调
script.onload = function() {}
// script加载失败的回调
script.onerror = function() {}
// 将script添加到head标签中
document.getElementsByTagName('head')[0].appendChild(script);
}
优点
- 兼容IE
- 可以跨域
- 改动较小,只需要后端改造JS文件内容,将数据传进回调函数
缺点
- 由于它是script标签,所以它无法读取到
Ajax那么精确,接收不到状态码及响应头等 - 用户认证缺失
- 由于它是script标签,因此它只能发
GET请求,不支持POST
2. CORS
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing),他允许浏览器向跨源服务器发送XMLHttpRequest请求,从而克服了Ajax只能同源使用的限制。浏览器将CORS请求分为简单请求和非简单请求两类。
简单请求
对于简单请求(GET,HEAD,POST),乙站点在响应头里添加 Access-Control-Allow-Origin:https://甲站点 即可.
// node.js设置响应头
response.setHeader('Access-Control-Allow-Origin','https://甲站点')
非简单请求
对于复杂请求,如 PATCH ,被访问(乙)站点需要
- 先响应 OPTIONS 请求,在响应中添加如下的响应头:
Access-Control-Allow-Origin: https://甲站点
Access-Control-Allow-Methods: POST, GET, OPTIONS, PATCH
Access-Control-Allow-Headers: Content-Type
- 然后响应 POST 请求,在响应中添加
Access-Control-Allow-Origin头。
其他
如果需要附带身份信息,JS 需要在 AJAX 里设置xhr.withCredentials = true
优缺点
优点
使用方便
缺点
不兼容IE6、7、8、9浏览器
3. Proxy(包含 Nginx / Node)
本地服务器作为请求代理对象
通过vue-cli脚手架工具搭建项目,我们可以通过webpack为我们起一个本地服务器作为请求的代理对象。通过该服务器转发请求至目标服务器,得到结果再转发给前端,但是最终发布上线时如果web应用和接口服务器不在一起仍会跨域
// 添加至 vue.config.js
amodule.exports = {
devServer: {
host: '127.0.0.1',
port: 8084,
open: true,// vue项目启动时自动打开浏览器
proxy: {
'/api': { // '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的
target: "http://xxx.xxx.xx.xx:8080", //目标地址,一般是指后台服务器地址
changeOrigin: true, //是否跨域
pathRewrite: { // pathRewrite 的作用是把实际Request Url中的'/api'用""代替
'^/api': ""
}
}
}
}
}
// 通过 axios发送请求,配置请求的根路径
axios.defaults.baseURL = '/api'
Nginx 代理
还可通过服务端实现代理请求转发,前端 => 后端 => 另一个域名的后端。
server {
listen 80;
# server_name www.josephxia.com;
location / {
root /var/www/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:3000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
node.js 代理
var express = require('express');
const proxy = require('http-proxy-middleware')
const app = express()
app.use(express.static(__dirname + '/'))
app.use('/api', proxy({ target: 'http://localhost:4000', changeOrigin: false
}));
module.exports = app
4. WebSocket
是一种网络传输协议,可在单个TCP连接上进行全双工通信,即通信允许数据在两个方向上同时传输,能更好的节省服务器资源和带宽并达到实时通讯,本身不受同源策略限制。
// 1. 申明
var ws = new WebSocket('wss://....'); // wss/ws => 加密/不加密
// 2. 建立链接
ws.onopen = function(e) {
console.log('开始连接...');
ws.send('hello WebSocket!');
}
// 3. 接受消息
ws.onmessage = function(e) {
console.log('接收数据:'+ e.data);
ws.close() // 关闭连接
}
// 4. 关闭连接的回调,确定以关闭连接
ws.onclose= function(e) {
console.log('连接关闭');
}
优点
- 控制开销小,数据包头部协议较小,不同于http每次请求需要携带完整的头部
- 实时性强,相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少
- 保持创连接状态,创建通信后,可省略状态信息,不同于HTTP每次请求需要携带身份验证
- 支持扩展,用户可以扩展websocket协议、实现部分自定义的子协议
- 定义了二进制帧,更好处理二进制内容
应用场景
- 弹幕
- 媒体聊天
- 协同编辑
- 基于位置的应用
- 体育实况更新
- 股票基金报价实时更新
5. PostMessage
// 如A源向B源发送数据data
// A, 参数含义:发送的数据,接受方的源或者"*"
window.postMessage('data', 'http/B.com');
// B: 监听message,判断消息来源,接受数据
window.addEventListerr('message', function(event) {
console.log(event);
// event.origin // 消息来源,'http/A.com'
// event.source // A的引用window
// event.data // 接受的数据
}, false)