# 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)
})()

最后更新: 2/2/2024, 3:21:37 PM