中高級前端

一些回答不夠好的前端面試題 著做權歸做者全部

javascript/jquery

瀏覽數:421javascript

2018-3-5html

相關問題

  • flex 佈局 與 grid 佈局。
  • 實現 Vue SSR 。
  • 從 SPA 使用最小成本遷移到 SSR 。
  • 實現方法: (未完成)
    根據指定元素,在數組裏面找出 ff 數組(ff 數組這個名字是我瞎說的)。好比數組 [2, 3, 6, 7] ,指定元素 7,則 ff 數組是 [2, 2, 3](2+2+3 = 7)和 [7]。若指定元素 6,則 ff 數組爲 [2, 2, 2], [3, 3], 和 [6] 。
  • 實現 Promise.finally。
  • 另外一種方式實現 Vue 的響應式原理。
  • Vue 組件 data 爲何必須是函數。
  • Vue computed 實現。
  • diff 算法實現。
  • Vue complier 實現。
  • 快排及其優化。
  • 緩存算法實現及其優化(緩存算法簡單模型:假設能夠緩存三個數據,請求前三個數據時,直接進緩存列表,當請求第四個數據時,若命中緩存,將被緩存的數據放入緩存列表頭部,不然把新加入的數據放入緩存列表頭部,淘汰最後一個數據)。
  • 怎麼快速定位哪一個組件出現性能問題。
  • http 狀態碼 202, 204 。
  • WebSocket 。
  • 儘量多的說出你對 Electron 的理解。

相關解答

flex 佈局 與 grid 佈局

這個問題比較簡單,用 flex 與 grid 實現以下便可:前端

實現方式以下:vue

<html>
   <head>
     <style>
       
       /* flex */
      .box {
        display: flex;
        flex-wrap: wrap;
        width: 100%;
      }
      .box div {
         width: calc(100% / 3 - 2px);
         height: 100px;
         border: 1px solid black;
      }
 
      /* grid */
      .box {
         display: grid;
         grid-template-columns: 1fr 1fr 1fr;
         width: 100%;
      }
 
      .box div {
         height: 100px;
         border: 1px solid black;
      }
     </style>
   <head>
   <body>
     <div class= "box" >
       <div></div>
       <div></div>
       <div></div>
       <div></div>
     </div> 
   <body>
</html>

grid 學習:https://www.jianshu.com/p/d183265a8dadjava

實現 Vue SSR

一些想法寫在下題。node

從 SPA 使用最小成本遷移到 SSR

Vue SSR 的好處就很少說了,這有一篇相關文章 服務端渲染與客戶端渲染 。
簡單的總結下 Vue SSR 的實現。
有一張實現圖:jquery

其基本實現原理:webpack

  • app.js 做爲客戶端與服務端的公用入口,導出 Vue 根實例,供客戶端 entry 與服務端 entry 使用。客戶端 entry 主要做用掛載到 DOM 上,服務端 entry 除了建立和返回實例,還進行路由匹配與數據預獲取。
  • webpack 爲客服端打包一個 Client Bundle ,爲服務端打包一個 Server Bundle 。
  • 服務器接收請求時,會根據 url,加載相應組件,獲取和解析異步數據,建立一個讀取 Server Bundle 的 BundleRenderer,而後生成 html 發送給客戶端。
  • 客戶端混合,客戶端收到從服務端傳來的 DOM 與本身的生成的 DOM 進行對比,把不相同的 DOM 激活,使其能夠可以響應後續變化,這個過程稱爲客戶端激活 。爲確保混合成功,客戶端與服務器端須要共享同一套數據。在服務端,能夠在渲染以前獲取數據,填充到 stroe 裏,這樣,在客戶端掛載到 DOM 以前,能夠直接從 store 裏取數據。首屏的動態數據經過 window.__INITIAL_STATE__ 發送到客戶端。

Vue SSR 的實現,主要就是把 Vue 的組件輸出成一個完整 HTML, vue-server-renderer 就是幹這事的。nginx

純客戶端輸出過程有一個 complier 過程(「下題」中有一個簡單描述),主要做用是將 template 轉化成 render 字符串 。git

Vue SSR 須要作的事多點(輸出完整 HTML),除了 complier -> vnode,還需如數據獲取填充至 HTML、客戶端混合(hydration)、緩存等等。

相比於其餘模板引擎(ejs, jade 等),最終要實現的目的是同樣的,性能上可能要差點。

參考:

  • https://ssr.vuejs.org/zh/
  • https://segmentfault.com/a/1190000006701796

ff 數組

實現 Promise.finally

finally 方法用於指定無論 Promise 對象最後狀態如何,都會執行的操做,使用方法以下:

Promise
   .then(result => { ··· })
   . catch (error => { ··· })
   .finally(() => { ··· })

finally 特色:

  • 不接收任何參數。
  • finally 本質上是 then 方法的特例。
Promise.prototype.finally = function (callback) {
   let P = this .constructor
   return this .then(
     value  => P.resolve(callback()).then(() => value),
     reason => P.resolve(callback()).then(() => { throw reason })
   )
}

另外一種方式實現 Vue 的響應式原理

Vue 的響應式原理是使用 Object.defineProperty 追蹤依賴,當屬性被訪問或改變時通知變化。

有兩個不足之處:

  • 不能檢測到增長或刪除的屬性。
  • 數組方面的變更,如根據索引改變元素,以及直接改變數組長度時的變化,不能被檢測到。

緣由差很少,無非就是沒有被 getter/setter 。

第一個比較容易理解,爲何數組長度不能被 getter/setter ?

在知乎上找了一個答案:若是你知道數組的長度,理論上是能夠預先給全部的索引設置 getter/setter 的。可是一來不少場景下你不知道數組的長度,二來,若是是很大的數組,預先加 getter/setter 性能負擔較大。

如今有一個替代的方案 Proxy,但這東西兼容性很差,早晚要上的。

Proxy,在目標對象以前架設一層攔截。具體,能夠參考 http://es6.ruanyifeng.com/#docs/reference

Vue 組件 data 爲何必須是函數

理解兩點:

  • 每一個組件都是 Vue 的實例。
  • 組件共享 data 屬性,當 data 的值是同一個引用類型的值時,改變其中一個會影響其餘。

Vue computed 實現

這個題目有兩家問了,感受都不是答得很好。

從兩個問題出發:

  • 創建與其餘屬性(如:data、 Store)的聯繫;
  • 屬性改變後,通知計算屬性從新計算。

實現時,主要以下

  • 初始化 data, 使用 Object.defineProperty 把這些屬性所有轉爲 getter/setter。
  • 初始化 computed, 遍歷 computed 裏的每一個屬性,每一個 computed 屬性都是一個 watch 實例。每一個屬性提供的函數做爲屬性的 getter,使用 Object.defineProperty 轉化。
  • Object.defineProperty getter 依賴收集。用於依賴發生變化時,觸發屬性從新計算。
  • 若出現當前 computed 計算屬性嵌套其餘 computed 計算屬性時,先進行其餘的依賴收集。

參考:https://segmentfault.com/a/1190000010408657

diff 算法實現

之前寫過兩篇文章討論這個算法的實現,沒想到過的過久,忘記了。(文章地址:https://github.com/jkchao/blog/issues/3 ,https://github.com/jkchao/blog/issues/4) 。
也好,稱此機會總結下

diff 的實現主要經過兩個方法,patchVnode 與 updateChildren 。

patchVnode 有兩個參數,分別是老節點 oldVnode, 新節點 vnode 。主要分五種狀況:

  • if (oldVnode === vnode),他們的引用一致,能夠認爲沒有變化。
  • if(oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text),文本節點的比較,須要修改,則會調用Node.textContent = vnode.text。
  • if( oldCh && ch && oldCh !== ch ), 兩個節點都有子節點,並且它們不同,這樣咱們會調用 updateChildren 函數比較子節點,這是diff的核心,後邊會講到。
  • if (ch),只有新的節點有子節點,調用createEle(vnode),vnode.el已經引用了老的dom節點,createEle函數會在老dom節點上添加子節點。
  • if (oldCh),新節點沒有子節點,老節點有子節點,直接刪除老節點。

updateChildren 是關鍵,這個過程能夠歸納以下:

oldCh 和 newCh 各有兩個頭尾的變量 StartIdx 和 EndIdx ,它們的2個變量相互比較,一共有4種比較方式。若是 4 種比較都沒匹配,若是設置了key,就會用key進行比較,在比較的過程當中,變量會往中間靠,一旦 StartIdx > EndIdx 代表 oldCh 和 newCh 至少有一個已經遍歷完了,就會結束比較。

Vue complier 實現

之前寫過一篇 「Vue 生面週期總結的文章 」的文章,裏面提到了 complier 的做用,沒有作深刻了解。。。

模板解析這種事,本質是將數據轉化爲一段 html ,最開始出如今後端,通過各類處理吐給前端。隨着各類 mv* 的興起,模板解析交由前端處理。
總的來講,Vue complier 是將 template 轉化成一個 render 字符串。
能夠簡單理解成如下步驟:

  • parse 過程,將 template 利用正則轉化成 AST 抽象語法樹。
  • optimize 過程,標記靜態節點,後 diff 過程跳過靜態節點,提高性能。
  • generate 過程,生成 render 字符串。

參考:

快排及其優化

前端對算法的要求仍是比較低的,但也是必不可少的一部分。

找到一篇比較不錯的文章:https://www.cnblogs.com/zichi/p/4788953.html

緩存算法實現及其優化

最簡單的一種思路就是使用數組存儲,而後讓我優化。
我。。。一臉懵逼。
有興趣的同窗能夠參考這個: http://www.cnblogs.com/dolphin0520/p/3749259.html 。

ps: 看來我得補補數據結構和算法相關的知識了。

怎麼快速定位哪一個組件出現性能問題

當面試官問這個問題,沒有 get 到面試官的點,扯了一堆亂七八糟沒用的 – -。
後來面試官說主要是用 timeline 工具。
大意是經過 timeline 來查看每一個函數的調用時常,定位出哪一個函數的問題,從而能判斷哪一個組件出了問題。

附上兩個使用 timeline 的文章:

  • https://juejin.im/post/5a6e78abf265da3e3f4cf085
  • https://developers.google.cn/web/tools/chrome-devtools/?hl=zh-cn

http 狀態碼 202, 204

面試官不知道爲什麼扯到了 202, 204。。。好像是由本身帶進坑的。- –

202: 服務器已接受請求,但還沒有處理。
204: 服務器成功處理了請求,沒有返回任何內容。

這些狀態碼感受只要能記住經常使用的就 ok 了,固然還得了解 200 +, 300+, 400+, 500+ 表明什麼意思。

WebSocket

WebSocket 應該算是一個比較常問的面試點,若是問的不深的話,應該比較好回答。

因爲 http 存在一個明顯的弊端(消息只能有客戶端推送到服務器端,而服務器端不能主動推送到客戶端),致使若是服務器若是有連續的變化,這時只能使用輪詢,而輪詢效率太低,並不適合。因而 WebSocket 被髮明出來。

相比與 http 具備如下有點:

  • 支持雙向通訊,實時性更強;
  • 能夠發送文本,也能夠二進制文件;
  • 協議標識符是 ws,加密後是 wss ;
  • 較少的控制開銷。鏈接建立後,ws客戶端、服務端進行數據交換時,協議控制的數據包頭部較小。在不包含頭部的狀況下,服務端到客戶端的包頭只有2~10字節(取決於數據包長度),客戶端到服務端的的話,須要加上額外的4字節的掩碼。而HTTP協議每次通訊都須要攜帶完整的頭部;
  • 支持擴展。ws協議定義了擴展,用戶能夠擴展協議,或者實現自定義的子協議。(好比支持自定義壓縮算法等)
  • 無跨域問題。

實現比較簡單,服務端庫如 socket.iows ,能夠很好的幫助咱們入門。而客戶端也只須要參照 api 實現便可。

參考:

  • http://www.ruanyifeng.com/blog/2017/05/websocket.html
  • https://www.cnblogs.com/chyingp/p/websocket-deep-in.html

儘量多的說出你對 Electron 的理解

之前寫過一篇簡單的關於 electron-vue 的文章,沒想到真有面試官問,並且問的挺深的。

最最重要的一點,electron 其實是一個套了 Chrome 的 node 程序。

因此應該是從兩個方面說開來:

  • Chrome (無各類兼容性問題);
  • Node (Node 能作的它也能作)。

Chrome 沒什麼好說的,是個前端都懂。

Node 方面可說的就多了。

有個面試官問我,在 electron 怎麼解決跨域問題?

在我本身的項目裏,確實遇到了這個問題,惋惜選擇了一個不怎麼好的方法的方法,設置 nginx 。

爲何很差,若是項目是公司的,還須要運維同窗幫忙。- –

也聊到了使用 CORS 容許跨域,也以爲很差,由於須要後端接口處理。
一臉懵逼的我,直到面試官提醒使用 node 來代理如下,才恍然大悟。(原來還能夠這種操做。。。。)

固然也能夠鏈接數據庫,上家公司原本打算要作一個 electron 配合鏈接數據庫的桌面應用。(還沒開始作就離職了- -)
挺惋惜的,當時數據庫都已經選擇好了,leveldb 或者 lowdb ,以爲應該不難。

附上兩個 electron 配合數據庫使用的連接:

  • https://github.com/typicode/lowdb/issues/169
  • https://github.com/Level/electron-demo

功力不足,不免有錯誤之處,還望多多指出。

原文連接:https://jkchao.cn/article/5a784fc46c89ce0ff8dbfa36

相關文章
相關標籤/搜索