「一週一個大廠」覆盤一下Taptap的面經,發現不足,逐一擊破!

Sunshine_Lin發表於2021-12-24

前言

大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心

一週一個大廠 是我新出的一個系列文章,大概的流程是這樣的:

1639711909(1).png

  • 我會收集一些大廠的面經,並試著去回答
  • 如果全都會則等待下一週重新一輪
  • 如果有不會的,則記錄下來,並去克服它們,寫成文章,然後下一輪

這個系列的目的就是:逼自己學習,寫文章鞏固新知識,且複習舊知識

Taptap

今天是 一週一個大廠 的第一期,今天我們們來複盤一下Taptap的面經,發現不足,並逐一擊破他們吧!

一面

1、換膚都做過什麼處理,有沒有處理過可能改變尺寸的換膚

我分別在在 微信小程式 PC管理系統 中做過換膚

  • 小程式:我們公司做的是一款遊戲類的小程式,這款小程式中涉及到換膚的地方是閱讀故事情節的彈出抽屜,需求是通過使用者點選按鈕,改變三種皮膚,並且同時改變閱讀文字的顏色。我的解決方案是,利用Map儲存三種皮膚資訊,點選按鈕後會匹配Map中對應的皮膚,進行抽屜背景圖片的替換,並通過正則的方式,修改閱讀文字HTML程式碼,替換style裡的color屬性,達到換膚效果
  • PC管理系統:我們公司的一款後臺管理系統,做了 黑夜模式 白天模式 的切換功能,跟產品確定主題色之後,開始程式設計工作。由於我們的系統是使用 antd 元件庫進行編寫的,所以需要引入 antd 相應的主題檔案,並封裝相應的 切換主題hook ,同時也要儲存主題標識到 localStorage 中,確保重新整理後主題復原。接著就是封裝一個 AppDarkModeToggle 的一個切換元件,切換後觸發事件,改變 html data-dark 屬性,通過屬性選擇器,觸發底下標籤的樣式切換,同時改變 Header,Sider,Footer 等佈局元件的 背景顏色、字型顏色 等的切換

2、i18n在團隊內部都做了哪些實踐?

專案實施

i18n 的意思是 讓產品無需做大的改變就能夠適應不同語言和地區的需要 。在我的專案中也做過 i18n ,我們做的是 官網 管理系統 國際化語言切換 ,切換的是 中文 英文 兩種語言,我當時的步驟是

  • 1、根據設計稿確定 中文 英文 的文字情況
  • 2、整理成兩個資料夾 en zh ,分別存放對應語言的資料
  • 3、確定一下資料的形式是物件的鍵值對形式
  • 4、通過 vite import.meta.globEager 方法,將兩種資料整理成 i18n外掛 所需的格式(不同外掛所需格式不同)
  • 5、在專案初始化時,初始化 i18n外掛
  • 6、封裝對應的切換語言元件—— AppLocalePicker ,用來切換主題,同時改變 html 標籤的 lang 屬性
  • 7、同時做好 語言標識 的初始化,避免 重新整理後狀態丟失

外掛選擇

我會選擇一些使用起來比較方便的外掛,來實現 i18n

  • Vue:Vue我選擇的外掛是 vue-i18n
  • React:Reacr我選擇的外掛是 i18next

3、Webpack 遷移 Vite 遇到了哪些問題

  • 1、環境變數需要 Vite_ 字首
  • 2、獲取環境變數方式為 import.meta
  • 3、Vue中使用tsx時報錯: React is not defined

    // vite.config.ts
    esbuild: {
      jsxFactory: 'h',
      jsxFragment: 'Fragment',
      jsxInject: 'import { h } from "vue";',
    },
  • 4、主題外掛 vite-plugin-theme
  • 5、windicss的外掛 vite-plugin-windicss
  • 6、儘量不用 commonjs ,使用 es module

4、CI/CD做過哪些實踐?

  • CI代表了 持續整合 ,我理解的意思就是,我們們每天都會編寫程式碼,並且提交到主幹分支上,天天如此,是一個重複性很高的操作, 持續整合 我理解就是會以最快的速度幫你把程式碼提交到主分支上,這樣的好處就是,當開發人員數量多的時候,這樣快速的整合,有利於減少開發人員程式碼之間的衝突
  • CD代表了 持續部署 ,我理解就是既然有 持續整合 程式碼的話,而程式碼的整合肯定是為了最後的釋出部署,所以 持續部署 就是在自動整合程式碼後,自動部署到環境上,節約了很多人力

曾經在之前的公司裡參與過CI/CD的工作流程,並使用過,但是沒有去深入地實現過,當時公司是使用 Jenkins

5、鑑權有了解過嗎?jwt如何實現踢人,session 和 jwt 鑑權的區別?

session鑑權

session鑑權 是需要服務端儲存的

  • 1、使用者登入
  • 2、服務端接收到登入請求,生成相應的使用者標識,存在服務端
  • 3、服務端將標識傳給前端,這個標識存在 Cookie 中,並存在瀏覽器
  • 4、前端接下來每次請求都會帶著這個 Cookie 去請求
  • 5、服務端接收到標識,拿標識去資料庫裡匹配,匹配到則接受,匹配不到則不接受
  • 6、使用者登出時,前後端都會銷燬這個標識

jwt鑑權

jwt鑑權 是不需要服務端儲存的

  • 1、使用者登入
  • 2、服務端接收到登入請求,根據使用者資訊,生成一個 token 給前端
  • 3、前端接收到 token ,存在了 瀏覽器本地快取 中(例如 LocalStorage
  • 4、接下來每次請求,都需將 token 帶在 請求頭
  • 5、服務端解析前端傳來的 token ,有效則接受,無效則返回 401
  • 6、使用者登出時,只需要前端銷燬這個 token

jwt 踢人

我理解的 jwt踢人 ,就是後端控制某個人的 token 無效,我能想到四種方法

  • 1、服務端生成 token 的同時,生成一段 標識1 ,把 標識1 + token 發給前端,當我想踢掉某個人時,後端就生成一個新的 標識2 ,並修改此使用者對應的token為 標識2 + token ,那麼下次此使用者下次帶著 標識1 + token 訪問時,便會檢驗不通過。這種方法需要用到服務端儲存,違背了 jwt鑑權 的初衷
  • 2、設定兩個token,一個 access_token 一個 refresh_token ,前者過期時間較短,為1小時,後者過期時間較長,為一個月。將 refresh_token 存在後端資料庫(例如redis), access_token 發給前端,前端拿著 access_token 請求時,都要判斷redis中 refresh_token 過期了沒,沒過期的話就通過,或者 access_token 過期了,只要 refresh_token 還沒過期,就重新整理 access_token 返回給前端。那麼想要踢某人下線,就好辦了,直接把 refresh_token 從redis中清除就行。缺點跟第1點一樣
  • 3、黑名單模式。就是把需要踢的使用者的 token 放在一個陣列裡,此使用者訪問時,遍歷陣列看有沒有這個人,有的話就踢下線
  • 4、另外一種踢人模式是,在我們公司的遊戲業務中,踢人下線是使用了 Websocket 的技術,實時將人踢出房間,不涉及 token

6、TCP 三次握手 http1.0 http1.1 http2都有哪些區別?

HTTP一直是我的不足之處,記錄下來,記錄下來

7、https,為什麼可以防止中間人攻擊?

推薦我寫的一篇文章我畫了13張圖,用最通俗易懂的話講HTTPS,拿下!

8、氣泡排序

平均時間複雜度

O(n^2)

思路

陣列中有 n 個數,比較每相鄰兩個數,如果前者大於後者,就把兩個數交換位置;這樣一來,第一輪就可以選出一個最大的數放在最後面;那麼經過 n-1(陣列的 length - 1) 輪,就完成了所有數的排序。

實現

 //從大到小排序
    var array=[10,20,9,8,79,65,100];
    //比較輪數
    for ( var i=0;i<array.length-1;i++){
        //每輪比較次數,次數=長度-1-此時的輪數
        for (var j=0;j<array.length-1-i;j++) {
            if (array[j] > array[j + 1]) {
                var temp = array[i];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            } //end if
        }//end for 次數
    } //end for 輪數
    console.log(array);

二面

1、給你一個已經升序排列的陣列,給一個數字,找一下這個數字在這個陣列裡出現了幾次

思路

既然是升序排列過了,那說明這個數字如果多次出現。那肯定是緊挨著的,所以不需要遍歷完整個陣列,只要以遇到這個數字開始,離開這個數字為結束,這段時間統計完就可以跳出遍歷了,不需要做後面的無用功(這是我僅能想到的優化點)

實現

const arr = [1, 2, 3, 4, 5, 5, 6, 6, 7]

const computed = (arr: number[], num: number): number => {
  let flag = false
  let sum = 0
  for (let item of arr) {
    if (item === num) {
      sum++
      flag = true
      continue
    }
    if (item !== num && flag) break
  }

  return sum
}

console.log(computed(arr, 5))

2、洗牌演算法,如何驗證這個洗牌演算法可以把牌洗得足夠亂

1、隨機索引I

  • 1、建立一個空陣列
  • 2、生成一個 0 —— length - 1 的隨機索引,並將此隨機索引對應元素放到新陣列裡
  • 3、刪除原陣列中此索引對應元素,原陣列length更新
  • 4、重複2、3,直到原陣列 length === 0
  • 5、返回新陣列

    const shuffle = (arr: number[]) => {
    if (!arr.length) return []
    let random: number
    let res: number[] = []
    while (arr.length) {
      random = Math.floor(Math.random() * arr.length)
      res.push(arr[random])
      arr.splice(random, 1)
    }
    return res
    }

2、隨機索引II

  • 1、選取陣列(長度n)中最後一個元素 (arr[length-1]) ,將其與n個元素中的任意一個交換,此時最後一個元素已經確定
  • 2、選取倒數第二個元素 (arr[length-2]) ,將其與 n - 1 個元素中的任意一個交換
  • 3、重複第 1 2 步,直到剩下1個元素為止
const shuffle = (arr: number[]) => {
  if (!arr.length) return []
  let index = arr.length - 1
  let random: number
  while (index) {
    random = Math.floor(Math.random() * index--)
    // 或者
    // random = (Math.random() * index--) >>> 0
    const temp = arr[index]
    arr[index] = arr[random]
    arr[random] = temp
    // 這樣也行,但是我的eslint不允許哈哈
    // [arr[lastIndex], arr[random]] = [arr[random], arr[lastIndex]]
  }

  return arr
}

3、sort方法

const shuffle = (arr: number[]) => {
  return arr.sort(() => 0.5 - Math.random())
}

3、node stream 去取一個超大資料量的日誌,由於記憶體限制每次只能取一部分,現在希望在全部日誌中隨機取一萬條,如何做?

這一道題不會。。記錄下來,記錄下來

4、介紹一下專案 有哪些是由你主導提出的方案做的事情

主導過兩、三個專案:

  • 專案A:小程式
  • 專案B:官網
  • 專案C:後臺管理系統

不足記錄

  • TCP 三次握手 http1.0 http1.1 http2都有哪些區別?
  • CI/CD的實踐經驗不足
  • node stream的使用

結語

我是林三心,一個熱心的前端菜鳥程式設計師。如果你上進,喜歡前端,想學習前端,那我們們可以交朋友,一起摸魚哈哈,摸魚群

image.png

題目摘取

此次面經題目摘取自時隔一年半,我,一個卑微的前端菜雞,又來寫面經了

相關文章