相关概念


1、什么是同源请求,什么是跨域请求?

客户端和服务器进行数据交互请求时,请求协议、请求的url地址、请求的端口,这三项设定如果都相同称为同源请求,这三项设定如果有一项不同称为跨域请求。

2、相对路径都是同源请求,可以获取响应体数据。

3、服务器为了保护数据安全,只允许同源请求获取响应体数据,如果是跨域请求,不能正确的获取响应体数据,会有跨域访问报错。

解决跨域的三种方法


一、cors

后端程序提供的解决跨域访问的方法,和前端程序没有任何关系。

定义后端程序代码让所有的前端程序都可以访问后端的数据,后端程序告诉服务器这个后端程序的数据,任何前端请求都可以访问,服务器就不再执行跨域访问拦截数据的操作。如下所示:

<?php

// PHP的cors代码

// 开启 跨域
header("Access-Control-Allow-Credentials: true");
// 允许任意的url访问这个文件
header("Access-Control-Allow-Origin: *");
// 设定允许请求的方式 
header("Access-Control-Request-Methods:GET, POST, PUT, DELETE, OPTIONS");
// 设定允许的请求的格式
header('Access-Control-Allow-Headers:x-requested-with,content-type,test-token,test-sessid');


echo '我是PHP程序返回的响应体数据';

二、服务器代理 proxy

我目前使用的是 nginx 配置服务器代理,找到 配置文件 一般是 扩展名是 conf 的文件。

设定服务器代理 的步骤:

1、在配置文件的server{}中,一般是在最下方定义服务器代理

2、必须严格遵守语法规范

示例:

location /php1 {

       proxy_pass 'http://localhost/bk_2123/week6_day01/04_proxy/demo.php';

}

设定好之后一定要重启服务器

3、定义ajax请求

使用代理名称/php1执行跨域请求

const response = await myPromiseAjax('/php1' , 'get');


查找数据接口

查找数据接口就是找其他网页获取数据的接口地址,理论上我们找到其他网页获取数据的接口地址,发送请求我们也可以获取到对应的数据结果;但是实际项目中其他网页的数据接口地址往往会有数据保护,会限制其他请求不能获取数据结果,只有没有数据保护的数据接口我们才能获取到数据。

查找数据接口的方法:

1、在network中 找 ajax或者js 类型的请求。

2、刷新页面或者滚动页面发起新的请求。

3、查看请求的响应体数据 和 显示内容对应的 就是 数据接口请求。

三、jsonp

基本原理:

使用script标签src属性值,完成跨域请求。script标签src属性,定义导入加载的外部文件的路径地址,也就是同时src属性可以导入加载外部文件。服务器对于src要导入的文件,如果路径是跨域路径,服务器本身不会阻止;也就是src的路径地址,如果是跨域请求的路径地址,服务器也不会阻止,可以正常加载导入文件。

对于src属性导入的外部文件,不管是什么类型的文件,都会按照JavaScript语法执行外部文件中定义输出的内容,也就是只要导入的文件内容符合JavaScript语法,就会按照JavaScript程序执行。

总结jsonp完成跨域的原理:

1、服务器对于src的跨域请求不会阻止。

2、script标签src导入的外部文件,内容/输出 都会按照JavaScript语法程序执行。

jsonp和proxy完成跨域的比较


proxy

核心原理:

通过服务器代理将跨域的ajax请求执行成同源的ajax请求,有ajax参与执行。就是将一个跨域的ajax请求,通过服务器代理,代理成同源请求,完成数据交互。

步骤过程:

1、定义服务器代理,通过代理名称定义跨域的url地址。

2、定义ajax请求,使用代理名称来执行跨域的url地址。

jsonp

核心原理:

通过script标签src属性来导入执行跨域的请求,没有使用ajax技术。

script标签src导入的外部文件,文件中的内容都会按照JavaScript程序来执行。

在后端程序中调用前端定义的函数程序,后端程序调用前端函数时,输入后端操作数据获取的数据结果作为实参。

步骤过程:

1、定义js程序、定义变量、定义函数,函数必须有一个形参存储后端程序输入的实参数据

函数程序的内容是操作这个形参,也就是操作后端返回的数据。

2、定义一个script标签,标签src属性的属性值是跨域请求的url地址,同时携带参数数据,有一个参数一定是调用函数的函数名称。

实际项目中,使用jsonp还是proxy是根据后端程序决定的

如果后端程序返回的响应体结果是数据数值/json字符串,一般是使用proxy。

如果后端程序返回的响应体结果是函数调用或者参数中有callback  cb  传参函数名称,一般是使用jsonp。

瀑布流


瀑布流又称瀑布流式布局,是比较流行的一种网站页面布局方式。视觉表现为参差不齐的多栏布局。简单的理解就是,当页面要显示到底部时,再次发起新的请求,获取新的响应体数据,再次动态渲染生成新的页面内容。

原理:

页面上卷距离 + 视窗窗口高度 + 预留高度 > 最矮ul占位高度

需要再次发起请求,获取新的响应体数据,再次动态渲染生成新的页面内容

JavaScript中 函数的节流


scroll事件 鼠标滚动一次 触发多次函数执行,多次触发函数执行,执行的都是相同的内容。

节流的目的就是:同时触发多次函数执只执行第一次请求。

方法:定义类似于开关的效果

定义一个变量存储默认值,触发执行函数之前,先判断变量存储的数据;如果是原始值,变量赋值新的数据;如果不是原始值,执行return终止之后程序的执行。

JavaScript中 函数的防抖


同时触发多次函数执行,执行最后一次请求,多次触发函数执行,执行的是不同的内容

实现原理:

通过延时器延迟事件执行程序,先清除延时器,再触发定义新的延时器执行函数程序。

每次触发生成新的延时器之前,先清除上一次生成的延时器。

函数的节流和函数的防抖,核心目的是减少服务器请求次数,减轻服务器压力。

案例一


此案例运用了以下知识点 :服务器代理、瀑布流布局,函数的节流

其逻辑:

逻辑:

1、html css
有 4个ul 每次生成 一个 li 写入 ul 中,因为图片的高度不同,写入 4个ul中 最矮的ul 。
图片 的 url地址是一个 网址,如果通过服务器运行 默认不能正确显示图片,需要在 head标签中 添加 meta标签:<meta name="referrer" content="never">

数据接口的数据

data:
imit: 24

 一共有24条数据
more: 1

next_start: 24

下一次请求 起始数据的索引下标

object_list: (24) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
当前24条数据的数组

total: 10000

status: 1

提前定义好 存储img标签的div 标签的高度
div标签的高度 可以根据 图片高度定义

 div的宽      图片的宽
   ------- === --------
 div的高      图片的

2、动态生成页

给 button标签 添加点击事件 
(1) 获取/设定 数据 

获取 点击的button标签的内容

设定 查询的起始数据的索引下标 默认是 0

(2) 设定 4个空ul标签

获取 4个ul标签

(3) 发送ajax请求

3-1 创建 时间对象 获取 时间戳

3-2 使用 await 发送 ajax请求
      请求地址 是 服务器代理名称/dt
      请求方式 是 设定的 get方式
      携带参数 是 设定的 携带参数数据
      分类名称关键词 
      查询起始数据的索引下标
      时间戳

3-3 根据响应体结果 设定数据动态渲染生成页面

      设定 下一次请求 起始数据的索引下标

      获取 24条数据的数组 根据数组 动态渲染生成页面
      定义 li标签 
      提前定义 存储img图片的div标签的高度
      根据 数组中存储对象的数据 设定标签的内容
      如果 点赞收藏的数量是0 不显示点赞收藏标签

      找最矮的ul标签
      默认第一个ul是最矮的ul
      从第二个ul开始循环遍历
      如果循环ul占位小于存储的ul占位
      变量存储 循环的ul标签对象
      循环结束 变量中存储 最矮的ul标签对象

      对 最矮的ul标签 拼接写入 li 标签

      3-4 给 变量 赋值原始数据
      可以再次发起新的请求 
                                       

3,  再次发起请求 

定义 页面滚动监听事件
获取 占位高度 
页面上卷的占位高度
浏览器视窗窗口的占位高度
预留高度

最矮ul的占位高度

执行判断
上卷高度 + 视窗窗口高度 + 预留高度 > 最矮ul占位高度

先执行判断 防止发起多次重复的ajax请求
    判断 变量 存储的数据 
    如果是 原始值 给变量赋值新的数值
    如果是 新的数值 执行 return 终止之后函数的调用

    发起请求 获取新的响应体数据 
    再次动态渲染生成新的页面内容

            瀑布流的核心
                1,  根据 ajax请求的响应体结果 动态渲染生成页面

                    请求地址 请求方式 参数键名 都是 后端程序定义的
                    前端 只能根据需求 定义 携带的具体的参数数据

                    响应体数据是后端程序返回的数据结果 
                    只能获取数据结果 不能修改数据结果

                    可以根据 响应体数据结果 动态渲染生成页面内容
                        可以使用 三元运算符 给标签定义属性等

                2,  瀑布流 再次 发起请求的判断一句

                    上卷高度 + 视窗窗口高度 + 预留高度 > 最矮ul占位高度

                3,  函数的节流

                    同时触发 多次执行 相同的函数程序
                    只需要触发执行 第一次 函数程序的调用 

                    原理:
                        定义一个 开关变量 

                        变量储存原始数据 

                        执行判断 
                            如果 变量 存储原始数据  变量赋值其他数据
                            如果 变量 存储其他数据  执行 return 终止之后程序的执行

                        当 函数的所有程序都触发执行结束 
                        变量 赋值原始值 可以再次触发 新的函数

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- 服务器加载网络图片的meta标签 -->
    <meta name="referrer" content="never">

    <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> 
        *{
            margin: 0;
            padding: 0;
        }

        .btn{
            width: 300px;
            margin : 50px auto;
        }

        ul,ol,li{
            list-style: none;
        }

        img{
            display: block;
            width: 100%;
            height: 100%;
        }

        a,a:hover,a:active{
            text-decoration: none;
        }

        .box{
            width: 1200px;
            margin: 0 auto;
        }

        .box::after{
            display: block;
            content:"";
            clear:both;
        }

        .box ul{
            width: 260px;
            float:left;
            margin: 0 20px;
        }

        .box ul li{
            width: 100% ;
            display: flex;
            flex-direction: column;
            border: 2px solid #000;
            margin: 0 0 15px 0;
        }

        .box ul li .imgBox{
            width: 100%;

        }

        .box ul li .contentBox{
            width: 100%;
            padding: 15px;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
        }

        .box ul li .contentBox p{
            display: flex;
            justify-content: flex-start;
            align-items: center;
        }

        .box ul li .contentBox p:nth-child(1){
            margin: 10px 0;
        }

        .box ul li .contentBox p:nth-child(2){
            margin: 10px 0;
        }

        .box ul li .contentBox p:nth-child(2) span{
            margin: 0 10px 0 0 ;
        }

        .box ul li .contentBox p:nth-child(2) span i{
            margin: 0 10px 0 0 ;
        }


        .box ul li .contentBox p:nth-child(3) span:nth-child(1){
            display: block;
            width: 40px;
            height: 40px;
            border-radius: 50%;
            overflow: hidden;
            margin : 0 10px 0 0 ;
        }

    </style>

</head>
<body>
    <div class="btn"> 
        <button>美食菜谱</button>
        <button>美妆造型</button>
        <button>家居生活</button>
        <button>人物明星</button>
    </div>

    <!-- div中的内容是 动态生成的 -->
    <div class="box"></div>


    <script src="../../ajax.js"></script>
    <script>
        // 定义全局变量 存储相关的数据信息
        let start;
        let time;
        let keyword;

        // 获取父级div标签
        const oBox = document.querySelector('.box');

        // 定义全局变量 存储 要使用的数据
        let oUls ;
        let flag = true ;


        // 获取 所有的button按钮
        const oBtns = document.querySelectorAll('button');
        // 循环遍历 给 所有的button标签 添加 点击事件
        oBtns.forEach( item => {
            item.addEventListener('click' , function(){
                // 默认第一次显示的是从索引下标是0开始的第一条数据
                start = 0 ;

                // 获取关键词
                keyword = this.innerHTML;

                // 向 div标签中写入 4个ul空标签
                oBox.innerHTML = "<ul></ul><ul></ul><ul></ul><ul></ul>";

                // 获取 所有的ul标签对象 
                oUls = oBox.querySelectorAll('ul');

                // 调用执行 async await 和 promise执行的 ajax请求
                setPage();
            })
        })

        // 定义 页面滚动监听事件
        window.addEventListener( 'scroll' , function(){
            // 获取 占位数据数值

            // 页面上卷高度
            let scrollTop = document.documentElement.scrollTop;

            // 视窗窗口占位高度
            let winHeight = document.documentElement.clientHeight;

            // 设定预留高度
            let height = 500 ;

            // 获取最矮ul标签对象
            // 循环结束 minUl 变量中 存储 最矮ul标签对象
            let minUl = oUls[0];
            for( let i = 0+1 ; i <= oUls.length-1 ; i++ ){
                // 如果 minUl 变量中 存储的 ul标签 高度 大于 oUls[i]循环遍历的ul标签的占位 
                // 变量minUl 中 存储 oUls[i] 循环遍历的ul标签 
                if( minUl.offsetHeight > oUls[i].offsetHeight ){
                    minUl = oUls[i] ;
                }
            }

            // 执行判断
            //瀑布流布局 
            if( scrollTop + winHeight + height > minUl.offsetHeight ){
                // 再次发起请求 动态渲染生成新的页面内容

                /*
                    因为 scroll事件 鼠标滚动一次 多次触发 scroll事件
                    也就是 鼠标滚动一次 多次触发执行 setPage() 函数 
                    也就是 鼠标滚动一次 多次触发执行 新的 ajax请求 

                    因为 ajax请求是异步程序 结果就是 多次触发的ajax请求 执行的是相同的请求参数
                    获取的 响应体结果 是 相同的响应体结果
                
                    实际项目中 多次请求 只需要执行 第一次请求
                
                
                */

                //JavaScript节流的方法
                // 判断变量储存的数据 防止多次调用函数
                if( flag ){
                    flag = false ;
                }else{
                    return ;
                }

                // 再次调用函数 发起新的请求 动态渲染生成新的页面
                setPage();
            }
        })


        // 使用 async 定义 异步请求函数程序
        async function setPage(){
            // 获取 时间对象 和 时间戳
            const t = new Date();
            time = t.getTime();

            // 发起请求时 根据需求 设定 ajax请求携带的参数数据
            // response中存储的是响应体数据 
            const response = JSON.parse( await myPromiseAjax( '/dt' , 'get' , `include_fields=top_comments%2Cis_root%2Csource_link%2Citem%2Cbuyable%2Croot_id%2Cstatus%2Clike_count%2Csender%2Calbum%2Creply_count&filter_id=${keyword}&start=${start}&_=${time}`) );

 

            console.log( response );

            // 给下一次 请求 赋值起始数据的索引下标
            start = response.data.next_start ;

            // 获取 24条数据的数组
            const arr = response.data.object_list;

            // 循环遍历 数组中的24个单元 
            arr.forEach( item => {
                // 根据数组单元的数据 生成 li标签
                let str = `
                    <li>
                    
                    <div class="imgBox" style="height:${ 260 * item.photo.height / item.photo.width }px">
                        <img src="${item.photo.path}" alt="">
                    </div>

                    <div class="contentBox">
                        <p>${item.msg}</p>

                        <p>
                            <span style="${ item.like_count === 0 ? 'display:none': '' }">
                                <i>点赞</i>${item.like_count}
                            </span>
                            <span style="${ item.favorite_count === 0 ? 'display:none': '' }">
                                <i>收藏</i>${item.favorite_count}
                            </span>
                        </p>

                        <p>
                            <span>
                                <img src="${item.sender.avatar}" alt="">
                            </span>

                            <span>
                                ${item.sender.username} <br> 
                                发布到 <a href="JavaScript:;">${item.album.name}</a>
                            </span>
                        </p>
                    </div>
                </li>`;
            
                // 每生成一个li 就要拼接写入最矮的ul标签中

                // 获取最矮的ul标签 
                let minUl = oUls[0];
                for( let i = 0+1 ; i <= oUls.length-1 ; i++ ){
                    // 如果 minUl 变量中 存储的 ul标签 高度 大于 oUls[i]循环遍历的ul标签的占位 
                    // 变量minUl 中 存储 oUls[i] 循环遍历的ul标签 
                    if( minUl.offsetHeight > oUls[i].offsetHeight ){
                        minUl = oUls[i] ;
                    }
                }
                // 循环结束minUl 中 存储的是 最矮的ul标签对象
                // 向 最矮的ul标签中 拼接写入 li标签
                minUl.innerHTML += str ;
            })
            
            // 当请求执行结束 当新的页面内容生成完毕 
            // 可以再次发起新的请求 给 变量赋值原始数据
            flag = true ;
        }

    </script>
</body>
</html>

案例二


 此案例运用了:jsonp、函数的防抖

<!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>
        *{
            margin: 0;
            padding: 0;
        }

        ul,ol,li{
            list-style: none;
        }

        .box{
            width: 500px;
            margin: 50px auto;
            position: relative;
        }

        .box input{
            width: 400px;
            padding: 0 0 0 15px;
        }

        .box ul{
            width: 400px;
            padding:0 0 0 15px ;
            position: absolute;
            left:0;
            top:22px;
            display: none;
        }
    </style>
</head>
<body>
    <div class="box">
        <input type="text"><button>百度一下</button>
        <ul></ul>        
    </div>

    <script>
        // 获取标签对象
        const oIpt = document.querySelector('input');
        const oUl = document.querySelector('ul');

        // 定义一个变量 存储 延时器
        let tt = 0;

        // input标签获取和失去焦点 
        // 暂时就执行一个简单的 显示 / 隐藏 ul标签

        // 给 input标签 添加 获取焦点事件
        // 获取焦点时 ul 标签显示
        oIpt.addEventListener('focus' , function(){
            oUl.style.display = 'block';
        })

        // 给 input标签 添加 失去焦点事件
        // 失去焦点是 ul 标签隐藏
        oIpt.addEventListener('blur' , function(){
            oUl.style.display = 'none';
        })

        // 给 input标签 添加输入数据事件
        oIpt.addEventListener('input' , function(){

            // 直接调用使用函数 
            // 每次输入数据都会触发事件
            // setPage() ;
            
            
            // 函数的防抖 

            /*
                每次 input输入框 输入新的数据时 

                先 清除 异步池中存储的上一次input输入数据时 触发的延时器
                也就是 清除了 上一次输入数据的搜索请求

                最终就只会执行 最后一次 触发的 延时器
                搜索的数据也就是最后一次输入的 关键词
            
            */

            // 先清除 延时器
            clearInterval( tt );

            // 定义延时器 延迟执行 函数程序 发送请求 搜索关键词
            tt = setTimeout( function(){
                
                // 延时器 延迟一秒 执行程序
                setPage() ;

            } , 1000 );

        })


        // 定义函数 执行 jsonp 动态生成对应的页面内容
        function setPage(){
            // 获取 input标签 输入的数据 也就是 搜索的关键词
            let keyword = oIpt.value ;

            // 定义时间对象 获取时间戳
            const t = new Date();
            let time = t.getTime();

            // 定义 script标签节点
            const s = document.createElement('script');

            // 设定 script标签节点 src 属性
            // wd 是 关键词 
            // _  是 时间戳
            // cb 是 函数名称
            s.setAttribute( 'src' , `https://www.baidu.com/sugrec?pre=1&p=3&ie=utf-8&json=1&prod=pc&from=pc_web&sugsid=35834,35106,31253,35488,34584,35490,35872,35801,35797,35317,26350,35885,35746&wd=${keyword}&req=2&csor=3&pwd=11&cb=fun&_=${time}` );

            // 将 script标签节点 写入 body标签的最下方
            // 写入script标签 会 同时 导入 src路径的外部文件
            // 同时 就会 立即执行其中的程序代码
            document.body.appendChild( s ); 

            // 执行完的script标签 可以 直接删除了
            document.body.removeChild( s );
            
        }

        // 定义一个函数 也就是 jsonp传参给后端程序执行的函数程序
        function fun( response ){
            // 响应体结果 存储在 形参 response 中
            console.log( response );

            // 根据形参 response 中的数据 动态渲染生成ul标签中的li标签
            
            // 执行判断 
            // 如果 有 搜索结果 response.g 是 一个 10个单元的数组
            // 如果 没有 搜索结果 response.g 是 undefined
            if( response.g === undefined ){
                // 没有匹配的搜索结果 
                // 让ul标签隐藏
                oUl.style.display = 'none';

            }else{
                // 让 ul标签显示
                oUl.style.display = 'block';

                // 有匹配的搜索结果 
                // 根据 response.g 中 的 q 属性存储的数据 
                // 动态渲染生成 li标签和li标签内容
                let str = '';
                response.g.forEach( item => {
                    str += `<li>${item.q}</li>` ;
                })
                oUl.innerHTML = str ;
            }

        }


    </script>

</body>
</html>

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐