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

xiaoxiao2021-02-28  6

终于可来搞一搞日志模块的源码了,其实代码都很简单(哈哈哈),我开了一下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对象的网络请求流程

在后台与服务器交换数据,就会用到XMLHttpRequest对象网络请求发生时,先回创建XMLHttpRequest对象即readyState = 0 也是调用open()方法紧接着XMLHttpRequest对象调用send()方法,发送客户端网络请求,等待服务器返回,即readyState的值为1 ,2,3的变化中(其中何时变化为何值可以参考下https://www.cnblogs.com/liujiale/p/5388110.html)再次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; }

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

转载请注明原文地址: https://www.6miu.com/read-2450215.html

最新回复(0)