# Ajax標(biāo)簽(空格分隔): 筆記整理---[TOC]### 從輸入網(wǎng)址開始:- 在學(xué)習(xí)ajax之前,你應(yīng)該先了解一下,當(dāng)你在瀏覽器的地址欄輸入網(wǎng)址按下回車,然后到頁面顯示的這個(gè)過程都發(fā)生了什么。>- 在地址欄上輸入了URL,也就是(Uniform Resource Locator:統(tǒng)一資源定位符)翩瓜。- 第一步就是瀏覽器對(duì)你輸入的地址進(jìn)行解析,主要作用就是分析你要對(duì)哪個(gè)服務(wù)器上面的那個(gè)文件路徑進(jìn)行訪問携龟。 >> 例如:http://www.abcd.com/path/user/target.html - 意思就是找到www.abcd.com這臺(tái)服務(wù)器上 'path/user'的target.html - 對(duì)地址進(jìn)行DNS解析 - DNS服務(wù)器的大接力 - 最終找到目標(biāo)服務(wù)器兔跌,然后建立連接 - 處理請(qǐng)求 然后返回用戶需要的數(shù)據(jù)### 用node.js簡(jiǎn)單搭建一個(gè)服務(wù)器:- 安裝node.js- 新建一個(gè)js文件 ,如test.js峡蟋, 代碼如下:```const http = require('http'); //加載http模塊const fs = require('fs'); //讀取文件模塊const path = require('path'); //識(shí)別路徑模塊http.createServer((request,response)=>{let url = request.url;if(url === '/'){ //指的是根目錄 //當(dāng)前文件的目錄 下的'public/index.html' file接收一下fs.readFile( path.resolve(__dirname,'public/index.html') ,(err,file)=>{response.end(file); // 返回file})}//response.end('helloha,hworld'); }).listen(5000,()=>{console.log('A New Sever is running at PORT 5000');//搭建完成返回的字段})```- 在建立的Js文件目錄下調(diào)出命令框 輸入node test.js- 回車運(yùn)行完以后簡(jiǎn)單的服務(wù)器就搭建完畢坟桅,通過訪問localhost:端口就可以訪問华望。- 也可以在局域網(wǎng)下,查看自己的ip地址(ipconfig)仅乓,通過ip:端口 訪問到赖舟。### 用express 框架簡(jiǎn)易搭建:```const path = require('path'); // 路徑模塊const express = require('express');//express模塊const hbs = require('hbs'); //模板引擎模塊let bodyParser = require('body-parser');//消息體解析模塊const app = express();const PORT = 8080 ;//開啟模板引擎app.engine('html',hbs.__express);//設(shè)置后綴名app.set('view engine','html');//設(shè)置模板路徑app.set('views',path.resolve(__dirname,'views'));//設(shè)置靜態(tài)資源目錄app.use(express.static(path.resolve(__dirname,'assets')));// parse application/x-www-form-urlencodedapp.use(bodyParser.urlencoded({ extended: false }));// parse application/jsonapp.use(bodyParser.json());//首頁app.get('/',(req,res)=>{res.render('home');})//-----------接口-----------------//---------錯(cuò)誤 信息 處理-------------------app.use((req,res,next)=>{res.status(404);res.send('404 not found')});app.use((err,req,res,next)=>{res.status(500);res.send('500 server error');})app.listen(PORT,(err)=>{if(err){console.log(err);}console.log(`Server is running at ${PORT}`);})```### Ajax:- Ajax全稱:Asynchronous JavaScript +And+ XML,其核心對(duì)象是 XMLHttpRequest 對(duì)象(簡(jiǎn)稱XHR)其技術(shù)核心就是無刷新頁面就可以從服務(wù)器獲得數(shù)據(jù)夸楣,但是不局限于XML數(shù)據(jù)宾抓。- Ajax四個(gè)步驟:> -1. const xhr = new XMLHttpRequest();XHR對(duì)象-2.xhr.open(method,adress,true)-3.如果是post方法 xhr.setRequestHeadeer()-4.xhr.send()- Ajax get 請(qǐng)求 ```//模擬個(gè)電影搜索searchBtn.onclick = function (){ const xhr = new XMLHttpRequest(); xhr.open('get', 'search_movie?search=' + encodeURIComponent(searchText.value), true); //將要搜索的內(nèi)容編碼然后拼接到search xhr.send(); xhr.onload = function (){ let imgData = JSON.parse(xhr.responseText);//接受返回的圖片地址 for(let item of imgData){ const img = new Image(); img.src = item; img.onload = function (){ moveImg.appendChild(this); } } }; }; ----------------------- //模擬后端的處理(express): // 搜索電影接口app.get('/search_movie', (req, res) => { let type = req.query.search; let resData = []; for(let item of movieData){ //數(shù)據(jù)庫(kù)搜索 if(item.genres.indexOf(type) !== -1){ resData.push(item.images.large); } } res.json(resData); //返回的內(nèi)容});```- Ajax post 請(qǐng)求```//模擬用戶賬號(hào)注冊(cè)form.btn.onclick = function (e){ e.preventDefault(); let json = { userName: form2.userName.value, passWord: form2.passWord.value }; const xhr = new XMLHttpRequest(); xhr.open('post', '/ajax_post_form', true); // 如果傳的數(shù)據(jù)格式是一個(gè)JSON格式的,那么需要設(shè)置下面的請(qǐng)求頭信息 xhr.setRequestHeader('Content-Type', 'application/json'); // 如果是一個(gè)表單格式的 也就是 格式 像 queryString的數(shù)據(jù) // xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); // 如果是json格式的豫喧,這里面的參數(shù)必須是一個(gè)標(biāo)準(zhǔn)的json // 如果是表單格式的石洗,直接發(fā)送就可以了 xhr.send(JSON.stringify(json)); // xhr.onload = function (){ console.log(xhr.responseText); } }----------------------------------------------后端接口:app.post('/ajax_post_form', (req, res) => { let user = req.body; fs.readFile('./data/user.json', (err, data) => { let resData = JSON.parse(data.toString()); resData.push(user) fs.writeFile('./data/user.json', JSON.stringify(resData), (err) => { if(err) { res.send('Error'); return; } res.send('ok'); // res.render('thanks'); }) })})```- Ajax post 請(qǐng)求(上傳文件)```//接受文件上傳的接口app.post('/fileupdate', (req, res) => { let form = new Formidable.IncomingForm(); form.encoding = 'utf-8'; form.uploadDir = path.resolve(__dirname, 'user'); form.keepExtensions = true; // 保留擴(kuò)展名 form.maxFieldsSize = 3 * 1024 * 1024; // 限制上傳文件的總大小3mb,默認(rèn)為2mb form.parse(req) .on('progress', (received, expected) => { console.log(`上傳進(jìn)度:${received / expected * 100}%`); }) .on('fileBegin', (name, file) => { // 當(dāng)某個(gè)文件開始上傳的時(shí)候 console.log(`${file.name}文件正在上傳`); }) .on('file', (name, file) => { // 當(dāng)某個(gè)文件上傳結(jié)束的時(shí)候 let fileExt = path.extname(file.name); // 拿到上傳文件的擴(kuò)展名 let oldFilePath = path.normalize(file.path); // 拿到上傳文件的路徑,并進(jìn)行規(guī)范化處理 let newFileName = 'file_' + Date.now() + fileExt; let newFilePath = path.resolve(__dirname, 'user', newFileName); // 對(duì)文件進(jìn)行重命名操作 fs.rename(oldFilePath, newFilePath, (err) => { if(err){ console.log('改名失敗!'); return; } console.log(`改名成功,新文件名為:${newFileName}`); }) }) .on('end', () => { // 當(dāng)全部文件上傳完成的時(shí)候 console.log(`上傳完成.`); if(req.xhr){ res.send('ok!'); }else{ res.redirect(303, '/'); } }) .on('error', (err) => { // 當(dāng)發(fā)生錯(cuò)誤的時(shí)候 console.log(err); res.send('Error!') });});``````表單上傳:
表單上傳
開始上傳
//multiple屬性寫了以后支持多個(gè)文件上傳//enctype="multipart/form-data" 上傳文件類型//enctype="application/x-www-form-urlencoded" 表單數(shù)據(jù)提交//這樣的方式是同步的 表單上傳的時(shí)候是不能干別的事情的``````使用ajax上傳? ? ? ? ? ? const? InpFile? =? document.querySelector('#Inp_file'); const? btn =? document.querySelector('.btn_upload'); const? progess = document.querySelector('.progress'); const? proSpan =? document.querySelector('span'); const? dragBox = document.querySelector('.box');? btn.onclick = function(){ //拿到文件 let files =? InpFile.files; //這是一個(gè)類數(shù)組 //放到表單數(shù)據(jù)對(duì)象里面進(jìn)行序列化 //這個(gè)對(duì)象存放數(shù)據(jù)的格式是鍵=>值形式 //表單數(shù)據(jù)對(duì)象下面有一個(gè)append方法? let formObj = new FormData();? for(fileitem of files){ formObj.append('userfiles[]',fileitem); //第一個(gè)參數(shù)可寫可不寫 加個(gè)[]主要表示多個(gè)文件的上傳 }? //實(shí)例化一個(gè)ajax對(duì)象 let xhr =? new XMLHttpRequest();? xhr.open('post','fileupdate',true);? //上傳文件的post頭信息要求? //并且這樣設(shè)置后紧显,后端就能夠識(shí)別前端是不是 ajax上傳的文件 xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');? //設(shè)置進(jìn)度條 //xhr 對(duì)象 下面有個(gè)上傳監(jiān)聽事件 //其事件 對(duì)象下 可以找到? loaded total? 兩個(gè)的數(shù)值的 比值就可以算出上傳進(jìn)度 xhr.upload.onprogress = function(e){ let rate = e.loaded/e.total*100; progess.value = rate; proSpan.innerHTML = rate.toFixed(1)+'%';? }? xhr.send(formObj); xhr.load = function(){ console.log(xhr.responseText); } }? dragBox.ondragover = function(e){ e.preventDefault();//取消默認(rèn)行為 不僅要取消drop的? //很奇怪? 瀏覽器對(duì)拖拽文件下載或者打開 的默認(rèn)行為竟然發(fā)生over }? dragBox.ondrop = function (e){? ? e.preventDefault();? ? //拿到 那個(gè)在我頭頂?shù)奈募? ? //console.log(e); let files =? e.dataTransfer.files;? let formObj = new FormData(); for(fileitem of files){ formObj.append('userfiles[]',fileitem); //第一個(gè)參數(shù)可寫可不寫 加個(gè)[]主要表示多個(gè)文件的上傳 } let xhr =? new XMLHttpRequest();? xhr.open('post','fileupdate',true);? xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');? xhr.upload.onprogress = function(e){ let rate = e.loaded/e.total*100; progess.value = rate; proSpan.innerHTML = rate.toFixed(1)+'%';? }? xhr.send(formObj); xhr.load = function(){ console.log(xhr.responseText); }? }```- 封裝一個(gè)ajax函數(shù)```function ajax(opt){? ? let method = opt.method||'GET';? ? let url? = opt.url||'';? ? let send =? opt.send || null;? ? let dataType = opt.dataType || 'json'; // 要傳輸?shù)臄?shù)據(jù)類型? ? let resType = opt.resType||'json';? //? 希望接受到的數(shù)據(jù)類型? ? let succ = opt.succ || function(){};? ? let fail = opt.fail || function(){};? ? ? ? let xhr = new? XMLHttpRequest();? ? ? ? xhr.open(method,url,true);? ? ? ? if(method.toUpperCase()== 'POST'){? ? ? ? ? ? ? ? switch(dataType){? ? ? ? ? ? case 'json':? ? ? ? ? ? xhr.setRequestHeader('Content-type','application/json');? ? ? ? ? ? break;? ? ? ? ? ? case 'form':? ? ? ? ? ? xhr.setRequestHeader('Content-type','application/x-www.form/urlencoded');? ? ? ? ? ? break;? ? ? ? ? ? case 'file':? ? ? ? ? ? xhr.setRequestHeader('X-Requested-With','XMLHtppRequest')? ? ? ? ? ? break;? ? ? ? }? ? ? ? send(send);? ? }? ? ? ? xhr.onload = function(){? ? ? ? if((xhr.status>=200&& xhr.status<300 )|| xhr.status == 304){? ? ? ? ? ? ? ? ? let data =? xhr.responseText讲衫;? ? ? ? ? ? ? ? ? ? ? ? if(resType == 'json'){? ? ? ? ? ? ? ? data = JSON.parse(data);? ? ? ? ? ? ? ? }? ? ? ? ? ? succ(data);? ? ? ? }else{? ? ? ? ? ? fail(xhr.status);? ? ? ? }? ? ? ? ? ? ? ? }}```### 關(guān)于回調(diào)地獄:- 假設(shè)用戶發(fā)送請(qǐng)求后,得到回應(yīng)孵班,然后對(duì)數(shù)據(jù)進(jìn)行一大堆操作涉兽,然后繼續(xù)發(fā)送請(qǐng)求,然后重復(fù)第一步,一直重復(fù)篙程。枷畏。。呵呵```? ? xhr.onload = function(){? ? ? ? ? ? if(status >=200 && status<300){? ? ? ? ? ? success1(xhr.responseText)? ? ? ? }? ? }? ? ? ? let success1 = function(data){? ? ? ? data = .....? ? ? ? //ajax發(fā)送? ? ? ? ? ? ? ? xhr.onload = function(){? ? ? ? ? ? success2(xhr.responseText)? ? ? ? }? ? }? ? ? ? let success2.....? ? ? ? //如果一步出現(xiàn)錯(cuò)誤房午。矿辽。那就GG```###Promise:> - 提醒:一個(gè)去了解一個(gè)新的對(duì)象丹允,從兩個(gè)方面入手 - 對(duì)象原型的屬性和方法 - 對(duì)象本身的靜態(tài)方法* promise:是異步編程的一種解決方案郭厌,比傳統(tǒng)的解決方案回調(diào)函數(shù)和事件更合理和更強(qiáng)大。所謂promise雕蔽,簡(jiǎn)單說就是一個(gè)容器折柠,里面保存著某個(gè)未來才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果。* 這個(gè)結(jié)果有三種狀態(tài) pedding為默認(rèn)狀態(tài)批狐、Resolved(已完成扇售,又稱Fulfilled)和Rejected(已失敗)嚣艇。* 狀態(tài)的轉(zhuǎn)換 pedding=>>Resolved 或者pedding=>>Rejected 承冰,狀態(tài)轉(zhuǎn)換后就固定下來* 定義一個(gè)promise實(shí)例```var promise = new Promise( (resolve,reject)=>{? ? ..dosometing? ? if(succ){? ? ? ? resolve(responseData);? ? }else{? ? ? ? reject(error)? ? }})// 1. Promise 實(shí)例化時(shí)接受一個(gè)回調(diào)函數(shù)作為參數(shù);// 2. 這個(gè)回調(diào)函數(shù)有兩個(gè)參數(shù)食零,分別為resolve 和 reject困乒,他們倆都是函數(shù);// 3. resolve函數(shù)的作用就是,把這個(gè)實(shí)例的promise狀態(tài)由pedding變?yōu)閞esolved贰谣,然后執(zhí)行這個(gè)函數(shù)娜搂,將異步操作的結(jié)果作為參數(shù)傳出去;// 4. reject函數(shù)的作用就是迁霎,把這個(gè)實(shí)例的promise狀態(tài)由pedding變?yōu)閞ejected,然后執(zhí)行這個(gè)函數(shù)百宇,將異步操作的錯(cuò)誤作為參數(shù)傳遞出去;```> 主要用到的方法1.這個(gè)對(duì)象的原型上面的方法? -then? -catch 2. 這個(gè)對(duì)象本身的靜態(tài)方法-Promise.resolve-Promise.all- then方法 - then方法可以分別指定Resolved狀態(tài)和Rejected狀態(tài)的回調(diào)函數(shù)``` var promise = new Promise( (resolve,reject)=>{? ? ..dosometing? ? if(succ){? ? ? ? resolve(responseData);? ? }else{? ? ? ? reject(error)? ? } }) promise.then( (val)=>{},(err)=>{}) //Resolved狀態(tài)和Rejected狀態(tài)的回調(diào)函數(shù) //并且可以接受返回的參數(shù) //then這個(gè)行為是一個(gè)異步行為考廉,promise響應(yīng)后才能運(yùn)作 //第二個(gè)參數(shù) 可有可無,也可以用后面講到的catch方法代替? ```- then 運(yùn)行后返回的是一個(gè)新的promise實(shí)例;? ? - 這個(gè)新的promise實(shí)例還比較特殊携御;? ? - 這個(gè)then里頭沒有return 東西 或者 return 的不是一個(gè)promise實(shí)例,那個(gè)then返回的實(shí)例就是一個(gè)默認(rèn)狀態(tài)為resolved的實(shí)例昌粤,并且resolve函數(shù)返回的參數(shù)是上一個(gè)then返回的內(nèi)容;? ? - 如果then里頭return 了一個(gè)promise實(shí)例 , 那么then的返回結(jié)果就是這個(gè)指定的promise,如果后面再鏈接上一個(gè)then,需要等到這個(gè)指定promise狀態(tài)的改變才能運(yùn)行新的then;? ? - 上面講的情況都是基于第一個(gè)promise是Resolved狀態(tài)的時(shí)候啄刹,那么如果是Rejected狀態(tài)的時(shí)候,就不走then了婚苹,此時(shí)then返回的promise就和它的調(diào)用者promise一模一樣,這就可以解釋為什么.then后面可以.catch。```var pom =? new Promise( (res,rej)=>{res(true);})var pom2 =? pom.then((val)=>{console.log(val);})console.log(pom2);//這樣沒有return 的話? pom2 就是 Resolved? value:undefined 的promisevar pom2 =? pom.then((val)=>{console.log(val);return 1}console.log(pom2);//這樣return 1的話? pom2 就是 Resolved? value:1pom.then((val)=>{console.log(val);return 1? ? ? ? }).then( (val)=>{? ? ? ? ? ? console.log(val);? ? ? ? })//在后面再用then添加回調(diào)的話回立馬執(zhí)行鸵膏,并且val 為1 因?yàn)樵損romise的狀態(tài)為Resolved``````//另外一種情況是返回一個(gè)指定的promise對(duì)象var pom =? new Promise( (res,rej)=>{res(true);})var pom2 =? pom.then((val)=>{var pom3 = new Promise((res,rej)=>{setTimeout(()=>res(false) ,2000) ;})return? pom3;})console.log(pom2);//一開始狀態(tài)是pedding//兩秒后狀態(tài)變?yōu)镽esolved 并且value為fasle//證明返回的promise對(duì)象就是pom3//所以如果在后面繼續(xù)鏈接上then的話不會(huì)立馬執(zhí)行膊升,而是等它調(diào)用對(duì)象的狀態(tài)改變才能執(zhí)行,這樣的話作鏈?zhǔn)降膶懛ㄌ菲螅⑶铱梢宰龅揭粋€(gè)異步事件執(zhí)行完之后才去執(zhí)行另外一個(gè)廓译,不要以前的金字塔式的回調(diào)寫法,并且可以統(tǒng)一捕捉錯(cuò)誤信息```- catch方法- 用于處理promise異步操作不成功的方法```let promise? //這是一個(gè)實(shí)例化的promisepromise.then((succ)=>{},(fail)=>{})? // 可以轉(zhuǎn)換成promise.then((succ=>{}).catch((fail)=>{})债查;//如果類似錯(cuò)誤處理都差不多非区,那么推薦這種寫法promise.then().then().then().catch();//還有一點(diǎn)需要主要的是是catch()同樣返回一個(gè)promise對(duì)象,后面可以繼續(xù)then catchpromise.then().catch().then();var promise = new Promise((res,rej)=>{? ? }) promise.then((val)=>{? ? console.log(y)}).catch((error)=>{? ? })```- Promise.all -? Promise.all方法就是是接受多個(gè)Promise對(duì)象,然后包裝成一個(gè)新的Promise實(shí)例 -? 關(guān)于參數(shù),接受一個(gè)數(shù)組[p1,p2,p3]盹廷,里面每一項(xiàng)是一個(gè)Promise實(shí)例征绸,如果不是Promise實(shí)例則會(huì)使用后面后面講到的Promise.resolve轉(zhuǎn)化為Promise,當(dāng)然也不一定是數(shù)組,只要具有Iterator接口也可以 -? 關(guān)于狀態(tài),當(dāng)里面的實(shí)例狀態(tài)全部都完成俄占, 才變?yōu)閒ulfilled狀態(tài)管怠,然后返回一個(gè)由實(shí)例返回的值組成的數(shù)組,通過then回調(diào)可以拿到缸榄。只要有其中一個(gè)實(shí)例的狀態(tài)為rejected渤弛,返回的就是rejected狀態(tài),并且返回該錯(cuò)誤甚带。 ``` //一個(gè)圖片加載實(shí)例 //就是一組圖片加載完 同時(shí)顯示在頁面上? function loadImg(src){? return new Promise((res,rej)=>{? ? var img = new Image ()? ? img.src = src;? ? img.onload = function(){? ? ? ? res(img)? ? }? ? img.onerror = function(){? ? ? ? rej(new Error('圖片加載失敗'))? ? } } Promise.all([loadImg(src1), loadImg (src2),loadImg (src3)]).then((val)=>{? ? for(item of val){? ? ? ? document.body.appendChild(item)? ? }? })? ? ```- Promise.race- 跟Promise.all一樣她肯,同樣是將一組實(shí)例包裝成一個(gè)新的實(shí)例- 關(guān)于狀態(tài),只要有其中一個(gè)狀態(tài)改變了鹰贵,整個(gè)實(shí)例的狀態(tài)也就跟著改變晴氨,返回的值也是最先改變狀態(tài)的返回值- 一個(gè)fetch請(qǐng)求例子```var p = Promise.race([? fetch('/resource-that-may-take-a-while'),? new Promise(function (resolve, reject) {? ? setTimeout(() => reject(new Error('request timeout')), 5000)? })])p.then(response => console.log(response))p.catch(error => console.log(error))//上面代碼中,如果fetch5秒之內(nèi)方法無法返回結(jié)果碉输,p變量的狀態(tài)就會(huì)變?yōu)閞ejected籽前,從而觸發(fā)catch方法指定的回調(diào)函數(shù)。```- Promise.resolve - 有時(shí)我們有這樣的需求,把現(xiàn)有的對(duì)象轉(zhuǎn)化Promise對(duì)象聚假,這就需要用到Promise.resolve. `` var jsPromise = Promise.resolve($.ajax('/whatever.json')); `` - 上面的代碼就是把Jquery生成的deferred對(duì)象块蚌,轉(zhuǎn)化為一個(gè)新的Promise對(duì)象。 -? Promise.resolve 等價(jià)于下面的寫法膘格。 > Promise.resolve('foo')// 等價(jià)于new Promise(resolve => resolve('foo'));- 關(guān)于Promise.resolve的使用大致分為四種情況:1.參數(shù)傳一個(gè)Promise實(shí)例峭范,則原封不動(dòng)地返回這個(gè)Promise實(shí)例 2.參數(shù)是一個(gè)thenable對(duì)象3.參數(shù)不具有then方法,或者根本不是對(duì)象瘪贱,則返回一個(gè)Resolved狀態(tài)的新實(shí)例纱控,返回值為傳入的Promise.resolve()的原始值4.不帶參數(shù)Promise.resolve(),直接返回一個(gè)狀態(tài)為resolved新的Promise ```thenable對(duì)象:var theable? = {? ? then: function(res,rej){? ? ? ? res(true);? ? }}var p1 = Promise.resolve(thenable);等價(jià)于:var p1 = new Promise((res,req)=>{? ? res(true);})沒有then方法或者不是一個(gè)對(duì)象var p1 =? Promise.resolve('hello');p1.then((val)=>{? ? console.log(val)? //'hello'})```###跨域問題:#### Ajax 跨域:- CORS 時(shí)默認(rèn)只支持「application/x-www-form-urlencoded」- 如果服務(wù)端跨域是CORS處理的話,就不能提交json格式了- jquery ajax默認(rèn)提交的編碼格式是「application/x-www-form-urlencoded」- 而Augular 默認(rèn)提交的格式是[ application/json]- 所以還是看服務(wù)端如何處理- 也可以了解一下啊nginx 反向代理 - 那么現(xiàn)在就有兩個(gè)問題 :如何跨域上傳json數(shù)據(jù) 和 跨域上傳文件#### Jsonp跨域:- 原理很簡(jiǎn)單 - 第一步 : 動(dòng)態(tài)創(chuàng)建一個(gè)script 標(biāo)簽- 第二步? : src 指向請(qǐng)求地址,并且 把需要傳續(xù)的內(nèi)容附帶在queryString菜秦,最重要的是 cb/callback = 你自定義并且掛載在window的函數(shù)- 第三步? :? 后端拿到queryString 返回一個(gè) `函數(shù)名(data)` , 相當(dāng)于執(zhí)行請(qǐng)求方掛載在window上的函數(shù) 并且把數(shù)據(jù)當(dāng)作參數(shù)傳到里面去- 第四步? :? 清除script就可以了- jsonp 只能使 get 方法#### 封裝一個(gè)Ajax + Jsonp方法:```//調(diào)用? ? fetch({data:{? q:val,? start:0,? count:100},? ? ? ? type: 'jsonp',? ? ? ? ? url: `https://api.douban.com/v2/book/search`,? ? ? ? timeout: 3000,? ? ? ? cb: 'callback'? ? ? }) //返回一個(gè)實(shí)例? ? ? .init() // 返回一個(gè)promise實(shí)例 init 還接受一個(gè)參數(shù) 指定回收的數(shù)據(jù)類型? ? ? .then((data) => {? ? ? }).catch((err)=>{? ? ? })? ? ? ? ? fetch({type:'ajax', data:{},url:'/fileupdate',dataType:'form',? //form file jsonmethod:'post' }) .init()? ? .then...//function fetch(opt){let {type} = opt;if(type === 'ajax'){? return new CreatAjax(opt);}if(type === 'jsonp'){? return new CreatJsonp(opt);}}class CreatAjax{constructor(opt){if( typeof opt.url == 'undefined'){throw new Error('url must be included at opt');}this.method = 'get';this.data =? {};this.dataType = 'json';Object.assign(this,opt);return this;}init(acceptType = ''){let {url,data,method,dataType} = this;let dataForm? = formData(data);method = method.toLowerCase();dataType = dataType.toLowerCase();acceptType = acceptType.toLowerCase();return Promise.resolve({then(resolve,reject){ const xhr = new XMLHttpRequest();if(method === 'get'){url += `?`+ dataForm;}xhr.open(method,url,true);if(method === "post"){switch(dataType){case 'json':xhr.setRequestHeader('Content-Type', 'application/json');xhr.send(JSON.stringify(data));break;case 'form':xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');xhr.send(dataForm);break;case 'file':xhr.setRequestHeader('X-Requested-With','XMLHttpRequset');xhr.send(data);break;}}else{xhr.send();}xhr.onload = function(){let {status,responseText} = this;if((status>=200&&status<300) ||? status == 304 ){resolve( acceptType== 'json'? JSON.prase(responseText):responseText );}else{reject(status);}}}})}}class CreatJsonp{constructor(opt){if(typeof opt.url === 'undefined '){throw new Error('url must be included ad opt');};this.method = 'get';this.cb = 'cb';this.timeout = 10000;this.data = {};Object.assign(this,opt);return this;}init(acceptType = ''){let {url,cb,data,timeout} = this;dataType = dataType.toLowerCase();return? Promise.resolve({then(res,rej){let callBackName = 'cb' + Date.now();let script = document.createElement('script');? ? data[cb]? =? callBackName;data =? formData(data);url +=? '?' + data ;script.setAttribute('src' , url) window[callBackName] = function(data){? ? //在window下面掛載 這個(gè)函數(shù)res(acceptType==='json'? JSON.parse(data):data);delete window[callBackName];document.body.removeChild(script)? //清除遺留的方法和標(biāo)簽clearTimeout(script.timer);}script.timer = setTimeout(()=>{rej( new Error('Server do not response'));delete window[callBackName];document.body.removeChild(script)? //清除遺留的方法和標(biāo)簽},timeout)document.body.appendChild(script);//這里已經(jīng)就發(fā)送請(qǐng)求了,返回的就是 執(zhí)行這個(gè)函數(shù)甜害,然后把數(shù)據(jù)當(dāng)成 參數(shù)傳到里面}})}}function formData(data){let query = [];for(let key in data ){query.push( `${ encodeURIComponent(key) }=${ encodeURIComponent(data[key]) } `)}return query.join('&');}```