本文主要是介绍关于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对象的网络请求流程
- 在后台与服务器交换数据,就会用到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 functionsXMLReq._requestID = id;XMLReq._method = method;XMLReq._url = url;// 模拟XMLHttpRequest的onreadystatechangelet _onreadystatechange = XMLReq.onreadystatechange || function() {};let onreadystatechange = function() {let item = that.reqList[id] || {};// update statusitem.readyState = XMLReq.readyState;item.status = 0;if (XMLReq.readyState > 1) {item.status = XMLReq.status;}item.responseType = XMLReq.responseType;if (XMLReq.readyState == 0) {// UNSENTif (!item.startTime) {item.startTime = (+new Date());}} else if (XMLReq.readyState == 1) {// OPENEDif (!item.startTime) {item.startTime = (+new Date());}} else if (XMLReq.readyState == 2) {// HEADERS_RECEIVEDitem.header = {};let header = XMLReq.getAllResponseHeaders() || '',headerArr = header.split("\n");// extract plain text to key-value formatfor (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) {// DONEclearInterval(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);};//覆盖原始默认的onreadystatechangeXMLReq.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 dataif (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;}
结束了,以上是个人见解,欢迎指正批评!谢谢
这篇关于关于vConsole 源码的理解分享(vConsole一个移动端调试控制台工具)(2)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!