UNPKG

rn-web-cli

Version:

React Native for Web

359 lines (325 loc) 11.3 kB
/** * 名称:简单异常捕获调试工具 * 日期:2016-12-25 * 描述: */ (function () { function CustomLocalStorage() { } CustomLocalStorage.prototype.getItem = function (name) { return ''; } CustomLocalStorage.prototype.setItem = function (name, value) { return ''; } CustomLocalStorage.prototype.getStorage = function () { return this.isStorageSupported() ? window.localStorage : this; } CustomLocalStorage.prototype.isStorageSupported = function () { var testKey = 'supoort'; var storage = window.localStorage; try { storage.setItem(testKey, 'true'); storage.removeItem(testKey); return true; } catch (error) { return false; } } window.localStorage2 = new CustomLocalStorage();//null }()); (function () { //设置 var canClick = false; var localStorage2 = window.localStorage2; var localStorage = localStorage2.getStorage(); /** * 异常捕获工具构造函数 * @constructor */ function ErrorCapture() { //这里暂时使用bj-report进行进行错误上报,而不是使用自带的异常捕获流程 this.initCapture(); //初始化本地调试 this.initLocalDebug(); } /** * 返回一个无缓存url */ ErrorCapture.prototype.getTimestampUrl = function () { var url = location.href; var joinChar = "?"; var timers = +new Date(); var version = '__devtool=' + timers; if (url.indexOf("?") > -1) { joinChar = "&"; } if (url.indexOf("?__devtool=") > -1) { url = url.replace(/\?__devtool\=\d+/, '?=' + version); } else if (url.indexOf("&__devtool=") > -1) { url = url.replace(/\&__devtool\=\d+/, '&=' + version); } else { url = [url, version].join(joinChar); } return url; } /** * bind实现 * @param handler 要代理的函数 * @param context 代理函数额外参数 */ ErrorCapture.prototype.bind = function (handler, context) { var self = this; return function () { var args = Array.prototype.slice.call(arguments, 0, arguments.length).concat(context); return handler.apply(self, args); } } /** * 初始化异常捕获 */ ErrorCapture.prototype.initCapture = function () { this.captureCustom(); //捕获window.error this.captureWindowError(); //监听setTimeout 异常 this.tryCatchOf(window, 'setTimeout'); //监听setInterval异常 this.tryCatchOf(window, 'setInterval'); } /** * 监听全局自定义异常函数 */ ErrorCapture.prototype.captureCustom = function () { var originErrorHandler = window.onWebpackRequireErrorCapture; var thisContext = this; window.onWebpackRequireErrorCapture = function (error) { if (typeof originErrorHandler === 'function') { originErrorHandler.apply(this, error); } thisContext.onErrorOccur(error.message, error); } } /** * 初始化异常本地调试信息 */ ErrorCapture.prototype.initLocalDebug = function () { this.initShowPanel(); this.__debug = localStorage.getItem('__debug') == 'true'; canClick = this.__debug; this.initDebug(); } /** * 添加调试模式标记 */ ErrorCapture.prototype.initDebug = function (force) { if (this.__debug || force) { var thisContext = this; var pane = document.createElement('div'); pane.style.cssText = [ 'z-index:9999999999;padding:10px;', 'word-wrap: break-word;position:fixed;', 'top:0px;left:0px;right:0px;background-color:rgba(19, 18, 18, 0.83);', 'color:#fff;white-space:pre-line;' ].join(''); pane.innerHTML = '开启调试,再次连击10次关闭调试模式'; document.body.appendChild(pane); pane.onclick = remove; function remove() { thisContext.debugPane = null; pane.onclick = null; if (pane.parentNode) { document.body.removeChild(pane); } } setTimeout(remove, 2000); this.debugPane = pane; } } /** * InitShowErrorPanel */ ErrorCapture.prototype.initShowPanel = function () { var context = this; this.addEventListener(document, 'touchstart', this.createTimesHandler(function (ev) { if (ev.times == 10) { //重置掉timers ev.reset(); var debug = !context.__debug; if (debug) { context.initDebug(true); setTimeout(function () { canClick = true; }, 2000); if (context.pane) { context.pane.style.display = ''; } } else if (context.debugPane && context.debugPane.parentElement) { context.debugPane.parentElement.removeChild(context.debugPane); context.debugPane = null; canClick = true; } context.__debug = debug; localStorage.setItem('__debug', debug); } })); } /** * 初始化消息浮层 */ ErrorCapture.prototype.initErrorPanel = function () { var pane = document.createElement('div'); pane.style.cssText = [ 'z-index:999999;padding:10px;word-wrap: break-word;', 'display:none;position:fixed;', '' + 'top:0px;bottom:0px;left:0px;right:0px;', 'background-color:rgba(19, 18, 18, 0.83);', 'color:#fff;white-space:pre-line;', 'overflow-y:auto;' ].join('') pane.innerHTML = ''; this.pane = pane; if (document.body) { document.body.appendChild(pane); } else { pane.id = '______errorpanel'; document.write(pane.outerHTML); this.pane = document.getElementById(pane.id); } this.addEventListener(this.pane, 'click', this.bind(function () { if (canClick) { this.pane.style.display = 'none'; canClick = false; } })); } /** * 技能:捕获window.error */ ErrorCapture.prototype.captureWindowError = function () { this.addEventListener(window, 'error', this.bind(function (ev) { this.onErrorOccur(ev.message, ev.error, [ev.filename, ev.lineno, ev.colno].join(':')); })); } /** * 监听指定对象上的指定函数 */ ErrorCapture.prototype.tryCatchOf = function (obj, name) { var originMethod = obj[name]; if (typeof originMethod !== 'function') { return; } var tryCatchMethod = this.tryCatch(originMethod); //复制function的属性 this.doAssign(tryCatchMethod, originMethod); obj[name] = tryCatchMethod; } /** * 包裹函数,监听指定函数异常,并且返回函数代理 * @param handler 要监听的函数 */ ErrorCapture.prototype.tryCatch = function (handler) { var self = this; return function () { try { return handler.apply(this, arguments); } catch (ex) { self.onErrorOccur(ex.message, ex); } } } /** * 返回一个调用次数统计函数,在指定的间隔中连续调用会进行调用次数统计,并且传递给回调函数 * @param delay {Number} 调用间隔 * @param handler {Function} 回调函数 * @returns {Function} 统计函数 */ ErrorCapture.prototype.createTimesHandler = function (delay, handler) { if (arguments.length == 1) { handler = delay; delay = null; } delay = delay || 250; var callTimes = 0; var prevCallTime = null; function reset() { callTimes = 0; } return function (ev, others) { if (prevCallTime == null) { prevCallTime = +new Date(); } var currentTime = +new Date(); if (currentTime - prevCallTime > delay) { callTimes = 0; prevCallTime = null; } else { callTimes++; prevCallTime = currentTime; } ev.reset = reset; ev.times = callTimes; return handler.apply(this, [ev, reset]); } } /** * 出现异常啦 * @param message 异常消息 * @param error 异常对象 */ ErrorCapture.prototype.onErrorOccur = function (message, error, filename) { error = error || {}; var message = error.stack || message; this.showPane([filename, message].join('\n')); } /** * 显示异常消息 */ ErrorCapture.prototype.showPane = function (message) { if (!this.pane) { this.initErrorPanel(); } var pane = this.pane; pane.style.display = this.__debug ? '' : 'none'; pane.innerHTML = message; } /** * 浅复制对象 * @param target 目标对象 * @param from1 复制来源1 * ..... * @param fromN 复制来源N */ ErrorCapture.prototype.doAssign = function (target, from1, fromN) { var sources = Array.prototype.slice.call(arguments, 1, arguments.length); var source = null; for (var i = 0, k = sources.length; i < k; i++) { source = sources[i]; for (var key in source) { target[key] = source[key]; } } return target; } /** * 兼容方式为指定元素添加指定类型事件 * @param element {HTMLELEMENT} dom元素 * @param name 事件名称 例如:click dblclick * @param handler 事件处理函数 */ ErrorCapture.prototype.addEventListener = function (element, name, handler) { if (element.addEventListener) { element.addEventListener(name, handler); } else if (element.attachEvent) { element.attachEvent('on' + name, handler); } else { var origin = element['on' + name]; element[name] = function () { origin && origin(); return handler.apply(this, arguments); } } } //创建异常捕获实例 window.ExCapture = new ErrorCapture(); }());