014-Axios Ajax-尚硅谷2022+黑马2023:Ajax概述 同步异步,(Axios 发送普通请求参数,发送请求体JSON,服务器端返回JSON数据)后端是servlet方式

第一节 Ajax概述

1、概述

  • 概念: Asynchronous JavaScript And XML,异步的JavaScript和XML。
  • 作用:
    • 数据交换:通过Ajax可以给服务器发送请求,并获取服务器响应的数据。
    • 异步交互:可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术,如:搜索联想、用户名是否可用的校验等等。

在这里插入图片描述

2、页面渲染方式

2.1、服务器端渲染

在这里插入图片描述

2.2、Ajax渲染(局部更新)

在这里插入图片描述

3、前后端分离

彻底舍弃服务器端渲染,数据全部通过Ajax方式以JSON格式来传递。

4、同步与异步

Ajax本身就是Asynchronous JavaScript And XML的缩写,直译为:异步的JavaScript和XML。在实际应用中Ajax指的是:不刷新浏览器窗口,不做页面跳转,局部更新页面内容的技术。

『同步』『异步』是一对相对的概念,那么什么是同步,什么是异步呢?

4.1、同步

多个操作按顺序执行,前面的操作没有完成,后面的操作就必须等待。所以同步操作通常是串行的。
在这里插入图片描述

4.2、异步

多个操作相继开始并发执行,即使开始的先后顺序不同,但是由于它们各自是在自己独立的进程或线程中完成,所以互不干扰,谁也不用等谁
在这里插入图片描述

5、Axios简介

使用原生的JavaScript程序执行Ajax极其繁琐,所以一定要使用框架来完成。而Axios就是目前最流行的前端Ajax框架。

Axios官网http://www.axios-js.com/

在这里插入图片描述
使用Axios和使用Vue一样,导入对应的*.js文件即可。官方提供的script标签引入方式为:

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

我们可以把这个axios.min.js文件下载下来保存到本地来使用。

第二节 Axios基本用法

1、在前端页面引入开发环境

在这里插入图片描述

    <!--引入vue.js文件-->
    <script language="JavaScript" src="script/vue.js"></script>
    <!--引入axios.min.js文件-->
    <script language="JavaScript" src="script/axios.min.js"></script>

2、入门案例(黑马)

步骤:

  • 引入Axios的js文件:略
  • 使用Axios发送请求,并获取响应结果
    在这里插入图片描述
<!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>Ajax-Axios</title>
    <script src="script/axios.min.js"></script>
</head>
<body>
    
    <input type="button" value="获取数据GET" onclick="get()">

    <input type="button" value="删除数据POST" onclick="post()">

</body>
<script>
    function get(){
        //通过axios发送异步请求-get,参数拼接在url地址中进行传递
        //原始写法:
        // axios({
        //     method: "get",
        //     url: "http://yapi.smart-xwork.cn/mock/169327/emp/list" //这是黑马提供的一个服务器地址是个json对象
        // }).then(function (result){
        //     console.log(result.data);//获取响应的结果
        // })

        //原始写法-箭头函数
        // axios({
        //     method: "get",
        //     url: "http://yapi.smart-xwork.cn/mock/169327/emp/list"
        // }).then(result => {
        //     console.log(result.data);
        // })

        //简化写法:
        axios.get("http://yapi.smart-xwork.cn/mock/169327/emp/list").then(function (result){
            console.log(result.data);//获取响应的结果
        })

        //简化写法-箭头函数
        // axios.get("http://yapi.smart-xwork.cn/mock/169327/emp/list").then(result => {
        //     console.log(result.data);
        // })

    }

    function post(){
        //通过axios发送异步请求-post
        //原始写法:
        // axios({
        //     method: "post",
        //     url: "http://yapi.smart-xwork.cn/mock/169327/emp/deleteById",
        //     data: "id=1"
        // }).then(function (result){
        //     console.log(result.data);//获取响应的结果
        // })

        //原始写法-箭头函数
        // axios({
        //     method: "post",
        //     url: "http://yapi.smart-xwork.cn/mock/169327/emp/deleteById",
        //     data: "id=1"
        // }).then(result => {
        //     console.log(result.data);
        // })

        //简化写法:
        axios.post("http://yapi.smart-xwork.cn/mock/169327/emp/deleteById","id=1").then(function (result){
            console.log(result.data);//获取响应的结果
        })

        //简化写法-箭头函数
        // axios.post("http://yapi.smart-xwork.cn/mock/169327/emp/deleteById","id=1").then(result => {
        //     console.log(result.data);
        // })
    }
</script>
</html>
  • 测试:
    • YAPI所生成的MocK测试数据的地址,是个json对象
      在这里插入图片描述
    • 可以看到正确的获取到了响应的数据 在这里插入图片描述
  • 简化写法:
    在这里插入图片描述

3、Vue+Axios完成数据的动态加载展示(黑马)

需求:

  • 数据准备的url:YAPI所生成的MocK测试数据的地址

    • http://yapi.smart-xwork.cn/mock/169327/emp/list
      在这里插入图片描述
  • 在页面加载完成后,自动发送异步请求,加载数据,渲染展示页面

    • (性别:1代表男,2代表女)。
      在这里插入图片描述

测试:

  • html代码:
    在这里插入图片描述
<!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>Ajax-Axios-案例</title>
    <script src="script/axios.min.js"></script>
    <script src="script/vue.js"></script>
</head>
<body>
    <div id="app">
        <table border="1" cellspacing="0" width="60%">
            <tr>
                <th>编号</th>
                <th>姓名</th>
                <th>图像</th>
                <th>性别</th>
                <th>职位</th>
                <th>入职日期</th>
                <th>最后操作时间</th>
            </tr>

            <tr align="center" v-for="(emp,index) in emps">
                <!--绑定文本-->
                <!--编号要求从1开始,索引从0开始,所以加上iddex+1表示编号为从1开始-->
                <td>{{index + 1}}</td>
                <td>{{emp.name}}</td>
                <!--绑定属性:-->
                <td>
                    <img :src="emp.image" width="70px" height="50px">
                </td>
                <td>
                    <span v-if="emp.gender == 1"></span>
                    <span v-if="emp.gender == 2"></span>
                </td>
                <td>{{emp.job}}</td>
                <td>{{emp.entrydate}}</td>
                <td>{{emp.updatetime}}</td>
            </tr>
        </table>
    </div>
</body>
<script>
    var vue = new Vue({
       el: "#app",
       data: {
           emps:[]
       },
        //页面加载完成后自动执行:vue中的钩子函数mounted,他会在页面加载完成后自动执行
       mounted () {
          //发送异步请求,加载数据
          //方式一:箭头函数
          axios.get("http://yapi.smart-xwork.cn/mock/169327/emp/list").then(result => {
              //result.data:获取到响应的数据,此案例返回的是个json对象
              //result.data.data:获取到json对象的data属性,此属性的值为一个数组,里面封装的是一个个的对象。
              //如何把获取到的data属性中存的数组,和html标签绑定在一块呢??
              //  答:只需要把值赋值给vue当中的一个数据模型emps就可以了。
              //     this:代表当前的vue对象
              //     this.emps:代表为当前vue对象中的emps这个数据模型赋值。
              //之后只需要遍历数据模型中的数组获取每个对象,然后通过对象.属性获取即可。
              this.emps = result.data.data;
          })

           //方式二:普通函数
           //注意:箭头函数this代表当前对象,而在普通函数中只能使用vue对象名来调用对象属性
           // axios.get("http://yapi.smart-xwork.cn/mock/169327/emp/list").then(function(result){
           //     vue.emps = result.data.data;
           // })
       }
    });
</script>
</html>
  • 效果:
    在这里插入图片描述

4、发送普通请求参数

2.1、基本格式

客户端向服务器端异步发送普通参数值:使用params表示

 - 基本格式: axios().then().catch()
    - 示例:
      axios({             //括号里面是一个对象
        method:"POST",
        url:"....",
        params:{
            uname:"lina",
            pwd:"ok"
        }
      })      
      //成功响应时执行的回调:then() 
      //                 value就是成功时服务器端返回的响应数据对象
      //                 value.data通过对象调用data属性,就可以获取到服务器响应体内容
      .then(function(value){})
      //有异常时执行的回调catch():
      //                reason就是出错时服务器端返回的响应数据
      //                reason.response在服务器端处理请求失败后,获取axios封装的JSON格式的响应数据对象
      //                reason.response.data可以获取到响应的内容
      //                reason.response.status在服务器端处理请求失败后,获取响应状态码
      //                reason.response.statusText在服务器端处理请求失败后,获取响应状态说明文本
      //                reason.message / reason.stack 可以查看错误的信息             
     .catch(function(reason){});                                                                 

2.2、前端代码

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--引入vue.js文件-->
    <script language="JavaScript" src="script/vue.js"></script>
    <!--引入axios.min.js文件-->
    <script language="JavaScript" src="script/axios.min.js"></script>
    <script language="JavaScript">
        window.onload=function(){//文档就绪事件函数

            var vue = new Vue({
                el:"#div0",
                data:{
                    uname:"lina",
                    pwd:"ok"
                },
                methods:{
                    axios01:function(){
                        axios({
                            method:"post",
                            url:"http://localhost:8080/Vue/axios01.do",
                            params:{
                                //注意:不能使用this.uname只能使用vue.uname,因为已经进入到axios中了,this代表的是axios
                                //     里面的属性,想要使用date中的属性只能通过vue来调用。
                                uname:vue.uname,
                                pwd:vue.pwd
                            }
                        })
                            .then(function (value) {
                                console.log(value);
                            })
                            .catch(function (reason) {
                                console.log(reason);
                            });
                    }

                }

            });
        }
    </script>
</head>
<body>
<div id="div0">
    uname:<input type="text" v-model="uname"/><br/>
    pwd:<input type="text" v-model="pwd"/><br/>
    <input type="button" @click="axios01" value="发送一个带普通请求参数值的异步请求"/>
</div>
</body>
</html>

2.3、后端代码

在这里插入图片描述

package axios;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/axios01.do")//Servlet3.0使用注解代替原先的xml方式配置路径
public class Axios01Servlet extends HttpServlet {
    private static final long serialVersionUID = -8499971529938553156L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");

        String uname = request.getParameter("uname");
        String pwd = request.getParameter("pwd");

        System.out.println("uname = " + uname);
        System.out.println("pwd = " + pwd);

        response.setCharacterEncoding("utf-8");
        //设置返回的类型是普通文本
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write(uname+"_"+pwd);

       // throw new NullPointerException("这里故意抛出一个空指针异常....");

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

P.S.:由于我们不需要Thymeleaf了,所以ModelBaseServlet可以跳过ViewBaseServlet直接继承HttpServlet。

在这里插入图片描述

2.4、运行测试

说明:启动Tomact,首先访问到demo10.html页面,在页面点击按钮执行点击事件,通过axios方式post请求。
在这里插入图片描述

可以看到在控制层方法中获取到了请求参数:
在这里插入图片描述
请求参数:所有请求参数都被放到URL地址后面了,哪怕我们现在用的是POST请求方式。
在这里插入图片描述

在这里插入图片描述
响应的内容:
在这里插入图片描述

2.5、DeBug查看成功响应的对象value结构

说明:使用js的DeBug调试,查看成功响应时执行的回调函数中的value参数值:
在这里插入图片描述

在这里插入图片描述
可以看到:data数据在value中
在这里插入图片描述

属性名作用
config调用axios(config对象)方法时传入的JSON对象
data服务器端返回的响应体数据
headers响应消息头
request原生JavaScript执行Ajax操作时使用的XMLHttpRequest
status响应状态码
statusText响应状态码的说明文本

2.6、DeBug查看失败响应的对象reson结构

说明:使用js的DeBug调试,查看失败响应时执行的回调函数中的reson参数值:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
可以看到:data数据在reson下面的response中。
在这里插入图片描述
可以看到:response对象的结构还是和then()函数传入的回调函数中的response是一样的:

./images

回调函数:开发人员声明,但是调用时交给系统来调用。像单击响应函数、then()、catch()里面传入的都是回调函数。回调函数是相对于普通函数来说的,普通函数就是开发人员自己声明,自己调用:

普通函数:

function sum(a, b) {
    return a+b;
}

var result = sum(3, 2);
console.log("result="+result);

5、发送请求体JSON

5.1、JSON概述

什么是JSON:

  • JSON是一种数据格式
  • XML也是一种数据格式

JSON格式表示两个学员信息的代码如下:

//JSON表达数据更简洁,更能够节约网络带宽
  [{sid:"s001",age:18},{sid:"s002",age:19}]

XML格式表示两个学员信息的代码如下:

      <students>
        <student sid="s001">
            <sname>jim</sname>
            <age>18</age>
        </student>
        <student sid="s002">
            <sname>tom</sname>
            <age>19</age>
        </student>
      </students>

5.2、注意事项

  1. 客户端 axios里面的params需要修改成: data:

  2. 服务器获取参数值不再是 request.getParameter()...
    而是:
    StringBuffer stringBuffer = new StringBuffer(“”);
    BufferedReader bufferedReader = request.getReader();
    String str = null ;
    while((str=bufferedReader.readLine())!=null){
    stringBuffer.append(str);
    }
    str = stringBuffer.toString() ;

  3. 我们会发现 str的内容如下:
    {"uname":"lina","pwd":"ok"}

5.3、前端代码

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--引入vue.js文件-->
    <script language="JavaScript" src="script/vue.js"></script>
    <!--引入axios.min.js文件-->
    <script language="JavaScript" src="script/axios.min.js"></script>
    <script language="JavaScript">
        window.onload=function(){//文档就绪事件函数

            var vue = new Vue({
                el:"#div0",
                data:{
                    uname:"lina",
                    pwd:"ok"
                },
                methods:{
                    axios02:function(){
                        axios({
                            method:"post",
                            url:"http://localhost:8080/Vue/axios02.do",
                            data:{
                                //注意:不能使用this.uname使能使用vue.uname,因为已经进入到axios中了,this代表的是axios
                                //     里面的属性,想要使用date中的属性只能通过vue来调用。
                                uname:vue.uname,
                                pwd:vue.pwd
                            }
                        })
                            .then(function (value) {
                                console.log(value);
                            })
                            .catch(function (reason) {
                                console.log(reason);
                            });
                    }
                }
            });
        }
    </script>
</head>
<body>
<div id="div0">
    uname:<input type="text" v-model="uname"/><br/>
    pwd:<input type="text" v-model="pwd"/><br/>
    <input type="button" @click="axios02" value="发送JSON格式的参数值的异步请求"/>
</div>
</body>
</html>

5.4、后端代码

1) 加入Gson包

Gson是Google研发的一款非常优秀的JSON数据解析和生成工具,它可以帮助我们将数据在JSON字符串和Java对象之间互相转换。

将jar包添加到类路径:Add as Library
在这里插入图片描述

2) User实体类

作用:封装json字符串转化为一个java对象后的数据。

package pojo;

public class User {
    private String uname ;
    private String pwd ;

    public User(){}

    public User(String uname, String pwd) {
        this.uname = uname;
        this.pwd = pwd;
    }

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "uname='" + uname + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

3) Servlet代码

在这里插入图片描述

package axios;


import com.google.gson.Gson;
import pojo.User;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;

@WebServlet("/axios02.do")//Servlet3.0使用注解代替原先的xml方式配置路径
public class Axios02Servlet extends HttpServlet {
    private static final long serialVersionUID = -8499971529938553156L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1.创建StringBuffer对象来累加存储从请求体中读取到的每一行
        StringBuffer stringBuffer = new StringBuffer("");
        // 2.由于请求体数据有可能很大,所以Servlet标准在设计API的时候要求我们通过输入流来读取
        BufferedReader bufferedReader = request.getReader();
        // 3.声明临时变量
        String str = null ;
        // 4.循环读取
        while((str=bufferedReader.readLine())!=null){
            stringBuffer.append(str);
        }
        // 5.关闭流
        bufferedReader.close();
        // 6.累加的结果就是整个请求体
        str = stringBuffer.toString() ;
        System.out.println(str);// {"uname":"lina","pwd":"ok"} 获取到的是json字符串类型的数据

        // 7.后台操作的是java对象,所以我们需要把json字符串转化为java对象
        //  创建Gson对象:2种
        //  Gson gson =new GsonBuilder().create(); 这种方式功能更强大,eg:可以做格式化
        Gson gson =new Gson();
        // Gson有两个API:
        //  1).fromJson(string,T) 将字符串转化成java object
        //  2).toJson(java Object) 将java object转化成json字符串,这样才能响应给客户端
        User user = gson.fromJson(str, User.class);
        System.out.println(user);//User{uname='lina', pwd='ok'}
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

P.S.:看着很麻烦是吧?别担心,将来我们有了SpringMVC之后,一个@RequestBody注解就能够搞定,非常方便!

4) 运行测试

P.S.:Chrome浏览器中将『请求负载』显示为英文:『Request Payload』。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

6、服务器端返回JSON数据

说明:服务器端给客户端响应JSON格式的字符串,然后客户端需要将字符串转化成json对象,使用axios会自动把服务端响应给客户端的json字符串转化为json对象。

6.1、前端代码

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--引入vue.js文件-->
    <script language="JavaScript" src="script/vue.js"></script>
    <!--引入axios.min.js文件-->
    <script language="JavaScript" src="script/axios.min.js"></script>
    <script language="JavaScript">
        window.onload=function(){//文档就绪事件函数

            var vue = new Vue({
                el:"#div0",
                data:{
                    uname:"lina",
                    pwd:"ok"
                },
                methods:{
                    axios03:function(){
                        axios({
                            method:"post",
                            url:"http://localhost:8080/Vue/axios03.do",
                            data:{
                                //注意:不能使用this.uname使能使用vue.uname,因为已经进入到axios中了,this代表的是axios
                                //     里面的属性,想要使用date中的属性只能通过vue来调用。
                                uname:vue.uname,
                                pwd:vue.pwd
                            }
                        })
                            .then(function (value) {
                                var data = value.data;
                                // data对应的数据:{uname:"鸠摩智",pwd:"ok"}  ---->json对象
                                // 可以看出:会自动把服务端传来的json字符串转化为json对象,所以可以直接调用它的属性
                                vue.uname=data.uname;
                                vue.pwd=data.pwd;

                                //此处value中的data返回的是 js object,因此可以直接点出属性
                                //如果我们获取的是一个字符串:  "{uname:\"鸠摩智\",pwd:\"ok\"}"

                                //js语言中 也有字符串和js对象之间互转的API
                                //string JSON.stringify(object)     object->string
                                //object JSON.parse(string)         string->object
                            })
                            .catch(function (reason) {
                                console.log(reason);
                            });
                    }
                }
            });
        }
    </script>
</head>
<body>
<div id="div0">
    uname:<input type="text" v-model="uname"/><br/>
    pwd:<input type="text" v-model="pwd"/><br/>
    <input type="button" @click="axios03" value="发送JSON格式的参数值的异步请求,服务器端给客户端响应JSON格式的字符串"/>
</div>
</body>
</html>

6.2、后端代码

1) 加入Gson包

仍然需要Gson支持,不用多说

./images

2) User实体类

略,这个地方是测试响应后的数据,没用到这个类,只不过现在页面流程是先发送请求在响应给页面数据,发送请求时json字符串转化为java对象时用到了。

3) Servlet代码

在这里插入图片描述

package axios;


import com.google.gson.Gson;
import pojo.User;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;

@WebServlet("/axios03.do")//Servlet3.0使用注解代替原先的xml方式配置路径
public class Axios03Servlet extends HttpServlet {
    private static final long serialVersionUID = -8499971529938553156L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1.创建StringBuffer对象来累加存储从请求体中读取到的每一行
        StringBuffer stringBuffer = new StringBuffer("");
        // 2.由于请求体数据有可能很大,所以Servlet标准在设计API的时候要求我们通过输入流来读取
        BufferedReader bufferedReader = request.getReader();
        // 3.声明临时变量
        String str = null ;
        // 4.循环读取
        while((str=bufferedReader.readLine())!=null){
            stringBuffer.append(str);
        }
        // 5.关闭流
        bufferedReader.close();
        // 6.累加的结果就是整个请求体
        str = stringBuffer.toString() ;
        System.out.println(str);// {"uname":"lina","pwd":"ok"} 获取到的是json字符串类型的数据

        // 7.后台操作的是java对象,所以我们需要把json字符串转化为java对象
        //  创建Gson对象:2种
        //  Gson gson =new GsonBuilder().create(); 这种方式功能更强大,eg:可以做格式化
        Gson gson =new Gson();
        // Gson有两个API:
        //  1).fromJson(string,T) 将字符串转化成java object
        //  2).toJson(java Object) 将java object转化成json字符串,这样才能响应给客户端
        User user = gson.fromJson(str, User.class);
        user.setUname("鸠摩智");
        user.setPwd("123456");
        System.out.println(user);//User{uname='lina', pwd='ok'}

        //假设user是从数据库查询出来的,现在需要将其转化成json格式的字符串,然后响应给客户端
        String userJsonStr = gson.toJson(user);//注意:转化后的是一个json字符串
        response.setCharacterEncoding("UTF-8");
        //MIME-TYPE  设置返回的类型是json
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(userJsonStr);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

4) 运行测试

流程:页面发送请求携带json格式的参数,之后在服务端获取并测试json字符串和java对象的转化,之修改下User实体类的数据便于看到页面效果,把json对象转化为json字符串返回给页面,客户端使用axios会自动把接收的json字符串转化为json对象,直接取值即可。把取出的值复制给了uname、pwd,这2个属性又绑定了文本框,所以点击提交按钮值会发生变化。
在这里插入图片描述

点击按钮前:
在这里插入图片描述
点击按钮后:
在这里插入图片描述

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值