2022凜冬之時三年經驗前端面經

語言: CN / TW / HK

今年的就業形式簡直一片黑暗,本著明年會比今年還差的“僥倖心理”,我還是毫不猶豫地選擇裸辭了,歷經一個半月的努力,收到了還算不錯的 offer,薪資和平臺都有比較大的提升,但還是和自己的心理預期有著很大的差距。所以得出最大的結論就是:不要裸辭!不要裸辭!不要裸辭!因為面試期間帶給人的壓力,和現實與理想的落差對心理的摧殘是不可估量的,在這樣一個環境苟著是不錯的選擇。

接下來總結一般情況下前端面試中會經歷的以下四個階段和三個決定因素:

image-20221204185636679.png 作為前端人員,技術的深度廣度是排在第一位的,三年是一個分割線,一定要在這個時候找準自己的定位和方向。

其次良好的溝通表達能力、著裝和表現等場外因素能提高面試官對你的認可度。

有的人技術很牛逼,但是面試時讓面試官覺得不爽,覺得你盛氣逼人/形象邋遢/自以為是/表述不清,就直接不要你,那是最得不償失的。

以下是我整個面試準備以及被問到過的問題的一個凝練,因為在面試過程中,和麵試官的交流很大程度並不是簡單的背書,最好是要將知識通過自己的總結和凝練表達出來,再根據面試官的提問臨場發揮將之補足完善。

一、自我介紹

面試官讓你自我介紹,而且不限定自我介紹的範圍,肯定是面試官想從你的自我介紹中瞭解到你,所以介紹一定要保證簡短和流暢,面對不同的面試官,自我介紹的內容可以是完全一樣的,所以提前準備好說辭很重要,並且一定要注意:不要磕磕巴巴,要自信!流暢的表達和溝通能力,同樣是面試官會對候選人考核點之一。我也曾當過面試官,自信大方的候選人,往往更容易受到青睞。

1、個人介紹(基本情況),主要的簡歷都有了,這方面一定要短

2、個人擅長什麼,包括技術上的和非技術上的。技術上可以瞭解你的轉場,非技術可以瞭解你這個人

3、做過的專案,撿最核心的專案說,不要把所有專案像背書一樣介紹

4、自己的一些想法、興趣或者是觀點,甚至包括自己的職業規劃。這要是給面試官一個感覺:熱衷於"折騰"或者"思考"

示例:

面試官您好,我叫xxx,xx年畢業於xx大學,自畢業以來一直從事著前端開發的相關工作。

我擅長的技術棧是 vue 全家桶,對 vue2 和 vue3 在使用上和原始碼都有一定程度的鑽研;打包工具對 webpack 和 vite 都比較熟悉;有從零到一主導中大型專案落地的經驗和能力。

在上家公司主要是xx產品線負責人的角色,主要職責是。。。。。。

除了開發相關工作,還有一定的技術管理經驗:比如擔任需求評審、UI/UE互動評審評委,負責開發排期、成員協作、對成員程式碼進行review、組織例會等等

平常會在自己搭建的部落格上記錄一些學習文章或者學習筆記,也會寫一些原創的技術文章發表到掘金上,獲得過xx獎。

總的來說自我介紹儘量控制在3 - 5分鐘之間,簡明扼要為第一要義,其次是突出自己的能力和長處。

對於普通的技術面試官來說,自我介紹只是習慣性的面試前的開場白,一般簡歷上列舉的基本資訊已經滿足他們對你的基本瞭解了。但是對於主管級別的面試官或者Hr,會看重你的性格、行為習慣、抗壓能力等等綜合能力。所以要讓自己在面試過程儘可能表現的積極向上,愛好廣泛、喜歡持續學習,喜歡團隊合作,可以無條件加班等等。當然也不是說讓你去欺騙,只是在現在這種環境中,這些“側面能力”也是能在一定程度提升自己競爭力的法寶。

二、專案挖掘

在目前這個行情,當你收到面試通知時,有很大概率是因為你的專案經驗和所招聘的崗位比較符合。 所以在專案的準備上要額外上心,比如: 1. 對專案中使用到的技術的深挖 2. 對專案整體設計思路的把控 3. 對專案運作流程的管理 4. 團隊協作的能力。 5. 專案的優化點有哪些

這些因人而異就不做贅述了,根據自己的情況好好挖掘即可。

三、個人

先說個人,當你通過了技術面試,到了主管和hr這一步,不管你當前的技術多牛逼,他們會額外考察你個人的潛力、學習能力、性格與團隊的磨合等軟實力,這裡列出一些很容易被問到的:

為什麼跳槽?

直接從個人發展入手錶現出自己的上進心:

  1. 一直想去更大的平臺,不但有更好的技術氛圍,而且學到的東西也更多
  1. 想擴充套件一下自己的知識面,之前一致是做x端的 xx 產品,技術棧比較單一一點,相對xx進行學習。
  1. 之前的工作陷入了舒適圈,做來做去也就那些東西,想要換個平臺擴寬自己的技術廣度,接觸和學習一些新的技術體系,為後續的個人發展更有利

講講你和普通前端,你的亮點有哪些?

1、善於規劃和總結,我會對自己經手的專案進行一個全面的分析,一個是業務拆解,對個各模組的業務通過腦圖進行拆解;另一個就是對程式碼模組的拆解,按功能去區分各個程式碼模組。再去進行開發。我覺得這是很多隻會進行盲目業務開發的前端做不到的

2、喜歡專研技術,平常對 vue 的原始碼一直在學習,也有輸出自己的技術文章,像之前寫過一篇逐行精讀 teleport 的原始碼,花了大約有三十個小時才寫出來的,對每一行原始碼的功能和作用進行了解讀(但是為啥閱讀和點贊這麼低)。

你有什麼缺點?

性子比較沉,更偏內向一點,所以我也會嘗試讓自己變得外向一點。

一個是要開各種評審會,作為前端代表需要我去準備各種材料和進行發言。

所以在團隊內做比較多的技術分享,每週主持例會,也讓我敢於去表達和探討。

最近有關注什麼新技術嗎?

  1. 包依賴管理工具 pnpm(不會重複安裝依賴,非扁平的node_modules結構,符號連結方式新增依賴包)
  2. 打包工具 vite (極速的開發環境)
  3. flutter (Google推出並開源的移動應用程式(App)開發框架,主打跨平臺、高保真、高效能)
  4. rust(聽說是js未來的基座)
  5. turbopack,webpack的繼任者,說是比 vite快10倍,webpack快700倍,然後尤雨溪親自驗證其實並沒有比 vite 快10倍
  6. webcomponents

你是偏向於走各個方向探索還是一直向某個方向研究下去?

我對個人的規劃是這樣的:

3 - 5 年在提高自己的技術深度的同時,擴寬自己的知識面,就是深度和廣度上都要有提升,主要是在廣度上,充分對大前端有了認知才能更好的做出選擇

5 - 7 年就是當有足夠的知識積累之後再選擇某一個感興趣方向深研下去,爭取成為那個領域的專家

團隊規模,團隊規範和開發流程

這個因人而異,如實準備即可,因為不同規模團隊的研發模式差別是很大的。

程式碼 review 的目標

1、最注重的是程式碼的可維護性(變數命名、註釋、函式單一性原則等)

2、擴充套件性:封裝能力(元件、程式碼邏輯是否可複用、可擴充套件性)

3、ES 新特性(es6+ 、ES2020, ES2021 可選鏈、at)

4、函式使用規範(比如遇到用 map 拿來當 forEach 用的)

5、效能提升,怎樣運用演算法,寫出更加優雅,效能更好的程式碼

如何帶領團隊的

我在上家公司是一個技術管理的角色。

0、落實開發規範,我在公司內部 wiki 上有發過,從命名、最佳實踐到各種工具庫的使用。新人進來前期我會優先跟進他們的程式碼質量

1、團隊分工:每個人單獨負責一個產品的開發,然後公共模組一般我會指定某幾個人開發

2、程式碼質量保證:每週會review他們的程式碼,也會組織交叉 review 程式碼,將修改結果輸出文章放到 wiki中

3、組織例會:每週組織例會同步各自進度和風險,根據各自的進度調配工作任務

4、技術分享:還會組織不定時的技術分享。一開始就是單純的我做分享,比如微前端的體系,ice stark 的原始碼

5、公共需求池:比如webpack5/vite的升級;vue2.7的升級引入setup語法糖;pnpm的使用;拓撲圖效能優化

6、優化專項:在第一版產品出來之後,我還發起過效能優化專項,首屏載入效能,打包體積優化;讓每個人去負責對應的優化項

對加班怎麼看?

我覺得加班一般會有兩種情況:

一是專案進度比較緊,那當然以專案進度為先,畢竟大家都靠這個吃飯

二是自身能力問題,對業務不熟啊或者引入一個全新的技術棧,那麼我覺得不僅要加班跟上,還要去利用空閒時間抓緊學習,彌補自己的不足

有什麼興趣愛好?

我平常喜歡閱讀,就是在微信閱讀裡讀一些心理學、時間管理、還有一些演講技巧之類的書

然後是寫文章,因為我發現單純的記筆記很容易就忘了,因為只是記載別人的內容,而寫自己的原創文章,在這個過程中能將知識非常高的比例轉換成自身的東西,所以除了自個發掘金的文章,我也經常會對專案的產出有文章輸出到 wiki 上

其他愛好就是和朋友約著打籃球、唱歌

四、技術

技術面試一定要注意:簡明扼要,詳略得當,不懂的就說不懂。因為在面試過程中是一個和麵試官面對面交流的過程,沒有面試官會喜歡一個絮絮叨叨半天說不到重點候選人,同時在說話過程中,聽者會被動的忽略自己不感興趣的部分,所以要著重突出某個技術的核心特點,並圍繞著核心適當展開。

大廠基本都會通過演算法題來篩選候選人,演算法沒有捷徑,只能一步一步地刷題再刷題,這方面薄弱的要提前規劃進行個學習了。

技術面過程主要會對前端領域相關的技術進行提問,一般面試官會基於你的建立,而更多的是,面試官基於他之前準備好的面試題,或者所在專案組比較熟悉的技術點進行提問,因為都是未知數,所以方方面面都還是要求比較足的。

如果想進入一箇中大型且發展前景不錯的公司,並不是照著別人的面經背一背就能糊弄過去的,這裡作出的總結雖然每一條都很簡短,但都是我對每一個知識點進行全面學習後才提煉出來的部分核心知識點,所以不懼怕面試官的“發散一下思維”。

面試過程一般會涉及到以下八大知識型別的考量:

JS/CSS/TypeScript/框架(Vue、React)/瀏覽器與網路/效能優化/前端工程化/架構/其他

image.png

所以面試前的技術準備絕不是一蹴而就,還需要平時的積累,比如可以利用每天十到二十分鐘對其中一個小知識點進行全面的學習,長此以往,無論是幾年的面試,都足夠侃侃而談。

JS篇

JS的學習梭哈紅包書和冴羽老師的深入JS系列部落格就基本ok了

常見的JS面試題一般會有這些

image-20221206221056905.png

什麼是原型/原型鏈?

原型的本質就是一個物件

當我們在建立一個建構函式之後,這個函式會預設帶上一個prototype屬性,而這個屬性的值就指向這個函式的原型物件。

這個原型物件是用來為通過該建構函式建立的例項物件提供共享屬性,即用來實現基於原型的繼承和屬性的共享

所以我們通過建構函式建立的例項物件都會從這個函式的原型物件上繼承上面具有的屬性

當讀取例項的屬性時,如果找不到,就會查詢與物件關聯的原型中的屬性,如果還查不到,就去找原型的原型,一直找到最頂層為止(最頂層就是Object.prototype的原型,值為null)。

所以通過原型一層層相互關聯的鏈狀結構就稱為原型鏈

什麼是閉包?

定義:閉包是指引用了其他函式作用域中變數的函式,通常是在巢狀函式中實現的。

從技術角度上所有 js 函式都是閉包。

從實踐角度來看,滿足以下倆個條件的函式算閉包

  1. 即使建立它的上下文被銷燬了,它依然存在。(比如從父函式中返回)
  2. 在程式碼中引用了自由變數(在函式中使用的既不是函式引數也不是函式區域性變數的變數稱作自由變數)

使用場景:

  • 建立私有變數

    vue 中的data,需要是一個閉包,保證每個data中資料唯一,避免多次引用該元件造成的data共享

  • 延長變數的生命週期

    一般函式的詞法環境在函式返回後就被銷燬,但是閉包會儲存對建立時所在詞法環境的引用,即便建立時所在的執行上下文被銷燬,但建立時所在詞法環境依然存在,以達到延長變數的生命週期的目的

應用

  • 柯里化函式
  • 例如計數器、延遲呼叫、回撥函式等

this 的指向

在絕大多數情況下,函式的呼叫方式決定了 this 的值(執行時繫結)

1、全域性的this非嚴格模式指向window物件,嚴格模式指向 undefined

2、物件的屬性方法中的this 指向物件本身

3、apply、call、bind 可以變更 this 指向為第一個傳參

4、箭頭函式中的this指向它的父級作用域,它自身不存在 this

瀏覽器的事件迴圈?

js 程式碼執行過程中,會建立對應的執行上下文並壓入執行上下文棧中。

如果遇到非同步任務就會將任務掛起,交給其他執行緒去處理非同步任務,當非同步任務處理完後,會將回調結果加入事件佇列中。

當執行棧中所有任務執行完畢後,就是主執行緒處於閒置狀態時,才會從事件佇列中取出排在首位的事件回撥結果,並把這個回撥加入執行棧中然後執行其中的程式碼,如此反覆,這個過程就被稱為事件迴圈。

事件佇列分為了巨集任務佇列和微任務佇列,在當前執行棧為空時,主執行緒回先檢視微任務佇列是否有事件存在,存在則依次執行微任務佇列中的事件回撥,直至微任務佇列為空;不存在再去巨集任務佇列中處理。

常見的巨集任務有setTimeout()setInterval()setImmediate()、I/O、使用者互動操作,UI渲染

常見的微任務有promise.then()promise.catch()new MutationObserverprocess.nextTick()

巨集任務和微任務的本質區別

  1. 巨集任務有明確的非同步任務需要執行和回撥,需要其他非同步執行緒支援
  2. 微任務沒有明確的非同步任務需要執行,只有回撥,不需要其他非同步執行緒支援。

javascript中資料在棧和堆中的儲存方式

1、基本資料型別大小固定且操作簡單,所以放入棧中儲存

2、引用資料型別大小不確定,所以將它們放入堆記憶體中,讓它們在申請記憶體的時候自己確定大小

3、這樣分開儲存可以使記憶體佔用最小。棧的效率高於堆

4、棧記憶體中變數在執行環境結束後會立即進行垃圾回收,而堆記憶體中需要變數的所有引用都結束才會被回收

講講v8垃圾回收

1、根據物件的存活時間將記憶體的垃圾回收進行不同的分代,然後對不同分代採用不同的回收演算法

2、新生代採用空間換時間的 scavenge 演算法:整個空間分為兩塊,變數僅存在其中一塊,回收的時候將存活變數複製到另一塊空間,不存活的回收掉,周而復始輪流操作

3、老生代使用標記清除和標記整理,標記清除:遍歷所有物件標記標記可以訪問到的物件(活著的),然後將不活的當做垃圾進行回收。回收完後避免記憶體的斷層不連續,需要通過標記整理將活著的物件往記憶體一端進行移動,移動完成後再清理邊界記憶體

函式呼叫的方法

1、普通function直接使用()呼叫並傳參,如:function test(x, y) { return x + y}test(3, 4)

2、作為物件的一個屬性方法呼叫,如:const obj = { test: function (val) { return val } }, obj.test(2)

3、使用callapply呼叫,更改函式 this 指向,也就是更改函式的執行上下文

4、new可以間接呼叫建構函式生成物件例項

defer和async的區別

一般情況下,當執行到 script 標籤時會進行下載 + 執行兩步操作,這兩步會阻塞 HTML 的解析;

async 和 defer 能將script的下載階段變成非同步執行(和 html解析同步進行);

async下載完成後會立即執行js,此時會阻塞HTML解析;

defer會等全部HTML解析完成且在DOMContentLoaded 事件之前執行。

瀏覽器事件機制

DOM 事件流三階段:

  1. 捕獲階段:事件最開始由不太具體的節點最早接受事件, 而最具體的節點(觸發節點)最後接受事件。為了讓事件到達最終目標之前攔截事件。

    比如點選一個div,則 click 事件會按這種順序觸發: document => <html> => <body> => <div>,即由 document 捕獲後沿著 DOM 樹依次向下傳播,並在各節點上觸發捕獲事件,直到到達實際目標元素。

  2. 目標階段

    當事件到達目標節點的,事件就進入了目標階段。事件在目標節點上被觸發(執行事件對應的函式),然後會逆向迴流,直到傳播至最外層的文件節點。

  3. 冒泡階段

    事件在目標元素上觸發後,會繼續隨著 DOM 樹一層層往上冒泡,直到到達最外層的根節點。

所有事件都要經歷捕獲階段和目標階段,但有些事件會跳過冒泡階段,比如元素獲得焦點 focus 和失去焦點 blur 不會冒泡

擴充套件一

e.target 和 e.currentTarget 區別?

  • e.target 指向觸發事件監聽的物件。
  • e.currentTarget 指向新增監聽事件的物件。

例如:

```

     
  • hello 1

​ let ul = document.querySelectorAll('ul')[0] let aLi = document.querySelectorAll('li') ul.addEventListener('click',function(e){ let oLi1 = e.target   let oLi2 = e.currentTarget console.log(oLi1)   // 被點選的li console.log(oLi2)   // ul console.og(oLi1===oLi2) // false }) ```

給 ul 綁定了事件,點選其中 li 的時候,target 就是被點選的 li, currentTarget 就是被繫結事件的 ul

事件冒泡階段(上述例子),e.currenttargete.target是不相等的,但是在事件的目標階段,e.currenttargete.target是相等的

作用:

e.target可以用來實現事件委託,該原理是通過事件冒泡(或者事件捕獲)給父元素新增事件監聽,e.target指向引發觸發事件的元素

擴充套件二

addEventListener 引數

語法:

addEventListener(type, listener); addEventListener(type, listener, options || useCapture);

  • type: 監聽事件的型別,如:'click'/'scroll'/'focus'

  • listener: 必須是一個實現了 EventListener 介面的物件,或者是一個函式。當監聽的事件型別被觸發時,會執行

  • options:指定 listerner 有關的可選引數物件

    • capture: 布林值,表示 listener 是否在事件捕獲階段傳播到 EventTarget 時觸發
    • once:布林值,表示 listener 新增之後最多呼叫一次,為 true 則 listener 在執行一次後會移除
    • passive: 布林值,表示 listener 永遠不會呼叫 preventDefault()
    • signal:可選,AbortSignal,當它的abort()方法被呼叫時,監聽器會被移除
  • useCapture:布林值,預設為 false,listener 在事件冒泡階段結束時執行,true 則表示在捕獲階段開始時執行。作用就是更改事件作用的時機,方便攔截/不被攔截。

Vue篇

筆主是主要從事 Vue相關開發的,也做過 react 相關的專案,當然 react 也只是能做專案的水平,所以在簡歷中屬於一筆帶過的那種,框架貴不在多而在精,對Vue原始碼系列的學習讓我對Vue還是十分自信的。學習過程也是如此,如果你能夠對一門框架達到精通原理的掌握程度,學習其他框架不過只是花時間的事情。

image.png

vue和react的區別

1、資料可變性

  • React 推崇函數語言程式設計,資料不可變以及單向資料流,只能通過setState或者onchange來實現檢視更新
  • Vue 基於資料可變,設計了響應式資料,通過監聽資料的變化自動更新檢視

2、寫法

  • React 推薦使用 jsx + inline style的形式,就是 all in js
  • Vue 是單檔案元件(SFC)形式,在一個元件內分模組(tmplate/script/style),當然vue也支援jsx形式,可以在開發vue的ui元件庫時使用

3、diff 演算法

  • Vue2採用雙端比較,Vue3採用快速比較
  • react主要使用diff佇列儲存需要更新哪些DOM,得到patch樹,再統一操作批量更新DOM。,需要使用shouldComponentUpdate()來手動優化react的渲染。

擴充套件:瞭解 react hooks嗎

元件類的寫法很重,層級一多很難維護。

函式元件是純函式,不能包含狀態,也不支援生命週期方法,因此無法取代類。

React Hooks 的設計目的,就是加強版函式元件,完全不使用"類",就能寫出一個全功能的元件

React Hooks 的意思是,元件儘量寫成純函式,如果需要外部功能和副作用,就用鉤子把外部程式碼"鉤"進來。

vue元件通訊方式

  • props / $emit
  • ref / $refs
  • $parent / $root
  • attrs / listeners
  • eventBus / vuex / pinia / localStorage / sessionStorage / Cookie / window
  • provide / inject

vue 渲染列表為什麼要加key?

Vue 在處理更新同類型 vnode 的一組子節點(比如v-for渲染的列表節點)的過程中,為了減少 DOM 頻繁建立和銷燬的效能開銷:

對沒有 key 的子節點陣列更新是通過就地更新的策略。它會通過對比新舊子節點陣列的長度,先以比較短的那部分長度為基準,將新子節點的那一部分直接 patch 上去。然後再判斷,如果是新子節點陣列的長度更長,就直接將新子節點陣列剩餘部分掛載;如果是新子節點陣列更短,就把舊子節點多出來的那部分給解除安裝掉)。所以如果子節點是元件或者有狀態的 DOM 元素,原有的狀態會保留,就會出現渲染不正確的問題

有 key 的子節點更新是呼叫的patchKeyedChildren,這個函式就是大家熟悉的實現核心 diff 演算法的地方,大概流程就是同步頭部節點、同步尾部節點、處理新增和刪除的節點,最後用求解最長遞增子序列的方法區處理未知子序列。是為了最大程度實現對已有節點的複用,減少 DOM 操作的效能開銷,同時避免了就地更新帶來的子節點狀態錯誤的問題。

綜上,如果是用 v-for 去遍歷常量或者子節點是諸如純文字這類沒有”狀態“的節點,是可以使用不加 key 的寫法的。但是實際開發過程中更推薦統一加上 key,能夠實現更廣泛場景的同時,避免了可能發生的狀態更新錯誤,我們一般可以使用 ESlint 配置 key 為 v-for 的必需元素。

想詳細瞭解這個知識點的可以去看看我之前寫的文章:v-for 到底為啥要加上 key?

vue3 相對 vue2的響應式優化

vue2使用的是Object.defineProperty去監聽物件屬性值的變化,但是它不能監聽物件屬性的新增和刪除,所以需要使用$set$delete這種語法糖去實現,這其實是一種設計上的不足。

所以 vue3 採用了proxy去實現響應式監聽物件屬性的增刪查改。

其實從api的原生效能上proxy是比Object.defineProperty要差的。

而 vue 做的響應式效能優化主要是在將巢狀層級比較深的物件變成響應式的這一過程。

vue2的做法是在元件初始化的時候就遞迴執行Object.defineProperty把子物件變成響應式的;

而vue3是在訪問到子物件屬性的時候,才會去將它轉換為響應式。這種延時定義子物件響應式會對效能有一定的提升

Vue 核心diff流程

前提:當同類型的 vnode 的子節點都是一組節點(陣列型別)的時候,

步驟:會走核心 diff 流程

Vue3是快速選擇演算法

  • 同步頭部節點
  • 同步尾部節點
  • 新增新的節點
  • 刪除多餘節點
  • 處理未知子序列(貪心 + 二分處理最長遞增子序列)

Vue2是雙端比較演算法

在新舊位元組點的頭尾節點,也就是四個節點之間進行對比,找到可複用的節點,不斷向中間靠攏的過程

diff目的:diff 演算法的目的就是為了儘可能地複用節點,減少 DOM 頻繁建立和刪除帶來的效能開銷

vue雙向繫結原理

基於 MVVM 模型,viewModel(業務邏輯層)提供了資料變化後更新檢視檢視變化後更新資料這樣一個功能,就是傳統意義上的雙向繫結。

Vue2.x 實現雙向繫結核心是通過三個模組:Observer監聽器、Watcher訂閱者和Compile編譯器。

首先監聽器會監聽所有的響應式物件屬性,編譯器會將模板進行編譯,找到裡面動態繫結的響應式資料並初始化檢視;watchr 會去收集這些依賴;當響應式資料發生變更時Observer就會通知 Watcher;watcher接收到監聽器的訊號就會執行更新函式去更新檢視;

vue3的變更是資料劫持部分使用了porxy 替代 Object.defineProperty,收集的依賴使用元件的副作用渲染函式替代watcher

v-model 原理

vue2 v-model 原理剖析

V-model 是用來監聽使用者事件然後更新資料的語法糖。

其本質還是單向資料流,內部是通過繫結元素的 value 值向下傳遞資料,然後通過繫結 input 事件,向上接收並處理更新資料。

單向資料流:父元件傳遞給子元件的值子元件不能修改,只能通過emit事件讓父元件自個改。

// 比如 <input v-model="sth" /> // 等價於 <input :value="sth" @input="sth = $event.target.value" />

給元件新增 v-model 屬性時,預設會把value 作為元件的屬性,把 input作為給元件繫結事件時的事件名:

``` // 父元件 ​ // 子元件

```

如果想給繫結的 value 屬性和 input 事件換個名稱呢?可以這樣:

在 Vue 2.2 及以上版本,你可以在定義元件時通過 model 選項的方式來定製 prop/event:

```

轉換成code:scriptalert(你被xss攻擊了)/script`

2、充分利用內容安全策略 CSP(content-security-policy),可以通過 http 頭資訊的 content-security-policy 欄位控制可以載入和執行的外部資源;或者通過html的meta 標籤<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">

3、cookie設定為 http-only, cookie 就無法通過 document.cookie 來讀取

csrf基本概念

Csrf(cross site request forgery)跨站請求偽造,指黑客引導使用者訪問黑客的網站。

CSRF 是指黑客引誘使用者開啟黑客的網站,在黑客的網站中,利用使用者的登入狀態發起的跨站請求。簡單來講,CSRF 攻擊就是黑客利用了使用者的登入狀態,並通過第三方的站點來做一些壞事。

Csrf 攻擊場景

  1. 自動發起 get 請求

    比如黑客網站有個圖片:

    <img src="https://time.geekbang.org/sendcoin?user=hacker&number=100">

    黑客將轉賬的請求介面隱藏在 img 標籤內,欺騙瀏覽器這是一張圖片資源。當該頁面被載入時,瀏覽器會自動發起 img 的資源請求,如果伺服器沒有對該請求做判斷的話,那麼伺服器就會認為該請求是一個轉賬請求,於是使用者賬戶上的 100 極客幣就被轉移到黑客的賬戶上去了。

  2. 自動發起 post 請求

    黑客在頁面中構建一個隱藏的表單,當用戶點開連結後,表單自動提交

  3. 引誘使用者點選連結

    比如頁面上放了一張美女圖片,下面放了圖片下載地址,而這個下載地址實際上是黑客用來轉賬的介面,一旦使用者點選了這個連結,那麼他的極客幣就被轉到黑客賬戶上了

防止csrf方法

1、設定 cookie 時帶上SameSite: strict/Lax選項

2、驗證請求的來源站點,通過 origin 和 refere 判斷來源站點資訊

3、csrf token,瀏覽器發起請求伺服器生成csrf token,發起請求前會驗證 csrf token是否合法。第三方網站肯定是拿不到這個token。我們的 csrf token 是前後端約定好後寫死的。

websocket

websocket是一種支援雙向通訊的協議,就是伺服器可以主動向客戶端發訊息,客戶端也可以主動向伺服器發訊息。

它是基於 HTTP 協議來建立連線的的,與http協議的相容性很好,所以能通過 HTTP 代理伺服器;沒有同源限制。

WebSocket 是一種事件驅動的協議,這意味著可以將其用於真正的實時通訊。與 HTTP 不同(必須不斷地請求更新),而使用 websockets,更新在可用時就會立即傳送

當連線終止時,WebSockets 不會自動恢復,這是應用開發中需要自己實現的機制,也是存在許多客戶端開源庫的原因之一。

像webpack和vite的devServer就使用了websocket實現熱更新

Post 和 Get 區別

  • 應用場景: (GET 請求是一個冪等的請求)一般 Get 請求用於對伺服器資源不會產生影響的場景,比如說請求一個網頁的資源。(而 Post 不是一個冪等的請求)一般用於對伺服器資源會產生影響的情景,比如註冊使用者這一類的操作。(冪等是指一個請求方法執行多次和僅執行一次的效果完全相同
  • 是否快取: 因為兩者應用場景不同,瀏覽器一般會對 Get 請求快取,但很少對 Post 請求快取。
  • 傳參方式不同: Get 通過查詢字串傳參,Post 通過請求體傳參。
  • 安全性: Get 請求可以將請求的引數放入 url 中向伺服器傳送,這樣的做法相對於 Post 請求來說是不太安全的,因為請求的 url 會被保留在歷史記錄中。
  • 請求長度: 瀏覽器由於對 url 長度的限制,所以會影響 get 請求傳送資料時的長度。這個限制是瀏覽器規定的,並不是 RFC 規定的。
  • 引數型別: get引數只允許ASCII字元,post 的引數傳遞支援更多的資料型別(如檔案、圖片)。

效能優化篇

效能優化是中大型公司非常注重的點,因為和前端人的KPI息息相關,所以自然會作為面試題的常客。

image.png

效能優化有哪些手段

  1. 從快取的角度

    • 將一些不常變的大資料通過localstorage/sessionStorage/indexdDB 進行讀取
    • 活用 http 快取(強快取和協商快取),將內容儲存在記憶體或者硬碟中,減少對伺服器端的請求
  2. 網路方面比較常用的是靜態資源使用 CDN

  3. 打包方面

    • 路由的按需載入
    • 優化打包後資源的大小
    • 開啟 gzip 壓縮資源
    • 按需載入三方庫
  4. 程式碼層面

    • 減少不必要的請求,刪除不必要的程式碼
    • 避免耗時過長的js處理阻塞主執行緒(耗時且無關 DOM 可以丟到web worker去處理或者拆分成小的任務)
    • 圖片可以使用懶載入的方式,長列表使用虛擬滾動
  5. 首屏速度提升

    • 程式碼壓縮,減少打包的靜態資源體積(Terser plugin/MiniCssExtratplugin)
    • 路由懶載入,首屏就只會請求第一個路由的相關資源
    • 使用 cdn加速第三方庫,我們是toB的產品,會需要部署在內網,所以一般不用,toC用的多
    • ssr 服務端渲染,由伺服器直接返回拼接好的html頁面
  6. vue 常見的效能優化方式

    • 圖片懶載入: vue-lazyLoad
    • 虛擬滾動
    • 函式式元件
    • v-show/ keep-alive 複用 dom
    • deffer延時渲染元件(requestIdleCallback)
    • 時間切片 time slicing

前端監控 SDK 技術要點

  1. 可以通過window.performance獲取各項效能指標資料

  2. 完整的前端監控平臺包括:資料採集和上報、資料整理和儲存、資料展示

  3. 網頁效能指標:

    • FP(first-paint)從頁面載入到第一個畫素繪製到螢幕上的時間
    • FCP(first-contentful-paint),從頁面載入開始到頁面內容的任何部分在螢幕上完成渲染的時間
    • LCP(largest-contentful-paint),從頁面載入到最大文字或影象元素在螢幕上完成渲染的時間
  4. 以上指標可以通過PerformanceObserver獲取

  5. 首屏渲染時間計算:通過MutationObserver監聽document物件的屬性變化

如何減少迴流、重繪,充分利用 GPU 加速渲染?

首先應該避免直接使用 DOM API 操作 DOM,像 vue react 虛擬 DOM 讓對 DOM 的多次操作合併成了一次。

  1. 樣式集中改變,好的方式是使用動態 class

  2. 讀寫操作分離,避免讀後寫,寫後又讀

    // bad 強制重新整理 觸發四次重排+重繪 div.style.left = div.offsetLeft + 1 + 'px'; div.style.top = div.offsetTop + 1 + 'px'; div.style.right = div.offsetRight + 1 + 'px'; div.style.bottom = div.offsetBottom + 1 + 'px'; ​ ​ // good 快取佈局資訊 相當於讀寫分離 觸發一次重排+重繪 var curLeft = div.offsetLeft; var curTop = div.offsetTop; var curRight = div.offsetRight; var curBottom = div.offsetBottom; ​ div.style.left = curLeft + 1 + 'px'; div.style.top = curTop + 1 + 'px'; div.style.right = curRight + 1 + 'px'; div.style.bottom = curBottom + 1 + 'px';

    原來的操作會導致四次重排,讀寫分離之後實際上只觸發了一次重排,這都得益於瀏覽器的渲染佇列機制:

    當我們修改了元素的幾何屬性,導致瀏覽器觸發重排或重繪時。它會把該操作放進渲染佇列,等到佇列中的操作到了一定的數量或者到了一定的時間間隔時,瀏覽器就會批量執行這些操作。

  3. 使用display: none後元素不會存在渲染樹中,這時對它進行各種操作,然後更改 display 顯示即可(示例:向2000個div中插入一個div)

  4. 通過 documentFragment 建立 dom 片段,在它上面批量操作 dom ,操作完後再新增到文件中,這樣只有一次重排(示例:一次性插入2000個div)

  5. 複製節點在副本上操作然後替換它

  6. 使用 BFC 脫離文件流,重排開銷小

Css 中的transformopacityfilterwill-change能觸發硬體加速

大圖片優化的方案

  1. 優化請求數

    • 雪碧圖,將所有圖示合併成一個獨立的圖片檔案,再通過 background-urlbackgroun-position來顯示圖示
    • 懶載入,儘量只加載使用者正則瀏覽器或者即將瀏覽的圖片。最簡單使用監聽頁面滾動判斷圖片是否進入視野;使用 intersection Observer API;使用已知工具庫;使用css的background-url來懶載入
    • base64,小圖示或骨架圖可以使用內聯 base64因為 base64相比普通圖片體積大。注意首屏不需要懶載入,設定合理的佔位圖避免抖動。
  2. 減小圖片大小

    • 使用合適的格式比如WebP、svg、video替代 GIF 、漸進式 JPEG
    • 削減圖片質量
    • 使用合適的大小和解析度
    • 刪除冗餘的圖片資訊
    • Svg 壓縮
  3. 快取

程式碼優化

  1. 非響應式變數可以定義在created鉤子中使用 this.xxx 賦值

  2. 訪問區域性變數比全域性變數塊,因為不需要切換作用域

  3. 儘可能使用 const宣告變數,注意陣列和物件

  4. 使用 v8 引擎時,執行期間,V8 會將建立的物件與隱藏類關聯起來,以追蹤它們的屬性特徵。能夠共享相同隱藏類的物件效能會更好,v8 會針對這種情況去優化。所以為了貼合”共享隱藏類“,我們要避免”先建立再補充“式的動態屬性複製以及動態刪除屬性(使用delete關鍵字)。即儘量在建構函式/物件中一次性宣告所有屬性。屬性刪除時可以設定為 null,這樣可以保持隱藏類不變和繼續共享。

  5. 避免記憶體洩露的方式

    • 儘可能少建立全域性變數
    • 手動清除定時器
    • 少用閉包
    • 清除 DOM 引用
    • 弱引用
  6. 避免強制同步,在修改 DOM 之前查詢相關值

  7. 避免佈局抖動(一次 JS 執行過程中多次執行強制佈局和抖動操作),儘量不要在修改 DOM 結構時再去查詢一些相關值

  8. 合理利用 css 合成動畫,如果能用 css 處理就交給 css。因為合成動畫會由合成執行緒執行,不會佔用主執行緒

  9. 避免頻繁的垃圾回收,優化儲存結構,避免小顆粒物件的產生

感興趣的可以看看我之前的一篇效能優化技巧整理的文章極意 · 程式碼效能優化之道

前端工程化篇

前端工程化是前端er成長路上最必不可少的技能點,一切能夠提高前端開發效率的功能都可以當做前端工程化的一部分。剛入門的可以從零搭建腳手架開始萬字長文詳解從零搭建企業級 vue3 + vite2+ ts4 框架全過程

對於面試而言,考察的最多的還是對打包工具的掌握程度。

image.png

webpack的執行流程和生命週期

webpack 是為現代 JS 應用提供靜態資源打包功能的 bundle。

核心流程有三個階段: 初始化階段、構建階段和生成階段

1、初始化階段,會從配置檔案、配置物件和Shell引數中讀取初始化的引數並與預設配置結合成最終的引數,之以及建立 compiler 編譯器物件和初始化它的執行環境

2、構建階段,編譯器會執行它的 run()方法開始編譯的過程,其中會先確認 entry 入口檔案,從入口檔案開始搜尋和入口檔案有直接或者簡介關聯的所有檔案建立依賴物件,之後再根據依賴物件建立 module 物件,這時候會使用 loader 將模組轉換標準的 js 內容,再呼叫 js 的直譯器將內容轉換成 AST 物件,再從 AST 中找到該模組依賴的模組,遞迴本步驟知道所有入口依賴檔案都經過了本步驟的處理。最後完成模組編譯,得到了每個模組被翻譯的內容和他們之間的關係依賴圖(dependency graph),這個依賴圖就是專案所有用到的模組的對映關係。

3、生成階段,將編譯後的 module 組合成 chunk ,再把每個 chunk 轉換成一個單獨的檔案輸出到檔案列表,確定好輸出內容後,根據配置確定輸出路徑和檔名,就把檔案內容寫入檔案系統

webpack的plugin 和loader

loader

webpack只能理解 JS 和 JSON 檔案,loader 本質上就是個轉換器,能將其他型別的檔案轉換成 webpack 識別的東西

loader 會在 webpack 的構建階段將依賴物件建立的 module 轉換成標準的 js 內容的東西。比如 vue-loader 將vue檔案轉換成 js 模組,圖片字型通過 url-loader 轉換成 data URL,這些 webpack 能夠識別的東西。

可以在 module.rules 中配置不同的 loader 解析不同的檔案

plugin

外掛本質是一個帶有 apply 函式的類class myPlugin { apply(compiler) {} },這個apply 函式有個引數 compiler 是webpack 初始化階段生成的編譯器物件,可以呼叫編譯器物件中的 hooks 註冊各種鉤子的回撥這些 hooks 是貫穿整個編譯的生命週期。所以開發者可以通過鉤子回撥在裡面插入特定的程式碼,實現特定的功能。

比如stylelint plugin可以指定 stylelint 的需要檢查檔案型別和檔案範圍;HtmlWebpackPlugin 用來生成打包後的模板檔案;MiniCssExtactPlugin會將所有的css提取成獨立的chunks,stylelintplugin可以在開發階段提供樣式的檢查功能。

webpack的hash策略

MiniCssExtractPlugin 對於瀏覽器來說,一方面期望每次請求頁面資源時,獲得的都是最新的資源;一方面期望在資源沒有發生變化時,能夠複用快取物件。這個時候,使用檔名+檔案雜湊值的方式,就可以實現只要通過檔名,就可以區分資源是否有更新。而webpack就內建了hash計算方法,對生成檔案的可以在輸出檔案中新增hash欄位。

Webpack 內建 hash 有三種

  • hash: 專案每次構建都會生成一個hash,和整個專案有關,專案任意地方有改變就會改變

    hash會更據每次工程的內容進行計算,很容易造成不必要的hash變更,不利於版本管理。 一般來說,沒有什麼機會直接使用hash。

  • content hash: 和單個檔案的內容相關。指定檔案的內容發生改變,就會改變hash,內容不變hash 值不變

    對於css檔案來說,一般會使用MiniCssExtractPlugin將其抽取為一個單獨的css檔案。

    此時可以使用contenthash進行標記,確保css檔案內容變化時,可以更新hash。

  • chunk hash:和webpack打包生成的chunk相關。每一個entry,都會有不同的hash。

    一般來說,針對於輸出檔案,我們使用chunkhash。

    因為webpack打包後,最終每個entry檔案及其依賴會生成單獨的一個js檔案。

    此時使用chunkhash,能夠保證整個打包內容的更新準確性。

擴充套件:file-loader 的 hash 可能有同學會表示有以下疑問。

明明經常看到在處理一些圖片,字型的file-loader的打包時,使用的是[name]_[hash:8].[ext]

但是如果改了其他工程檔案,比如index.js,生成的圖片hash並沒有變化。

這裡需要注意的是,file-loader的hash欄位,這個loader自己定義的佔位符,和webpack的內建hash欄位並不一致。

這裡的hash是使用md4等hash演算法,對檔案內容進行hash。

所以只要檔案內容不變,hash還是會保持一致。

vite原理

Vite 主要由兩個部分組成

  1. 開發環境

    Vite 利用瀏覽器去解析 imports,在伺服器端按需編譯返回,完全跳過了打包這個概念,伺服器隨起隨用(就相當於把我們在開發的檔案轉換成 ESM 格式直接傳送給瀏覽器)

    當瀏覽器解析 import HelloWorld from './components/HelloWorld.vue' 時,會向當前域名傳送一個請求獲取對應的資源(ESM支援解析相對路徑),瀏覽器直接下載對應的檔案然後解析成模組記錄(開啟 network 面板可以看到響應資料都是 ESM 型別的 js)。然後例項化為模組分配記憶體,按照匯入匯出語句建立模組和記憶體的對映關係。最後執行程式碼。

    vite 會啟動一個 koa 伺服器攔截瀏覽器對 ESM 的請求,通過請求路徑找到目錄下對應的檔案並處理成 ESM 格式返回給客戶端

    vite的熱載入是在客戶端和服務端之間建立了 websocket 連線,程式碼修改後服務端傳送訊息通知客戶端去請求修改模組的程式碼,完成熱更新,就是改了哪個 view 檔案就重新請求那個檔案,這樣保證了熱更新速度不受專案大小影響。

    開發環境會使用 esbuild 對依賴進行個預構建快取,第一次啟動會慢一點,後面的啟動會直接讀取快取

  2. 生產環境

    使用 rollup 來構建程式碼,提供指令可以用來優化構建過程。缺點就是開發環境和生產環境可能不一致;

webpack 和 vite 對比

Webpack 的熱更新原理簡單來說就是,一旦發生某個依賴(比如 a.js )改變,就將這個依賴所處的 module 的更新,並將新的 module 傳送給瀏覽器重新執行。每次熱更新都會重新生成 bundle。試想如果依賴越來越多,就算只修改一個檔案,理論上熱更新的速度也會越來越慢

Vite 利用瀏覽器去解析 imports,在伺服器端按需編譯返回,完全跳過了打包這個概念,伺服器隨起隨用,熱更新是在客戶端和服務端之間建立了 websocket 連線,程式碼修改後服務端傳送訊息通知客戶端去請求修改模組的程式碼,完成熱更新,就是改了哪個檔案就重新請求那個檔案,這樣保證了熱更新速度不受專案大小影響。

所以vite目前的最大亮點在於開發體驗上,服務啟動快、熱更新快,明顯地優化了開發者體驗,生產環境因為底層是 rollup ,rollup更適合小的程式碼庫,從擴充套件和功能上都是不如 webpack 的,可以使用vite作為一個開發伺服器dev server使用,生產打包用webpack這樣的模式。

做過哪些 wewbpack 的優化

0、升級 webpack 版本,3升4,實測是提升了幾十秒的打包速度

1、splitChunksPlugin 抽離共用模組輸出單獨的chunks ,像一些第三方的依賴庫,可以單獨拆分出來,避免單個chunks過大。

2、DllPlugin 作用同上,這個依賴庫相當於從業務程式碼中剝離出來,只有依賴庫自身版本變化才會重新打包,提升打包速度

3、loaders的執行是同步的,同各模組會執行全部的loaders

  • 可以使用oneOf,只要匹配上對應的loader就不會繼續執行loader
  • 使用 happyPack 將loader的同步執行轉換成並行比如(style-loader,css-loader,less-loader合併起來執行)

4、exclude/include 指定匹配範圍;alias指定路徑別名;

5、cache-loader持久化儲存;

6、ESM專案開啟 useExport標記,使用 tree-shaking

7、exclude/include 指定匹配範圍;alias指定路徑別名;cache-loader持久化儲存;

8、terserPlugin 可以提供程式碼壓縮,去除註釋、去除空格的功能;MiniCssExtractPlugin 壓縮 css

npm run 執行過程

0、在 package.json 檔案中可以定義 script 配置項,裡面可以定義執行指令碼的鍵和值

1、在 npm install 的時候,npm 會讀取配置將執行指令碼軟連結node_modules/.bin目錄下,同時將./bin加入當環境變數$PATH,所以如果在全域性直接執行該命令會去全域性目錄裡找,可能會找不到該命令就會報錯。比如 npm run start,他是執行的webpack-dev-server帶上引數

2、還有一種情況,就是單純的執行指令碼命令,比如 npm run build,實際執行的是 node build.js,即使用 node 執行 build.js 這個檔案

ESM和CJS 的區別

ES6

  • ES6模組是引用,重新賦值會編譯報錯,不能修改其變數的指標指向,但可以改變內部屬性的值;
  • ES6模組中的值屬於動態只讀引用。
  • 對於只讀來說,即不允許修改引入變數的值,import的變數是隻讀的,不論是基本資料型別還是複雜資料型別。當模組遇到import命令時,就會生成一個只讀引用。等到指令碼真正執行時,再根據這個只讀引用,到被載入的那個模組裡面去取值。
  • 對於動態來說,原始值發生變化,import 載入的值也會發生變化。不論是基本資料型別還是複雜資料型別。
  • 迴圈載入時,ES6模組是動態引用。只要兩個模組之間存在某個引用,程式碼就能夠執行。

CommonJS

  • CommonJS模組是拷貝(淺拷貝),可以重新賦值,可以修改指標指向;
  • 對於基本資料型別,屬於複製。即會被模組快取。同時,在另一個模組可以對該模組輸出的變數重新賦值。
  • 對於複雜資料型別,屬於淺拷貝。由於兩個模組引用的物件指向同一個記憶體空間,因此對該模組的值做修改時會影響另一個模組。 當使用require命令載入某個模組時,就會執行整個模組的程式碼。
  • 當使用require命令載入同一個模組時,不會再執行該模組,而是取到快取之中的值。也就是說,CommonJS模組無論載入多少次,都只會在第一次載入時執行一次,以後再載入,就返回第一次執行的結果,除非手動清除系統快取。
  • 當迴圈載入時,指令碼程式碼在require的時候,就會全部執行。一旦出現某個模組被"迴圈載入",就只輸出已經執行的部分,還未執行的部分不會輸出。

設計模式篇

代理模式

代理模式:為物件提供一個代用品或佔位符,以便控制對它的訪問

例如實現圖片懶載入的功能,先通過一張loading圖佔位,然後通過非同步的方式載入圖片,等圖片載入好了再把完成的圖片載入到img標籤裡面

裝飾者模式

裝飾者模式的定義:在不改變物件自身的基礎上,在程式執行期間給物件動態地新增方法

通常運用在原有方法維持不變,在原有方法上再掛載其他方法來滿足現有需求。

像 typescript 的裝飾器就是一個典型的裝飾者模式,還有 vue 當中的 mixin

單例模式

保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。實現的方法為先判斷例項存在與否,如果存在則直接返回,如果不存在就建立了再返回,這就確保了一個類只有一個例項物件

比如 ice stark 的子應用,一次只保證渲染一個子應用

觀察者模式和釋出訂閱模式

1、雖然兩種模式都存在訂閱者和釋出者(具體觀察者可認為是訂閱者、具體目標可認為是釋出者),但是觀察者模式是由具體目標排程的,而釋出/訂閱模式是統一由排程中心調的,所以觀察者模式的訂閱者與釋出者之間是存在依賴的,而釋出/訂閱模式則不會。

2、兩種模式都可以用於鬆散耦合,改進程式碼管理和潛在的複用。

3、在觀察者模式中,觀察者是知道Subject的,Subject一直保持對觀察者進行記錄。然而,在釋出訂閱模式中,釋出者和訂閱者不知道對方的存在。它們只有通過訊息代理進行通訊

4、觀察者模式大多數時候是同步的,比如當事件觸發,Subject就會去呼叫觀察者的方法。而釋出-訂閱模式大多數時候是非同步的(使用訊息佇列)

其他

正則表示式

正則表示式是什麼資料型別?

是物件,let re = /ab+c/等價於let re = new RegExp('ab+c')

正則貪婪模式和非貪婪模式?

量詞

*:0或多次; ?:0或1次; +:1到多次; {m}:出現m次; {m,}:出現至少m次; {m, n}:出現m到n次

貪婪模式

正則中,表示次數的量詞預設是貪婪的,會盡可能匹配最大長度,比如a*會從第一個匹配到a的時候向後匹配儘可能多的a,直到沒有a為止。

非貪婪模式

在量詞後面加?就能變成非貪婪模式,非貪婪即找出長度最小且滿足條件的

貪婪& 非貪婪特點

貪婪模式和非貪婪模式,都需要發生回溯才能完成相應的功能

獨佔模式

獨佔模式和貪婪模式很像,獨佔模式會盡可能多地去匹配,如果匹配失敗就結束,不會進行回溯,這樣的話就比較節省時間。

寫法:量詞後面使用+

優缺點:獨佔模式效能好,可以減少匹配的時間和 cpu 資源;但是某些情況下匹配不到,比如:

| | 正則 | 文字 | 結果 | | ----- | --------- | ---- | --- | | 貪婪模式 | a{1,3}ab | aaab | 匹配 | | 非貪婪模式 | a{1,3}?ab | aaab | 匹配 | | 獨佔模式 | a{1,3}+ab | aaab | 不匹配 |

a{1,3}+ab 去匹配 aaab 字串,a{1,3}+ 會把前面三個 a 都用掉,並且不會回溯

常見正則匹配

| 操作符 | 說明 | 例項 | | ----- | -------------------- | ------------------------------------ | | . | 表示任何單個字元 | | | [ ] | 字符集,對單個字元給出範圍 | [abc]表示 a、b、c,[a-z]表示 a-z 的單個字元 | | [^ ] | 非字符集,對單個字元給出排除範圍 | [^abc]表示非a或b或c的單個字元 | | _ | 前一個字元零次或無限次擴充套件 | abc_表示 ab、abc、abcc、abccc 等 | | | | 左右表示式的任意一個 | abc|def表示 abc、def | | $ | 匹配字串結尾 | abc$表示 abc 且在一個字串結尾 | | ( ) | 分組標記內部只能使用 | (abc)表示 abc,(abc|def)表示 abc、def | | \D | 非數字 | | | \d | 數字,等價於0-9 | | | \S | 可見字元 | | | \s | 空白字元(空格、換行、製表符等等) | | | \W | 非單詞字元 | | | \w | 單詞字元,等價於[a-z0-9A-Z_] | | | ^ | 匹配字串開頭 | ^abc表示 abc 且在一個字串的開頭 | | {m,n} | 擴充套件前一個字元 m 到 n 次 | ab{1,2}c表示 abc、abbc | | {m} | 擴充套件前一個字元 m 次 | ab{2}c表示 abbc | | {m,} | 匹配前一個字元至少m 次 | | | ? | 前一個字元 0 次或 1 次擴充套件 | abc? 表示 ab、abc |

單元測試

概念:前端自動化測試領域的, 用來驗證獨立的程式碼片段能否正常工作

1、可以直接用 Node 中自帶的 assert 模組做斷言:如果當前程式的某種狀態符合 assert 的期望此程式才能正常執行,否則直接退出應用。

function multiple(a, b) {    let result = 0;    for (let i = 0; i < b; ++i)        result += a;    return result; } ​ const assert = require('assert'); assert.equal(multiple(1, 2), 3));

2、常見單測工具:Jest,使用示例

const sum = require('./sum'); ​ describe('sum function test', () => {  it('sum(1, 2) === 3', () => {    expect(sum(1, 2)).toBe(3); });    // 這裡 test 和 it 沒有明顯區別,it 是指: it should xxx, test 是指 test xxx  test('sum(1, 2) === 3', () => {    expect(sum(1, 2)).toBe(3); }); })

babel原理和用途

babel 用途

  • 轉義 esnext、typescript 到目標環境支援 js (高階語言到到低階語言叫編譯,高階語言到高階語言叫轉譯)
  • 程式碼轉換(taro)
  • 程式碼分析(模組分析、tree-shaking、linter 等)

bebel 如何轉換的?

對原始碼字串 parse 生成 AST,然後對 AST 進行增刪改,然後輸出目的碼字串

轉換過程

  1. parse 階段:首先使用 @babel/parser將原始碼轉換成 AST
  2. transform 階段:接著使用@babel/traverse遍歷 AST,並呼叫 visitor 函式修改 AST,修改過程中通過@babel/types來建立、判斷 AST 節點;使用@babel/template來批量建立 AST
  3. generate 階段:使用@babel/generate將 AST 列印為目的碼字串,期間遇到程式碼錯誤位置時會用到@babel/code-frame

技術面.png

好的,以上就是我對三年前端經驗通過面試題做的一個總結了,祝大家早日找到心儀的工作~

<>''<>