# XMLHttpRequest(ajax)
ajax的全称:Asynchronous Javascript And XML。
异步传输js和xml。
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息
(3)设置响应HTTP请求状态变化的函数
(4)发送HTTP请求
(5)获取异步调用返回的数据
(6)实现局部刷新
function ajax(url){
var xhr = window.XMLHttpRequest ? new XMLHttpRequest()
: ActiveXObject("microsoft.XMLHttp")
xhr.open("get",url,true);
xhr.send();
xhr.onreadystatechange = () =>{
if(xhr.readyState == 4){
if(xhr.status == 200){
var data = xhr.responseText;
return data;
}
}
}
}
//xmlhttp.readyState
//0 初始化
//1 已连接
//2 已发送
//3 已接收 -头部
//4 已接收 - body
//get请求
function btnClick() {
//创建核心对象
xmlhttp = null;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
//编写回调函数
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
alert(xmlhttp.responseText)
}
}
//open设置请求方式和请求路径
xmlhttp.open("get", "/Ajax/ajax2?username=张三");
//open的第三个参数,true为异步,false为同步
//很多浏览器已不太支持同步操作
//send 发送
xmlhttp.send();
//send里面放的是http-body
}
//post方式
function btnClick() {
......
//同上
//open设置请求方式和请求路径
xmlhttp.open("post", "/Ajax/ajax2");
//设置请求头
xmlhttp.setRequestHeader("content-type", "application/x-www-form-urlencoded")
//send 发送
xmlhttp.send("username=张三");
}
请求头的设置要放在
open之后(必须连接了才能发送!!!)send之前
# XMLHttpRequest.overrideMimeType()
指定一个 MIME 类型用于替代服务器指定的类型,使服务端响应信息中传输的数据按照该指定 MIME 类型处理。例如强制使流方式处理为"text/xml"类型处理时会被使用到,即使服务器在响应头中并没有这样指定。此方法必须在 send 方法之前调用方为有效。
XMLHttpRequest.overrideMimeType(mimeType)
// 如果服务器没有指定类型,那么 XMLHttpRequest 将会默认为 "text/xml".
备注: 如果服务器没有指定一个Content-Type 头,XMLHttpRequest 默认 MIME 类型为"text/xml". 如果接受的数据不是有效的 XML,将会出现格”格式不正确“的错误。你能够通过调用 overrideMimeType() 指定各种类型来避免这种情况。
但有时候服务器无法返回我们需要的数据类型, 但客户端不知道服务器无法返回, 所以就算xhr.responseType设置了想拿的数据类型, 但也拿不到, 而且xhr.response使用xhr.responseType设置的值来解析一个非xhr.responseType类型的值, 此时就会发生问题.
这时我们需要知道服务器返回的原始值, 不让浏览器自动解析, 所以我们可以使用: xhr.overrideMimeType(), 它可以覆盖xhr.responseType和服务器返回的MIME类型, 如果要返回原始值, 可以使用:
xhr.overrideMimeType('text/plain')
// Interpret the received data as plain text
req = new XMLHttpRequest();
req.overrideMimeType("text/plain");
req.addEventListener("load", callback, false);
req.open("get", url);
req.send();
# xhr.upload 进度progress
Progress Events定义了与客户端服务器通信有关的事件。这些事件最早其实只针对XHR操作,但目前也被其它API借鉴。有以下6个进度事件。
- loadstart:在接收到相应数据的第一个字节时触发。
- progress:在接收相应期间持续不断触发。
- error:在请求发生错误时触发。
- abort:在因为调用abort()方法而终止链接时触发。
- load:在接收到完整的相应数据时触发。
- loadend:在通信完成或者触发error、abort或load事件后触发。
<html>
<head >
<meta charset="UTF-8">
</head>
<body class="m-2">
<label for="a" class="btn btn-primary">点击上传</label>
<input id='a' name="file" type="file"
accept="image/png, image/jpeg, video/*" style="display:none;" multiple='multiple'>
<script>
async function main() {
const l = console.log
let fileEle = document.querySelector('#a')
fileEle.onchange = e => {
let files = fileEle.files
if(files.length === 0) return false;
let data = new FormData()
for(const file of files){
data.append('files', file)
}
let xhr = new XMLHttpRequest()
xhr.upload.addEventListener('progress', e => {
if (e.lengthComputable) {
var percentage = Math.round((e.loaded * 100) / e.total);
l(`${percentage}%`)
}
})
xhr.upload.addEventListener('loadstart', e => {
l('上传开始')
})
xhr.upload.addEventListener('error', e => {
l('上传失败')
})
xhr.upload.addEventListener('abort', e => {
l('上传终止')
})
xhr.upload.addEventListener('timeout', e => {
l('由于预设时间到期,上传终止')
})
xhr.upload.addEventListener('load', e => {
l('上传成功了')
})
xhr.upload.addEventListener('loadend', e => {
l('上传已经停止了')
})
xhr.open('post', 'http://localhost:9000/upload')
xhr.responseType = 'json'
xhr.send(data)
xhr.onload = () => {
console.log(xhr)
// l(...xhr.response);
// l(...xhr.response.imgsSrc);
}
}
}
main();
</script>
</body>
</html>
使用formidable模块来处理上传图片
// "formidable": "^3.5.1"
var http=require('http')
var fs=require('fs')
// formidable是nodejs中用来上传图片的模块
var {formidable}=require('formidable')
// path是路径模块
var path=require('path')
http.createServer(function(req,res){
// 如果请求的方法为post (在form中的method='post')
res.writeHead(200, {
"Access-Control-Allow-Origin": "*",
});
if(req.method.toLowerCase()=='post'){
const test1 = path.join(__dirname,'./test1')
const test2 = path.join(__dirname,'./test2')
const test3 = 'test1'
if(!fs.existsSync('uploadImg')){
fs.mkdirSync('./uploadImg');
}
var form= formidable({
uploadDir:'./uploadImg'
});
// 设置上传之后图片的地址 这个文件夹要提前创好,否则报错
// form.uploadDir='./uploadImg'
form.parse(req,function(err,fields,files){
// 图片的所有信息都在这个 files 中,console.log可以查看
console.log(files)
if(err){
throw err
}
// 原名字
var oldpath= files.files[0].filepath
// 时间戳
var time= +new Date()
// 随机数
var random=parseInt(Math.random()*10000)
// 图片拓展名 .JPG .png
var extname=path.extname(files.files[0].originalFilename)
// 新的文件名=__dirname+存放上传图片的文件夹地址+时间戳+随机数+后缀名
var newpath=__dirname + '/uploadImg/'+ time + random +extname
fs.rename(oldpath,newpath,function(err){
if(err){
console.log(err)
throw Error("false")
}
console.log("success")
let obj={img:newpath}
res.end(JSON.stringify(obj))
})
})
}else{
res.end("action")
}
}).listen(9000,'127.0.0.1')
上传开始
5%
10%
.....
97%
100%
上传成功了
上传已经停止了
XMLHttpRequest {
onreadystatechange: null,
readyState: 4,
timeout: 0,
withCredentials: false,
upload: XMLHttpRequestUpload,
…
}
警告
- oAjax.onprogress 下载进度
- oAjax.upload.onprogress 上传进度
upload事件应该要放在open前面!!!~
# elementui 实现上传进度条
主要就是on-progress事件的控制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>上传文件</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
width: 178px;
height: 178px;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
.image-preview {
width: 178px;
height: 178px;
position: relative;
border: 1px dashed #d9d9d9;
border-radius: 6px;
float: left;
}
.image-preview .image-preview-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.image-preview .image-preview-wrapper img {
width: 100%;
height: 100%;
}
.image-preview .image-preview-action {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
background-color: rgba(0, 0, 0, .5);
transition: opacity .3s;
cursor: pointer;
text-align: center;
line-height: 200px;
}
.image-preview .image-preview-action .el-icon-delete {
font-size: 32px;
}
.image-preview:hover .image-preview-action {
opacity: 1;
}
</style>
</head>
<body>
<div id="app">
<el-upload v-show="imageUrl.length < 1"
class="avatar-uploader"
:action="serverUrl"
:show-file-list="false"
:before-upload="beforeUpload"
:on-success="handleSuccess"
:on-progress="uploadProcess">
<i v-show="imageUrl =='' && imgFlag == false" class="el-icon-plus avatar-uploader-icon"></i>
<el-progress v-show="imgFlag == true" type="circle" :percentage="percent" style="margin-top: 20px"></el-progress>
</el-upload>
<div class="image-preview" v-show="imageUrl.length > 1">
<div class="image-preview-wrapper">
<img :src="imageUrl">
<div class="image-preview-action">
<i @click="handleRemove" class="el-icon-delete"></i>
</div>
</div>
</div>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script>
new Vue({
el: '#app',
data() {
return {
serverUrl: "xxx", // 后台上传接口
imgFlag: false,
percent: 0,
imageUrl: '',
};
},
mounted() {},
methods: {
handleRemove(file, fileList) {
this.imageUrl = '';
},
beforeUpload(file) {
const isLt10M = file.size / 1024 / 1024 < 10;
if (
['image/jpeg',
'image/gif',
'image/png',
'image/bmp'
].indexOf(file.type) == -1) {
this.$message.error('请上传正确的图片格式');
return false;
}
if (!isLt10M) {
this.$message.error('上传图片不能超过10MB哦!');
return false;
}
},
handleSuccess(res, file) {
this.imgFlag = false;
this.percent = 0;
if (res) {
this.imageUrl = URL.createObjectURL(file.raw); // 项目中用后台返回的真实地址
} else {
this.$message.error('视频上传失败,请重新上传!');
}
},
uploadProcess(event, file, fileList) {
this.imgFlag = true;
console.log(event.percent);
this.percent = Math.floor(event.percent);
},
}
})
</script>
</html>
# ajax请求终止
对于原生XHR对象来说,取消的ajax的关键是调用XHR对象的 abort 方法
var native = new XMLHttpRequest();
native.open("GET","https://api.github.com/");
native.send();
native.onreadystatechange=function(){
if(native.readyState==4&&native.status==200){
console.log(native.response);
}else{
console.log(native.status);
}
}
native.abort();
var jp = $.ajax({
type:"get",
url:"https://api.github.com/",
dataType:"json",
success:function(data){
console.log(data);
},
error:function(err){
console.log(err);
}
})
jp.abort();
在axios中取消ajax请求不同于上面两种形式,在axios中是通过axios.CancelToken.source()方法取消请求
...
constructor(props) {
this.state=store.getState()
this.source = axios.CancelToken.source() //生成取消令牌用于组件卸载阻止axios请求
}
...
componentDidMount = () => {
const _t = this
const url="xxxx";
axios.get(url, {
cancelToken: _t.source.token
})
.then(res => {
...
})
.catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
console.log(thrown)
}
})
}
componentWillUnmount = () => {
//阻止请求
this.source.cancel('组件卸载,取消请求');
}
- api接口
import request from '@/utils/request'
import axios from 'axios'
export function areaDetailTotal(data,_this) {
return request({
url: '/expenseReport/otherReport/areaDetailTotal',
method: 'post',
data,
cancelToken: new axios.CancelToken((c) => {
_this.cancelFun = c
}),
})
}
- request.js
import axios from 'axios'
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
// baseURL: '/ct-admin',
baseURL: '/billing',
// 超时
timeout: 1200000
})
service.interceptors.request.use(
config => {
console.log(config)
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
if (getToken() && !isToken) {
config.headers['token'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
return config
},
error => {
Promise.reject(error)
}
)
- 应用的vue文件,传参把this带过去
<script>
export default{
data(){
return {
cancelFun :null,
}
},
methods:{
cancelRequest() {
if (typeof this.cancelFun === 'function') {
this.cancelFun()
}
},
t1fun(){
if (istrue) {
this.cancelRequest()
areaDetailTotal(data,this).then(res => {
this.totalData1 = res.data
})
}
}
}
}
</script>
# 轮询和长轮询
- 轮询:客户端定时去请求服务端,是客户端主动请求来促使数据更新;
- 长轮询:说白了也是客户端请求服务端,但是服务端并不是即时返回,而是当有内容更新的时候才返回内容给客户端,从流程上讲,可以理解为服务器向客户端推送内容;
轮询:
1:大量耗费服务器内存和宽带资源,因为不停的请求服务器,很多时候并没有新的数据更新,因此绝大部分请求都是无效请求
2:数据不一定是实时更新,要看设定的请求间隔,基本会有延迟。
长轮询:
1:解决了轮询的两个大问题,数据实时更新;
2:唯一的缺点是服务器在挂起的时候比较耗内存;
# fetch
参见链接
# websocket
性能高,双工通信,支持跨域
# jsonp
原理:动态创建script标签(script标签只执行一次,与img不同,所以 根据需要可能要持续创建删除)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script>
function show(json){
let oUl=document.getElementById('ul1');
oUl.innerHTML='';
json.s.forEach(str=>{
let oLi=document.createElement('li');
oLi.innerHTML=str;
oUl.appendChild(oLi);
});
}
window.onload=function (){
let oTxt=document.getElementById('txt1');
oTxt.oninput=function (){
let oS=document.createElement('script');
oS.src=`https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/
su?wd=${encodeURIComponent(oTxt.value)}&cb=show`;
document.head.appendChild(oS);
document.head.removeChild(oS)
};
};
</script>
</head>
<body>
<input type="text" id="txt1">
<ul id="ul1"></ul>
</body>
</html>
# superagent
一个可用于浏览器端和Node服务端的小型渐进式客户端HTTP请求库
cnpm install superagent
(function() {
// superagent is exposed as `window.superagent`
// if you wish to use "request" instead please
// uncomment the following line of code:
// `window.request = superagent;`
superagent
.post('/api/pet')
.send({ name: 'Manny', species: 'cat' }) // sends a JSON post body
.set('X-API-Key', 'foobar')
.set('Accept', 'application/json')
.end(function (err, res) {
// Calling the end function will send the request
});
})();
# 爬虫相关操作
使用superagent获取html文件内容;cheerio可以使用类似jquery这种方法
cnpm i cheerio --save
# puppeteer 无头浏览器爬数据
cnpm i puppeteer
const puppeteer = require('puppeteer')
const url = `https://movie.douban.com/tag/#/?sort=R&range=6,10&tags=%E7%94%B5%E5%BD%B1,%E6%BE%B3%E5%A4%A7%E5%88%A9%E4%BA%9A`
const sleep = time => new Promise(resolve => {
setTimeout(resolve, time)
})
;(async () => {
console.log('Start visit the target page')
const browser = await puppeteer.launch({
args: ['--no-sandbox'],
dumpio: false
})
const page = await browser.newPage()
await page.goto(url, {
waitUntil: 'networkidle2'
})
await sleep(3000)
await page.waitForSelector('.more')
for (let i = 0; i < 2; i++) {
await sleep(3000)
await page.click('.more')
}
const result = await page.evaluate(() => {
var $ = window.$
var items = $('.list-wp a')
var links = []
if (items.length >= 1) {
items.each((index, item) => {
let it = $(item)
let doubanId = it.find('div').data('id')
let title = it.find('.title').text()
let rate = Number(it.find('.rate').text())
let poster = it.find('img').attr('src').replace('s_ratio', 'l_ratio')
links.push({
doubanId,
title,
rate,
poster
})
})
}
return links
})
browser.close()
process.send({result})
process.exit(0)
})()