前端工程師都知道 JavaScript 有基本的異常處理能力。我們可以 throw new Error(),瀏覽器也會在我們調用 API 出錯時拋出異常。但估計絕大多數前端工程師都沒考慮過收集這些異常信息
反正只要 JavaScript 出錯后刷新不復現,那用戶就可以通過刷新解決問題,瀏覽器不會崩潰,當沒有發生過好了。這種假設在 Single Page App 流行之前還是成立的?,F在的 Single Page App 運行一段時間后狀態復雜無比,用戶可能進行了若干輸入操作才來到這里的,說刷新就刷新???之前的操作豈不要完全重做?所以我們還是有必要捕獲和分析這些異常信息的,然后我們就可以修改代碼避免影響用戶體驗。
捕獲異常的方式
我們自己寫的 throw new Error() 想要捕獲當然可以捕獲,因為我們很清楚 throw 寫在哪里了。但是調用瀏覽器 API 時發生的異常就不一定那么容易捕獲了,有些 API 在標準里就寫著會拋出異常,有些 API 只有個別瀏覽器因為實現差異或者有缺陷而拋出異常。對于前者我們還能通過 try-catch 捕獲,對于后者我們必須監聽全局的異常然后捕獲。
try-catch
如果有些瀏覽器 API 是已知會拋出異常的,那我們就需要把調用放到 try-catch 里面,避免因為出錯而導致整個程序進入非法狀態。例如說 window.localStorage 就是這樣的一個 API,在寫入數據超過容量限制后就會拋出異常,在 Safari 的隱私瀏覽模式下也會如此。
對于 try-catch 覆蓋不到的地方,如果出現異常就只能通過 window.onerror 來捕獲了。
屬性丟失
假設我們有一個 reportError 函數用來收集捕獲到的異常,然后批量發送到服務器端存儲以便查詢分析,那么我們會想要收集哪些信息呢?比較有用的信息包括:錯誤類型(name)、錯誤消息(message)、腳本文件地址(script)、行號(line)、列號(column)、堆棧跟蹤(stack)。如果一個異常是通過 try-catch 捕獲到的,這些信息都在 Error 對象上(主流瀏覽器都支持),所以 reportError 也能收集到這些信息。但如果是通過 window.onerror 捕獲到的,我們都知道這個事件函數只有 3 個參數,所以這 3 個參數意外的信息就丟失了。
序列化消息
如果 Error 對象是我們自己創建的話,那么 error.message 就是由我們控制的?;旧衔覀儼咽裁捶胚M error.message 里面,window.onerror 的第一個參數(message)就會是什么。(瀏覽器其實會略作修改,例如加上 'Uncaught Error: ' 前綴。)因此我們可以把我們關注的屬性序列化(例如 JSON.Stringify)后存放到 error.message 里面,然后在 window.onerror 讀取出來反序列化就可以了。當然,這僅限于我們自己創建的 Error 對象。
第五個參數
瀏覽器廠商也知道大家在使用 window.onerror 時受到的限制,所以開始往 window.onerror 上面添加新的參數。考慮到只有行號沒有列號好像不是很對稱的樣子,IE 首先把列號加上了,放在第四個參數。然而大家更關心的是能否拿到完整的堆棧,于是 Firefox 說不如把堆棧放在第五個參數吧。但 Chrome 說那還不如把整個 Error 對象放在第五個參數,大家想讀取什么屬性都可以了,包括自定義屬性。結果由于 Chrome 動作比較快,在 Chrome 30 實現了新的 window.onerror 簽名,導致標準草案也就跟著這樣寫了。
我們之前討論到的 Error 對象屬性,其名稱都是基于 Chrome 命名方式的,然而不同瀏覽器對 Error 對象屬性的命名方式各不相同,例如腳本文件地址在 Chrome 叫做 script 但在 Firefox 叫做 filename。因此,我們還需要一個專門的函數來對 Error 對象進行正規化處理,也就是把不同的屬性名稱都映射到統一的屬性名稱上。具體做法可以參考這篇文章。盡管瀏覽器實現會更新,但人手維護一份這樣的映射表并不會太難。
類似的是堆棧跟蹤(stack)的格式。這個屬性以純文本的形式保存一份異常在發生時的堆棧信息,由于各個瀏覽器使用的文本格式不一樣,所以也需要人手維護一份正則表達,用于從純文本中提取每一幀的函數名(identifier)、文件(script)、行號(line)和列號(column)。
安全限制
如果你也遇到過消息為 'Script error.' 的錯誤,你會明白我在說什么的,這其實是瀏覽器針對不同源(origin)腳本文件的限制。這個安全限制的理由是這樣的:假設一家網銀在用戶登錄后返回的 HTML 跟匿名用戶看到的 HTML 不一樣,一個第三方網站就能把這家網銀的 URI 放到 script.src 屬性里面。HTML 當然不可能被當做 JS 解析啦,所以瀏覽器會拋出異常,而這個第三方網站就能通過解析異常的位置來判斷用戶是否有登錄。為此瀏覽器對于不同源腳本文件拋出的異常一律進行過濾,過濾得只剩下 'Script error.' 這樣一條不變的消息,其它屬性統統消失。
對于有一定規模的網站來說,腳本文件放在 CDN 上,不同源是很正常的?,F在就算是自己做個小網站,常見框架如 jQuery 和 Backbone 都能直接引用公共 CDN 上的版本,加速用戶下載。所以這個安全限制確實造成了一些麻煩,導致我們從 Chrome 和 Firefox 收集到的異常信息都是無用的 'Script error.'。
CORS
想要繞過這個限制,只要保證腳本文件和頁面本身同源即可。但把腳本文件放在不經 CDN 加速的服務器上,豈不降低用戶下載速度?一個解決方案是,腳本文件繼續放在 CDN 上,利用 XMLHttpRequest 通過 CORS 把內容下載回來,再創建 <script> 標簽注入到頁面當中。在頁面當中內嵌的代碼當然是同源的啦。
這說起來很簡單,但實現起來卻有很多細節問題。用一個簡單的例子來說:
如果我們已經有一整套工具來生成網站上不同頁面的 <script> 標簽的話,我們就需要調整一下這套工具讓它對 <script> 標簽做出改動:
接下來我們還需要實現一套完整的機制,保證這些由 scheduleRemoteScript 根據地址下載回來的文件內容和由 scheduleInlineScript 直接獲取到的代碼能夠按照正確的順序一個接一個地執行。詳細的代碼我就不在這里給出了,大家有興趣可以自己去實現。
行號反查
通過 CORS 獲取內容再把代碼注入頁面能夠突破安全限制,但會引入一個新的問題,那就是行號沖突。原本通過 error.script 可以定位到唯一的腳本文件,再通過 error.line 可以定位到唯一的行號?,F在由于都是頁面內嵌的代碼,多個 <script> 標簽并不能通過 error.script 來區分,然而每一個 <script> 標簽內部的行號都是從 1 算起的,結果就導致我們無法利用異常信息定位錯誤所在的源代碼位置。
為了避免行號沖突,我們可以浪費一些行號,使得每一個 <script> 標簽中有實際代碼所使用的行號區間互相不重疊。舉個例子來說,假設每個 <script> 標簽中的實際代碼都不超過 1000 行,那么我可以讓第一個 <script> 標簽中的代碼占用第 1亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久香蕉国产线看观看网| 久青草国产97香蕉在线视频| 日韩精品中文字| 97在线观看免费| 中日韩美女免费视频网址在线观看| 国产精品视频xxxx| 欧美裸体xxxx| 国产成人涩涩涩视频在线观看| 国产精品福利片| 亚洲人在线视频| 欧美美女操人视频| 欧美视频在线观看 亚洲欧| 欧美电影免费观看| 亚洲自拍偷拍视频| 国产精品福利在线观看| 色www亚洲国产张柏芝| 欧美性猛交99久久久久99按摩| 97超碰色婷婷| 欧美性猛交xxx| 国产专区欧美专区| 国产精品99久久久久久人| 久久99国产精品自在自在app| 国产一区二区三区毛片| 日韩av在线天堂网| 欧美精品电影在线| 日韩欧美在线观看视频| 91视频-88av| 精品国产网站地址| 视频一区视频二区国产精品| 最新的欧美黄色| 不卡av电影在线观看| 亚洲福利视频专区| 青青青国产精品一区二区| 欧美在线观看视频| 日韩av日韩在线观看| 亚洲va码欧洲m码| 亚洲日韩第一页| 国产精品女人网站| 日韩免费在线免费观看| 亚洲热线99精品视频| 国产午夜精品麻豆| 久久久久久尹人网香蕉| 欧美日韩激情网| 日韩大片在线观看视频| 久久精品男人天堂| 欧美中文在线观看| 日本午夜人人精品| 亚洲欧洲第一视频| 亚洲成人黄色在线| 国产精品专区一| 国产一区二区免费| 亚洲精品久久久久| 亚洲精选一区二区| 亚洲综合在线中文字幕| 久久久97精品| 欧美成年人视频网站欧美| 海角国产乱辈乱精品视频| 精品亚洲va在线va天堂资源站| 欧美一级大胆视频| 亚洲精品av在线播放| 国产精品wwww| 国产99久久精品一区二区| 亚洲肉体裸体xxxx137| 日本国产精品视频| 久久久久久这里只有精品| 亚洲欧美日本精品| 日韩在线观看免费全集电视剧网站| 亚洲男人av电影| 欧美黄色性视频| 国产欧美va欧美va香蕉在线| 亚洲成人1234| 国产视频精品一区二区三区| 日韩欧美精品免费在线| 午夜精品美女自拍福到在线| 日本免费在线精品| 久久久久女教师免费一区| 久久免费在线观看| 亚洲欧美日韩在线高清直播| 欧美成人高清视频| 国产精品一区二区在线| 久久亚洲成人精品| 亚洲电影成人av99爱色| 成人免费淫片aa视频免费| 久久综合88中文色鬼| 久久亚洲精品一区| 日韩中文字幕视频在线观看| 综合国产在线视频| 久久琪琪电影院| 成人激情电影一区二区| 欧美日本中文字幕| 欧美富婆性猛交| 日韩视频免费在线| 欧美中文字幕视频在线观看| 91精品91久久久久久| xvideos亚洲| 国产午夜精品全部视频在线播放| 色777狠狠综合秋免鲁丝| 成人精品视频在线| 91视频国产高清| 欧美国产精品va在线观看| 国产97免费视| 亚洲电影免费观看高清完整版| 久久久噜噜噜久噜久久| 97国产精品免费视频| 日韩精品免费在线视频观看| 亚洲精品久久久久中文字幕二区| 国产精品三级网站| 欧美激情2020午夜免费观看| 欧美日韩中文在线| 欧美成人精品在线播放| 孩xxxx性bbbb欧美| 国产视频精品久久久| 亚洲精品一区中文| 国产精品一区二区久久久久| 久久躁日日躁aaaaxxxx| 高清在线视频日韩欧美| 国产精品久久久久一区二区| 欧美日本啪啪无遮挡网站| 亚洲爱爱爱爱爱| 丝袜情趣国产精品| 亚洲色图17p| 欧美高清视频在线播放| 国产精品丝袜久久久久久高清| 法国裸体一区二区| 91网站免费观看| 欧美麻豆久久久久久中文| 91精品国产电影| 国产日韩综合一区二区性色av| 国产欧美 在线欧美| 久久精品男人天堂| 亚洲精品97久久| 国产亚洲精品一区二区| 亚洲免费小视频| 欧美成人免费一级人片100| 日韩精品在线看| 日韩中文在线中文网在线观看| 日韩视频在线观看免费| 97精品国产97久久久久久免费| 日韩在线观看视频免费| 久久久久久国产精品久久| 国产精品美女免费视频| 在线观看日韩视频| 国产精品久久久久久超碰| 国产日韩欧美在线视频观看| 在线观看久久久久久| 亚洲人在线观看| 92看片淫黄大片欧美看国产片| 日韩视频免费观看| 国产精品国内视频| 欧美专区在线播放| 高清欧美性猛交xxxx黑人猛交| 国产精品免费一区豆花| 狠狠色狠狠色综合日日五| 中文字幕不卡在线视频极品| 亚洲精品999| 欧美老女人性生活| 亚洲a级在线观看| 欧美日韩性视频| 国产美女精品视频免费观看| 日韩美女视频在线观看| 国产精品久久久久久婷婷天堂| 成人羞羞国产免费| 亚洲国产精彩中文乱码av在线播放| 米奇精品一区二区三区在线观看|