大廠面試官:你做過什么有亮點的項目嗎?

前言
大廠面試中除了問常見的算法網(wǎng)絡(luò)基礎(chǔ),和一些八股文手寫體之外,經(jīng)常出現(xiàn)的一個問題就是,你做過什么項目嗎?
看似簡單的題目卻讓很多同學(xué)不知如何回答,因為面試就像相親一樣,你心儀的妹子不想聽你說小學(xué)二年級還拿過三好學(xué)生,她其實只想了解現(xiàn)在的你,有什么特點和優(yōu)點
項目也是一樣, 面試官其實是想看看你做過什么有亮點的項目, 其實大家日常做的項目都差不多,增刪改查,登錄注冊,彈窗等等,所謂有亮點,就是在這些實現(xiàn)功能的基礎(chǔ)上,在以下幾個方面做出了探索和優(yōu)化, 個人能力有限,先聊這幾個方面
大數(shù)據(jù)量優(yōu)化
研發(fā)效率的提高
研發(fā)質(zhì)量的提高
性能優(yōu)化
用戶體驗優(yōu)化
復(fù)雜 & 新場景
....
我們以大家都做過的需求舉例,通過優(yōu)化,每個需求都可以做成有亮點的需求,也就是所謂企業(yè)級的項目
歡迎加我,暢聊前端 & 英語學(xué)習(xí)
大數(shù)據(jù)量
想做出亮點,首先做一些你周圍同事做不到的需求,首先就是數(shù)據(jù)量變大,變得賊大,雖然大部分場景遇不到,但是擋不住騷包面試官喜歡問,我們只聊面試
場景1: 課程頁面,增刪改查
這種場景,我們可以讓數(shù)據(jù)量變成1W行,大部分場景都是分頁,只有極端場景(移動端無限滾動的商品頁),如果直接渲染1W行列表,不出意外你的頁面就要卡了,比較常見的優(yōu)化方案就是虛擬滾動,就是只渲染你能看到的視窗中的幾十行,然后通過監(jiān)聽滾動來更新這幾十個dom,大致原理圖如下 (網(wǎng)上找的)
解決方案出來后,無論是Vue還是React解決方案都是類似的,這里用React+Typescript舉栗子,首先我們就得完成下面的任務(wù),為了簡化場景,先假定每個元素高度都一樣
可視區(qū)的高度固定 viewHeight (clientHeight
每個列表高度height ?(固定
可視區(qū)域的數(shù)據(jù)索引start和end ?(scrollTop / height
基于startIndex計算出offset偏移(scrollTop - (scrollTop % height);
渲染數(shù)據(jù) & 監(jiān)聽滾動事件
代碼大致如下
??//?列表容器的dom
??const?container?=?useRef<HTMLDivElement>(null)
??//?開始位置
??const?[start,?setStart]?=?useState(0)
??//?視圖中的數(shù)據(jù)
??const?[visibleData,?setVisibleData]?=?useState<VirtualProps['list']>([])
??//?控制偏移量
??const?[viewTransfrom,?setViewTransfrom]?=?useState('translate3d(0,0,0)')
??useEffect(()?=>?{
????const?containerDom?=?container.current
????const?viewHeight?=?containerDom?.clientHeight?||?500?//?視窗高度
????const?visibleCount?=?Math.ceil(viewHeight?/?HEIGHT)?//?視窗內(nèi)有幾個元素
????const?end?=?start?+?visibleCount
????setVisibleData(list.slice(start,?end))
??},?[])
??function?handleScroll(e:?React.UIEvent<HTMLDivElement,?UIEvent>)?{
????const?scrollTop?=?e.currentTarget.scrollTop?//?滾動的距離
????const?containerDom?=?container.current
????const?viewHeight?=?containerDom?.clientHeight?||?500?//?視窗高度
????const?start?=?Math.floor(scrollTop?/?HEIGHT)
????const?end?=?start?+?Math.ceil(viewHeight?/?HEIGHT)
????setVisibleData(list.slice(start,?end))
????setStart(start)
????setViewTransfrom(`translate3d(0,${start?*?HEIGHT}px,0)`)
??}
稍微難一丟丟,或者更騷包的面試官還會問,如果每行都是一段文字,不知道有多高呢?其實解決方案也不復(fù)雜,可以預(yù)估一個大致的高度,然后渲染的時候獲取實際dom的高度 + 緩存到數(shù)組
下面是偽代碼,由于位置數(shù)組是一個累加的數(shù)組,其實還可以用二分算法繼續(xù)優(yōu)化,給同學(xué)們留個作業(yè)吧
//?預(yù)估高度60
const?PREDICT_HEIGHT?=?60
??//?不定高數(shù)組,維護(hù)一個位置數(shù)據(jù)
const?[positions,?setPosition]?=?useState<{?top:?number;height:?number?}[]>([])
//?渲染數(shù)組之后,更新positions數(shù)組
Array.from(listDom?.children).forEach((node,?index)?=>?{
??const?{?height?}?=?node.getBoundingClientRect()
??//?console.log(start+index,?node.id)
??if?(height?!==?positions[start?+?index].height)?{
????setPosition((prev)?=>?{
??????const?newPos?=?[...prev]
??????newPos[start?+?index].height?=?height
??????for?(let?k?=?index?+?1;?k?<?prev.length;?k++)
????????newPos[k].top?=?newPos[k?-?1].top?+?newPos[k?-?1].height
??????return?newPos
????})
??}
})
},?[visibleData])

場景2: 文件上傳
字節(jié)跳動面試官,我也實現(xiàn)了大文件上傳這個場景之前我寫過一篇文章,這里也回顧一下,其實就是數(shù)據(jù)量大了之后,想繼續(xù)讓用戶有比較好的交互體驗,就得不斷地解決新問題
上傳普通文件axios.post + 進(jìn)度條就搞定了,如果想有亮點,可以把文件的體積想的大一些,比如2個G,直接上傳容易斷,我們需要斷點續(xù)傳,就誕生幾個新的問題
文件切片 + 秒傳 + 暫停
文件計算hash值,就像文件的身份證號,用來問后端有沒有切片存在
計算hash的卡頓 可以使用
web-worker
,時間切片
,抽樣Hash
三種解決方案上傳文件切片
上面這些解決方案搞完,文件上傳這個需求如果面試官問起,我覺得聊半個小時沒問題,web-workder
,從React源碼學(xué)到的時間切片
,布隆過濾器思想的抽樣hash
,TCP的慢啟動理想,字節(jié)高頻面試題異步任務(wù)并發(fā)數(shù)控制
項目實戰(zhàn)


web-worker計算md5 (影分身策略)

React16之前的架構(gòu)中,有一個性能瓶頸,就是當(dāng)虛擬dom的diff時間過長的時候,可能會導(dǎo)致卡頓,React16使用了fiber,也就是時間切片架構(gòu)解決了,現(xiàn)在計算ms5也是類似的場景,計算量過大導(dǎo)致的卡頓,我們也可以借鑒


md5也計算完了,如果現(xiàn)在有100個切片,直接Promise.all上傳,同時發(fā)起100個請求也會讓瀏覽器產(chǎn)生卡頓,我們需要控制異步任務(wù)的并發(fā)數(shù)量,這本身就是字節(jié)常問的題目

這里可以參考我的Github,用隊列和Promise.race的特性都可以實現(xiàn)
function?limit(maxCount){
??//?任務(wù)隊列
??let?queue?=?[]
??let?activeCount?=?0
??const?next?=?()=>{
????//下一個任務(wù)
????activeCount--
????if(queue.length>0){
??????queue.shift()()
????}
??}
??const?run?=?async?(fn,resolve,args)=>{
????//執(zhí)行一個函數(shù)
????activeCount++
????const?result?=?(async()=>fn(...args))()
????resolve(result)
????await?result
????next()?//下一個
??}
??const?push?=?async?(fn,resolve,args)=>{
????queue.push(run.bind(null,fn,resolve,args))
????if(activeCount<maxCount?&&?queue.length>0){
??????//?隊列沒滿?并且還有任務(wù)?啟動任務(wù)
??????queue.shift()()
????}
??}
??let?runner?=?(fn,...args)=>{
????return?new?Promise((resolve)=>{
??????push(fn,resolve,args)
????})
??}
??return?runner
}
還可以用Promise來實現(xiàn) 僅供參考
async?function?asyncPool({
??limit,
??items,
??fn
})?{
??const?promises=?[]
??const?pool?=?new?Set()
??for?(const?item?of?items)?{
????const?promise?=?fn(item)
????promises.push(promise)
????pool.add(promise)
????const?clean?=?()?=>?pool.delete(promise)
????promise.then(clean,?clean)
????if?(pool.size?>=?limit)?await?Promise.race(pool)
??}
??return?Promise.all(promises)
}
綜上所述,你現(xiàn)在做的大部分需求,只需要把數(shù)據(jù)量想的很大,然后逐步解決大數(shù)據(jù)量導(dǎo)致的性能問題,就算是亮點之一了
研發(fā)效率的提高
程序員也是一種很貴的資源,能提高他們的研發(fā)效率,也算是給公司省錢了,當(dāng)然是亮點了,但是每個人的開發(fā)能力不同,我們可以從團(tuán)隊協(xié)作
和多項目間復(fù)用
兩條路,來尋求研發(fā)效率的提高
團(tuán)隊效率
最常見的就是統(tǒng)一規(guī)范,js規(guī)范,git 分支規(guī)范,log規(guī)范,項目文件規(guī)范,并且用恰當(dāng)?shù)墓ぞ哌M(jìn)行自動化校驗和修正
然后是多項目間的復(fù)用率,也能極大地提高效率
代碼初始化,可以封裝成腳手架,類似
create-vite
, 可以內(nèi)置上面說的各種規(guī)范,新項目直接啟動代碼研發(fā)效率, 前端主要就是
組件庫
和工具庫utils
封裝代碼聯(lián)調(diào)效率, 比如接口json自動生成Typescript接口類型等等,比如mock數(shù)據(jù)工具
代碼上線效率,發(fā)布部署,部署結(jié)果同步到聊天群等等,把日常重復(fù)的行為自動化
這部分也有大量的開源代碼可以參考,比如React生態(tài)的AntDesign,Vue生態(tài)的Antd-vue和element-ui, vueuse, 通用工具庫參考lodash
等等,這里就不贅述了
這也是大部分團(tuán)隊能有機(jī)會做開源的領(lǐng)域,由于需要多個項目之間的共用,所以對代碼質(zhì)量,版本管理,代碼文檔也會有更高的要求,無形中也提高了我們的段位和能力
現(xiàn)在很火的rust生態(tài),可以極大地提高前端編譯的速度,其實也無形中提高了開發(fā)者寫代碼時候的心情和效率,比如webpack換成vite,babel換成swc,還有現(xiàn)在很火的rspack,都是努力讓前端開發(fā)環(huán)境能有絲滑秒開的體驗
還有一些非代碼層面的協(xié)作效率,比如敏捷看板,高效開會,代碼review啥的,不在本篇文章討論范圍之內(nèi),先略過
研發(fā)質(zhì)量的提高
質(zhì)量的提高,也是程序員上限的提高
這一部分其實也是一個大話題,【重構(gòu)】【整潔代碼的藝術(shù)】【代碼大全】等經(jīng)典書籍?dāng)?shù)不勝數(shù),不過在前端這個比較蠻荒的領(lǐng)域,能把自動化測試做好,就已經(jīng)非常難得了
業(yè)務(wù)性的頁面寫測試成本過高,但是上面說的多項目之間共享的組件庫
,工具庫
還是需要用測試來確保代碼質(zhì)量,學(xué)會jest
或者vitest
寫測試,也是我們有機(jī)會參與熱門開源項目的機(jī)會,代碼測試覆蓋率也是一個項目質(zhì)量高低的重要指標(biāo),而且也是代碼可維護(hù)性高的體現(xiàn)
你可以現(xiàn)在就嘗試用vitest給你項目中寫的工具函數(shù) or組件庫來點測試代碼保駕護(hù)航把
除了代碼層面的單元測試,還有流程層面的,比如code-review,
性能優(yōu)化
天下武功,唯快不破
如何讓頁面打開速度更快是一個永恒的話題,性能優(yōu)化第一課首先你就得知道頁面性能幾個常見的指標(biāo),FCP
,TTI
,LCP
,就像我們想提高游戲水平,就得了解攻擊力防御力這些參數(shù)的含義

然后前端優(yōu)化可以先從兩個方向開始
1. 更快的加載文件
首先前端工程化中的打包壓縮,就是減少了文件的體積和數(shù)量,并且通過很好的文件緩存管理,最大限度的利用瀏覽器的緩存,達(dá)到更快加載文件的目的
文件體積里,其實圖片的格式選擇和優(yōu)化是大頭,jpg, png, webp的選擇,還有打包中圖片的壓縮,都可以獲得比較可觀的體積收益,靜態(tài)資源還可以使用cdn繼續(xù)提高加載文件的速度
還可以使用懶加載的思想,減少首屏加載的文件數(shù)量,也可以很好的提高文件加載的速度,圖片/路由懶加載現(xiàn)在在前端開發(fā)領(lǐng)域都是必備特性了,快去手寫一個lazy-load把
rollup帶來的tree-shaking能力(跟互聯(lián)網(wǎng)公司裁員還挺像,囧),可以去除項目中的無用代碼,現(xiàn)在也成了前端工程化的標(biāo)配,這也是為什么我們要盡量向esm規(guī)范靠攏的原因之一,而且靜態(tài)分析還可以給我們帶來一些額外的收益,比如vite中的預(yù)打包等等
這些我們都可以在工程化環(huán)節(jié)使用工具or插件的形式存在,所以學(xué)會定制webpack/vite的插件也成為前端架構(gòu)師的必備能力之一, 快去學(xué)起來把
2. 代碼執(zhí)行的更快
這里也很好理解,執(zhí)行的速度也是鑒定代碼好壞的一個指標(biāo),比如同樣一個leftpad函數(shù)(前面補齊字符),如果這么寫
function?leftpad(str,length,ch){
??let?len?=?length-str.length+1
??return?Array(len).join(ch)+str
}
console.log(leftpad('hello',10,'0'))
相比于我們用二分法+位運算的優(yōu)化思路的寫法
function?leftpad2(str,length,ch){
??let?len?=?length-str.length
??total?=?''
??while(true){
????//?if(len%2==1){
????if(len?&?1){
??????total+=ch
????}
????if(len==1){
??????return?total+str
????}
????ch?+=?ch
????len?=?len?>>?1
????//?len?=?parseInt(len/2)
??}
}
console.log(leftpad2('hello',10,'0'))console.time('leftpad')
for(let?i=0;i<10000;i++){
??leftpad('hello',1000,'0')
}
console.timeEnd('leftpad')
console.time('leftpad2')
for(let?i=0;i<10000;i++){
??leftpad2('hello',1000,'0')
}
console.timeEnd('leftpad2')
??node?leftpad.js
00000hello
00000hello
leftpad:?51.97ms
leftpad2:?2.077ms
數(shù)據(jù)量越大,性能差距就越大,數(shù)據(jù)量是1W的時候,性能有25倍的差距,當(dāng)然也可以看出來算法和數(shù)據(jù)結(jié)構(gòu)對前端的必要性,對不同的場景選擇合適的算法or數(shù)據(jù)結(jié)構(gòu)也是高級前端的必備能力
不同的框架內(nèi)部也有不同的性能優(yōu)化方式,減少組件不必要的rerender,減少瀏覽器的重繪回流,減少頁面內(nèi)部的dom操作等等經(jīng)典的優(yōu)化方式 就不贅述了
還有按需執(zhí)行代碼的思想,比如vue3種的靜態(tài)標(biāo)記,只有dom種的動態(tài)部分需要參與計算diff,靜態(tài)的dom會直接略過, ?還有astro,nuxt3這種ssr框架中的島嶼架構(gòu),就是只對頁面中的動態(tài)組件進(jìn)行js激活,都是按需思想的表達(dá)
復(fù)雜場景
一些天生復(fù)雜的場景,或者是前端新興的領(lǐng)域或者熱門的領(lǐng)域,面試官也比較喜歡,這個方向就比較多了,以后有機(jī)會展開詳細(xì)說說
很火的低代碼(搭建平臺
文檔技術(shù) (在線office,notion筆記
圖形學(xué)(figma,白板
3D (可視化,游戲,webgl
....更多未來的可能性
不要陷入成長陷阱
寫了這么多,我們要無限進(jìn)步,但是不要陷入低水平的成長陷阱中,也就是我們要努力學(xué)會通用技能,而不是單純的招式
比如當(dāng)年瀏覽器混戰(zhàn)的時候,我花了很多時間研究ie6/7/8的兼容性問題,并且感覺自己進(jìn)步很大,但是瀏覽器兼容是一個特定混亂時期的問題,現(xiàn)在那些兼容性的寫法對于現(xiàn)在的我,毫無積累
我們要花更多的時間學(xué)習(xí)能夠?qū)ξ覀冇蟹e累效果的技能,現(xiàn)在webpack和vite是工程化領(lǐng)域混亂發(fā)展的時期,過于關(guān)注api的使用,以后有一個工具統(tǒng)一這個領(lǐng)域后,你現(xiàn)在努力學(xué)習(xí)的工程化工具技能,跟我當(dāng)年的ie6兼容性一樣,都被歷史埋了
我們可以學(xué)習(xí)webpack和vite內(nèi)部的原理,學(xué)習(xí)他們內(nèi)部優(yōu)化的思想,怎么收集文件的依賴關(guān)系,怎么實現(xiàn)模塊化,怎么實現(xiàn)loader和plugin機(jī)制擴(kuò)展自身,怎么實現(xiàn)高效的熱更新等等,這些才是能夠幫助我們對未來有積累的技能
少學(xué)點api,多研究點問題和本質(zhì)
總結(jié)
送幾個我畫的架構(gòu)圖把



能看到這里的都是以真愛,歡迎加我微信:?itdasheng124 領(lǐng)取前端簡歷模板 & 面試題,還可以一起學(xué)英語,項目相關(guān)的優(yōu)化歡迎隨時聊,嘿嘿,祝大家都能擁有持續(xù)自由的職業(yè)生涯
我根據(jù)以上難點設(shè)計了全套的Vue3和React技術(shù)棧的企業(yè)級實戰(zhàn)+組件庫
課程,新課特價中 歡迎聯(lián)系