页面局部刷新的里程碑:AJAX

AJAX

Async JavaScript And XML:异步的JS+XML

请求回顾

1
2
3
4
5
6
7
8
请求行:GET / index.html HTTP/1.1
请求头:Host:1.2.3.4
Accept: html, xhtml, xml
User-Agent: Chrome / Mac
回车
消息体:username=xxxx&password=yyyyy&c=zzzzz
---------
常用的请求方法:POST\DELETE\PUT、PATCH\GET\HEAD\TRACK\CONNCET\OPTIONS

发请求的方法

  1. 地址栏回车

  2. 发起特定类型的请求:

    • 图片发起请求:<img src=/xxx>

      请求头里:Accept: image/webp,image/,/* 期待接收图片

    • js发请求:<script src=/xxx

      请求头里:Accept:test/javascript, */*

    • css发请求:<link rel=stylesheet href=/xxx>

      请求头里:Accept:test/css,*/*

  3. form表单发起请求:<form action='/xxx' method=GET (Accept:*/*)

缺点:1、3请求方法会刷新页面并替换当前页面内容,2请求方法只能请求特定类型资源

局部更新

要求:发一个请求,这个请求去更新页面里的局部内容,而不刷新整个页面。

  1. js改变地址栏(更新了整个页面,不符合要求)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    前端:
    <button id="button">click</button>
    <div>空的</div>
    <script>
    button.onclick=function(){
    location.href = '/xxx'
    }
    </script>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    服务器端:
    var http = require('http')
    var fs = require('fs')
    var url = require('url')
    //console.log(Object.keys(http))
    var port = process.env.PORT || 8888;
    var server = http.createServer(function(request, response){
    var temp = url.parse(request.url, true)
    var path = temp.pathname
    var query = temp.query
    var method = request.method
    //从这里开始看,上面不要看
    if(path === '/'){ // 如果用户请求的是 / 路径
    var string = fs.readFileSync('./index.html')
    response.setHeader('Content-Type', 'text/html;charset=utf-8')
    response.end(string)
    }else{
    response.statusCode = 404
    response.setHeader('Content-Type', 'text/html;charset=utf-8')
    response.end('找不到对应的路径,你需要自行修改 index.js')
    }
    // 代码结束,下面不要看
    console.log(method + ' ' + request.url)
    })
    server.listen(port)
    console.log('监听 ' + port + ' 成功,请用在空中转体720度然后用电饭煲打开 http://localhost:' + port)

  2. 发一个图片请求(可以局部发请求,但是无法获取响应的数据内容,不符合要求)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    前端:
    <button id="button">click</button>
    <div>空的</div>
    <script>
    button.onclick=function(){
    var img = new Image()
    img.src = '/xxx'
    }
    </script>
    1
    2
    3
    4
    5
    6
    7
    服务器端:
    ......
    if(path === '/xxx'){
    response.end('这是xxx的内容')
    }else{
    response.end('404')
    }

  3. CSS发请求(同样的,无法拿到响应中的内容)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    前端:
    <body>
    <button id="button">click</button>
    <div>空的</div>
    <script>
    button.onclick=function(){
    var link = document.createElement('link')
    link.rel='stylesheet'
    link.href='/xxx'
    link.onload = ()=>{
    console.log('link.innerHTML')
    console.log(link.innerHTML) //无法打印出里面的响应内容
    }
    document.head.appendChild(link)
    }
    </script>
    </body>

    1. JS发请求

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <body>
      <button id="button">click</button>
      <div id="output">空的</div>
      <script>
      button.onclick=function(){
      var script = document.createElement('script')
      script.src = '/xxx'
      document.head.appendChild(script)
      }
      </script>

      报错:js请求的路径是’/xxx’,那么路径对应的内容因该是合法的js字符串;而我们返回的是’here is xxx’字符串,所以报错。(但是浏览器把它当成js解析了,这是转机)

      • 修改路径内容,使之能被js那样被解析

        1
        2
        3
        4
        if(path === '/xxx'){
        response.setHeader('Content-Type','text/plain;charset=utf-8')
        response.end('output.innerText="here is xxx"')
        //或者一个随机数response.end(`output.innerText=${Math.random()}`)

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        前端:
        <script>
        window.callback = function(){
        console.log(arguments[0]) //打出第一个参数
        }
        button.onclick=function(){
        var script = document.createElement('script')
        script.src = '/xxx'
        document.head.appendChild(script)
        }
        </script>
        1
        2
        3
        4
        5
        服务器端:
        if(path === '/xxx'){
        response.setHeader('Content-Type','text/plain;charset=utf-8')
        var user = 'name:tom,age:18'
        response.end(`window.callback.call(null,"${user}")`)

      • 变换

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        前端:
        <body>
        <button id="button">click</button>
        <div id="output">空的</div>
        <script>
        window.callback = function(data){
        var array = data.split(',') //["name: tom "," age: 18""] //字符串转成数组
        for(var i=0;i<array.length;i++){ //遍历数组
        var parts = array[i].split(":") //["name"," tom "]
        var dl = document.createElement("dl")
        var dt = document.createElement('dt')
        dt.textContent = parts[0]
        var dd = document.createElement('dd')
        dd.textContent = parts[1]
        dl.appendChild(dt)
        dt.appendChild(dd)
        output.appendChild(dl)
        }
        }
        button.onclick=function(){
        var script = document.createElement('script')
        script.src = '/xxx'
        document.head.appendChild(script)
        }
        </script>
        1
        2
        3
        4
        5
        服务器端:
        else if(path === '/xxx'){
        response.setHeader('Content-Type','text/plain;charset=utf-8')
        var user = 'name: tom , age: 18'
        response.end(`window.callback("${user}")`)

      总结:

      • 发起请求:创造一个script放入页面,script请求一个路径
      • 服务器响应:回调一个函数,这个函数对参数(数据)进行处理分解,构造成有结构的html页面
    2. 检测请求是否正确

      • 前端:onerror和onload

        1
        2
        3
        4
        5
        6
        7
        8
        9
        //前端出错:可以用onerror来提示:status只要不是2xx就触发onerror
        button.onclick=function(){
        var script = document.createElement('script')
        script.onerror = ()=>{
        alert('不好意思,出错了')
        }
        script.src = '/xxxx' //请求路径出错
        document.head.appendChild(script)
        }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        后端出错:可以用onload来提示前端请求没问题;只要status是2xx,就触发onload
        button.onclick=function(){
        var script = document.createElement('script')
        script.onload = ()=>{
        alert('请求没问题,后端出错了')
        }
        script.src = '/xxxx' //请求路径出错
        document.head.appendChild(script)
        }

      • 局限性:不知道具体哪里错误,不知道错误码。

XHMHttpRequest

XMLHttpRequest 是一个API, 它为客户端提供了在客户端和服务器之间传输数据的功能。它提供了一个通过 URL 来获取数据的简单方式,并且不会使整个页面刷新。

  • 在不重新加载页面的情况下更新网页
  • 在页面已加载后从服务器请求数据
  • 在页面已加载后从服务器接收数据
  • 在后台向服务器发送数据
  • 前身:微软的ActiveX对象,提供给ie的接口(帮你发请求,并且把请求的响应用字符串的形式响应 给你)
  • 火狐跟进:XMLHttpRequest ,用这个对象发请求,并得到响应,并且不会对整个页面刷新。
  1. 实例化:var request = new XMLHttpRequest()

  2. 方法:

    • abort():如果请求已经被发送,则立刻终止请求
    • getAllResponseHeaders():返回所有响应头信息(响应头名和值)
    • getResponseHeader():返回指定的响应头的值
  3. 请求:

    请求行 - open(method,ulr,async):request.open('GET' , '/xxx?wd=s',true)(查询字符串写在url里)

    请求头 - setRequestHeader(header,value):setRequestHeader('tom','18')

    消息体 - send():发送请求send('44444')

  • 1
    2
    3
    4
    5
    6
    7
    8
    <script>
    button.onclick = ()=>{
    var request = new XMLHttpRequest()
    request.open('POST','/xxx?aa=b')
    request.setRequestHeader('tom','18')
    request.send('sfsdf')
    }
    </script>

  1. 响应:

    • 获取响应头:request.getAllResponseHeaders()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <script>
    button.onclick = ()=>{
    var request = new XMLHttpRequest()
    request.open('POST','/xx x?aa=b')
    request.setRequestHeader('tom','18')
    request.send('sfsdf')
    request.onload = ()=>{
    console.log(request.getAllResponseHeaders())
    }
    request.onerror = ()=>{
    console.log(request.getAllResponseHeaders())
    }
    }
    </script>

    • 获取响应消息体(第四部分):request.responseText / window.eval(request.responseText)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      <script>
      button.onclick = ()=>{
      var request = new XMLHttpRequest()
      request.open('POST','/xxx?aa=b')
      request.setRequestHeader('tom','18')
      request.send('sfsdf')
      request.onload = ()=>{
      console.log(request.responseText) //打印出来文本
      window.eval(request.responseText) //eval可以执行字符串,即执行了响应的第四部分
      }
      }
      </script>

    • 获取响应第一部分:状态行

      1
      2
      状态码 request.status //200
      状态码描述 request.statusText //OK
  2. 精确检查响应的状态:onreadystatechange ,只要readyState变化,这个函数就会被调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    request.onreadystatechange = function(){
    console.log(request.readyState) //01234个值
    if(request.readyStatus === 4){ //响应下载完毕
    if(request.status >=200 && request.status <300){
    console.log('成功')
    }else{
    console.log('失败')
    }
    }
    }
    //request.onload //响应下载完毕,而且status是2XX
    //request.onerror //1.响应出错(未发出、 网络等),2.下载完毕但是status是4xx 5xxx

小结

ajax总共有4个过程 ,

  1. 创建ajax对象:const ajax = new XMLHttpRequest()

  2. 连接到服务器: ajax.open(method,url,async)

  3. 发送请求: ajax.send()

  4. 接收返回的信息:

    1
    2
    3
    4
    5
    6
    7
    8
    ajax.onreadystatechange=function(){
    ajax.readyState
    //0 未初始化,还没调用open方法
    //1 载入,已经调用send()方法,正在发送请求
    //2 载入完成,send()方法完成,已经收到全部相应内容
    //3 解析,正在解析响应内容
    //4(普遍是4) 完成,响应内容完成解析(可能成功,可能失败,用ajax.status来判断。成功时候可以用ajax.responseText来获取响应的内容
    }