JS学习之——Ajax(136~160)

116 阅读8分钟

Ajax

优势
不需要插件的支持,原生js就可以用
用户体验好,不需要刷新页面就可以更新数据
减轻服务端和带宽的负担
缺点
搜索引擎的支持度不够
因为数据都不在页面上,搜索引擎搜不到

Ajax的使用

在js中有内置的构造函数来创建Ajax对象

创建Ajax对象以后,我们就使用Ajax对象的方法发送请求和接受响应

onreadystatechange
 //1.创建一个对象XHR  new XMLHttpRequesT()
    var xhr=new XMLHttpRequest()
    console.log(xhr)

    //2.配置 open(请求方式,请求地址,是否异步),录入我们的手机号
    xhr.open("GET","http://127.0.0.1:5500/01.js/1.txt")

    //3.send 发送出去,播出去
    xhr.send()

    //4.接受数据,注册一个事件,等结果
    //btn.onclick=function(){},不过这个需要自己点
    //下面这个不用
    xhr.onreadystatechange=function(){
      //console.log(xhr.readyState)
      //http的状态码在200~299都是成功的
      if(xhr.readyState===4 && xhr.status===200){
        console.log("数据解析完成",xhr.responseText)
      }else if(xhr.readyState===4 && xhr.status===404){
        console.error("没有找到这个页面")
        //location.href=""
      }
    }

onload

在第四步接受数据的时候,除了onreadystatechange方法之外,还有一个onload方法(它在4的时候才会进来)

  xhr.onload=function(){
      console.log(xhr.readyState)
      if(xhr.status===200){
        console.log(JSON.parse(xhr.responseText))
      }else if(xhr.status===404){
        console.error("没有找到这个页面")
      }
    }

image.png tip:字符串到对象,需要转一下 JSON.parse()

Ajax同步异步(第三个参数)

true表示异步请求

false表示同步请求

(同步时逐步执行,而异步是可以同时多个执行)

function会后执行

请求方式

1.get post put delete

get 偏向于获取数据
post 偏向于提交数据

put偏向于更新(全部)
delete 偏向于删除信息
patch偏向于部分修改,更新
header options connect(不常用)

实战

npm install json-server -g

json-server -v

json-server 文件(按tap键补全) --watch

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <button id="myget">get</button>
    <button id="mypost">post</button>
    <button id="myput">put</button>
    <button id="mypatch">patch</button>
    <button id="mydelete">delete</button>

    <script>
//get
        //绑事件
        myget.onclick = function () {
            var xhr = new XMLHttpRequest()
            xhr.open("GET", "http://localhost:3000/user")
             // 如果get需要传参,就这样写(?关键字,然后后面写你的参数)
            //xhr.open("GET", "http://localhost:3000/user?username=ximen")
            //回调
            xhr.onload = function () {
                if (xhr.status === 200) {
                    console.log(JSON.parse(xhr.responseText))
                } else if (xhr.status === 404) {
                    console.error("没有找到这个页面")
                }
            }
            //发送
            xhr.send()
        }

//post
        mypost.onclick = function () {
            var xhr = new XMLHttpRequest()
            xhr.open("POST", "http://localhost:3000/user")
            //回调
            xhr.onload = function () {
                if (/^2\d{2|$/.test(xhr.status)) {
                    console.log(JSON.parse(xhr.responseText))
                } else if (xhr.status === 404) {
                    console.error("没有找到这个页面")
                }
            }
            /*1.
            post的方法需要你把需要提交的信息放到send中
            ↓这个表示,传的信息是这种的(带来的格式):name=kerwin&age=100字符串
            xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
            xhr.send(`username=gangdaner&password=123`)*/

            //2.
            //↓这个表示,传的信息是这种的(带来的格式):{"name":"kerwin"}
            xhr.setRequestHeader("Content-Type", "application/json")
            xhr.send(JSON.stringify({
                username: "ximen",
                password: "789"
            }))
        }

//put(全部修改)
        myput.onclick = function () {
            var xhr = new XMLHttpRequest()
            //传id
            xhr.open("PUT", "http://localhost:3000/user/1")
            xhr.onload = function () {
                if (/^2\d{2|$/.test(xhr.status)) {
                    console.log(JSON.parse(xhr.responseText))
                } else if (xhr.status === 404) {
                    console.error("没有找到这个页面")
                }
            }
            xhr.setRequestHeader("Content-Type", "application/json")
            xhr.send(JSON.stringify({
                username: "ximen1111111"
            }))
        }
//patch(部分修改)
        mypatch.onclick = function () {
            var xhr = new XMLHttpRequest()
            xhr.open("PATCH", "http://localhost:3000/user/2")
            xhr.onload = function () {
                if (/^2\d{2|$/.test(xhr.status)) {
                    console.log(JSON.parse(xhr.responseText))
                } else if (xhr.status === 404) {
                    console.error("没有找到这个页面")
                }
            }
            xhr.setRequestHeader("Content-Type", "application/json")
            xhr.send(JSON.stringify({
                username: "ximen1111111"
            }))
        }
//delete
        mydelete.onclick = function () {
            var xhr = new XMLHttpRequest()
            xhr.open("DELETE", "http://localhost:3000/user/2")
            xhr.onload = function () {
                if (/^2\d{2|$/.test(xhr.status)) {
                    console.log(JSON.parse(xhr.responseText))
                } else if (xhr.status === 404) {
                    console.error("没有找到这个页面")
                }
            }
           xhr.send()
        }
    </script>
</body>

</html>

json上:

image.png

封装Ajax

  //封装
        ajax({
            url:"http://localhost:3000/user",//地址
            method:"GET",//请求方式
            async:true,//异步
            data:{
                username:"kerwin",
                password:"123"
            },//data传入的数据
            headers:{},
            //回调函数
            success:function(res){
                console.log(res)
            },
            error:function(err){
                console.log(err)
            }
        })

读代码(js:1111~5555)

get

.111111.

(这些在js文件中) image.png 写了上面那个之后,在html中需要引入一下

image.png

然后在html中传参并调用: image.png

.444444.

js中:

//发送请求——————————————————
        const xhr =new XMLHttpRequest()
        xhr.open(method,url,async)
        xhr.onload=function(){
            console.log(xhr.readyState)
            if (!/^2\d{2}$/.test(xhr.status)) {
                error('错误状态码:${xhr.status}')
           //!!!!回调↑,对应上面封装里的error
                return
            }
//执行解析——————————————————
//try catch即使解析错误,也不报红错(把返回来的值用json-parse来解析)
            try{
                let result=JSON.parse(xhr.responseText)
 //解析完后再用success回调
                success(result)
            }catch(err){
                error(`解析失败!因为后端返回的结果不是json格式字符串`)
            }
        }

上面在json中对success,error传了实参;所以这时候:要在html传形参res,err image.png 最后js中发送一下:

xhr.send()


post

如果是post的请求方式的话:

要写headers,headers与POST有关,里面传"Content-Type","application/x-www-form-urlencoded"这个或者另一个

.555555.

然后最后这样写

//设置请求头内的信息
for(let k in headers)xhr.setRequestHeader(k,headers[k])
xhr.send(data)

tip...js中 这是一个小函数,用于如果你的data中想传一个对象,把一个对象转换为key=value&key=value的形式

       function queryStringify(obj){
            let str=``
            for(let k in obj)str +=`${k}=${obj[k]}&`
            return str.slice(0,-1)
        }

如果是需要转的话,js中需要再补一个代码 data=queryStringify(data)

.222222.

判断一下你的data是对象吗

所以,综上,如果想把data对象形式转为post里面的两种形式的话,就可以写这段代码(直接一举两得)

 if(typeof data ===`object`&& headers["Content-type"]?.indexOf
        ("json")>-1){
            data=JSON.stringify(data)
        }else{
            data=queryStringify(data)
        }

你这个send(data)有局限性,这样的话你的post能用,但是get又用不了了,所以你可以这样写个小判断在js中

  if(/^get$/i.test(method)){
            xhr.send()
        }else{
            xhr.send(data)
        }

.333333.

如果是get请求,并且有参数,那么直接组装一下url信息 if(/^get$/i/test(method)&&data)url += '?'+data

回调地狱(嵌套金字塔)

test.json

{
  "news":[
    {"id":1,"title":"男人看了沉默,女人看了流泪","author":"kerwin"}
    ,
    {"id":2,"title":"震惊!他年薪仅有1元","author":"tiechui"}
  ],
  "comments":[
    {"id":1,"body":"我是男人","newsId":1},
    {"id":2,"body":"我是女人","newsId":1},
    {"id":3,"body":"我年薪2元","newsId":2},
    {"id":4,"body":"我年薪3元","newsId":2}
  ]
}

html中

<script src="util.js"></script>
    <script>
        //Ajax嵌套,也就是回调地狱(也就是回调函数嵌套过多导致)
        //tiechui已经登录
        ajax({
            url: "http://localhost:3000/news",
            method: "GET",
            data: {
                author: "tiechui"
            },
            success: function (res) {
                console.log(res[0])
                //拿tiechui的id对应的评论
                ajax({
                    url: "http://localhost:3000/comments",
                    data: {
                        newsId: res[0].id
                    },
                    success: function (res) {
                        console.log(res)
                    }
                })
            }
        })
   </script>

在JS中,当我们使用AJAX进行异步操作时,经常会遇到需要多个操作按顺序执行的情况比如你找一篇文章,就会有对应的很多评论,这些评论又会有一些对应的别的.....)

所以如果我们使用传统的回调函数来处理这种情况,代码会变得非常复杂,这就是所谓的“回调地狱”也就是回调函数嵌套过多导致)。

Promise

(是一种ES6的语法,并且是一个专门用来解决异步回调地狱问题)

111.基础语法

Promise主要有三种状态

pending(正在执行中)→fulfilled(成功)/reject(失败)

        //Promise构造函数
        //resolve,reject这两的参数,分别对应下面的then和catch函数
        var q = new Promise(function(resolve,reject){
            //这里放异步代码
            //resolve("win")
            //reject("error")

        })
         
        //q就是promise对象

        q.then(function(res){
            //兑现承诺,则then执行
            console.log("success",res)

        }).catch(function(err){
            //拒绝承诺,catch被执行
            console.log("fail",err)

        })

222.Promise封装Ajax(.then.then链式用法)

image.png js中

            function pajax(options) {
            return new Promise((resolve, reject) => {
                ajax({
                    ...options,//展开
                    success(res) {
                        resolve(res)
                    },
                    error(err) {
                        reject(err)
                    }
                })
            })
            //return q
        }
        /*pajax().then(function(){

        }).catch(err=>{

        })*/

async和await语法

(是一个ES7的语法,这个语法是回调地狱的终极解决方案)

async(异步)await(等待):更加优雅的异步编程的写法

await

  1. 也就是,它必须等await后面这个句子执行了,它才会继续往下执行

  2. 不过这个只针对于async内部的异步成同步,外部该怎么还怎么

  3. await后可以跟同步代码,但是没有意义,也就是写不写wait都是一个效果

  4. 除了同步代码,await后只能放promise对象

  <script type="module">
          import{pajax} from`./util.js`
          async function test(){
            //这个意思也就是,它必须等await后面这个句子执行了,它才会继续往下执行
            //不过这个只针对于async内部的异步成同步,外部该怎么还怎么
            //await后可以跟同步代码,但是没有意义,也就是写不写wait都是一个效果
            //除了同步代码,await后只能放promise对象
            var res = await pajax({
                url:"http://localhost:3000/news",
                data:{
                    author:"kerwin"
                }
            })
            //console.log(res)
            //再往下连着写就会构成链式
            var res1=await pajax({
                url:"http://localhost:3000/comments",
                data:{
                    newsId:res[0].id
                }
            })

             return res1
        }


        test() .then(res=>{
                console.log("返回结果",res)
            }).catch(err=>{
                console.log(err)
            })
            console.log(33333)
    </script>

问: 上面代码的catch捕获在async外,那如果现在就是要求你在async内实现,该怎么写呢?

答:用try-catch包住

 <script type="module">
          import{pajax} from`./util.js`
          async function test(){
        try{
            var res = await pajax({
                url:"http://localhost:3000/news",
                data:{
                    author:"kerwin"
                }
            })
            //console.log(res)
            //再往下连着写就会构成链式
            var res1=await pajax({
                url:"http://localhost:3000/comments",
                data:{
                    newsId:res[0].id
                }
            })
            console.log("success",res1)
        }catch(err){
            console.log("err",err)
        }
    }

        test() 
    </script>

fetch

  1. fetch兼容性不好

  2. 是为了取代XMLHttpRequest,而不是为了取代Ajax

  3. fetch不能直接使用catch,所以我们的应对方案是用一个if判断,并在else中使用拒绝承诺,然后catch才有用

get

联想截图_20231119031821.png

//默认是get请求
//fetch传参是拼到地址上的方式
var username="kerwin"
fetch('http://localhost:3000/user?username=${username}')
.then(res=>{
    console.log(res)
    if(res.ok){
         //拿数据,res.json返回的是一个promise对象
        return res.json()
    }else{
        //拒绝承诺
        return Promise.reject({
            //...准确告诉你哪里错了
        })
    }
})
.then(res=>{
    console.log("success",res)
}).catch(err=>{
    console.log("error",err)
})

其他同理,换汤不换药

image.png

cookie(本地存储)

  1. 本地存储功能(比如记住用户名,我们在之前用的是localStorage)

  2. cookie是浏览器关了就没有了,而localStorage可以“永久”存储


如何代替localStorage实现增删改查?

  1. 增(存)
<body>
    <button id="savebtn">存(增)</button>
    
    <script>
        savebtn.onclick=function(){
            //只能一条一条存
            //document.cookie="username=kerwin;path=/文件名"  key=value的格式;path路径(为了安全)
            document.cookie="age=18"
            
            //过期时间设置(到时间,这条数据就没了)
            var date=new Date()
            date.setMinutes(date.getMinutes()+1)
            document.cookie='username=kerwin;expires=${date.toUTCString()}'
        }
    </script>
</body>
  1. 查(获取) image.png
  2. 修(同名覆盖)

给删除键设置过期时间(也就是设置让它比当前时间还要少,就可以达到一点删除,就可以删了的效果)

image.png

cookie的特点

只能存文本,存不了对象 image.png

jsonp

【精选】JSONP 是什么?-CSDN博客

是json的一种“使用模式”,可以让网页从别的域名(网站)那获取资料,即跨域读取数据

同源策略: image.png 虽然同源策略在一定程度上提高了网站的安全,但也会给程序员带来一些麻烦,例如在访问一些开发接口时,由于同源策略的存在,会调用失败。要解决这种问题就需要用到跨域,跨域的方法有许多种,其中最经典的就是 JSONP。

解决:

image.png