关于ajax学习笔记

一、什么是AJAX,为什么要使用Ajax(请谈一下你对Ajax的认识)

  • ajax全称Asynchronous JavaScript and XML(异步的javascript和XML),为什么会有这么一种技术的出现呢,因为前端时常会有这样的需求,我们只要局部刷新,不需要整一个刷新的时候,便催生了这样的技术。
  • 在 Ajax应用中信息是通过XML数据或者字符串在浏览器和服务器之间传递的(json字符串居多)
  • 在浏览器端通过XMLHttpRequest对象的responseXMl属性,得到服务器端响应的XML数据。
  • AJAX优点:
    • 最大的一点是页面无刷新,用户的体验非常好。
    • 使用异步方式与服务器通信,具有更加迅速的响应能力。
    • 可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,ajax的原则是“按需取数据”,可以最大程度的减少冗余请求,和响应对服务器造成的负担。
    • 基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。
  • AJAX缺点:
    • ajax不支持浏览器back按钮。
    • 安全问题 AJAX暴露了与服务器交互的细节。
    • 对搜索引擎的支持比较弱。
    • 破坏了程序的异常机制。
    • 不容易调试。
  • AJAX应用和传统Web应用有什么不同?
    • 传统的web前端与后端的交互中,浏览器直接访问Tomcat的Servlet来获取数据。Servlet通过转发把数据发送给浏览器。
    • 当我们使用AJAX之后,浏览器是先把请求发送到XMLHttpRequest异步对象之中,异步对象对请求进行封装,然后再与发送给服务器。服务器并不是以转发的方式响应,而是以流的方式把数据返回给浏览器
    • XMLHttpRequest异步对象会不停监听服务器状态的变化,得到服务器返回的数据,就写到浏览器上【因为不是转发的方式,所以是无刷新就能够获取服务器端的数据】
  • AJAX是异步执行的,如图所示,异步执行不会阻塞.

二、ajax 的执行过程

  1. 创建XMLHttpRequest对象,也就是创建一个异步调用对象
  2. 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息
  3. 设置响应HTTP请求状态变化的函数
  4. 发送HTTP请求
  5. 获取异步调用返回的数据
  6. 使用JavaScript和DOM实现局部刷新

基本示例:

//创建 XMLHttpRequest 对象
var ajax = new XMLHttpRequest();
// 规定请求的类型、URL 以及是否异步处理请求。
ajax.open('GET',url,true);
//发送信息至服务器时内容编码类型
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
//发送请求
ajax.send(null);  
//接受服务器响应数据
ajax.onreadystatechange = function () {
    if (obj.readyState == 4 && (obj.status == 200 || obj.status == 304)) { 
    }
};

简单应用示例:

    oBtn.onclick = function () {
        //创建对象
        var xhr = getXMLHttpRequest();
        //当xhr对象的readyState属性发生改变的时候触发
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) { //ajax的状态4表示加载完成
                if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {// http的状态是以上才算正常
                    pp.innerHTML = xhr.responseText;
                } else {
                    throw new Error("文件读取错误");
                }
            }
        }
        //open方法表示配置这次请求
        xhr.open("get", "test.txt", true);
        //发送请求
        //get请求中,没有任何的上行主体的,所以写null
        xhr.send(null);
    }

    //工厂函数(兼容浏览器)
    function getXMLHttpRequest() {
        if (window.XMLHttpRequest) {
            //高级浏览器,IE7,IE7+
            return new XMLHttpRequest();
        } else {
            //老版本浏览器,IE6
            return new ActiveXObject("Microsoft.XMLHTTP");
        }
    }

2.1 `open()`方法

xhr.open("get","test.txt",true);

调用open方法并不会真正发送请求,而只是启动一个请求以备发送。

它接受三个参数:

  • 要发送的请求的类型
  • 请求的URL
  • 表示是否异步的布尔值。

2.2 `send()`方法

如果要发送请求,用send()方法。

要发送特定的请求,需要调用send()方法。

  • 它接受一个参数:请求主体发送的数据。
  • 如果不需要通过请求主体发送数据,则必须传入null,不能留空。
  • 请求主体:HTTP上行请求,有头部、主体。
    • 一般来说,GET请求是只有头部,没有主体
    • 而POST请求有请求主体。

一但调用send()方法,HTTP上行请求就将发出。

2.3 `readyState`属性

表示“就绪状态”

  • 0 (uninitialized) 未初始化
  • 1 (loading) XMLHttpRequest对象正在加载
  • 2 (loaded) XMLHttpRequest对象加载完毕
  • 3 (interactive) 正在传输数据
  • 4 (complete) 全部完成

一般来说,只需要使用4状态就可以了

只要这个属性值发生了变化,就会触发一个事件onreadystatechange事件,就可以使用xhr.onreadystatechange = function(){}来捕获readyState变化之后做的事情。

三、关于http的状态

ajax 也是使用 http 协议的,所以也需要了解 http协议的状态。

1XX    100-101 信息提示
2XX    200-206 成功
3XX    300-305 重定向
4XX    400-415 客户端错误
5XX    500-505 服务器错误

200 OK 服务器成功处理了请求(这个是我们见到最多的)
301/302 Moved Permanently(重定向)请求的URL已移走。Response中应该包含一个Location URL, 说明资源现在所处的位置
304 Not Modified(未修改)客户的缓存资源是最新的, 要客户端使用缓存
404 Not Found 未找到资源
501 Internal Server Error服务器遇到一个错误,使其无法对请求提供服务

这是比较齐全的状态表:

四、关于函数封装(ajax封装)

  • 变量、函数的作用域,是定义这个变量、函数时,包裹它的最近父函数。
  • 没有在任何function中定义的变量,称为全局变量。全局变量都是window对象的属性。所以,如果想在函数内,向全局暴露顶层变量,只需要把顶层变量设置为window对象的属性。
  • 越是大的项目,越需要让全局变量越少越好。这是为了防止不同工程师之间的程序,命名冲突。所以,每一个功能包,只能向全局暴露唯一的顶层变量,就是这个功能包自己的命名空间。
  • jQuery、YUI、underscore都是这样的做法。
  • 向外暴露全局变量,设置window的变量(也是这个函数的命名空间),类似jquery的$其实也就是window.$
  • 良好的代码风格
    • `//=======================属性=======================`
    • `//=======================方法=====================`
    • `//=======================内部方法=====================`
    • `_`代表内部方法或者属性,主要是给编程人员看的
    • 属性和方法写在前面,内部属性或者内部方法写在后面
  • 通过判断arguments.length来实现函数重载
(function () {
    var myAjax = {};  //空对象
    //向外暴露这么一个全局变量
    //就是这个函数的命名空间
    window.myAjax = myAjax;

    //=======================属性=======================
    myAjax.version = "0.2.0";

    //=======================方法=======================
    myAjax.get = function () {
        //参数个数
        var argLength = arguments.length;
        var URL, json, callback;
        if (argLength == 2 && typeof arguments[0] == "string" && typeof arguments[1] == "function") {
            //两个参数
            URL = arguments[0];
            callback = arguments[1];
            //传给我们的核心函数来发出Ajax请求
            myAjax._doAjax("get", URL, null, callback);
        } else if (argLength == 3 && typeof arguments[0] == "string" && typeof arguments[1] == "object" && typeof arguments[2] == "function") {
            //3个参数
            URL = arguments[0];
            json = arguments[1];
            callback = arguments[2];
            //传给我们的核心函数来发出Ajax请求
            myAjax._doAjax("get", URL, json, callback);
        } else {
            throw new Error("get方法参数错误!");
        }
    }

    myAjax.post = function () {
        //参数个数
        var argLength = arguments.length;
        if (argLength == 3 && typeof arguments[0] == "string" && typeof arguments[1] == "object" && typeof arguments[2] == "function") {
            //3个参数
            var URL = arguments[0];
            var json = arguments[1];
            var callback = arguments[2];
            //传给我们的核心函数来发出Ajax请求
            myAjax._doAjax("post", URL, json, callback);
        } else {
            throw new Error("post方法参数错误!");
        }
    }

    //post方式提交所有表单
    myAjax.postAllForm = function (URL, formId, callback) {
        //将表单数据转为json
        var json = myAjax._formSerialize(formId);
        myAjax._doAjax("post", URL, json, callback);
    }

    //=======================内部方法=====================
    //将JSON转换为URL查询参数写法
    //传入{"id":12,"name":"考拉"}
    //返回id=12&name=%45%45%ED
    myAjax._JSONtoURLparams = function (json) {
        var arrParts = [];  //每个小部分的数组
        for (k in json) {
            //组成参数数组,然后用& 连接
            arrParts.push(k + "=" + encodeURIComponent(json[k]));//需要uri编码特殊字符串,例如中文或者符号
        }
        return arrParts.join("&");
    }

    //最核心的发出Ajax请求的方法
    myAjax._doAjax = function (method, URL, json, callback) {
        //Ajax的几个公式
        if (XMLHttpRequest) {
            var xhr = new XMLHttpRequest();
        } else {
            var xhr = ActiveXObject("Microsoft.XMLHTTP");
        }

        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
                    callback(null, xhr.responseText);
                } else {
                    callback("文件没有找到" + xhr.status, null);
                }
            }
        }

        //现在要根据请求类型进行判断
        if (method == "get") {
            //请求类型是get
            //如果用户传输了json,此时要连字
            if (json) {
                //判断URL本身是否有?,没有就需要&连接
                var combineChar = URL.indexOf("?") == -1 ? "?" : "&";
                //将json转为url参数后拼接
                URL += combineChar + myAjax._JSONtoURLparams(json);
            }
            //增加一个随机数参数,防止缓存
            var combineChar = URL.indexOf("?") == -1 ? "?" : "&";
            URL += combineChar + Math.random().toString().substr(2);

            xhr.open("get", URL, true);
            xhr.send(null);
        } else if (method == "post") {
            //增加一个随机数参数,防止缓存
            var combineChar = URL.indexOf("?") == -1 ? "?" : "&";
            URL += combineChar + Math.random().toString().substr(2);

            xhr.open("post", URL, true);
            //post需要有header
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xhr.send(myAjax._JSONtoURLparams(json));
        }
    }
})();

五、关于ajax缓存问题

当Ajax第一次发送请求后,会把请求的URL和返回的响应结果保存在缓存内,当下一次调用Ajax发送相同的请求时,注意,这里相同的请求指的是URL完全相同,包括参数,浏览器就不会与服务器交互,而是直接从缓存中把数据取出来,这是为了提高页面的响应速度和用户体验。(服务端也会收到请求响应304)

浏览器会自作主张的把所有异步请求来的文件缓存,当下一次请求的URL和之前的一样,那么浏览器将不会发送这个请求,而是直接把缓存的内容当做xhr.responseText

需要注意的是,post 请求方式不会被缓存,只有 get 请求方式会被缓存。

5.1 如何避免 ajax 缓存问题

方法1:随机数

//随机数,我们不要0. 只要小数点后面的数字:
var random = Math.random().toString().substring(2);
 //URL上面就拼接一个随机字符串,保证每次URL不一样
myAjax.get("text.txt?" + random,function(err,data){
    alert(data);
});

方法2:时间戳 从1970年1月1日0:00到这一刻的毫秒数。就叫做时间戳。英语属于timestamp。 JS里面时间戳就是

//时间戳:
var timestamp = Date.parse(new Date());
 //URL上面就拼接一个随机字符串,保证每次URL不一样
myAjax.get("text.txt?" + timestamp,function(err,data){
    alert(data);
});

总的来说,原理就是通过将 get 请求的 url 做成每次都不一样,这样就不会被浏览器缓存了。

六、json检测

判断返回的 json 数据是否可用,这个只是属性一些日常使用 ajax 的点而已。

6.1 使用 JSON.parse

通过JSON.parse转换为json格式,如果无法转换,会报错。

var jsonObj = JSON.parse(str);

6.2 用hasOwnProperty进行判断

hasOwnProperty 这个方法能够判断对象里面是否有某个键属性。

var obj = {"a":1,"b":2};
console.log(obj.hasOwnProperty("aaa"));

这个示例比较详细,并且加入了错误之后的处理:

         //得到页面上的用户名的文本框、下拉列表
        var oUsername = document.getElementById("username");
        var oDomain = document.getElementById("domain");

        //得到good、bad、tuijian
        var oTuijian = document.getElementById("tuijian");
        var oBadTip = document.getElementById("badTip");
        var oGoodTip = document.getElementById("goodTip");

        //得到4个li(事先给定或者从其他接口获取的)
        var tuijianLis = oTuijian.getElementsByTagName("li");

        //失去焦点和改变下拉列表,都是做同一个事情
        oUsername.onblur = check;
        oDomain.onchange = check;

        function check(){
            clearAllTip();  //清除所有提示框
            //得到值
            var username = oUsername.value;  //文本框
            //获取所有用户选中的邮箱选项,并放入到domain数组
            var domain = (function(){
                //得到所有option
                var options = oDomain.getElementsByTagName("option");
                //遍历,看看哪个被selected了
                for(var i = 0 ; i < options.length ; i++){
                    if(options[i].selected){
                        return options[i].value;
                    }
                }
            })();

            //如果这个值是空,那么什么也不做。
            if(!username) {
                return;
            }
            //正则验证合法性
            //6~18个字符,可使用字母、数字、下划线,需以字母开头
            var reg = /^[A-Za-z][\w]{5,17}$/;
            if (!reg.test(username)) {
                showWrong("6~18个字符,可使用字母、数字、下划线,需以字母开头");
                return; //不合法的时候,就返回,不执行下面的语句了
            }

            //这里请求一个静态json,实际上要请求后台php页面。
            myAjax.post("check.json",{"username" : username},function(err,data){
                if(err){
                    showWrong("服务器错误,稍后再试");
                    return;
                }
                //转为json格式:
                var dataJSON = JSON.parse(data);
                //获得result对象(即获取服务器返回的验证结果)
                var result = dataJSON.result;
                //如果没有result对象。就创造一个result对象
                if(!result){
                    var result = {}; //因为需要给后续的hasOwnProperty校验
                }
                //检测是否可用
                if(result.hasOwnProperty(domain)){//服务器验证结果跟用户选项一致的时候
                    showRight("恭喜,可用!");
                }else{ //服务器验证结果跟用户选项不一致的时候
                    //就要给用户显示推荐的邮箱
                    oTuijian.style.display = "block";   //显示推荐框
                    //我们要依次查找这些域名是否可用(事先给定或者从其他接口获取的)
                    var domainArray = ["163.com","126.com","yeah.net"];
                    //我们再写一个结果数组
                    var usableArray = [];
                    //遍历domainArray,把domainArray中的每一个项,进行检测
                    //检测result对象中是不是有这个属性
                    //直接获取了判断的结果的数组
                    for(var i = 0 ; i < domainArray.length ; i++){
                        var tOrf = result.hasOwnProperty(domainArray[i]) ? true : false;
                        usableArray.push(tOrf);
                    }
                    console.log(usableArray);

                    //遍历4个li标签,根据我们的结果数组来决定他们
                    //是否有disable类、里面的span的内容、b的内容
                    for(var i = 0 ; i < tuijianLis.length ; i++){
                        var thisli = tuijianLis[i];

                        //通过判断的结果的数组的值来控制是否设置class
                        //决定这个li是否有disable类
                        thisli.className = usableArray[i] ? "" : "disable";

                        //往span里面写内容
                        //得到这唯一一个span
                        var thisspan = thisli.getElementsByTagName("span")[0];
                        //有时候需要重新解析一些值的格式
                        if(domainArray[i] == "vip163"){
                            domainArray[i] = "vip.163.com";
                        }
                        thisspan.innerHTML = username + "@" + domainArray[i];

                        //往b里面写内容
                        var thisb = thisli.getElementsByTagName("b")[0];
                        //通过判断的结果的数组的值来控制显示内容
                        thisb.innerHTML = usableArray[i] ? "可以使用" : "已经被占用";
                    }
                }
            });
        }

        //得到焦点
        oUsername.onfocus = clearAllTip;

        function clearAllTip(){
            //让所有的提示框消失
            oTuijian.style.display = "none";
            oBadTip.style.display = "none";
            oGoodTip.style.display = "none";
        }

        //显示错误提示框
        function showWrong(info){
            oBadTip.innerHTML = info;
            oBadTip.style.display = "block";
        }

        //显示正确提示框
        function showRight(info){
            oGoodTip.innerHTML = info;
            oGoodTip.style.display = "block";
        }

七、关于跨域问题

已经在另外一篇文章里面说过了,jsonp 是其中一种解决办法。

微信:https://mp.weixin.qq.com/s?__biz=MzA3MjIxMzE4NQ==&mid=303969760&idx=1&sn=e12c5dc5109a64340dce072c737ce765&chksm=08cb224e3fbcab5842d1ba5125863a6c5a6d3f0b9b218ca326d5e759c22eaf10c9af57e7db3b#rd)

blog:https://www.godblessyuan.com/2018/07/jsonp-反向代理-CORS解决JS跨域问题的个人总结(更新%20v2.0).html

7.1 使用jsonp

//给按钮添加监听
        oBtn.onclick = function(){
            //得到用户填写的手机号
            var danhao = odanhao.value;
            var kuaidigongsi = okuaidigongsi.value;

            //创建script
            var script = document.createElement("script");
            script.src = "https://sp0.baidu.com/9_Q4sjW91Qh3otqbppnN2DJv/pae/channel/data/asyncqury?cb=xixi&appid=4001&com=" + kuaidigongsi +"&nu=" + danhao +"&vcode=&token=&_=1438916675664"

            //追加然后删除
            document.body.appendChild(script);
            document.body.removeChild(script);
        }

        function xixi(data){
            console.log(data);
        }

八、关于ajax的示例:瀑布流

要实现2个地方:

  1. 滚动到底部判断(包含视口的底部和总的底部)
  2. 瀑布流里面的内容需要错位显示

8.1 滚动到底部判断

我们需要知道:

  • 总文档高度
  • 已经滚动的高度
  • 视口高度,通过$(document).height(); 获取,视口底部来触发ajax 获取下一页的数据
  • 总文档高度-已经卷动高度-视口高度 < 200 基本上就是滚动到底了,滚动到文档底部就停止 ajax 请求。
  • scroll事件,一定是要截流的。因为用户滚一个鼠标滚轮的“小咯噔”就触发一次scroll事件;滑动滚动条的时候,是每一像素触发一次这个事件。还有pageDown、下箭头按钮,都能触发scroll事件。
  • 如何判断文章是否到头,说白了前端开发工程师不知道一共有多少页。比如今天又53页,明天就有55页了,所以你的JS里面无法写死一个文章总页数。所以办法就是,请求下去,请求到page.php?pagenum=54的时候,发现终止标记,或者这个页面返回的json是空,就表示到头了。

8.2 瀑布流里面的内容需要错位显示

  • 这里分成三列瀑布流,组成一个数组管理
    • 这个数组会不断计算三列之中的最小值
    • 然后按照每次的最小值进行高度插入
  • 图片判断是否加载完成需要用load方法,并且图片需要先new image才能加载方法
  • 图片的插入次序不是固定的(ajax异步),所以用之前的数组进行管理,每次都对最小值的高度插入值,这样就能保证每次都往最靠里面的图片位置进行放置
    • 并且需要使用绝对位置值,因为css里面,需要使用绝对值撑开位置(left 和top)

瀑布流的数组样例如下:

// 第一行
// [0,0,0]   minIndex: 0 left 0 top 20  [558,0,0 ]
// [558,0,0] minIndex: 1 left 300 top 20 [558,386,0]
// [558,386,0] minIndex:2 left 600 top 20 [558,386,722]
//第二行
//[558,386,722] minIndex:1 left 300 top 406 [558, 943, 722]
//[558, 943, 722] minIndex:0 left 0 top 578 [1193, 943, 722]
//[1193, 943, 722] minIndex:2 left 600 top 742 [1193, 943, 1128]

8.3 整个代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>Document</title>
    <style type="text/css">
        <!--省略样式-->
    </style>
</head>
<body>
<!--加载logo,默认隐藏-->
<div class="waterfall">

</div>
<div class="end">
    到最后了亲!
</div>
<script type="text/javascript" src="js/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="js/underscore.js"></script>
<!-- 模板,用underscore解析 -->
<script type="type/template" id="feed-template">
    <div class="feed-item">
        <p>
            <img src="<%= imgurl %>" alt=""/>
        </p>
        <p class="biaoti">
            <%= title %>
        </p>
        <p class="neirong">
            <%= content %>
        </p>
        <p class="zuozhe">
            <%= author %>
        </p>
    </div>
</script>
    var $waterfall = $(".waterfall");
    //得到模板
    var templateString = $("#feed-template").html();
    //准备模板函数,通过underscore将模板函数转为html模板,全局使用,所以单独拿出来
    var compile = _.template(templateString);

    //准备总高度数组
    var colAllHeight = [0, 0, 0]; //三个表示页面的瀑布的三列的每一个块的高度

    var pagenum = 1;    //页码

    getAndRender(1); //先渲染第一页的内容

    var lock = true;    //函数截流

    //窗口卷动监听
    //每滚动一次都会触发
    $(window).scroll(function () {
        //jquery帮我们做了关于滚动的三个兼容处理:总文档高度,已经卷动高度,视口高度
        var scrollTop = $(window).scrollTop();
        var windowHeight = $(window).height();
        var documentHeight = $(document).height();
        //已经滚动到底部并且已经被lock
        if (documentHeight - windowHeight - scrollTop < 200 && lock) {
            lock = false; //解除锁定
            pagenum++; //滚动一次加一次页数
            getAndRender(pagenum); //根据页数渲染数据,并且里面会重新锁定
        }
    });

    function getAndRender(pagenum) {
        //让加载logo显示
        $waterfall.addClass("loading");
        //发出Ajax请求
        //这里的页数是用简单的文件的数字编号来代替
        $.get("json/json" + pagenum + ".txt", function (data, statusText) { //jq的ajax的get方法
            //把字符串转为对象
            var dataJSON = JSON.parse(data);
            //news这个数组,仔细想想,news这个数组里面装的是什么?
            var dictionaryArray = dataJSON.news;

            //如果数组为空,就表示到最后了
            if (dictionaryArray.length == 0) {
                $(".end").show();
                $waterfall.removeClass('loading');
                return;
            }

            //遍历从接口获取的数据
            for (var i = 0; i < dictionaryArray.length; i++) {
                var thisDictionary = dictionaryArray[i];
                //马上发出请求这个字典里面图片的请求
                var image = new Image();
                //一旦设置src,上行HTTP请求将发出
                image.src = thisDictionary.imgurl;
                image.index = i; //设置这个image的索引值
                //监听这个图片是不是加载完毕
                $(image).load(function () {
                    //这张图片加载完毕了
                    //console.log(this.index + "号图片加载完毕");

                    //填充字典
                    //哪个图片已经填充完了,就注入几号字典
                    //例如第一个图片,传入转为html模板的函数
                    var compiledString = compile(dictionaryArray[this.index]);

                    //得到这个盒子,变为jQuery对象
                    var $box = $(compiledString);

                    //上DOM
                    $waterfall.append($box);

                    //寻找最小列
                    var min = _.min(colAllHeight);
                    //寻找最小列的索引
                    var minIndex = _.indexOf(colAllHeight, min);
                    //绝对定位:
                    $box.css("left", 300 * minIndex);
                    $box.css("top", colAllHeight[minIndex] + 20);
                    //将自己的高度,也加到数组的指定列中:
                    colAllHeight[minIndex] += $box.outerHeight() + 20;
                    //淡入
                    $box.fadeIn();
                    //让加载滚动的logo有高度,跟随移动位置
                    $waterfall.css("height", _.max(colAllHeight));
                    $waterfall.removeClass("loading");

                    lock = true;
                });
            }
        });
    }

原文发布于微信公众号 - 前端正义联盟(wnb020020)

原文发表时间:2018-08-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Spark学习技巧

Kafka源码系列之源码解析SimpleConsumer的消费过程

Kafka源码系列是以kafka 0.8.2.2源码为例讲解。浪尖纯属个人爱好,才写想写这个系列。希望对kafka的使用者们带来帮助。 一,消费者消费的过程讲解...

3847
来自专栏不止是前端

一次搞懂Event loop

4508
来自专栏ShaoYL

UIViewController的生命周期及iOS程序执行顺序

28911
来自专栏编程直播室

使用Ionic2开发Todo应用0 开始之前1 创建新的Ionic 2工程2. 设置主页(Home page)3 持久化数据保存4 总结

2515
来自专栏大史住在大前端

webpack4.0各个击破(2)—— CSS篇

以webpack4.0版本为例来演示CSS模块的处理方式,需要用到的插件及功能如下:

1253
来自专栏Java帮帮-微信公众号-技术文章全总结

springboot 入门教程(5) 基于ssm框架的crud操作(前端部分-附源码)

springboot 入门教程(5) 基于ssm框架的crud操作(前端部分-附源码) 上一篇介绍了spring boot整合mybatis实现了后端的CRUD...

4548
来自专栏简书专栏

基于bs4+requests爬取世界赛艇男运动员信息

2018年8月22日笔记 新手学习如何编写爬虫,可以注册1个网易账号,在网易云课堂上学习《Python网络爬虫实战》,链接:http://study.163....

1234
来自专栏小白课代表

Keil uVision 5 安装教程。

1834
来自专栏我的博客

安卓开发遇到的奇葩问题

第一个问题:R cannot be resolved to a variable 来自网络: Android开发过程中,碰到R cannot be resolv...

3206
来自专栏Python

Django---Ajax

Ajax准备知识:json 什么是json? 定义: JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换...

28310

扫码关注云+社区