複習 - ajax

Heymar發表於2022-11-23

複習呢有一個很直觀的感受,就是以前學的東西,萌懂半懂的,這一來全部都清楚了,你以前以為你學的並不好但是複習一次把以前的案例一做,居然能夠自己獨立完成,知識點看著掌握的還不錯。

1.

兩天時間就把整個ajax複習完了,一天目前還暫時做不到哈哈,確實還是有很多案例都要思考一會,直接從案例下手吧,一個圖書管理的案例,在這個案例裡面吧就是用三個介面來獲取圖書,增加圖書,刪除圖書,在js方面沒多大問題,在html方面,還讓我多熟悉了下vscode快速程式設計bootstrap,直接bs3,form-inline類名可以讓每個表單項為行內塊元素,table-hover可以增加表格的每一行懸停效果。

獲取圖書列表:封裝為一個函式,透過ajax發起get請求,然後把拿到的資料透過foreach迴圈出來。

增加圖書列表:也是發起一起請求,然後要重新獲取一下列表
file

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./lib/bootstrap.css">
    <style>
        .panel-body {
            text-align: center;
        }
        .input-group {
            width: 30%;
            margin:  0 10px;
        }
        .panel {
            width: 90%;
            margin: 0 auto;
        }
        .table {
            width: 90%;
            margin: 15px auto 0;
        }
    </style>
</head>

<body>
    
    <div class="panel panel-primary">
          <div class="panel-heading">
                <h3 class="panel-title">新增新圖書</h3>
          </div>
          <!-- 1.新增了一個類名form-inline 可使裡面的表單元素變成行內塊元素 -->
          <div class="panel-body form-inline">
               <div class="input-group ">
                   <div class="input-group-addon">書名</div>
                   <input type="text" class="form-control bookname" id="exampleInputAmount" placeholder="請輸入書名">
               </div>
               <div class="input-group">
                <div class="input-group-addon">作者</div>
                <input type="text" class="form-control author" id="exampleInputAmount" placeholder="請輸入作者">
            </div>
            <div class="input-group">
                <div class="input-group-addon">出版社</div>
                <input type="text" class="form-control publisher"  id="exampleInputAmount" placeholder="請輸入出版社">
            </div>
               <button type="button" class="btn btn-primary">新增</button>
          </div>
    </div>
    
    <!-- 2.table-borderred可以為每一個表單元素增加邊框 table-hover 新增滑鼠滑過表單的懸停狀態 -->
    
    <table class="table table-bordered table-hover">
        <thead>
            <tr>
                <th>id</th>
                <th>書名</th>
                <th>作者</th>
                <th>出版社</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            
        </tbody>
    </table>
    
    

    <script src="./lib/jquery.js"></script>
    <script>
        /* $.get('http://www.liulongbin.top:3006/api/getbooks', res => {
            // console.log(res);
            if (res.status !== 200) return console.log(獲取資料失敗);
            let htmlStr = ''
            let str = ''
            res.data.forEach(item => {
                htmlStr = `<tr>
                <td>${item.id}</td>
                <td>${item.bookname}</td>
                <td>${item.author}</td>
                <td>${item.publisher}</td>
                <td><a href="javascript:;">刪除</a></td>
            </tr>`
            str += htmlStr
        })
        document.querySelector('tbody').innerHTML =  str
        }) */
        // 3.上面我自己的做的方法固然可以但是這裡既然是用的jq那就用jq的方法來實現
        function getBook() {
            $.ajax({
            method : 'get',
            url : 'http://www.liulongbin.top:3006/api/getbooks',
            success : res => {
                if (res.status !== 200) return alert('獲取資料失敗')
                // jq的迴圈方法
                // 注意這裡的i不能省略
                let arr = []
                $.each(res.data, (i, item) => {
                    arr.push(`<tr>
                <td>${item.id}</td>
                <td>${item.bookname}</td>
                <td>${item.author}</td>
                <td>${item.publisher}</td>
                <td><a href="javascript:;" data-id="${item.id}" class="del">刪除</a></td>
            </tr>`)
                })
                $('tbody').empty().append(arr.join(''))
        }})
        }
        getBook()
        // 刪除圖書模組
        function delBook() {
            // 4.刪除圖書也要用到一個介面
           // 4.1注意這裡的jq的事件委託,在後代選擇器這裡,不管是寫id還是class還是標籤都不再需要$符號直接引號寫上來即可
           $('tbody').on('click', '.del' , function() {
                    // 這裡點誰就會觸發誰,用到了事件委託,說明現在每個a標籤上也有點選事件了他們就是事件的呼叫者
                    // attr這個方法可以設定可以獲取屬性的值,刪除用removeAttr
                     let id = $(this).attr('data-id')
                     console.log(id);
                     $.get('http://www.liulongbin.top:3006/api/delbook', {id : id}, res => {
                         if (res.status !== 200) return alert('刪除失敗')
                         // 刪除成功重新整理一下表格
                         getBook()
                     })
                })
        }
        delBook()
        // 新增圖書模組
        function addBook() {
            $('.btn').on('click', function() {
                $.post('http://www.liulongbin.top:3006/api/addbook',{
                    bookname : $('.bookname').val(),
                    author : $('.author').val(),
                    publisher : $('.publisher').val()
                }, res => {
                    console.log(res);
                    if (res.status !== 201) return alert(res.msg)
                    getBook()
                    return alert(res.msg)
                })
            })
        }
        addBook()
    </script>
</body>
</html>

2.

第二個是一個聊天機器人的案例,以前也說過這些案例,只是這次來做感想又深入了一步,這個案例分為了三步,先是把自己發的訊息能夠渲染到聊天介面,然後新增一個resetui重置右側捲軸的函式,可以讓聊天介面跟著我們的剛發的訊息走,然後把獲取機器人回覆訊息封裝為一個函式,具體裡面是透過我們發的訊息為一個引數然後去獲取請求,會給你傳回來一個引數,同時可以把這個引數以機器人回覆的姿態渲染到聊天介面,最後就是語音播放功能,把機器人的訊息作為一個引數封裝一個函式,html增加一個audio標籤,src就為我們這次請求的src,要加上網頁的一個根路徑哦
file

 <!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" />
    <link rel="stylesheet" href="css/reset.css" />
    <link rel="stylesheet" href="css/main.css" />
    <script type="text/javascript" src="js/jquery-1.12.4.min.js"></script>
    <script type="text/javascript" src="js/jquery-ui.min.js"></script>
    <script type="text/javascript" src="js/jquery.mousewheel.js"></script>
    <title>聊天機器人</title>
  </head>

  <body>
    <div class="wrap">
      <!-- 頭部 Header 區域 -->
      <div class="header">
        <h3>小思同學</h3>
        <img src="img/person01.png" alt="icon" />
      </div>
      <!-- 中間 聊天內容區域 -->
      <div class="main">
        <ul class="talk_list" style="top: 0px;">
       
          
        </ul>
        <div class="drag_bar" style="display: none;">
          <div
            class="drager ui-draggable ui-draggable-handle"
            style="display: none; height: 412.628px;"
          ></div>
        </div>
      </div>
      <!-- 底部 訊息編輯區域 -->
      <div class="footer">
        <img src="img/person02.png" alt="icon" />
        <input type="text" placeholder="說的什麼吧..." class="input_txt" />
        <input type="button" value="發 送" class="input_sub" />
      </div>
    </div>
    <audio src="" autoplay></audio>
    <!-- <script type="text/javascript" src="./js/scroll.js"></script> -->
    <!-- <script>
      $(function(){
        // 初始化右側捲軸
        // 這個方法定義在scroll.js中
        resetui()
      })
      
    </script> -->
    <!-- 1.下面兩個jq的js都是為了配合最後一個可以用resetui函式來初始化右側捲軸讓捲軸跟著螢幕內容走 -->
    <script src="./js/jquery-1.12.4.min.js"></script>
    <script src="./js/jquery-ui.min.js"></script>
    <script src="./js/jquery.mousewheel.js"></script>
    <script src="./js/scroll.js"></script>
    <script>
      // 先完成點選傳送新增訊息功能
      let content = ''
      $('.input_sub').on('click', function() {
        content = $('.input_txt').val()
        if (content.trim() == '') return
        $('.talk_list').append(`<li class="right_word">
            <img src="img/person02.png" /> <span>${content}</span>
          </li>`
      )
      $('.input_txt').val('')
      resetui()
      getBoot(content)
    })
    // 機器人回覆
    // 2.注意這裡機器人的回覆是怎麼做到的 是再點選後呼叫的這個函式
    function getBoot(text) {
      $.get('http://www.liulongbin.top:3006/api/robot',{spoken : text},res => {
      console.log(res);
      if (res.message == 'success') {
        $('.talk_list').append(`<li class="left_word">
            <img src="img/person01.png" /> <span>${res.data.info.text}</span>
          </li>`
      )
      }
      resetui()
      getSpeaker(res.data.info.text)
    })
    }
    // 語音回覆
    // 3.注意這裡也是需要被別人呼叫函式
    function getSpeaker(text) {
      $.ajax({
      method : 'get',
      url : 'http://www.liulongbin.top:3006/api/synthesize',
      data : {text : text},
      success : res => {
        if (res.status !== 200) return alert('獲取語音失敗')
        $('audio').attr('src', res.voiceUrl)
      }
    })
    }
    // 最後回車傳送訊息
    $('.input_txt').on('keyup', (e) => {
      // console.log(e.keyCode);
      if (e.keyCode == 13) {
        $('.input_sub').trigger('click')
      }
    })
    </script>
  </body>
</html>

3.

製作簡易版模板引擎,關於模板引擎這方面,只要牢牢記住他的一個規則,先匯入,然後定義資料,定義模板,再去呼叫,最後渲染,基本沒啥問題,然後定義模板那裡的一些標準語法等等

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

</head>
<body>
    <div></div>
    <!-- 2.模板 -->
    <script type="text/html" id="model">
        <div>我的名字:{{name}}</div>
        <div>我的年齡:{{age}}</div>
        <div>我的性別:{{sex}}</div>
        <div>我的家庭住址:{{address}}</div>
    </script>
    <script>
        // 3.封裝函式
        function template(id, data) {
            let str = document.getElementById(id).innerHTML
            // console.log(str);
            let reg = /{{([a-zA-Z]+)}}/
            let result = reg.exec(str)
            while (result !== null) {
                str = str.replace(result[0], data[result[1]])
                result = reg.exec(str)
            }
            return str
        }
    </script>
    <script>
        // 1.定義資料
        let data = {
            name : '王五',
            age : 18,
            sex : '男',
            address : '重慶市江北區'
        }
        // 4.呼叫
        let htmlStr = template('model', data)
        // 5.渲染
        document.querySelector('div').innerHTML =   htmlStr
    </script>
    
</body>
</html>

以下就是一個完整的模板引擎使用案例,在這個案例中我的幾個錯誤點:一個是如果你標準語法使用了迴圈後,在你的每一個迴圈的項裡面有一個字串你想給轉換成陣列,還記得你下面要獲取資料嗎,那麼就在這裡做一個迴圈,對每一條data資料裡面的字串,push進陣列,錯誤點二就是標準語法裡面關於圖片這一點,首先要知道凡事用到了標準語法那就必須{{}}包起來,因為你圖片還有一個根路徑嘛,所以也要一起包著進去。

<!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>Document</title>
    <link rel="stylesheet" href="./assets/news.css" />
    <script src="./lib/jquery.js"></script>
    <script src="./lib/template-web.js"></script>
  </head>
  <body>

    <div id="news-list">
      
    </div>

  </body>
  <script type="text/html" id="model">
    {{each data}}
    <div class="news-item">
      <!-- <img class="thumb" src="http://www.liulongbin.top:3006'+${{$value.img}}+'" alt="" /> -->
      <!-- 2.錯誤點二,凡是用到變數的地方都需要標準語法 這裡需要把整個都包起來 -->
      <img class="thumb" src={{'http://www.liulongbin.top:3006' + $value.img}} alt="" />
      <div class="right-box">
        <h1 class="title">{{$value.title}}</h1>
        <div class="tags">
          <!-- {{each {{tags}} | arrFormat}}
          <span>{{$value}}</span>
          {{/each}} -->
          <!-- 1.錯誤點一 這裡我想的太複雜了,不用過濾器來做,但是我認為這樣做應該可以只是不知道哪一步搞錯了,這裡有個更簡便的方法
          在獲取資料的時候就可以將每一個tags有字串改為陣列 -->
          <span>{{$value.tags[0]}}</span>
          <span>{{$value.tags[1]}}</span>
          <span>{{$value.tags[2]}}</span>
        </div>
        
        <div class="footer">
          <div>
            <span>{{$value.source}}</span>&nbsp;&nbsp;
            <span>{{$value.time | timeFormat}}</span>
          </div>
          <span>評論數:{{$value.cmtcount}}</span>
        </div>
      </div>
    </div>
    {{/each}}
  </script>
  <script>
    $.ajax({
      type : 'GET',
      url : 'http://www.liulongbin.top:3006/api/news',
      success : res => {
        if (res.status !== 200) return alert('獲取新聞列表失敗')
        console.log(res);
        for (let i = 0; i < res.data.length; i++) {
          // 1.1注意 這一換成陣列後需要將當前整個賦值給她
          res.data[i].tags = res.data[i].tags.split(',')
        }
        let htmlStr = template('model', res)
        $('#news-list').append(htmlStr)
      }
    })
    template.defaults.imports.timeFormat = function(value) {
      let date = new Date(value)
      let y = date.getFullYear()
      let m = addZero(date.getMonth() + 1)
      let d = addZero(date.getDate())
      let hh = addZero(date.getHours())
      let mm = addZero(date.getMinutes())
      let ss = addZero(date.getSeconds())
      return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
    }
    // 補零函式
    function addZero(n) {
      return n < 10? '0' + n : n
    }
  </script>
</html>

4.

然後封裝自己的ajax函式,用到原生js的xhr方法,在封裝裡面去判斷你是get還是post請求透過toUpperCase,包括xhr level2的一些新功能,設定HTTP時限,xhr.timout對應還有一個事件ontimeout,formdata表單管理可以拿來模擬表單資料,同樣也可以拿來獲取表單資料,第三個是可以上傳檔案了,關鍵步驟在於獲取到上傳檔案的表單元素後面跟一個.files就會得到一個上傳的檔案的陣列,跟第四個新特性組合起來就是有進度顯示,透過一個事件xhr.upload.onprogress裡面有三個e的屬性但是要注意這個時間必須寫到open和send函式之前,然後就是jq裡面的ajaxstart和ajaxend兩個事件。

然後就是jsonp,其原理就是透過script標籤不受同源策略限制而透過src發起的伺服器請求,把回到函式、引數都加進去,在jq裡面jsonp透過ajax方法來做,datatype改為jsonp。

一個案例來綜合展示jsonp、防抖和全域性快取,首先我們的關鍵字需要作為一個引數去獲取建議列表,然後再定義模板這裡迴圈res,裡面就寫一個標籤,因為裡面的value使我們陣列裡面的每一項,直接取第一個,然後會根據陣列的長度自動給你多少個標籤,渲染到頁面上

防抖就是會定義一個函式里面是一個定時器去執行我們獲取建議列表的函式,當你一觸發這個事件,首先會清除這個定時器然後在執行定時器函式,你想想我如果設定個時間50ms,那麼我們打字的速度,肯定是比這個還快的,所以你一直在輸入他也一直在清楚定時器,定時器就一直沒有執行,當你停下來了,這個時候正常執行定時器,獲取到我們的建議列表,下面會有兩個圖來詳細展示一下區別,有防抖和沒有防抖的。

然後就是全域性快取,就是我們的輸入一個apple再來一個mac,這個時候是兩個單詞吧,那我把mac一刪,難道又要去請求一次apple,那不就請求三次了嗎,我只請求兩次完成這個操作可以嗎,那就要用到全域性快取,定義一個全域性空物件,在我們渲染html那裡,空物件的屬性名為搜尋的關鍵字=為我們的res,然後當我們搜尋的時候先去判斷搜尋的關鍵字透過一個for in迴圈看看物件裡面有沒有這個關鍵字,如果有先執行快取裡面的res,就不再去請求伺服器了。

file
file

最後是一個節流策略,就是我們限制我們的觸發次數,跟防抖還是有本質區別的,透過一個節流閥,定義一個timer為null,進入這個事件給她定義為多少ms的定時器,進入這個事件先去檢測,timer是不是為空,如果不為空就return只有當為null才會去執行

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        img {
            position: absolute;
            left: 0;
            top: 0;
        }
        html,
        body {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <img src="./angel.gif" alt="">
    <script>
        let timer = null
        document.body.addEventListener('mousemove', (e) => {
            if (timer) return
           timer = setTimeout(() => {
            document.querySelector('img').style.left = e.pageX + 'px'
            document.querySelector('img').style.top = e.pageY+ 'px'
            console.log(11);
            timer = null
            // console.log(e.pageX);
           }, 15);
        })
    </script>
</body>
</html>