关于vConsole 源码的理解分享(vConsole一个移动端调试控制台工具)(2)

终于可来搞一搞日志模块的源码了,其实代码都很简单(哈哈哈),我开了一下git的日志,想来我们现在看到的代码,都是之前迭代的代码,不是一开始,一个函数就很多行代码的,所以要理清条路,理解为什么这样加代码,当然能一开始就分清楚函数定义多少个,后面好加代码,扩展的 这个思路架构也是我们要瞻仰,学习的。
log这个模块的类图结构知道,它在VConsolePlugin上又封装了一层,类图结构件上一篇文章上一篇文章
即VConsoleLogTab 作为一个log模块基础的类结构
废话不多说直接贴代码看实现,代码才是王道

什么构造函数和初始化就不说了,直接来看怎么替代js浏览器对象的打印

/**
     * replace window.console with vConsole method
     * @private
     */
    mockConsole() {
        const that = this;
        const methodList = ['log', 'info', 'warn', 'debug', 'error'];
        if (!window.console) {
            window.console = {};
        } else {
            methodList.map(function(method) {
                that.console[method] = window.console[method];
            });
            that.console.time = window.console.time;
            that.console.timeEnd = window.console.timeEnd;
            that.console.clear = window.console.clear;
        }

        methodList.map(method => {
            window.console[method] = (...args) => {
                this.printLog({
                    logType: method,
                    logs: args
                });
            };
        });

        const timeLog = {}
        window.console.time = function(label) {
            timeLog[label] = Date.now();
        };
        window.console.timeEnd = function(label) {
            var pre = timeLog[label];
            if (pre) {
                console.log(label + ':', (Date.now() - pre) + 'ms');
                delete timeLog[label];
            } else {
                console.log(label + ': 0ms');
            }
        };

        window.console.clear = (...args) => {
            that.clearLog();
            that.console.clear.apply(window.console, args);
        };
    }

该方法就是模拟console对象

  • 判断window的console对象是否存在
  • 如果不存在就让console = {},for循环自己实现,console的erron,log等方法,
  • 如果存在,劫持console.log的各个方法,将方法保存在that对象中,其实我也不知道这个是个什么意思,我觉得这个劫持可以不要,反正你后面都是全部自己实现了log,error,warn,clear,time,timeEnd,等方法,不知道作者这里是个什么意思

这样差不多log模块毕,接下来看看network,这里就不卖关子了,其实网络请求也是通过类似于日志模块模拟ajax的方式。

在此之前,来梳理下window.XMLHttpRequest对象的网络请求流程

  1. 在后台与服务器交换数据,就会用到XMLHttpRequest对象
  2. 网络请求发生时,先回创建XMLHttpRequest对象即readyState = 0 也是调用open()方法
  3. 紧接着XMLHttpRequest对象调用send()方法,发送客户端网络请求,等待服务器返回,即readyState的值为1 ,2,3的变化中(其中何时变化为何值可以参考下https://www.cnblogs.com/liujiale/p/5388110.html
  4. 再次XMLHttpRequest等待服务器响应完毕,readyState为4
    注意这个onreadystatechange这个事件很重要,每当readyState值变化是都会触发这个方法,在这个方法我们就可以取用XMLHttpRequest对象的一些有用的属性值来做文章了比如responseText,status等
    所以了解了这个过程,在来看下面的代码就很简单,说白了就是重写了send,open,onreadystatechange等方法
  /**
     * mock ajax request
     * @private
     */
    mockAjax() {
        let _XMLHttpRequest = window.XMLHttpRequest;
        if (!_XMLHttpRequest) { return; }
        //将this存储起来
        let that = this;
        let _open = window.XMLHttpRequest.prototype.open,
            _send = window.XMLHttpRequest.prototype.send;
        that._open = _open;
        that._send = _send;

        // 模拟XMLHttpRequest的open方法
        window.XMLHttpRequest.prototype.open = function() {
            let XMLReq = this;
            //分割请求的参数
            let args = [].slice.call(arguments),
                method = args[0],
                url = args[1],
                id = that.getUniqueID(); //设置一个值存储当前请求的唯一id,唯一标识
            //定义一个时间计时器
            let timer = null;

            // may be used by other functions
            XMLReq._requestID = id;
            XMLReq._method = method;
            XMLReq._url = url;

            //  模拟XMLHttpRequest的onreadystatechange
            let _onreadystatechange = XMLReq.onreadystatechange || function() {};
            let onreadystatechange = function() {
                let item = that.reqList[id] || {};
                // update status
                item.readyState = XMLReq.readyState;
                item.status = 0;
                if (XMLReq.readyState > 1) {
                    item.status = XMLReq.status;
                }
                item.responseType = XMLReq.responseType;

                if (XMLReq.readyState == 0) {
                    // UNSENT
                    if (!item.startTime) {
                        item.startTime = (+new Date());
                    }
                } else if (XMLReq.readyState == 1) {
                    // OPENED
                    if (!item.startTime) {
                        item.startTime = (+new Date());
                    }
                } else if (XMLReq.readyState == 2) {
                    // HEADERS_RECEIVED
                    item.header = {};
                    let header = XMLReq.getAllResponseHeaders() || '',
                        headerArr = header.split("\n");
                    // extract plain text to key-value format
                    for (let i = 0; i < headerArr.length; i++) {
                        let line = headerArr[i];
                        if (!line) { continue; }
                        let arr = line.split(': ');
                        let key = arr[0],
                            value = arr.slice(1).join(': ');
                        item.header[key] = value;
                    }
                } else if (XMLReq.readyState == 3) {
                    // LOADING
                } else if (XMLReq.readyState == 4) {
                    // DONE
                    clearInterval(timer);
                    item.endTime = +new Date(),
                        item.costTime = item.endTime - (item.startTime || item.endTime);
                    item.response = XMLReq.response;
                } else {
                    clearInterval(timer);
                }

                if (!XMLReq._noVConsole) {
                    that.updateRequest(id, item);
                }
                return _onreadystatechange.apply(XMLReq, arguments);
            };
            //覆盖原始默认的onreadystatechange
            XMLReq.onreadystatechange = onreadystatechange;
            //为了怕请求过程占用第三方应用汇修改xhr默认的方法,所以用了一个定时器循环来监听readyState的变化
            let preState = -1;
            timer = setInterval(function() {
                if (preState != XMLReq.readyState) {
                    preState = XMLReq.readyState;
                    onreadystatechange.call(XMLReq);
                }
            }, 10);

            return _open.apply(XMLReq, args);
        };

        // 默认send方法
        window.XMLHttpRequest.prototype.send = function() {
            let XMLReq = this;
            let args = [].slice.call(arguments),
                data = args[0];
            //重请求池找出相应的请求
            let item = that.reqList[XMLReq._requestID] || {};
            item.method = XMLReq._method.toUpperCase();
            //处理url后面跟着的参数,
            //1,先以?分割为数组
            let query = XMLReq._url.split('?'); // a.php?b=c&d=?e => ['a.php', 'b=c&d=', '?e']
            // 2,在去除最前面的数组
            item.url = query.shift(); // => ['b=c&d=', '?e']
            if (query.length > 0) {
                item.getData = {};
                //3,然后剩下的?又重新连接在一起
                query = query.join('?'); // => 'b=c&d=?e'
                //4,在以& 去键值对
                query = query.split('&'); // => ['b=c', 'd=?e']
                for (let q of query) {
                    q = q.split('=');
                    item.getData[q[0]] = q[1];
                }
            }
            //处理post请求方式,注意这里 会有url接参数,但又是post请求的情况,这里也能处理
            if (item.method == 'POST') {
                // save POST data
                if (tool.isString(data)) {
                    let arr = data.split('&');
                    item.postData = {};
                    for (let q of arr) {
                        q = q.split('=');
                        item.postData[q[0]] = q[1];
                    }
                } else if (tool.isPlainObject(data)) {
                    item.postData = data;
                }

            }

            if (!XMLReq._noVConsole) {
                that.updateRequest(XMLReq._requestID, item);
            }

            return _send.apply(XMLReq, args);
        };

    };

这个就比较底层了,其实也不是,只不过我们平时开发的时候,习惯用封装好的比如jq等,其实背后做了很多时候事情!

接下来是最后的存储模块,VConsole里主要是列了cookie和localStorage的存储
localStorage就不说了,贴心cookie的的代码


  getCookieList() {
    if (!document.cookie || !navigator.cookieEnabled) {
      return [];
    }

    let list = [];
    let items = document.cookie.split(';');
    for (let i=0; i<items.length; i++) {
      let item = items[i].split('=');
      let name = item.shift().replace(/^ /, ''),
          value = item.join('=');
      list.push({
        name: decodeURIComponent(name),
        value: decodeURIComponent(value)
      });
    }
    return list;
  }

结束了,以上是个人见解,欢迎指正批评!谢谢

发布了38 篇原创文章 · 获赞 14 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/sinat_23156865/article/details/80919430