Ajax笔记

1. 概述(了解)

1.1同步与异步概念

相关知识复习 (对于今天来说,不重要):

阻塞:在能开始做某件事件之前一直等着什么都不做,直到轮到你了就开始做要做的这件事情。

同步:自己亲自出马做这个事情

异步:让小弟帮你去做事情,做完了给反馈。

同步不一定阻塞。

  • 同步阻塞(BIO)。排队等吃饭,在轮到我之前,我什么都不做,一直盯着门口直到有位子可以坐下来吃饭。
  • 同步非阻塞(NIO)。排队等吃饭,不想干等着,就去逛街,但是每个1分钟回来看下有没有轮到自己。(轮询)
  • 只要是同步,必须是自己亲自做 -- 亲自排队。

异步肯定非阻塞。

  • 异步非阻塞(AIO):排队吃饭,在微信公众号上叫个号(相当于留了个微信),然后去逛街,到号了微信上直接通知你有位子了去吃饭。
  • 异步不需要自己亲自做,不用自己亲自排队,安排了微信公众号这个小弟帮你排队。

B/S结构中的同步和异步:

同步请求:浏览器在解析到网页代码某一行时,该行发起了一个请求;在服务器响应该请求前,浏览器什么都不能做,必须得等到请求完成返回数据之后,才会执行后续的代码;这期间浏览器处于假死状态

异步请求:浏览器在解析到网页代码某一行时发起请求后,浏览器可以继续执行后面的代码,继续做其他任何事情,而不用等待服务器响应。会有回调函数接收服务器响应数据并解析渲染。异步请求并不会影响页面的加载与用户的操作,相当于是在两条线上,各走各的,互不影响。

1597542593061

AJAX中的异步请求和局部刷新

AJAX:Asynchronous JavaScript and XML,异步的JavaScriptxml。现在应该叫(AJAJ)。

异步请求:用户对客户端的操作和服务器之间是异步非阻塞的。

局部刷新:把服务器响应的数据放在网页的某一区域刷新展示,而不会造成整个页面刷新。

体验好:不需要刷新整个页面,就不会有闪那一下;

效率高:因为可以只更新N条数据中的一条。

AJAX 不是新的编程语言,而是一种使用现有技术的新的用法。

2. 原生JS实现AJAX(了解)

知道原生JS操作的核心对象:XMLHTTPRequest

<body>
    <form autocomplete="off">
        姓名:<input type="text" id="username">
        <span id="uSpan"></span>
        <br>
        密码:<input type="password" id="password">
        <br>
        <input type="submit" value="注册">
    </form>
</body>
<script>
    //1.为姓名绑定失去焦点事件
    document.getElementById("username").onblur = function() {
        //2.创建XMLHttpRequest核心对象,使用原生js完成异步请求的核心对象
        let xmlHttp = new XMLHttpRequest();

        let username = document.getElementById("username").value;
        //3.打开链接
        // 第一个参数是请求方式  get/post
        // 第二个参数是  请求的url(get方式包含参数)
        // 第三个参数true表示异步,false表示同步
        xmlHttp.open("GET","userServlet?username="+username,true);
        //xmlHttp.open("GET","userServlet?username="+username,false);

        //5.设置监听,处理响应。原理上是应该先监听,后发送请求,这样服务器的响应肯定会被监听监听到并解析
        xmlHttp.onreadystatechange = function() {
            //判断请求和响应是否成功
            if(xmlHttp.readyState == 4 && xmlHttp.status == 200) {
                //将响应的数据显示到span标签
                document.getElementById("uSpan").innerHTML = xmlHttp.responseText;
            }
        };


        //4. 发送请求
        xmlHttp.send();
    }
</script>

3. jQuery实现AJAX

3.1 $.get()(重点)

<form autocomplete="off">
    姓名:<input type="text" id="username">
    <span id="uSpan"></span>
    <br>
    密码:<input type="password" id="password">
    <br>
    <input type="submit" value="注册">
</form>
</body>
<script src="js/jquery-3.3.1.min.js"></script>
<script>
    //1.为用户名绑定失去焦点事件
    $("#username").blur(function () {
        let username = $("#username").val();

        //2.jQuery的GET方式实现AJAX
        /*
            第一个参数   请求路径
            第二个参数   请求的参数
                支持json      {key:value}
                支持键值对     key=value
            第三个参数   回调函数
                用来处理服务器响应的数据的函数
                形参data      接收服务器响应的数据
            第四个参数   type  浏览器以何种形式解析服务器的响应数据
                text/不设置    浏览器以普通文本字符串的形式解析使用响应数据
                json          浏览器以json的形式解析使用响应数据,解析成js对象
                xml           浏览器以xml的形式解析使用响应数据,解析成document对象

         */
    	// $.ajaxSettings.Xxxx
        $.get(
            //请求的资源路径
            "userServlet",
            //请求参数
            "username=" + username,
            //回调函数
            function (xxxx) {
                //将响应的数据显示到span标签
                $("#uSpan").html(xxxx);
            },
            //响应数据形式
            "text"
        );
    });
</script>

3.2 $.post()(重点)

<body>
    <form autocomplete="off">
        姓名:<input type="text" id="username">
        <span id="uSpan"></span>
        <br>
        密码:<input type="password" id="password">
        <br>
        <input type="submit" value="注册">
    </form>
</body>
<script src="js/jquery-3.3.1.min.js"></script>
<script>
    //1.为用户名绑定失去焦点事件
    $("#username").blur(function () {
        let username = $("#username").val();
        // let username = document.getElementById("username").value;
        // 2.jQuery的POST方式实现AJAX
        /*
            和get方式请求的区别:
                1. 使用方法是$.post
                2. 请求资源路径后不能跟请求参数,请求参数只能放在post方法第二个参数位置
            其他没有任何区别

         */
        /*
            如果想在post请求或者get请求的时候指定更多参数
            可以在$.post/$.get发起请求之前,通过$.ajaxSettings.xxx设置更多参数
        */
        // $.ajaxSettings.Xxxx
        $.post(
            //请求的资源路径
            "userServlet",
            //请求参数
            "username=" + username,
            //回调函数
            function (data) {
                //将响应的数据显示到span标签
                $("#uSpan").html(data);
            },
            //响应数据形式
            "text"
        );
    });
</script>

3.3 $.ajax({json串})(了解)

/*
$.ajax({key:value,...});  // 所有参数通过json格式传递
​        url:请求的资源路径。
​        async:是否异步请求,true-是,false-否(默认是true)。
​        data:发送到服务器的数据,可以是键值对形式,也可以是js对象形式。
​        type:请求方式,POST或GET(默认是GET)。
​        dataType:返回数据的类型,取值可以是xml,html,js,json,text等,告诉浏览器以该格式解析。
​        success:请求成功时调用的回调函数,后跟匿名函数对象。
​        error:请求失败时调用的回调函数,后跟匿名函数对象。

	书写麻烦,不推荐使用。
*/

`$.get()`和`$.post()`同样可以实现。
只需要在调用这两个方法前,通过`$.ajaxSettings.xxx= yyyy`;

4. Json格式转换

4.1 Json概念

Json是一种轻量级的数据交换格式。

轻量级:相对于XML来说,Json本身就是一串字符串,数据量小且生成和解析简单。

数据交换格式:客户端和服务器传递数据的格式。

Json组成规则:

  1. 由键值对组成,由{}包裹;
  2. 键和值都用""包裹,键值之间使用:拼接,多个键值对之间使用,分割;键的双引号可以省略不写;
  3. key可以为字符串,value可以为数字、字符串、布尔、数组、json对象、json对象数组。

分类:

  1. Json对象:{"key1":"value1", "key2":"value2"}
  2. Json数组:[{"key1":"value1","key2":"value2"},{"key3":"value3","key4":"value4"}]
  3. 复杂Json{"k1":"v1","k2":[{"k21":"v21","k22":"v22"},{"k3":"v3","k4":"v4"}]}

使用:

  1. 前端中,Json中有两种形式

    • Json对象的形式存在,我们称之为Json对象(本质是一个js对象)。当我们需要使用Json中数据的时候,可以通过json对象.key获取对应的值;
    • Json格式的字符串形式存在,我们称之为Json字符串。当我们需要在前端需要和后台交换数据的时候,会把Json对象转成Json字符串;
    • 两种形式可以使用JSON类完成转换。stringify(json对象)转成strparse(str)解析json对象。

    前后台交换数据,一般都是通过字符串作为载体。所以说,如果要把某个json对象的内容传递到后台,一般先要把它转成json格式的字符串,然后再发送。

  2. 后台中,Json是以字符串形式存在

    • 后台中数据传递的载体是Model对象,前后台数据交换格式是json

    • 后台会做对象(model/list/map)和Json字符串之间的转换;

    • 手动转换:获取java对象的属性和对应的值,手动按照json要求的格式拼接成json字符串;手动切割Json字符串,然后设置到对象中;

    • 自动转换:使用第三方的工具类,完成java对象和json字符串的相互转换。

4.2 常见Json转换工具

两个概念

  • 序列化:java对象转换到Json字符串
  • 反序列化:Json字符串转换到java对象

常见工具(Json解析库)及特点

  • Jackson: 序列化效率最高,SpringMVC底层默认使用
  • fastjson:阿里巴巴公司开源,反序列化效率最高,移动端诸如Android使用较多
  • gson:谷歌公司开源,对复杂json支持最好,稳定;国内使用不多

4.3 JavaBean对象转Json字符串(重点)

// 导包,三个
// jackson-annotations-2.2.3.jar
// jackson-core-2.2.3.jar
// jackson-databind-2.2.3.jar

// 核心对象
OjbectMapper om = new ObjectMapper();

// 常用的是序列化,解析请求的时候是反序列化,不存在json格式字符串
// 转换成字符串
String jsonStr = om.writeValueAsString(bean/list/map);
// 转换成字符串并写入输出流,可以配合respone使用(response.getWriter()/getOutputStream())
om.writeValue(response.outputStream()/response.getWriter(),bean/list/map);

4.4 Json字符串转JavaBean(了解)

// 导包,三个
// jackson-annotations-2.2.3.jar
// jackson-core-2.2.3.jar
// jackson-databind-2.2.3.jar

// 核心对象
OjbectMapper om = new ObjectMapper();

// 反序列化不常用
// 如果是普通类型 bean/普通list/map
T t = om.readValue(jsonStr,T.class);
// 如果泛型是自定义类型(弃用)
//T t = om.readValue(jsonStr, new TypeReference<T>(){});

4.5 案例演示

/*
    JSON转换工具的使用
 */
public class ObjectMapperTest {
    private ObjectMapper mapper = new ObjectMapper();
    /*
        1.User对象转json, json转User对象
          json字符串 = {"name":"张三","age":23}
          user对象 = User{name='张三', age=23}
     */
    @Test
    public void test01() throws Exception{
        //User对象转json 响应时的序列化
        User user = new User("张三",23);
        String json = mapper.writeValueAsString(user);
        System.out.println("json字符串:" + json);

        //json转User对象
        User user2 = mapper.readValue(json, User.class);
        System.out.println("java对象:" + user2);
    }

    /*
         2.map<String,String>转json, json转map<String,String>
          json字符串 = {"姓名":"张三","性别":"男"}
          map对象 = {姓名=张三, 性别=男}
     */
    @Test
    public void test02() throws Exception{
        //map<String,String>转json
        HashMap<String,String> map = new HashMap<>();
        map.put("姓名","张三");
        map.put("性别","男");
        String json = mapper.writeValueAsString(map);
        System.out.println("json字符串:" + json);

        //json转map<String,String>
        HashMap<String,String> map2 = mapper.readValue(json, HashMap.class);
        System.out.println("java对象:" + map2);
    }

    /*
        3.map<String,User>转json, json转map<String,User>
          json字符串 = {"黑马一班":{"name":"张三","age":23},"黑马二班":{"name":"李四","age":24}}
          map对象 = {黑马一班=User{name='张三', age=23}, 黑马二班=User{name='李四', age=24}}
     */
    @Test
    public void test03() throws Exception{
        //map<String,User>转json
        HashMap<String,User> map = new HashMap<>();
        map.put("黑马一班",new User("张三",23));
        map.put("黑马二班",new User("李四",24));
        String json = mapper.writeValueAsString(map);
        System.out.println("json字符串:" + json);

        //json转map<String,User>
        // HashMap<String,User> map2 = mapper.readValue(json,new TypeReference<HashMap<String,User>>(){});
        HashMap<String,User> map2 = mapper.readValue(json, HashMap.class);
        System.out.println("java对象:" + map2);
    }

    /*
        4.List<String>转json, json转 List<String>
          json字符串 = ["张三","李四"]
          list对象 = [张三, 李四]
     */
    @Test
    public void test04() throws Exception{
        //List<String>转json
        ArrayList<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        String json = mapper.writeValueAsString(list);
        System.out.println("json字符串:" + json);

        //json转 List<String>
        ArrayList<String> list2 = mapper.readValue(json,ArrayList.class);
        System.out.println("java对象:" + list2);
    }

    /*
        5.List<User>转json, json转List<User>
          json字符串 = [{"name":"张三","age":23},{"name":"李四","age":24}]
          list对象 = [User{name='张三', age=23}, User{name='李四', age=24}]
     */
    @Test
    public void test05() throws Exception{
        //List<User>转json
        ArrayList<User> list = new ArrayList<>();
        list.add(new User("张三",23));
        list.add(new User("李四",24));
        String json = mapper.writeValueAsString(list);
        System.out.println("json字符串:" + json);

        //json转List<User>
        ArrayList<User> list2 = mapper.readValue(json,ArrayList.class);
        //ArrayList<User> list2 = mapper.readValue(json,new TypeReference<ArrayList<User>>(){});
        System.out.println("java对象:" + list2);
    }
}

5.综合案例一

优化细节1:修改触发事件

$("#username").keyup(function (e) {/*.....*/}

优化细节2:添加功能

// 通过事件委派为提示框的div绑定事件
// 添加:高亮切换时添加到输入框,背景黄色
$("#show").on("mouseover","div",function () {
    $(this).css("background","yellow");
    $("#username").val(this.innerText);
});
// 添加:移出时背景颜色还原
$("#show").on("mouseout","div",function () {
    $(this).css("background","none");
});
// 添加:点击时提示框消失
$("#show").on("click","div",function () {
    $("#show").hide();
});

完整代码

<script src="js/jquery-3.3.1.min.js"></script>
<script>
    //1.为用户名输入框绑定鼠标键盘敲击事件
    $("#username").keyup(function (e) {
        //2.获取输入的用户名
        let username = $("#username").val();

        //3.判断用户名是否为空
        if(username == null || username == "") {
            //4.如果为空,将联想框隐藏
            $("#show").hide();
            return;
        }
        // 通过事件委派为提示框的div绑定事件
        // 高亮切换时添加到输入框,背景黄色
        $("#show").on("mouseover","div",function () {
            $(this).css("background","yellow");
            $("#username").val(this.innerText);
        });
        // 移出时背景颜色还原
        $("#show").on("mouseout","div",function () {
            $(this).css("background","none");
        });
        // 点击时提示框消失
        $("#show").on("click","div",function () {
            $("#show").hide();
        });

        //5.如果不为空,发送AJAX请求。并将数据显示到联想框
        $.ajax({
            //请求的资源路径
            url:"userServlet",
            //请求参数
            data:{"username":username},
            //请求方式
            type:"POST",
            //响应数据形式
            dataType:"json",
            //请求成功后的回调函数
            success:function (data) {
                //将返回的数据显示到show的div
                let names = "";
                for(let i = 0; i < data.length; i++) {
                    names += "<div>"+data[i].name+"</div>";
                }
                $("#show").html(names);
                $("#show").show();
            }
        });
    });
</script>

UserServlet

@WebServlet("/userServlet")
public class UserServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置请求和响应的编码
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");

        //1.获取请求参数
        String username = req.getParameter("username");

        //2.调用业务层的模糊查询方法得到数据
        UserService service = new UserServiceImpl();
        List<User> users = service.selectLike(username);

        //3.将数据转成JSON,响应到客户端
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(users);
        resp.getWriter().write(json);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }
}

UserSeriveImpl

public class UserServiceImpl implements UserService {
    @Override
    public List<User> selectLike(String username) {
        List<User> users = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try{
            //1.加载核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            //2.获取SqlSession工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            //3.通过SqlSession工厂对象获取SqlSession对象
            sqlSession = sqlSessionFactory.openSession(true);
            //4.获取UserMapper接口的实现类对象
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            //5.调用实现类对象的模糊查询方法
            users = mapper.selectLike(username);
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            //6.释放资源
            if(sqlSession != null) {
                sqlSession.close();
            }
            if(is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //7.返回结果到控制层
        return users;
    }
}

UserMapper

public interface UserMapper {
    /*
        模糊查询
     */
    @Select("SELECT * FROM user WHERE name LIKE CONCAT('%',#{username},'%') ORDER BY search_count DESC LIMIT 0,4")
    public abstract List<User> selectLike(String username);
}

6.案例二

6.1 判断页面滚动

1602403740665

6.2 代码实现

index.html

<script>
    //1.定义发送请求标记,如果上一次请求还没有处理完,就不要再发起新的请求了
    // true表示可以发送请求,false反之
    let send = true;

    //2.定义当前页码和每页显示的条数
    let start = 1;
    let pageSize = 10;

    //3. 冗余的一段距离,提前开始加载下一页数据
    let bottom = 5;

    //4.设置页面加载事件
    // $(function () {
    //5.为当前窗口绑定滚动条滚动事件
    $(window).scroll(function () {
        if (send) {
            //6.获取必要信息,用于计算当前展示数据是否浏览完毕
            //当前窗口的高度
            let windowHeight = $(window).height();

            //滚动条从上到下滚动距离
            let scrollTop = $(window).scrollTop();

            //当前文档的高度
            let docHeight = $(document).height();

            //7.计算当前展示数据是否浏览完毕
            //当 滚动条距底部的距离 + 当前滚动条滚动的距离 + 当前窗口的高度 >= 当前文档的高度
            if ((bottom + scrollTop + windowHeight) >= docHeight) {
                //8.判断请求标记是否为true,表示上一次请求已经处理 完了,或者从来还没有发起过请求
                // 这个时候可以发起新的请求
                //9.将请求标记置为false,当前异步操作完成前,不能重新发起请求。
                send = false;
                //10.根据当前页和每页显示的条数来 请求查询分页数据
                queryByPage(start, pageSize);
                //11.当前页码+1
                start++;
            }
        }
    });
    // });

    //定义查询分页数据的函数
    function queryByPage(start, pageSize) {
        //加载动图显示
        $(".loading").show();
        //发起AJAX请求
        $.ajax({
            //请求的资源路径
            url: "newsServlet",
            //请求的参数
            data: {"start": start, "pageSize": pageSize},
            //请求的方式
            type: "POST",
            //响应数据形式
            dataType: "json",
            //请求成功后的回调函数
            success: function (data) {
                //加载动图隐藏
                $(".loading").hide();
                if (data.length == 0) {
                    $("#no").html("我也是有底线的...");
                    return;
                }

                //将数据显示
                let titles = "";
                for (let i = 0; i < data.length; i++) {
                    titles += "<li>\n" +
                        "                <div class=\"title-box\">\n" +
                        "                    <a href=\"#\" class=\"link\">\n" +
                        data[i].title +
                        "                        <hr>\n" +
                        "                    </a>\n" +
                        "                </div>\n" +
                        "            </li>";
                }

                //显示到页面
                $(".news_list").append(titles);
                //将请求标记设置为true
                // 到这里表示当前这次请求已经处理完成,后续的请求可以发送了,所以置为true
                send = true;
            }
        });
    }

</script>

NewsServlet.java

@WebServlet("/newsServlet")
public class NewsServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置请求和响应的编码
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");

        //1.获取请求参数
        String start = req.getParameter("start");
        String pageSize = req.getParameter("pageSize");

        /* 程序健壮性判断
        	1. 防止为提交这两个值
			2. 防止提交的值不符合格式要求
        */
        int startNum = 1;
        int pageSizeNum = 10;

        try {
            startNum = Integer.parseInt(start);

        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            pageSizeNum = Integer.parseInt(pageSize);

        } catch (Exception e) {
            e.printStackTrace();
        }
        /* 程序健壮性判断结束 */

        //2.根据当前页码和每页显示的条数来调用业务层的查询方法,得到分页Page对象
        NewsService service = new NewsServiceImpl();
        Page page = service.pageQuery(startNum, pageSizeNum);


        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        ////3.将得到的数据转为JSON
        //String json = new ObjectMapper().writeValueAsString(page);
        //
        ////4.将数据响应给客户端
        //resp.getWriter().write(json);

    new ObjectMapper().writeValue(resp.getWriter(), page);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}

6.3 案例二:页码分页

页面正文没有内容,但是发起请求后,会讲数据填充到正文的div中。填充时要覆盖html,不要追加append

控件效果不好,以后也不是我们用,理解就可以。

地址栏输入非法内容,不做健壮性判断就出问题了,测试人员负责。


开发人员选项,查看分页按钮是超链接

判断发送请求的参数根据按钮上的文本进行区分。

posted @ 2020-10-11 20:20  sonyan  阅读(112)  评论(0编辑  收藏  举报