從人如何思考看程序員解決問題的思路 - 論原理的重要性

威林厄姆教授的《為什么學(xué)生不喜歡上學(xué)》中闡述了,思考是把環(huán)境信息和長期記憶信息進(jìn)行重新加工的過程。長期記憶是你對(duì)已有知識(shí)經(jīng)驗(yàn)的認(rèn)知,包括事實(shí)性知識(shí)和過程性知識(shí)。當(dāng)某個(gè)問題超出了你的認(rèn)知范圍時(shí)人是沒有辦法思考的(譬如問你魯菜中的清湯如何能做到煮時(shí)無泡沫)。所以對(duì)基礎(chǔ)知識(shí)(事實(shí)性知識(shí))的學(xué)習(xí)是有必要的。推演到技術(shù)上,很多前端前輩強(qiáng)調(diào)面試很看中面試者解決問題的能力和思路,但是這些都不是憑空產(chǎn)生的。你必須要知道很多的基礎(chǔ)知識(shí)和日積月累的項(xiàng)目經(jīng)驗(yàn)(過程性知識(shí)),才有可能通過推理把你存在長期記憶中的基礎(chǔ)知識(shí)和面臨的問題進(jìn)行結(jié)合來解決問題。你不可能讓一個(gè)完全不知道 HTTP 協(xié)議的人回答“當(dāng)你在瀏覽器敲下地址后會(huì)發(fā)生什么”。

以下用一個(gè)我自己 debug 的實(shí)例來說明下基礎(chǔ)知識(shí)(原理)和經(jīng)驗(yàn)發(fā)揮的作用。

項(xiàng)目技術(shù)背景:

移動(dòng)端頁面,使用 react 和 material-ui,淘寶 lib.flexible 做移動(dòng)端適配。

bug現(xiàn)象

當(dāng)頁面加載時(shí),按鈕上的字會(huì)從下往上升的動(dòng)畫效果,見下圖:

bug.gif

對(duì)于這個(gè)問題 debug 的思路及分析步驟如下:

  1. 看上去是一個(gè)動(dòng)畫效果,通常是 js 修改了 css 某個(gè)屬性引起的。我們項(xiàng)目自己代碼沒有加過動(dòng)畫,查了下元素 css 看到 material-ui 在元素上加了 transition:all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms,根據(jù) all 這個(gè)值,難以推測(cè)是哪個(gè) css 屬性變化引起的動(dòng)畫。

  2. 現(xiàn)象是在頁面加載完后立即發(fā)生的,按經(jīng)驗(yàn)一般要么是 dom ready 的時(shí)候,要么是組件的 componentDidMount 中寫有 js 去修改了元素樣式。先去 componentDidMount 中逐一注釋代碼,幸運(yùn)的是通過注釋找到了產(chǎn)生此現(xiàn)象的函數(shù)調(diào)用。

  3. 查看步驟2中定位出來的函數(shù),其中有一句是獲取某個(gè)元素的 offsetHeight,就是這條語句觸發(fā)了這個(gè) bug(注釋掉此行代碼,現(xiàn)象消失),而獲取一個(gè)元素的 offsetHeight 會(huì)導(dǎo)致頁面 reflow。我猜測(cè)是這個(gè) reflow 導(dǎo)致某個(gè)不應(yīng)該生效的樣式生效了,但是此時(shí)依然無法定位那個(gè)樣式的變化導(dǎo)致這個(gè) bug。

  4. 仔細(xì)查看此按鈕的樣式,在我的知識(shí)范圍里面沒有找出可疑的樣式(這里就是我此次 debug 的難點(diǎn),因?yàn)閷?dǎo)致此 bug 涉及到的一個(gè) css 特性我不知道)。但是經(jīng)過我對(duì)自己已知 css 知識(shí)的排查,猜測(cè)可能是行高引起的問題,并不經(jīng)意間看到 body 設(shè)置了一個(gè)不太常見的 inline style:font-size: 12px。我想驗(yàn)證下是否是字體大小影響了垂直居中。所以我起 demo 測(cè)試獲知:一般來說一個(gè) div 高度和行高一樣時(shí),內(nèi)部文字會(huì)垂直居中,但是前提是這個(gè) div 的字體要小于高度。如果字體大于高度則文字會(huì)在中心線以下(具體原理還不清楚,遂記錄下來,需要去調(diào)研下行高和字體的關(guān)系)。

  5. 用 devtool 查看了下按鈕的字體樣式,果然繼承自 body,這個(gè) body 上設(shè)置的字體樣式是 lib.flexible 生成的,因?yàn)橹罢{(diào)研過一點(diǎn) lib.flexible 知道其會(huì)在 html 上也會(huì)設(shè)置字體樣式,而且 html 的字體是大于按鈕行高的。難道是因?yàn)?lib.flexible 先設(shè)置了 html 上的字體然后再設(shè)置 body 上的字體導(dǎo)致這個(gè)按鈕的字體樣式繼承關(guān)系從 html 變成了 body 導(dǎo)致了動(dòng)畫效果的產(chǎn)生?

  6. 那步驟3的觸發(fā)條件如何解釋呢?注釋掉獲取某個(gè)元素的 offsetHeight 這行代碼,頁面上的 lib.flexible 并不會(huì)產(chǎn)生這個(gè) bug ?。ㄕf明先設(shè)置 html 字體,再設(shè)置 body 字體,導(dǎo)致的按鈕字體樣式從繼承 html 再繼承 body 這一過程不會(huì)觸發(fā)動(dòng)畫)。而且從 js 引用關(guān)系來看(先引用的 flexible.js,然后再引用的項(xiàng)目 index.js),獲取某個(gè)元素的 offsetHeight 導(dǎo)致的 reflow 應(yīng)該是在 html body 被設(shè)置完字體樣式后?。扛鶕?jù)已有線索:一、reflow 觸發(fā)此次 bug,二、元素字體樣式繼承關(guān)系從 html 變成了 body。瞬間猜測(cè)設(shè)置 html 和 body 字體樣式的代碼可能是異步的,而 reflow 就發(fā)生在他們兩步中間。遂去查看 lib.flexible 源碼,果然設(shè)置 body 的代碼是異步的:

doc.addEventListener('DOMContentLoaded', function(e) {
    doc.body.style.fontSize = 12 * dpr + 'px';
}, false);

那基本就定下來產(chǎn)生這個(gè) bug 是因?yàn)橄仍O(shè)置了 html 的字體,然后獲取某個(gè)元素 offsetHeight 這行代碼產(chǎn)生的 reflow 導(dǎo)致按鈕字體樣式生效(繼承自 html 字體),最后設(shè)置 body 字體樣式導(dǎo)致按鈕字體樣式又變成了 body 的字體(繼承優(yōu)先級(jí)被 body 覆蓋),按鈕字體樣式的變化導(dǎo)致行高變化觸發(fā)了動(dòng)畫效果。

  1. 那為什么沒有那行導(dǎo)致 reflow 的代碼就不會(huì)產(chǎn)生這個(gè) bug 呢?我自己依稀記得瀏覽器并不會(huì)在 js 每次修改樣式時(shí)都及時(shí)去讓修改生效,而會(huì)合并某些修改。遂想 google 這方面的內(nèi)容,但是短時(shí)間內(nèi)沒有查到細(xì)節(jié),只查到了:

瀏覽器不會(huì)在每一次樣式變化后就去重新relfow一次,一般來說,瀏覽器會(huì)把這樣的操作積攢一批,然后做一次reflow,這又叫異步reflow或增量異步reflow。但是有些時(shí)候,我們的腳本會(huì)阻止瀏覽器這么干,比如:如果我們請(qǐng)求下面的一些DOM值:offsetTop, offsetLeft, offsetWidth, offsetHeight。

所以此次 debug 順利找到了產(chǎn)生 bug 的原因,此過程中產(chǎn)生了兩個(gè)待調(diào)研的新知識(shí)點(diǎn):一、字體和行高的關(guān)系。二、瀏覽器如何優(yōu)化 js 對(duì)樣式的修改。

回溯下整個(gè) debug 過程,步驟1,2是項(xiàng)目經(jīng)驗(yàn)起了作用(如何憑借第六感去定位問題,遇到類似現(xiàn)象以前是如何定位問題的),步驟3,5,6,7是基礎(chǔ)知識(shí)起了作用(步驟3 - reflow 知識(shí),步驟5 - css 樣式繼承知識(shí),步驟6 - js 異步原理,步驟7 - 籠統(tǒng)的瀏覽器繪制原理)。而此次 debug 花費(fèi)時(shí)間最長的是步驟4,也就是我 css 基礎(chǔ)知識(shí)盲點(diǎn)導(dǎo)致的。所以經(jīng)驗(yàn)可以積累,但基礎(chǔ)知識(shí)還得靠我們程序員自己花力氣去學(xué)習(xí)及完善。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容