最近一年,在開發實踐過程中遇到了不少問題,大多都能得到解決 部分知其原理,部分只能做到解決問題,而半年前遇到的問題,或多或少都忘得差不多了 是該記錄一下一些問題,防止再遇到就得再查資料了 1. 瀏覽器在開啟有道劃詞插件的時候,使用 AjaxFileUpload 插件上傳文件報錯 開啟插件時,該插件會 ...
最近一年,在開發實踐過程中遇到了不少問題,大多都能得到解決
部分知其原理,部分只能做到解決問題,而半年前遇到的問題,或多或少都忘得差不多了
是該記錄一下一些問題,防止再遇到就得再查資料了
1. 瀏覽器在開啟有道劃詞插件的時候,使用 AjaxFileUpload 插件上傳文件報錯
開啟插件時,該插件會往文檔中添加音頻元素節點
而AjaxFileUpload插件的上傳文件處理方式是,獲取返回的實體內容,直接進行eval 解析,解析失敗,報錯,則無法上傳
這插件在舊系統中常用到,解決辦法就是不用這個插件,或者停用有道劃詞插件
另外,我的解決方案則是用了FormData對象來非同步上傳文件
2. Uncaught TypeError: jQuery.handleError is not a function
使用某些舊插件的時候,會出現這個錯誤
插件使用了handleError這個方法,而新版的jQuery以及去除了這個方法,所以這時可以棄用插件或者為JQ加回此方法
jQuery.extend({ handleError: jQuery.handleError || function( s, xhr, status, e ) { // If a local callback was specified, fire it if ( s.error ) { s.error( xhr, status, e ); } // If we have some XML response text (e.g. from an AJAX call) then log it in the console else if(xhr.responseText) { console.log(xhr.responseText); } } });
3. 非同步方式實現導出Excel表格
用非同步的方式導出數據,用Ajax貌似不行(行的告訴我怎麼搞唄)
目前想到的方法就是用iframe,設置不同的src即可讓後端返回相應數據
4. 頁面使用Angular.js(1),頁面中iframe中初始設置src屬性的話,會導致頁面重新載入一次
例如設置一個初始值,某些操作之後再更改src
<iframe src="#" class="export-iframe"></iframe>
Controller似乎會觸發兩次,可以看到載入的請求多觸發了一次,且第二次的鏈接中會多了一個#號
解決辦法就是直接不設置這個屬性
<iframe class="export-iframe"></iframe>
5. 父頁面中有iframe,iframe裡面有分頁按鈕,在父頁面對iframe做載入之後監聽iframe中點擊事件的操作,初始第一頁正常,但點擊第二頁之後事件就失效了
原代碼:
第一次成功列印出來,即觸發了load事件,但點擊下一頁後,iframe實際上已經刷新了,但並不會再觸發這個load事件
後來的解決辦法是換了種監聽方法,區別主要是獲取iframe對象的方式變了,還不知為啥會這樣?
6. 在iframe中的預覽pdf文件時,有時embed元素未占滿整個iframe,而是正好一半,一半
目前還不知如何解決,把embed的寬高由100%設置成接近99%的時候,反而占滿iframe的概率增多了不少..
7. 在iPad下,無法實現自動聚焦
這問題應該是解決不了的,是iOS自帶的,方案只能是由用戶觸發mousedown、mouseup、click之類的事件後再調用
8. 有個插件叫做 magicsearch ,初期用得還好,不過之後斷斷續續發現了一些問題
在匹配不到數據的時候,匹配結果直接顯示了error文案,看看源碼,直接改掉
第二個坑是它直接把綁定元素的事件都註銷了,這樣太暴力很不好
第三個坑是它給只讀的style屬性賦值,這種方式在嚴格模式是被禁止的,而這插件正好自個又用了嚴格模式
坑就坑在:在Angular.JS(1)環境下使用iPad的時候才報錯,PC上用Angular.JS正常,iPad下用非Angular.js正常..
解決方法也很暴力,直接去掉插件的嚴格模式
第四個坑是它用了Array.from,而這方法支持度是chrome45+,所以稍低版本的就遭殃了
9. bootstrap v3 的collapse摺疊組件使用了click的事件監聽方式,在移動端會有300ms的延遲
官方貌似在v4中修複了,用v3的話,就自個添加touchstart事件的支持,還要註意touchstart事件觸發之後還會觸發原監聽的click事件
可按需來把它註銷掉,移動端即有如絲般順滑的collapse
// 移動端iOS click有延遲 添加摺疊的touchstart事件支持 if (isiOS) { $(document).off('click.bs.collapse.data-api', '[data-toggle="collapse"]'); $(document).on('touchstart.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { var $this = $(this), href var target = $this.attr('data-target') || e.preventDefault() || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 var $target = $(target) var data = $target.data('bs.collapse') var option = data ? 'toggle' : $this.data() var parent = $this.attr('data-parent') var $parent = parent && $(parent) if (!data || !data.transitioning) { if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed') $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') } $target.collapse(option) }); }
10. iOS10+會忽略meta 標簽的user-scalable=no,沒錯蘋果就是那麼牛別
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no">
頁面要禁止用戶縮放,可以使用JS來輔助處理
// 禁止縮放 iOS10+會忽略meta的user-scalable=no document.documentElement.addEventListener('touchstart', function (event) { if (event.touches.length > 1) { event.preventDefault(); } }, false);
另外要註意的是,上面只是禁用了雙指的縮放,還有一種縮放叫做雙擊縮放,而iPad下是沒有雙擊事件的,所以只能模擬
引用st上的一段雙擊事件支持
(function($){ // Determine if we on iPhone or iPad var isiOS = false; var agent = navigator.userAgent.toLowerCase(); if(agent.indexOf('iphone') >= 0 || agent.indexOf('ipad') >= 0){ isiOS = true; } $.fn.doubletap = function(onDoubleTapCallback, onTapCallback, delay){ var eventName, action; delay = delay == null? 500 : delay; eventName = isiOS == true? 'touchend' : 'click'; $(this).bind(eventName, function(event){ var now = new Date().getTime(); var lastTouch = $(this).data('lastTouch') || now + 1 /** the first time this will make delta a negative number */; var delta = now - lastTouch; clearTimeout(action); if(delta<500 && delta>0){ if(onDoubleTapCallback != null && typeof onDoubleTapCallback == 'function'){ onDoubleTapCallback(event); } }else{ $(this).data('lastTouch', now); action = setTimeout(function(evt){ if(onTapCallback != null && typeof onTapCallback == 'function'){ onTapCallback(evt); } clearTimeout(action); // clear the timeout }, delay, [event]); } $(this).data('lastTouch', now); }); }; })(jQuery);
然後就可以簡單地進行調用了,雙擊後執行e.preventDefault()即可
$(document).doubletap( /** doubletap-dblclick callback */ function(event){ event.preventDefault(); }, /** touch-click callback (touch) */ function(event){ }, /** doubletap-dblclick delay (default is 500 ms) */ 100 );
11. requestAnimationFrame的並行調用不能保證在不同幀執行
希望的效果是在一幀一幀地執行,然而瀏覽器會將多個操作合併到同一幀中,檢測發現
有分幀的策略,但得在回調中再次調用requestAnimationFrame才行
而實際操作中還需要一種並行調用就能分幀的方案,目前還沒找到
然而文檔中也指明瞭,是會放到同一幀的,所以估計這思路沒戲了
12. iOS高版本中,在微信內訪問網頁,音頻背景音樂無法自動播放
其實在高版本瀏覽器中,基於安全措施,已經不允許自動播放音頻了,但在微信內是可以的
微信安卓環境下正常,但在高版本的iOS下就失效了,解決辦法是在微信的WeixinJSBridgeReady事件中播放即可
document.addEventListener('WeixinJSBridgeReady', function() { ... audio.play(); ... }, false)
13. 分享微信頁面到朋友圈時,沒有圖片logo
文檔中指明瞭要只用絕對路徑,即協議名、功能變數名稱、路徑等等都要寫全,漏寫了就沒了
另外,路徑要填寫微信能夠訪問的地址,不能是內網的
14. 在某些手機的微信中,分享頁面成功後,會有已分享的提示信息,但有些手機卻沒有
所以開發頁面的時候,還得自行加個已分享的回調提示,心桑..
15. 測試的時候發現,微信里頁面的touchstart事件是不能取消的,即cancelable==false,在安卓的UC和Chrome中是為true的
16. 在smarty環境下,通過後端拿到了一個變數值放在a標簽的href屬性中,點擊後跳轉的鏈接不對,
即鏈接直接附在了當前頁面url的後面,將http:// 替換成 // 卻成功了,這還不知為啥..
17. z-index有拼爹的性質,
z-index值只決定同一父元素中的同級子元素的堆疊順序。父元素的z-index值(如果有)為子元素定義了堆疊順序(css版堆疊“拼爹”)
要註意這個特性,另外要提及的是,z-index只有設置了非static的position值才能生效
18. 可編輯的元素,即設置了contenteditable為true的元素是可編輯的,它有個游標的坑
當設置元素的內容innerHTML改變時,原先的游標位置會直接 跑到前面,這個不好解決
跟游標相關的還有選中位置的值的處理
假如要實現contenteditable為true的元素中內容的複製和粘貼功能,簡單地複製粘貼就會取到錯亂的HTML標簽
結合getSelection、clipboardData相關的操作(還得註意這個對象在新版瀏覽器中以及移到了原生事件對象originalEvent下,之前是在ClipboardEvent對象下),
可以實現出來,不過並不是很完美,反正就是不好搞
// 標題組件粘貼 .on('paste', '.component-title', function(e) { if (that.previewing) { return; } e.preventDefault(); var $this = $(this), $title = $this.find('.title'), t = e.originalEvent.clipboardData.getData('text/plain'), s = window.getSelection(), oldTitle = $title.text(), startOffset = s.anchorOffset < s.focusOffset ? s.anchorOffset : s.focusOffset, endOffset = s.anchorOffset > s.focusOffset ? s.anchorOffset : s.focusOffset, start = oldTitle.slice(0, startOffset), end = oldTitle.slice(endOffset); $title.html(start + t + end).attr('data-title', start + t + end); })
19. 有一種坑、或者說是特性叫做 Font Boosting,這個特性也叫做 Text Autosizer,
現象就是字體的顯示大小,與在CSS中指定的大小不一致
是 Webkit 給移動端瀏覽器提供的一個特性:當我們在手機上瀏覽網頁時,很可能因為原始頁面寬度較大,在手機屏幕上縮小後就看不清其中的文字了。而 Font Boosting 特性在這時會自動將其中的文字字體變大,保證在即不需要左右滑動屏幕,也不需要雙擊放大屏幕內容的前提下,也可以讓人們方便的閱讀頁面中的文本。
實踐中發現可以通過設置容器的max-height高度來實現,可以前去上述文章查看更多
/* 有滾動條時 基於瀏覽器自身對字體的自動縮放,容器里的字體可能會變大,可定義一個屬性避免 */ .job-type_list { max-height: 999999px; }
20. 這問題太久遠都忘了,直接放個圖吧
21. 有個HTML5的視頻插件叫做 Video.js,要實現視頻海報的大小占滿視頻大小的話
直接設置width、height無效,設置background-size: cover 可以解決
22. 有個彈窗組件叫做 Layer.js,發現個問題是在layer彈出層中播放視頻,視頻的全屏按鈕失效
沒啥辦法了,最後直接暴力地解決了
23. React的componentWillReceiveProps事件調用的時機還不太清晰,
雖然文檔中已經寫明瞭
在測試過程中發現,就算父組件不傳遞props,子組件的這個方法也會被調用,還不知道為什麼
也許是做淺比較 {} !== {} 吧 ?
24. React 的componentDidUpdate事件調用的時機還不太清晰,
雖說是在組件更新之後才調用,不過在一個複雜頁面中測試發現,componentDidUpdate已經觸發了,但卻獲取不到頁面中的元素(看起來像是組件還沒更新完成)
不知為啥,最後只能加個定時器處理了
25. React 的componentDidMount事件調用的時機還不太清晰,
雖說是在組件載入完成之後才調用,但在實踐中的一個需求發現一個問題,不太好解決,查了蠻久還沒看到合適的方案
比如要做一個彈窗組件,包含幾個component,彈窗是調用子component出來,原想在調子component的時候才觸發其componentDidMount事件,不料早在頁面載入時所有component的componentDidMount事件就已經觸發了,心桑..
26. jshint對redux中某些語法報錯,需要做一些處理
在文件起始處加上 /* jshint -W138 */ 即可
27. 排除由 input[type="file"] 點擊引起的 window.onblur事件
很簡單,使用document.activeElement 來處理即可
28. 在離開當前頁面時判斷是否有更改,做出提示
新版本瀏覽器基於安全機制,不能設置提示的樣式,也不能設置提示中操作(確認和取消)的回調,也不能設置提示的文案(舊版的可以設置文案)
實現檢測提示的方法很簡單,例如
// 離開當前頁面之前,判斷是否有更改,做出提示 window.onbeforeunload = function (e) { // 內容有改變且不是提交試卷之後的觸發 if (this.state.changed && !this.state.saved) { return '提示:當前內容有修改'; } }.bind(this);
29. chrome59以上的已經不支持頁面引入ftp文件了
30. 有個編輯器叫做 wangEditor,也有一些坑
wangEditor預設的吸頂 滾動會影響頁面上position: fixed的元素 可依據文檔中配置為false
word文檔中複製帶換行的內容到編輯器中會有亂碼,如
調試找到瞭解決辦法,改了源碼,給作者提了個pr就好了
31. requirejs可以使用urlArgs參數自定義文件是否緩存
32. checkbox和radio的樣式基本是很難自定義的,一種解決方式是用其他方式模擬出來
比如用-webkit-appearance: menulist 模擬下拉框,用 圓角的span模擬radio
而下拉框的樣式在手機上是調用原生內核的(瀏覽器的或WebView的),為了保證一致的效果(在測試過程中發現華為機型經常出現不一致的問題),可以統一用ul來模擬安卓下的下拉框彈層選擇,在iPhone下保持其原生即可
33. 有個插件叫做 jx-xlsx,可以用來給前端讀取excel文件里的內容
34. 有個編輯器叫 Ueditor,也有一些坑
它會在全局設置ul 和 li 的list-style為none,導致改出現的列表樣式消失了
還有一些與奇葩需求結合的坑,忘得差不多了
35. “微軟雅黑” 和 “Microsoft YaHei”是有區別的,tell me why ~
36. 有時已經開啟了Gzip壓縮,但從timeline上看似乎是全量下載了,且看
因暫重現不了,先用一幅圖表示,TTFB 是幾百ms左右,但Content Download卻有十幾秒
這種情況還不知為啥,但過一段時間又好了
對 TTFB 的理解還不夠清晰,在測試中發現,頁面載入資源緩慢
而頁面基本不需要後端操作,所以後端的耗時應該不是主要的,也部署了CDN節點,所以首個報文頭部傳輸太慢應該也不是主要的
後來發現,對頁面中資源的請求又亂了,從timeline瀑布流中發現資源並不是按照頁面代碼順序由上往下請求,比如<img 標簽中的src資源和css文件中的background-image屬性中的src資源載入的順序,資源並行載入的數量不清晰
一堆的不清晰之中,嘗試儘可能地在減小請求數與減小資源大小直接做平衡,
儘可能地讓關鍵的資源在最先的並行順序中載入,頁面整體載入感覺就快多了
難點TTFB還與資源的載入時機有關?還得多查查
37. 表格中有大量數據時,很容易就會出現性能問題
表格Reflow的Repaint代價都很高,在滾動、對錶格項操作的時候,經常就卡頓了
優化方案得按實際需求來看
首先可以嘗試:儘可能地只處理視窗可見的表格項即可,這樣一來性能就可以翻個幾十倍
然後嘗試:儘可能避免不必要的Reflow和Repaint,CSSTriggers關於樣式的,以及關於JS的DOM屬性
然後嘗試:儘可能地緩存,不必要的計算就不計算,十萬項,每項節約0.01ms,那都能減少1s的卡頓
然後:優化DOM選擇器等等
38: 移動端的動畫常常會碰到卡頓問題,多半是掉幀太嚴重了
關於幀的知識點,還得多去理解requestAnimation、GPU、JS的事件迴圈機制、setTimeout/setInterval 、瀏覽器繪製原理等等
基本原則是大多數情況下用setTimeout,上戰場時儘量避免setInterval,別忘了requestAnimation這個好助手,合理分配Composite Layer
還得多實踐才能發現更多坑
39. 頁面上可播放的視頻大多需要是mp4格式的,且其格式需是H.264,否則某些情況下會碰到有聲音沒畫面的現象
40. Angular.js(1)中經常會碰到 In Progress 的錯誤問題
估計是經驗還不夠吧,經常操作後就調用$scope.$apply()
目前的解決方式就是把操作放到$timeout中
41. 在數據量大的時候,Angular.js(1)中的input只要放到了$scope相關域之中,就一卡一卡的
還不知為何,目前直接換成了用JQ操作的方式,甚是順暢
42. 在數據量大的時候,Angular.js(1)重新更新視圖(ng-repeat)會很卡,目前還沒比較好的方案
而在更新數據操作的前一步,展示一個loading效果,竟會卡上好幾秒,然後loadIng才出來就立馬結束
可能是線程太繁忙GUI無法繪製?嘗試將操作放到下一輪事件迴圈中或使用requestAnimationFrame,loading能按照預期顯示出來,但視圖卻更新不成功,心桑..
43. Angular.js(1)的ng-repeat中過濾空的數據,在 討論 中看到有好幾種寫法
但是都失效..
44. mouseenter和mouseleave事件冒泡產生的問題,為了實現滑鼠划過tr標紅,划出tr取消標紅
而由於冒泡的問題,划過的td時候就觸發了父tr的mouseleave事件,所以加句
e.stopPropagation();
45. 使用webpack編譯的過程中發現,文件耦合略為嚴重
假設webpack要編譯15個頁面文件,因為需要提取一個common.js文件,只改一個字,15個頁面文件引用的common.js就得改
基於資源加戳,則這些頁面都有改動了
如果某個React組件被共用了,改動到一半的時候有線上問題要插單,那麼已經做的修改就只能按文件備份了,實在是不好管理
46. webpack編譯耗時過長,該如何優化
目前加上了chunkhash,相對好了一些,但還是有不夠快,可能還需要減少打包的文件數量,再看看吧
47. webpack打包後自動更新頁面的資源路徑
目前用著兩種方式
使用 html-webpack-plugin 插件,提供模版文件及目標文件,不過好像生成的路徑有點問題,基本還得自己再調整一下
直接讀取文件修改占位,提供模版文件和目標文件,Node.js
this.plugin('done', function(stats) { let asset = stats.toJson().assets; // console.log(asset); let commonItem = asset.filter(function(item) { return item.chunkNames[0] === 'common'; }); asset.forEach(function(item) { if (!files[item.chunkNames[0]] || item.chunkNames[0] === 'common') { return; } fs.readFile(files[item.chunkNames[0]].src, 'utf-8', function(err, doc) { if (err) { throw err; } doc = doc .replace('%' + item.chunkNames[0] + '.js%', item.name) .replace('%common.js%', commonItem[0].name); fs.writeFileSync(files[item.chunkNames[0]].dest, doc); }); }); });
48. 在PC上和模擬器上內容是垂直居中的,但在真機上內容卻偏上了一丟丟..
49. 表格的表頭、首行或首列固定等
表格數據多時,需要有個滾動時把某一信息行列固定的效果,方案有兩種
->直接設置該行列的position
這是最直接的,在一般表格中可以使用,但數據量很多的時候,或者表頭複雜(比如colspan=4等)的時候就不建議使用了,計算複雜且耗性能
->把需要固定的元素複製過來成新的表格,在需要的時候整個一起操作
這中方式可以很好地處理複雜表格的問題,且計算方式也容易一點
表格固定最大的難點在於保證固定項和內容項的寬高一致,在完全自適應內容的情況下是非常非常難做到的(在複雜表頭的時候)
所以可以考慮做一些寬高的限制(比如width或設置max-width也可以)
其實主要就是在開始時遍歷每一項所計算的寬高,賦值到固定表頭的屬性中,用colgroup輔助的效果會好一些,如
<colgroup> <col width="12%"></col> <col width="8%"></col> <col width="8%"></col> <col width="30%"></col> <col width="10%"></col> <col width="8%"></col> <col width="8%"></col> <col width="15%"></col> </colgroup>
另外,記得關註表頭固定產生的性能問題
50. 待續..