JavaScript之Ajax(四):同源跨域和JSONP的实现过程

1、同源策略和跨域
(1)同源
如果两个页面协议、域名端口都相同,则两个页面具有相同的源。
同源策略是浏览器的一个安全功能。
MDN给出的解释,同源策略限制了一个源加载的文档或者版本如何与来自呬个源的资源进行交互,这是一个用于隔离潜在恶意文件的重要安全限制。
通俗的说,浏览器规定,A网站的JS,不允许和非同源的网站C之间,进行资源交互,例如,
无法读取非同源网页的cook、localStorage、indexDB等;
无法接触非同源网页的DOM;
无法向非同源网址发送ajax请求。
(2)跨域
白龙网认为,跨域是URL的协议、域名、端口不一致,出现跨域的原因。是浏览器同源策略不允许非同源的URL之间进行资源交互。
浏览器驿跨域请求的拦截,浏览器允许发起跨域请求,跨域请求回来的数据,会初被浏览器的同源策略拦截,无法被页面获取到。
如何实现跨域数据请求,解决方案是JSONP。CORS,临时解决方案,缺点是只支持GET请求,不支持POST请求,但是可以兼容一些低版本的浏览器;CORS,支持GET/POST,缺点是存在兼容性。

2、JSONP

JSON的一种使用模式,由于浏览器同源策略的限制,网页中无法通过ajax请求非同源的接口数据,但是script标签不受浏览器同源策略的限制,可以通过scr属性,请求非同源数据。
因此,JSONP的实现原理,就是通过script标签的src属性,请求跨域的数据接口,并通过函数调用的形式,接收跨域接口回来的数据。
JSONP的实现,可以从以下三个方面理解:
同一个页面内,分成两个script标签,先定义函数,再调用函数。
在同一个页面内,在一个script标签内定义函数,并通过另外一个标签的scr属性调用本地js文件,JS文件内是调用函数内容。
在同一个页面内,在一个script标签内定义函数,在另外一个标签通过script标签的属性scr调用不同域名的js文件,但是要使用?callback=success指定函数名。
总而言之,JSONP的实现,首先要定义一个回调函数,然后通过script标签的src属性去请求JS文件,并且在scr中使用?ballback=函数名的形式指定函数名,服务器就会按钮约定函数响应对应数据。
(1)JSONP的实现
    <script>
        function success(data) {
            console.log('JSONP响应回来的数据');
            console.log(data);
        }
    </script>
    <script src="http://www.liulongbin.top:3006/api/jsonp?callback=success&name=zs&age=30"></script>
JSONP与Ajax没有任何关系,它也是ajax。
(2)发起JSONP请求
jQuery中的$.ajax()不但可以发起ajax请求,还可以发起JSONP请求。
    <script>
        $(function() {
            $.ajax({
                url: 'http://www.liulongbin.top:3006/api/jsonp?name=zs&age=20',
                //1.必须指定数据类型
                dataType: 'jsonp',
                //2.指定回调函数属性名
                jsonp: 'callbacks',
                //3.指定回调函数属性值
                jsonpCallback: 'cb',
                success: function(e) {
                    console.log(e);
                }
            })
        })
    </script>
(3)JQk JSONP的实现过程,
JQ腹胀的是动态创建和移除script标签的方式,来发起JSONP的数据请求。
在发起JSONP请求的时候,动态的向haeader中append一个script标签;在JSONP请求完成后,动态的从header中移除刚才append进去的script标签。
    <script>
       $(function() {
        $('#btn').on('click',function() {
            $.ajax({
                url: 'http://www.liulongbin.top:3006/api/jsonp?name=username&psd=password',
                dataType: 'json',
                success: function(e) {
                    console.log(e);
                }
            })
        })
       })
    </script>

3、案例:淘宝搜索-下拉列表功能实现

防抖策略debounce,是当事件被触发后,延迟n秒后执行回调,如果在n秒内事件又被触发,则重新计时。
防抖的应用场景,在用户输入完成之后,再去发起请求,提升浏览器性能,减少请求次数,节约服务器资源。如果不实行防抖策略,会导致每次按下键盘都会发起请求。
<!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="./css/search.css" />
  <!-- 导入 jQuery -->
  <script src="./lib/jquery.js"></script>
  <!-- 导入模板引擎 -->
  <script src="./lib/template-web.js"></script>
</head>
<body>
  <div class="container">
    <!-- Logo -->
    <img src="./images/taobao_logo.png" alt="" class="logo" />
    <div class="box">
      <!-- tab 栏 -->
      <div class="tabs">
        <div class="tab-active">宝贝</div>
        <div>店铺</div>
      </div>
      <!-- 搜索区域(搜索框和搜索按钮) -->
      <div class="search-box">
        <input id="ipt" type="text" class="ipt" placeholder="请输入要搜索的内容" /><button class="btnSearch">
          搜索
        </button>
      </div>
      <!-- 搜索建议列表 -->
      <div id="suggest-list"></div>
    </div>
  </div>
  <!-- 渲染一个模板 -->
  <script type="text/html" id="tpl-suggestlist">
    {{each result}}
    <!-- 模板生成的标签添加到id为suggest-list的div标签内部 -->
    <div id="suggest-item">{{ $value[0] }}</div>
    {{/each}}
  </script>
  <script>
   $(function() {
    //防抖1-1防抖策略,定义一个延时器
    var timer = null;
    //本地缓存1-1:定义一个缓存对象
    var cacheobj = {};
    //防抖1-2:定义防抖的函数
    function debounceSearch(kw) {
      setTimeout(function() {
        getSuggestlist(kw);
      }, 500);
    }
    //监听按键输入
    $('#ipt').on('keyup',function() {
    //清空定时器
    clearTimeout(timer);
     var keywords = $(this).val().trim();
    //  当搜索框内关键词为空时清空建议列表并隐藏
     if(keywords.length <= 0) {
      return $('#suggest-list').empty().hide();
     }
     //本地缓存1-3:先判断缓存中是否有数据
     if(cacheobj[keywords]) {
      return renderSuggestlist(cacheobj[keywords]);
     }
    //获取用户输入的关键词
    //  console.log(keywords);
    //  getSuggestlist(keywords);
    // 防抖1-3:调用
     debounceSearch(keywords);
    })
    //封装getSuggest函数,发起ajax请求
    function getSuggestlist(kw) {
      $.ajax({
        url: 'http://suggest.taobao.com/sug?q=' + kw,
        dataType: 'jsonp',
        success: function(res) {
        console.log(res);
        renderSuggestlist(res);  
        }
      })
    }
    //渲染UI结构
    function renderSuggestlist(res) {
      // 形参是rest,数组名是resutl
      if(res.result.length <= 0) {
        return $('#suggest-list').empty().hide();
      }
    var htmlstr = template('tpl-suggestlist',res);
    $('#suggest-list').html(htmlstr).show();
    //本地缓存1-2:把用户输入的内容当作对象的属性,把服务器响应的数据当作对象的属性值
    var k = $('#ipt').val().trim();
    cacheobj[k] = res;
    }
   })
  </script>
</body>
</html>

4、防抖和节流

节流策略,throttle,可以减少一段时间内事件触发的频率。
当鼠标连续不断的触发某个事件的时候,可以用节流保证单位时间触发一次。
懒加载要监听计算滚动条的位置,但是不必每次滑动都触发,可以降低计算的频率,而不必去浪费CPU资源。
节流阀,高铁上的红绿灯就是一个节流阀。如果节流阀为空,则可以执行下一次操作,如果不为空,则不能执行下一次操作。当前操作执行完了,则必须把节流阀重置为空。每次执行操作前,必须判断节流阀是否为空。
<!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>
    <script src="./js/jquery-3.6.0.min.js"></script>
</head>
<style>
    html,body {
        padding: 0;
        margin: 0;
        overflow: hidden;
    }
#angle {
    position: absolute;
}
</style>
<body>
   <img src="./angel.gif" alt="" id="angle">
   <script>
    $(function() {
        var angel = $('#angle');
        //节流阀1-1:定义一个节流阀
        var timer = null;
        $(document).on('mousemove',function(e) {
        //1-3判断节流阀不为空,则不执行下面的操作
        if(timer) {return}
        //节流阀1-2:开启节流阀
        timer = setTimeout(function() {
            $(angel).css('left',e.pageX + 'px').css('top',e.pageY + 'px');
            console.log('ok');
            timer = null;
        },50)
    })
    })
   </script>
</body>
</html>
防抖,能保证只有一次触发生效,前面N多次的触发被忽略;
节流,能减少事件触发的频率,有选择性的执行一部分事件。