兩萬字三月前端面經(含回答)
theme: smartblue
前言
從三月剛開始就瘋狂的海投簡歷,到最後順利拿下某中廠的日常實習OC,中間還是遇見了很多的坎坷,這裡來按照不同的公司來記錄一下我的前端面試經歷吧
知乎
這是我最後悔的一場面試,不是因為別的,就是因為我沒有準備充分,錯過了我這個很喜歡的公司 由於是第一次面試,所以緊張的不行,不過面試官人真的很好,一直笑眯眯的鼓勵我繼續說下去,作為所有面試的開頭,這場面試讓我對後面的面試更加有信心和好奇了
CSS部分
- 常用的選擇器有哪些?(我是傻逼,太緊張了亂說一通)
常用的選擇器包括標籤選擇器、類選擇器、ID選擇器、後代選擇器、子元素選擇器、相鄰兄弟選擇器、通用選擇器和屬性選擇器等
- 權重
CSS的權重是指在樣式衝突時,某一條樣式被應用的優先順序。權重值的計算方式是根據選擇器的型別和數量來確定的。選擇器的權重值從高到低為:
!important > 行內樣式 > ID選擇器 > 類選擇器、屬性選擇器和偽類選擇器 > 標籤選擇器和偽元素選擇器
。具體來說,ID選擇器的權重值為100,類選擇器、屬性選擇器和偽類選擇器的權重值為10,標籤選擇器和偽元素選擇器的權重值為1。在樣式衝突時,權重值高的樣式會覆蓋權重值低的樣式。
- 比如我想隱藏一個元素應該怎麼辦呢?這裡我回答了visibility和display,還自作聰明的說了z-index,自己給自己挖了坑
- 使用
display
屬性:設定元素的display屬性為none,這樣元素在頁面上不會佔用任何空間,同時也不會對其他元素產生影響。- 使用
visibility
屬性:設定元素的visibility屬性為hidden,這樣元素在頁面上不可見,但仍然佔用空間。- 使用
opacity
屬性:設定元素的opacity屬性為0,這樣元素在頁面上不可見,但仍然佔用空間。- 使用
position
屬性:將元素的position屬性設定為absolute或fixed,然後將元素移動到螢幕外或者使其超出容器的範圍之外,這樣元素在頁面上不可見,但仍然佔用空間。- 使用
z-index
屬性:將元素的z-index屬性設定為負值,這樣元素在頁面上不可見,但仍然佔用空間。- 使用
clip
屬性:將元素的clip屬性設定為一個矩形區域,這樣元素只會顯示矩形區域內的部分,其餘部分會被裁剪掉。
- 接著上面的繼續問,比如我想要一個元素,既不會被移除,但又要被隱藏(這裡我回答的是visibility,但面試官不滿意,讓我想想其他的方法) 最後說的方法是將其移除視窗外,或者直接縮小到不可見(這個方法確實出乎我的意料)
使用
position
屬性:將元素的position屬性設定為absolute或fixed,然後將元素移動到螢幕外或者使其超出容器的範圍之外,這樣元素在頁面上不可見,但仍然佔用空間。
- css當中如果出現了兩個一樣的類定義,你如何避免衝突(有點太緊張了,就說的是scoped進行樣式隔離,然後又按照自己的記憶隨便回答了個外部引入的方式,結果把面試官弄笑了,唉)
- 優先順序:CSS中每個選擇器都有一個優先順序,可以通過優先順序來確定哪個樣式定義將被應用。如果兩個類定義中的樣式相沖突,可以通過調整優先順序來解決衝突。
- 父元素選擇器:可以使用父元素選擇器來限定樣式的範圍,從而避免衝突。比如,如果兩個類定義中的樣式都應用到某個父元素的不同子元素上,可以使用父元素選擇器來限定樣式的範圍,從而避免衝突。
- 名稱空間:可以使用名稱空間來區分不同模組或元件的樣式定義,從而避免衝突。比如,可以為不同模組或元件的類定義新增不同的名稱空間字首,從而將它們區分開來。
- scoped樣式:可以使用scoped樣式來將樣式限定在特定的元件或模組中,從而避免與其他元件或模組的樣式衝突。scoped樣式是一種Vue框架提供的特殊樣式,可以通過在style標籤中新增scoped屬性來實現。
後面的有點記不清楚了
JavaScript部分
- 問我基本資料型別和引用資料型別(這裡大意了,說的很快,可能沒說全面。。。)
- 基本資料型別:Undefined null Boolean number string
- 引用資料型別:object array function
- 問我es6裡的新引入的東西,這裡面試官想讓我說map forEach,我說了箭頭函式那些,他就鼓勵我繼續說下去,結果大腦當機了沒想到這些
- 問我map和forEach的區別,我又亂回答了一通,js基礎真的太差了我,唉
map()
和forEach()
都是JavaScript陣列物件的方法,用於遍歷陣列。它們的區別在於返回值和使用方式。
forEach()
方法會對陣列的每個元素執行一次回撥函式,沒有返回值,僅僅是遍歷陣列。
arr = [1, 2, 3, 4]; arr.forEach((num) => { console.log(num * 2); }); // 輸出2 4 6 8
map()
方法會對陣列的每個元素執行一次回撥函式,並將回撥函式的返回值組成一個新的陣列返回,不會修改原陣列。
arr = [1, 2, 3, 4]; const newArr = arr.map((num) => { return num * 2; }); console.log(newArr); // 輸出[2, 4, 6, 8]
因此,如果我們想對陣列進行遍歷並執行一些操作,可以使用
forEach()
方法;如果我們需要在遍歷陣列的同時生成一個新的陣列,可以使用map()
方法。
記不清楚了後面,反正回答的有點差
Vue部分
- 面試官本身是react的,不是很清楚vue,就讓我介紹vue,我就說了Vue的一些特點和react的區別之類的
- 然後問我Vue雙向繫結之類的,我就又講了一些底層的東西
- 問我接觸過react沒,我回答沒😢
專案部分
我這次能拿到這個面試完全看的是元件庫的專案,面試官似乎對我元件庫的專案很感興趣
- 就說為什麼我的button按鈕那裡不進行一個直接的對映,而是還需要進行呼叫顏色,我說這部分實現遇見了bug
- 專案的元件還是太少了
- 用過git嗎?給我介紹一下,我就開始介紹了,又給自己挖了坑,提到了git merge,然後問我如何切換分支,我說不會,只能硬著頭皮說自己專案沒分支,面試官看上去很驚訝😢
手撕js部分
沒有我背的八股js,直接哭死
實現了將一個由鍵值對物件組成的陣列轉換成一個鍵為物件中key屬性值、值為value屬性值的物件
function change(arr) {
return arr.reduce((pre, { key, value }) => {
pre[key] = value;
return pre
}, {})
}
let a = change([{ key: 'a', value: '1' }, { key: 'b', value: '2' }])
console.log(a)
我直接不會,然後面試官教我怎麼實現
反問
我第一次面試,所以我感覺很爛,您覺得怎麼樣呢,還有後續嗎?
- 我感覺還行,但你的js部分可能還不是很好🥹😭感覺很陽光開朗
知乎的技術棧是什麼呢?
- 大部分是react,有一些是Vue
請問你們的元件是直接用現成的還是自己弄呢?
- b端的話會自己弄,c端需要很多自己定義樣式的地方,大都手搓
好的,沒有了,謝謝您
- 好的
總結
自己太緊張了,很多地方發揮的很失敗,面試沒準備好,在面試的時候我才在搭建環境,自己的js基礎太差了,git命令也很差,需要惡補一下,知乎的寄了,接下來加油吧
扁鵲健康
第二場面試,八股文基本上全答出來了,結果反手刪我微信給我掛了😓
第二次面試很明顯比第一次好多了,沒有那麼緊張,情緒也沒有失控,可能是沒開視訊的原因?感覺面試的時候還是得儘量嚴肅一點吧,控制好自己的情緒和麵部表情才能夠更好的回答問題,在面試前讓自己冷靜下來,像對待考試一樣去對待每一場面試
面試官遲到了6分鐘的樣子 電話面試 本以為只會面試10分鐘,結果面試了半小時
之前看牛客上的面經,以為是很簡單的那種,結果大意了,把我簡歷扒拉乾淨了的感覺
一上來還是自我介紹
資料結構
一上來就進行資料結構的拷打嗚嗚,我資料結構很爛誒
- 有哪些排序的方法?
- Array.prototype.sort():該方法可以對陣列進行原地排序,即直接修改原陣列,不會返回新的陣列。預設情況下,它會將陣列元素轉換為字串,然後按照Unicode碼點排序。如果需要按照其他方式排序,可以傳入一個比較函式作為引數。
- Array.prototype.reverse():該方法可以將陣列中的元素按照相反的順序重新排列,並返回新的陣列。
- 氣泡排序(Bubble Sort):這是一種簡單的排序演算法,它重複地遍歷要排序的陣列,比較相鄰的元素並交換位置,直到整個陣列都已經排序。
- 快速排序(Quick Sort):這是一種快速的排序演算法,它的基本思想是選擇一個基準元素,然後將陣列中的元素分為小於基準元素和大於基準元素的兩部分,再對這兩部分分別進行排序。
- 插入排序(Insertion Sort):這是一種簡單的排序演算法,它將陣列分為已排序和未排序兩部分,然後將未排序部分的第一個元素插入到已排序部分的正確位置上。
- 選擇排序(Selection Sort):這是一種簡單的排序演算法,它將陣列分為已排序和未排序兩部分,然後從未排序部分選擇最小的元素並放到已排序部分的末尾。
- 歸併排序(Merge Sort):這是一種分治的排序演算法,它將陣列分成兩個子陣列,分別對這兩個子陣列進行排序,然後將排序後的子數組合併成一個有序的陣列
- 我說了快排和氣泡排序後問我快排的時間複雜度
快速排序的平均時間複雜度為 O(nlogn)。具體來說,當待排序陣列的劃分比較平均時,快速排序的時間複雜度是最優的。而當待排序陣列已經有序或接近有序時,快速排序的時間複雜度會退化為 O(n^2)。
快速排序的時間複雜度分析如下:
- 每次劃分操作需要對整個陣列進行一次遍歷,時間複雜度為 O(n);
- 快速排序的遞迴樹的深度為 logn,因為每次劃分都會將陣列一分為二,所以深度為 logn;
- 每次劃分的時間複雜度為 O(n),因此快速排序的總時間複雜度為 O(nlogn)。
需要注意的是,快速排序的最壞時間複雜度為 O(n^2),但這種情況很少出現,通常情況下快速排序的時間複雜度為 O(nlogn),是一種高效的排序演算法。
- 我說了有兩種情況,然後追問我如何對快排進行優化(這裡沒回答好)
JavaScript 中,可以使用以下技巧來優化快速排序演算法:
- 三數取中:在選擇基準元素時,使用陣列中間、頭部和尾部的三個元素的中位數作為基準元素。這可以降低最壞情況的出現概率。
- 插入排序:在陣列的長度小於某個值(如10)時,使用插入排序演算法而不是快速排序。插入排序在處理小陣列時比快速排序更快。
- 隨機化陣列:在每次執行快速排序時,隨機打亂陣列,以增加演算法的隨機性。
- 尾遞迴優化:使用尾遞迴優化快速排序的實現,避免棧溢位。
- 問我快排最差的情況是什麼
最差情況是每次選取的基準元素都是當前子陣列中最大或最小的元素。在這種情況下,每次劃分都只能排除一個元素,因此需要進行 n 次劃分才能完成排序,時間複雜度為 O(n^2)。這種情況發生的概率非常低,但是如果資料本身就是有序的,或者是基本有序的,快排容易陷入最差情況。
後面突然問我計算機組成原理了解嗎?把我嚇到了,連忙說之前學過,但沒怎麼了解
CSS
CSS問題我記得不是很清楚了下次面試一定要錄音!
- CSS盒模型
- 出了個場景題,說content大小為100px,border為100px,問此時怪異盒模型的寬高(這裡我電話沒聽太清楚,好像是說我出了問題了,但我下來一看感覺是面試官說錯了(・∀・(・∀・(・∀・*))
js
- es6新特性
- 箭頭函式和普通函式區別
- 語法不同:箭頭函式使用箭頭符號(
=>
)來定義函式,而普通函式使用關鍵字function
來定義。this
的指向不同:箭頭函式沒有自己的this
,它會繼承父級作用域中的this
值。而普通函式中的this
則是在函式被呼叫時動態確定的,它的值取決於呼叫函式的方式。- 無法使用
arguments
物件:箭頭函式沒有自己的arguments
物件,因此在箭頭函式中使用arguments
會引用外部作用域的arguments
。- 不能用作建構函式:箭頭函式不能使用
new
關鍵字來建立例項,因為它們沒有自己的this
,也沒有原型物件。- 沒有原型:箭頭函式沒有
prototype
屬性,因此不能通過它來定義方法。- 沒有自己的
arguments
,super
,new.target
物件:箭頭函式沒有自己的arguments
,super
,new.target
物件,它們都是從外部繼承的。
- Promise
- Promise的引數有哪些
Promise 建構函式的引數為一個函式,這個函式接收兩個引數:resolve 和 reject,它們分別表示 Promise 物件的兩種狀態:已解決(fulfilled)和已拒絕(rejected)
- Promise.all瞭解嗎
- Promise.all的使用場景
- 多個非同步操作並行執行,且需要等待所有操作完成後進行下一步處理,比如從多個 API 介面獲取資料,然後將所有資料合併到一起再進行渲染
- 多個非同步操作中有一個操作失敗就立即停止所有操作,並執行錯誤處理邏輯
- 資料型別,基本資料型別和引用資料型別
- symbol瞭解嗎 說說他的使用場景
定義物件的私有屬性:Symbol 值作為屬性名是唯一的,可以防止屬性名的衝突,因此可以用來定義物件的私有屬性
防止物件屬性被意外修改:由於 Symbol 值是唯一的,因此可以用來定義物件屬性,防止屬性被意外修改
定義常量:由於 Symbol 值是唯一的,因此可以用來定義常量
定義列舉:由於 Symbol 值是唯一的,因此可以用來定義列舉
計算機網路
- 說說http和https的區別
- 安全性:HTTP 傳輸的資料是明文的,容易被竊取和篡改,而 HTTPS 使用 SSL/TLS 加密傳輸資料,可以保證資料的機密性和完整性,防止資料被竊取和篡改
- 埠號:HTTP 預設使用埠號 80,而 HTTPS 預設使用埠號 443
- 證書:HTTPS 使用 SSL/TLS 協議對傳輸資料進行加密,需要使用證書對網站進行身份驗證,防止中間人攻擊。HTTP 不需要證書進行身份驗證
- 速度:由於 HTTPS 使用 SSL/TLS 加密傳輸資料,會增加傳輸資料的時間和頻寬消耗,因此速度比 HTTP 慢一些
- 快取:HTTP 可以使用瀏覽器快取來提高訪問速度,而 HTTPS 在加密傳輸資料時會禁止瀏覽器快取,以保證資料的安全性
- 說說http狀態碼
- 瞭解https資料傳輸的過程嗎?(沒太回答上來)
- 客戶端向伺服器發起 HTTPS 請求,請求中包含了 SSL/TLS 支援的資訊,比如支援的 SSL/TLS 版本號、加密演算法等。
- 伺服器返回證書給客戶端,證書中包含了伺服器的公鑰、伺服器的身份資訊和證書的有效期等。
- 客戶端驗證伺服器的身份,包括驗證證書的有效性、證書是否過期、證書中的域名與伺服器的域名是否一致等。
- 如果證書驗證通過,客戶端生成一個隨機的加密金鑰,並使用伺服器的公鑰進行加密,然後傳送給伺服器。
- 伺服器使用私鑰解密客戶端發來的金鑰,然後生成一個隨機數作為會話金鑰,並將會話金鑰加密後傳送給客戶端。
- 客戶端和伺服器使用會話金鑰進行資料傳輸,客戶端和伺服器之間的所有資料都使用會話金鑰進行加密和解密,保證資料的機密性和完整性
專案
移動端媒體專案:
- 你這個專案為什麼不用vuex而是pinia呢?說說二者的區別吧
- 你這個token持久化是怎麼實現的?(亂編了我就)
- 除了pinia那個方法還有什麼呢?我說了session
- session關掉後就沒有了哦,還有嗎?(答案是localstorage)
元件庫專案:
- 為什麼想到做個元件庫的專案呢?
- 你是如何實現元件庫的封裝呢?(這裡回答defineComputed 然後講我是如何寫的元件就行)
- 說一下常用的git命令吧
- 經典問題之git merge和git rebase的區別
git merge
命令會生成一個新的合併提交,並且會保留原來的分支歷史記錄,合併後的提交包含兩個分支的修改。而git rebase
命令則是將當前分支的修改應用到目標分支上,重新生成一顆新的分支歷史記錄,使得分支歷史記錄更加線性化
- vite和webpack的區別
- 構建方式不同:Vite 利用 ES Modules 的特性進行構建,每個檔案都是一個獨立的模組,開發過程中只需要編譯修改的檔案,不需要每次都編譯整個專案;而 Webpack 採用靜態分析的方式進行構建,需要分析整個專案中的依賴關係,每次修改後需要重新編譯整個專案。
- 開發體驗不同:Vite 支援快速的熱更新和即時預覽,開發者可以在修改程式碼的同時,立即在瀏覽器中檢視到最新效果;而 Webpack 需要重新編譯後才能檢視最新效果。
- 對 Vue 的支援:Vite 是 Vue.js 官方推薦的開發工具,內建了對 Vue 單檔案元件的支援,可以直接在瀏覽器中執行 Vue 元件;而 Webpack 需要通過外掛等方式進行支援。
- 總體來說,Vite 更適合於輕量級的應用,對於 Vue 單檔案元件的支援更加完善,而 Webpack 則更適合於複雜的應用,可以通過外掛等方式進行更加靈活的配置
- vite支援熱過載
熱過載(Hot Reload)是指在應用程式執行時對程式碼進行修改,而無需重新啟動應用程式或重新載入整個頁面,即可使更改的部分立即生效並反映在應用程式中。熱過載可以幫助開發人員更快地除錯和開發應用程式,同時減少開發週期
還有一些我就忘記了哈哈
反問
- 公司技術棧?
react 少部分是vue2
- 多久出結果
一週內吧
感覺還算是很不錯的面試,基本上都回答出來了,不過這個是阿里的外包公司,一半員工都是阿里過去的,看了一下公司規模很小,估計也沒hc讓我進去,就當一次電話面試的體驗了,加油!
即刻app
很熱情的一個面試官!反問環節最有意思的一家,不過難度也算最高的一家
這是我目前面過的公司裡難度感覺最高的一家了,不是常規的那種八股文直接硬背,而是真的結合實際的場景進行出題的,對於一個技術棧的考察的深度很深,是一個很不錯的面試官
首先第一點給我感覺不一樣的就是,沒有自我介紹!上來就直接發了個連結,可能是打算結合實際程式碼來對我進行考察,可惜這裡失敗了(學校的網真的差。。。)
ref和reactive的區別
ref是一個函式,它可以將一個普通資料型別(如數字、字串等)轉換為一個響應式物件,從而讓這個資料在Vue的響應式系統中被追蹤。ref返回一個物件,這個物件有一個.value屬性,用來獲取和設定這個響應式物件的值
import { ref } from 'vue';
const count = ref(0);
console.log(count.value); // 0
count.value = 1;
console.log(count.value); // 1
而reactive則是一個函式,它可以將一個普通的Javascript物件轉換為一個響應式物件。它會遞迴地將這個物件的所有屬性都轉換為響應式物件,從而讓整個物件在Vue的響應式系統中被追蹤。reactive返回一個Proxy物件,用來代理原始物件的訪問和修改
import { reactive } from 'vue';
const state = reactive({
count: 0,
message: 'hello'
});
console.log(state.count); // 0
console.log(state.message); // 'hello'
state.count = 1;
state.message = 'world';
console.log(state.count); // 1
console.log(state.message); // 'world'
因此,ref主要用於建立一個單一的響應式資料,而reactive則適用於建立一個複雜的、包含多個屬性的響應式資料物件
ref可以大量的替換成reactive嗎
不能直接把ref替換成reactive。
ref主要用於將基本資料型別(如字串、數字等)轉換為響應式資料,並提供一個.value屬性用於訪問和修改該資料。而reactive則用於將一個普通的JavaScript物件轉換為響應式物件,並使用Proxy來攔截對該物件的訪問和修改,以實現響應式更新。
因此,如果你需要使用響應式資料來儲存基本資料型別,或者你只需要響應式地跟蹤一個值的變化,那麼ref仍然是更合適的選擇。而如果你需要管理一個物件的多個屬性,並希望這些屬性可以響應式地更新,那麼reactive會更加合適
為什麼vue和react都去選擇自己實現一個路由,是出於什麼目的呢
- 更好地整合到框架中:由於路由是前端應用中必不可少的一部分,因此框架整合路由功能可以提供更好的使用者體驗和開發效率。通過自己實現路由庫,Vue和React可以將路由功能無縫整合到框架中,提供更好的開發體驗和更高的開發效率。
- 更好地控制程式碼和依賴:Vue和React自己實現的路由庫可以更好地控制程式碼和依賴。如果使用第三方路由庫,可能會增加程式碼的複雜性和依賴關係,而自己實現路由庫可以避免這些問題。
- 更好地滿足框架的需求:Vue和React的路由庫可以更好地滿足框架的需求。由於框架本身的特性和設計思想,可能需要特定的路由實現方式來滿足這些需求。通過自己實現路由庫,可以更好地滿足框架的需求,提供更好的開發體驗和更高的效能。
- 更好地控制性能:Vue和React的路由庫可以更好地控制性能。由於路由是前端應用中的關鍵部分,效能往往是一個重要的考慮因素。通過自己實現路由庫,Vue和React可以更好地控制路由的效能,從而提供更好的使用者體驗和更高的效能
總之,Vue和React都實現了自己的路由庫,主要是為了更好地整合到框架中,更好地控制程式碼和依賴,更好地滿足框架的需求,以及更好地控制性能
瀏覽器自己本身就有路由,為什麼不直接用a標籤進行一個跳轉,而是選擇用router來進行一個跳轉呢
瀏覽器本身的路由是基於URL的,即每個頁面都有一個唯一的URL地址。使用瀏覽器本身的路由,需要在URL中手動編寫引數,處理頁面重新整理和前進/後退等操作的邏輯,這樣會使得程式碼複雜性增加,並且不太方便維護。
而使用router庫可以將路由相關的邏輯抽象出來,讓開發者可以更加方便地處理頁面跳轉和傳遞引數等操作。使用router可以實現單頁應用(SPA),使得使用者在應用中的操作更流暢,且無需每次跳轉都重新載入整個頁面。
此外,使用router還可以提供一些額外的功能,如路由守衛、動態路由等,這些功能可以幫助開發者更好地控制路由的行為,提高應用的效能和安全性
雖然瀏覽器本身也有路由,但使用router庫可以提供更好的開發體驗和更豐富的功能,使得應用的開發更加方便、高效和可維護
瀏覽器為什麼支援單頁面路由呢?
參考連結:https://developer.mozilla.org/zh-CN/docs/Web/API/History
瀏覽器支援單頁面路由的一個重要原因是History
API。
在傳統的多頁面應用中,頁面之間的跳轉通過超連結或表單提交等方式實現,每個頁面都有一個唯一的URL地址。而在單頁面應用中,頁面的跳轉是通過JavaScript程式碼控制,使用history API可以更加方便地實現這種頁面切換邏輯。
history API是HTML5規範中新增的一組API,可以讓開發者更加方便地操作瀏覽器的歷史記錄。通過history API,開發者可以在不重新載入整個頁面的情況下,改變瀏覽器的URL地址,新增或修改歷史記錄,以及監聽歷史記錄的變化等操作。
在單頁面應用中,開發者可以使用history API來實現前端路由,即在不重新載入整個頁面的情況下,通過改變URL地址,實現不同頁面之間的切換。這樣可以提高應用程式的效能,並且使得應用程式更具互動性和動態性
當我們在使用history進行導航的時候,我們的頁面真的進行了一個切換嗎?是怎麼做到的呢
當我們使用history進行導航時,實際上並沒有進行頁面的重新整理或重新載入。相反,瀏覽器僅僅是通過修改URL地址和瀏覽器歷史記錄,模擬了一個頁面的切換效果。
具體來說,使用history進行導航時,我們通常會呼叫history.pushState或history.replaceState方法,這些方法可以向瀏覽器歷史記錄中新增或修改一個記錄,並且同時修改當前URL地址。然後,瀏覽器會根據新的URL地址重新渲染頁面,並且在瀏覽器的歷史記錄中新增或修改一個記錄。
當我們使用history進行導航時,雖然頁面並沒有進行重新整理或重新載入,但是瀏覽器會觸發一些相關的事件,如popstate事件,用來處理導航過程中的一些邏輯。開發者可以在這些事件中新增相關的處理邏輯,從而實現前端路由的功能
vue如何監聽路由的變化呢?
在Vue中,可以使用Vue Router提供的導航守衛(Navigation Guards)來監聽路由的變化。
導航守衛是Vue Router提供的一組鉤子函式,可以在路由發生變化時被觸發。通過使用導航守衛,開發者可以實現一些常見的路由控制邏輯,如路由許可權控制、路由攔截、路由跳轉前的確認等等。
Vue Router提供了三種類型的導航守衛:
- 全域性守衛:在整個應用程式中,所有的路由變化都會觸發這些守衛。可以通過Vue Router例項的beforeEach、beforeResolve和afterEach方法來註冊全域性守衛。
- 路由獨享守衛:在某個路由上,只有該路由變化時才會觸發這些守衛。可以在路由配置物件中通過beforeEnter屬性來註冊路由獨享守衛。
- 元件內守衛:在某個路由對應的元件中,可以通過Vue元件生命週期鉤子函式來監聽路由的變化,實現一些元件內部的路由控制邏輯。
以下是一個使用全域性守衛來監聽路由變化的示例:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
})
router.beforeEach((to, from, next) => {
console.log('路由變化:', from.path, ' => ', to.path)
next()
})
在上面的示例中,我們通過Vue Router例項的beforeEach方法註冊了一個全域性守衛,在每次路由變化時都會觸發這個守衛,並且打印出路由變化的資訊。可以根據實際需求,編寫自己的路由守衛邏輯
原生js如何進行監聽路由的變化
在原生 JavaScript 中,可以使用 window.onpopstate
事件來監聽路由的變化。
當用戶在瀏覽器中進行前進或後退操作時,或者通過 JavaScript 呼叫 history.pushState()
或 history.replaceState()
方法時,都會觸發 window.onpopstate
事件。
可以通過如下程式碼來監聽 window.onpopstate
事件:
window.onpopstate = function(event) {
console.log("當前路由:", window.location.pathname);
};
在上面的程式碼中,當用戶在瀏覽器中進行前進或後退操作時,或者通過 JavaScript 呼叫 history.pushState()
或 history.replaceState()
方法時,會觸發 window.onpopstate
事件,並輸出當前路由路徑到控制檯。
需要注意的是,這種方式只能監聽瀏覽器歷史記錄中的路由變化,對於通過 AJAX 或其他方式進行的路由變化是無法監聽的。如果需要監聽所有的路由變化,可以考慮使用一些現有的路由庫,如 React Router、Vue Router 等
沒有hash的路由如何進行監聽
如果你使用的是 HTML5 History API 來管理路由,而不是 hash 路由,那麼可以通過監聽 popstate
事件來實現路由變化的監聽。
HTML5 History API 可以讓我們使用 pushState()
和 replaceState()
方法來操作瀏覽器的歷史記錄,並可以修改當前頁面的 URL,而不會導致頁面的重新整理。
當通過 pushState()
或 replaceState()
方法修改瀏覽器歷史記錄時,會觸發 popstate
事件。我們可以通過監聽這個事件來獲取路由的變化。
例如,我們可以使用如下程式碼來監聽路由的變化:
window.addEventListener('popstate', function(event) {
console.log("當前路由:", window.location.pathname);
});
在上面的程式碼中,當用戶通過瀏覽器前進或後退按鈕,或者通過 pushState()
或 replaceState()
方法修改瀏覽器歷史記錄時,會觸發 popstate
事件,並輸出當前路由路徑到控制檯
onpopstate可以監聽到一個pushstate的事件嗎
這裡回答錯了,我說的是可以進行監聽的,面試官讓我回去再好好看看
onpopstate
事件只能監聽到由瀏覽器觸發的歷史記錄變化,例如點選瀏覽器的前進或後退按鈕,或者呼叫 history.back()
或 history.forward()
方法。
如果你在 JavaScript 中呼叫 history.pushState()
或 history.replaceState()
方法來修改瀏覽器的歷史記錄,那麼不會觸發 onpopstate
事件。但是,呼叫這兩個方法會新增新的歷史記錄,並且可以在歷史記錄中回退和前進,這些歷史記錄變化會觸發 onpopstate
事件。
因此,如果你想要在呼叫 pushState()
或 replaceState()
方法後立即獲取路由變化,可以在呼叫這兩個方法後手動觸發 popstate
事件,例如
history.pushState({}, '', '/new-path');
window.dispatchEvent(new PopStateEvent('popstate'));
在上面的程式碼中,我們先呼叫 pushState()
方法來修改瀏覽器的歷史記錄,並修改當前頁面的 URL 為 /new-path
。然後,手動觸發 popstate
事件,這會立即觸發繫結在 window.onpopstate
上的事件處理函式,並獲取到新的路由資訊
ts泛型的作用,在開發當中最常用在哪裡
TypeScript 中的泛型(generics)是一種用於在編譯時期處理型別的工具。泛型可以讓我們編寫更通用、更可重用的程式碼。
泛型最常用的場景之一是在函式和類中使用。通過使用泛型,我們可以編寫可重用的函式或類,可以支援多種不同型別的引數或屬性。例如,下面是一個使用泛型的函式示例
```
function reverse
let numbers = [1, 2, 3, 4]; let reversedNumbers = reverse(numbers);
let letters = ['a', 'b', 'c']; let reversedLetters = reverse(letters); ```
在上面的程式碼中,我們定義了一個名為 reverse
的函式,它使用了一個型別引數 T
。我們可以將 reverse
函式應用於任何具有 reverse
方法的陣列型別。在呼叫 reverse
函式時,我們將一個型別為 T[]
的陣列作為引數傳遞,並返回一個型別為 T[]
的陣列。
除了函式和類,泛型還可以應用於 TypeScript 中的其它特性,例如介面、類型別名等
在開發中,我們最常使用泛型的場景是編寫通用的資料結構、演算法和函式,例如列表、樹、排序演算法、搜尋演算法等等。泛型可以讓我們編寫更通用、更可重用的程式碼,並且可以提高程式碼的靈活性和可擴充套件性。同時,使用泛型還可以讓我們在編譯時期發現型別錯誤,避免一些潛在的執行時錯誤
axios二次封裝的好處
通過對 axios
進行二次封裝,我們可以實現以下功能:
- 統一處理請求引數和響應資料格式:我們可以對請求引數和響應資料進行預處理或格式化,以便在多個請求中使用相同的格式。
- 統一處理錯誤資訊:我們可以對錯誤資訊進行統一處理或格式化,以便在多個請求中使用相同的錯誤資訊處理邏輯。
- 新增請求頭、設定超時時間等功能:我們可以在二次封裝中新增一些公共的請求頭、超時時間等引數,以便在多個請求中使用相同的引數。
- 支援自定義攔截器:我們可以通過自定義攔截器來對請求或響應進行處理,例如新增 token、在請求頭中新增認證資訊等
如何標識使用者已經登入
在 Web 應用程式中,標識使用者是否已經登入通常使用 Session 或 Token 的方式。
Session 是一種伺服器端的技術,用於跟蹤使用者的狀態。當用戶登入成功後,伺服器會建立一個 Session,併為該使用者分配一個唯一的 Session ID,將該 Session ID 儲存在 Cookie 中或者通過 URL 傳遞給客戶端。在使用者訪問其他頁面時,客戶端會將 Session ID 傳送回伺服器,並使用該 ID 來查詢伺服器端的 Session 資料。如果 Session 資料存在,說明使用者已經登入,否則使用者未登入。使用 Session 的優點是可以在伺服器端儲存敏感的使用者資訊,不會在客戶端暴露。
Token 是一種基於客戶端的技術,通常使用 JSON Web Token (JWT) 或類似的技術。當用戶登入成功後,伺服器會生成一個 Token,並將該 Token 傳送給客戶端。客戶端在後續的請求中攜帶該 Token,伺服器可以通過解析 Token 來驗證使用者的身份。使用 Token 的優點是可以讓客戶端快取使用者的登入狀態,減輕伺服器的負擔,同時可以在不同的服務之間共享使用者的登入狀態。
在實現登入功能時,通常需要將使用者的登入資訊儲存在伺服器端,例如資料庫、快取等等。當用戶登入成功後,伺服器會建立 Session 或生成 Token,並將其返回給客戶端。客戶端可以將 Session ID 儲存在 Cookie 中,或將 Token 儲存在本地儲存或會話儲存中。在後續的請求中,客戶端會將 Session ID 或 Token 傳送回伺服器,伺服器可以根據 Session ID 或解析 Token 來驗證使用者的身份
token已經過期的話,我想要重新整理token如何實現
在實現 Token 重新整理功能時,通常需要注意以下幾個步驟:
- 在伺服器端,需要定義一個用於重新整理 Token 的 API 介面,該介面需要驗證當前 Token 的有效性,並根據需要生成一個新的 Token,並返回給客戶端。
- 在客戶端,當發現當前 Token 已經過期時,需要向伺服器端傳送一個重新整理 Token 的請求,並將當前 Token 和重新整理 Token 的回撥函式傳遞給伺服器端。
- 在伺服器端,當收到客戶端傳送的重新整理 Token 的請求時,需要驗證當前 Token 的有效性。如果當前 Token 有效,生成一個新的 Token,並將其返回給客戶端。如果當前 Token 無效,需要向客戶端返回一個錯誤碼或提示資訊。
- 在客戶端,當收到伺服器端返回的新 Token 時,需要將新 Token 儲存到本地儲存或會話儲存中,並呼叫重新整理 Token 的回撥函式
無感重新整理token
在前後端分離的應用中,為了保證安全性和使用者體驗,通常會使用token來實現使用者身份認證。當token過期時,需要重新獲取新的token,以保持使用者的登入狀態。在這種情況下,無感重新整理token可以提高使用者體驗,使使用者無需手動重新登入即可繼續訪問應用程式。
以下是一種基本的無感重新整理token的實現思路:
- 定義token的過期時間,例如30分鐘。
- 在使用者每次傳送請求時,檢查token是否快要過期,例如在token過期時間前5分鐘進行檢查。
- 如果token即將過期,傳送一個請求給後端,請求新的token。
- 如果後端返回新的token,將新的token儲存在本地,同時更新所有請求中的token值。
- 如果後端返回錯誤或者新的token無效,清除本地token,跳轉到登入頁面。
通過這種方式,即使token過期,使用者也不需要手動重新登入即可繼續使用應用程式,從而提高使用者體驗和應用程式的安全性。當然,具體實現細節可能因具體應用場景而異,需要根據實際情況進行調整。
將上面的操作寫在哪裡呢?
在實現無感重新整理token的過程中,主要涉及到兩個方面的實現:前端和後端。
前端方面,可以在請求攔截器中實現token的檢查和更新。可以通過在請求頭中設定Authorization欄位,將token傳送給後端。當token即將過期時,可以在請求攔截器中傳送一個重新整理token的請求,並將新的token儲存在本地儲存中,同時更新所有請求的Authorization欄位。這樣,即使token過期,使用者也無需手動重新整理token即可繼續使用應用程式。
後端方面,需要實現一個token的重新整理介面,接收舊的token並返回新的token。在處理重新整理請求時,需要對舊的token進行驗證,以確保該請求是合法的。如果驗證通過,則生成新的token並返回給前端,否則返回錯誤資訊。
總之,在實現無感重新整理token的過程中,前端和後端都需要進行一定的實現。前端需要在請求攔截器中進行token的檢查和更新,後端需要實現一個token的重新整理介面,並對舊的token進行驗證。同時,還需要定義token的過期時間,並根據實際情況進行調整
響應攔截器的功能
響應攔截器是前端網路請求中一個非常重要的概念,它的主要功能是在從伺服器接收到響應資料之後,對響應資料進行處理,然後再將其返回到呼叫處。
響應攔截器的主要功能包括以下幾個方面:
- 錯誤處理:響應攔截器可以檢查響應資料中是否存在錯誤資訊,例如請求失敗、許可權不足等。如果存在錯誤資訊,則可以根據實際情況進行處理,例如跳轉到錯誤頁面、顯示錯誤資訊等。
- 資料處理:響應攔截器可以對響應資料進行處理,例如對資料進行格式化、過濾、排序等操作。這樣可以提高應用程式的可讀性和可維護性。
- 統一處理:響應攔截器可以對所有的響應資料進行統一處理,例如新增一些公共的響應頭、對返回的資料進行加密等操作。這樣可以提高應用程式的安全性和可擴充套件性。
- token重新整理:響應攔截器可以在響應資料中檢查token的過期時間,如果即將過期,則可以自動進行token的重新整理,從而實現無感重新整理token的功能。
- 快取處理:響應攔截器可以對響應資料進行快取處理,例如將響應資料儲存在本地儲存中,以提高應用程式的效能和使用者體驗
反問
- 您對我的面試表現和之後學習前端的建議
看我自己的興趣,看我對之後的哪些技術比較感興趣,然後說了好幾分鐘的一些我都沒聽過的前端技術/(ㄒoㄒ)/~~
一下子就感覺自己的前端之路才剛剛開始起步,後面的前端技術好多呀
- 之後還會有二面嗎?
楠哥會給我訊息,最遲是明天
- 公司技術棧和技術氛圍
你知道你投遞是小宇宙吧,我們公司小宇宙全是react,然後很詳細的給我介紹了react裡的很多東西
丁香園
面試體驗最差的一家!面試官一進來就挎著個臉,怨氣十足的樣子,隨便問了一個八股就開始手撕,因為當時我心裡想著另一家公司的二面,所以沒怎麼走心了(互相KPI?)
- 講一下防抖和節流
- 手撕一下
- 做個演算法(內容我忘記了)
反問環節,不好意思😅完全不想反問你任何問題,然後面試官就嘲諷了我一頓就掛電話了😅
合合資訊
面試官是一個聽聲音感覺年紀蠻大的一個人,人很和藹!很愉快的一次面試
上來面試官就自我介紹,然後介紹公司,然後就問我有什麼需要反問的?第一次遇見這種情況哈哈哈
vue響應式原理
http1和http1.1的區別
HTTP(Hypertext Transfer Protocol)是用於在Web上傳輸資料的協議。HTTP / 1.0和HTTP / 1.1是兩個版本的HTTP協議,下面是它們之間的一些區別:
- 持久連線:HTTP / 1.1引入了持久連線,這意味著在單個TCP連線上可以傳送多個請求/響應對,從而減少了每個請求的延遲。HTTP / 1.0在每個請求/響應之後關閉TCP連線。
- 塊傳輸編碼:HTTP / 1.1支援塊傳輸編碼,這意味著可以在接收響應時逐步解壓縮資料,而不必等待整個響應。這對於處理大型響應或流式資料非常有用。
- 身份驗證:HTTP / 1.1提供了更安全的身份驗證方法,例如基於令牌的身份驗證方案,可以替代HTTP / 1.0中的基本身份驗證。
- 快取處理:HTTP / 1.1對快取處理進行了改進,包括新的Cache-Control指令,可以更好地控制快取行為。
- 響應碼:HTTP / 1.1引入了更多的響應碼,例如“100 Continue”,這使得客戶端可以更好地控制它們的請求行為。
- 主機頭欄位:HTTP / 1.1要求在每個請求中都包含主機頭欄位,這使得伺服器可以更好地處理多個虛擬主機。
- 管道化:HTTP / 1.1支援管道化,允許客戶端同時傳送多個請求,從而提高效能。HTTP / 1.0不支援管道化。
除了前端你其他的學的怎麼樣?比如計算機組成原理和網路之類的
其他的我不太行誒(這裡希望是別問太難),結果面試官就笑了,說那咱們就不問了
然後就開始閒聊了😂
這裡面試官建議我好好學學計算機基礎那些,那些相比於前後端的技術,可以讓我在AI的衝擊下走的更遠,心裡也蠻有感觸的,畢竟大家都看見了chatGPT帶來的威力,算是一次很輕鬆愉快的面試吧
同程旅行
專案
看得出來面試官是提前對我的簡歷看了很多的,一上來就先對我的專案進行了分析,然後問我最近的一次實習(就這個最近的一次實習直接把我整不會了,一下就緊張了起來😭)和專案是什麼時候
我最近還沒有實習,零經驗,我直接介紹我自己的專案可以嗎?
這裡感覺自己介紹的專案太拉胯了,沒有很好的體現出自己的專案,接下來應該將自己的專案進行概括才行啊
你為什麼要用jsx進行開發元件庫呢?有什麼好處呢?
JSX 是 React 中一種用於編寫元件的語法,它可以將 HTML 和 JavaScript 結合起來,讓開發者更加方便地編寫動態元件。使用 JSX 進行元件庫開發的好處如下:
- 增加可讀性和可維護性:JSX 讓程式碼看起來更像是 HTML 模板,這使得程式碼更容易閱讀和理解,也更容易進行修改和維護。
- 提高開發效率:使用 JSX 可以減少開發者在編寫元件時需要編寫的模板程式碼,這可以減少程式碼量,提高開發效率。
- 更好的效能:JSX 可以通過使用虛擬 DOM 來優化元件渲染效能。React 在每次元件更新時會生成新的虛擬 DOM 樹,並與舊的虛擬 DOM 樹進行比較,然後只更新需要更新的部分,從而提高渲染效率。
- 易於與 React 整合:React 是一種流行的前端框架,使用 JSX 可以使元件庫更容易與 React 整合,從而提高元件庫的適用性。
綜上所述,使用 JSX 進行元件庫開發可以提高開發效率、可讀性和可維護性,並且可以提高元件渲染效能,從而使元件庫更加適用於 React 等前端框架
pinia在這個專案裡解決了什麼問題
在一個 Vue 3 專案中使用 Pinia 可以解決以下問題:
- 簡化狀態管理:Pinia 提供了一個簡潔的 API,使得我們可以更容易地定義和管理狀態,並在整個應用程式中共享它們。
- 更好的型別支援:Pinia 提供了一個型別安全的 API,可以讓我們更容易地編寫型別安全的程式碼,並減少錯誤。
- 更好的可測試性:Pinia 的狀態管理使得我們可以更容易地對 Vue 3 元件進行單元測試,從而提高程式碼的可測試性。
- 更好的效能:Pinia 的狀態管理實現了基於 Proxy 的響應式系統,從而提高了效能並減少了不必要的重渲染
綜上所述,Pinia 能夠幫助我們更好地管理 Vue 3 應用程式中的狀態,並且提供了更好的型別支援、可測試性和效能,從而使得我們可以更容易地編寫高質量的 Vue 3 應用程式。在這個專案中使用 Pinia,可以提高專案的開發效率和程式碼質量
pinia的store你是如何進行設計的
在設計 Pinia 的 store 時,我們通常需要考慮以下幾個方面:
- 狀態的劃分:我們需要考慮應用程式中需要管理的狀態,並根據不同的功能和需求進行劃分。通常情況下,我們會將狀態劃分為多個 store,每個 store 管理一部分相關的狀態。
- Store 的命名:我們需要為每個 store 提供一個唯一的名稱,以便在整個應用程式中引用它們。
- Store 的定義:我們需要定義每個 store 的結構,包括 store 的狀態、getter、mutation 和 action 等。
- Store 的註冊:我們需要將定義好的 store 註冊到應用程式中,以便在應用程式的其他地方使用。
- Store 的使用:我們需要在元件中使用 store,通過 getter 獲取 store 的狀態,並在需要時通過 mutation 和 action 來修改 store 的狀態。
在實際的應用程式中,我們通常會根據具體的業務需求來設計 store 的結構和劃分。一般來說,我們會將相關的狀態放在一個 store 中,並通過模組化的方式來組織多個 store,從而實現更好的可維護性和可擴充套件性。
在使用 Pinia 的過程中,可以參考官方文件提供的示例和最佳實踐,以便更好地設計和管理 store,從而提高應用程式的效能和可維護性
你的store有進行模組的拆分嗎?還是說放在一起
在實際應用程式中,將 store 模組化並組織成多個檔案通常是比較好的實踐,這有助於提高應用程式的可維護性和可擴充套件性。
通常情況下,我們會將 store 模組化為多個檔案,每個檔案對應一個 store 模組,然後通過 Vuex 的模組化功能來組織它們。每個 store 模組負責管理自己的狀態、getter、mutation 和 action,並且可以通過模組間的呼叫來實現跨 store 的狀態共享。
模組化的方式可以使得 store 更加易於維護和擴充套件,因為每個模組只負責自己的一部分功能,而不會將所有的狀態都放在同一個 store 中,從而導致程式碼的臃腫和難以維護。
在使用 Pinia 時,同樣可以將 store 模組化為多個檔案,並使用 Pinia 提供的模組化功能來組織它們。每個 store 模組也負責管理自己的狀態、getter、mutation 和 action,並且可以通過其他 store 模組的呼叫來實現跨 store 的狀態共享。
綜上所述,將 store 模組化並組織成多個檔案是一個比較好的實踐,可以提高應用程式的可維護性和可擴充套件性
你做的元件庫當中有遇見什麼困難嗎?可以舉例說明
html5
h5當中新增了哪些
以下是 HTML5 中一些新增的功能和特性:
- 新的語義化標籤:HTML5 新增了一些語義化標籤,如
<header>
、<footer>
、<nav>
、<article>
、<section>
、<aside>
等,可以更好地描述頁面的結構和內容,有助於提高頁面的可讀性和可訪問性。 - 多媒體支援:HTML5 提供了更好的多媒體支援,包括
<audio>
和<video>
標籤,可以直接在網頁中嵌入音訊和視訊。 - 本地儲存:HTML5 提供了本地儲存功能,包括 localStorage 和 sessionStorage,可以在客戶端瀏覽器中儲存資料,從而提高應用程式的效能和使用者體驗。
- Web Workers:HTML5 中新增了 Web Workers,可以在後臺執行緒中執行 JavaScript 程式碼,從而提高應用程式的效能和響應速度。
- Canvas:HTML5 中新增了
<canvas>
標籤,可以在網頁中繪製各種圖形和動畫,有助於實現更加複雜的互動效果。 - 地理位置 API:HTML5 提供了地理位置 API,可以獲取使用者的地理位置資訊,有助於實現基於地理位置的應用。
- Web Socket:HTML5 中新增了 Web Socket,可以實現雙向通訊,從而實現更加實時和高效的應用程式。
- Web Storage:HTML5 中新增了 Web Storage,可以在客戶端瀏覽器中儲存資料,從而提高應用程式的效能和使用者體驗。
綜上所述,HTML5 提供了許多新的功能和特性,可以幫助開發人員更加方便地實現一些複雜的應用場景,提高應用程式的效能和使用者體驗
html的行內元素和塊級元素的區別,都有哪些
HTML 元素可以分為兩類:行內元素和塊級元素。它們的主要區別在於:
- 顯示方式:塊級元素在頁面上以塊的形式展現,它會佔據一整行的空間,可以設定寬度、高度、內邊距和外邊距等屬性。而行內元素則不會獨佔一行,它們在一行內按照從左到右的順序排列,並且不能設定寬度、高度和內邊距等屬性。
- 內容模型:塊級元素通常用於包含其他塊級元素或行內元素,可以包含任何其他元素。而行內元素通常用於包含文字或其他行內元素,不能包含塊級元素。
- 預設樣式:塊級元素通常具有明顯的外觀特徵,例如:段落
<p>
元素會在前後新增空白,標題<h1>
~<h6>
元素會加粗並換行等等。而行內元素通常沒有這些明顯的外觀特徵,例如:超連結<a>
元素只是有下劃線,並且字型顏色有所變化等等。
以下是一些常見的 HTML 塊級元素和行內元素:
塊級元素:
<div>
<p>
<h1>
~<h6>
<ul>
、<ol>
、<li>
<table>
<form>
<hr>
<header>
、<footer>
、<nav>
、<section>
等 HTML5 新增的語義化標籤
行內元素:
<a>
<span>
<strong>
、<em>
<img>
<input>
<label>
<br>
<button>
<select>
<textarea>
img說行內還是塊呢?span說行內還是塊
元素預設是行內元素,但可以通過設定 display 屬性為 block 或 inline-block 等值來改變其顯示方式。
<span>
元素是一個純粹的行內元素,它不能包含塊級元素,但可以包含其他行內元素。
css
盒子模型
盒子模型是指 HTML 元素在渲染時呈現為一個矩形盒子的模型。這個矩形盒子包含了元素的內容、內邊距(padding)、邊框(border)和外邊距(margin)等部分。
具體來說,盒子模型包含以下幾個部分:
- 內容區域(content area):元素內部的實際內容,包括文字、影象、巢狀的元素等。
- 內邊距(padding):內容區域和邊框之間的空白區域,可以通過 CSS 屬性
padding
或padding-*
來設定。 - 邊框(border):包圍內容和內邊距的線條,可以通過 CSS 屬性
border
或border-*
來設定。 - 外邊距(margin):邊框和周圍元素之間的空白區域,可以通過 CSS 屬性
margin
或margin-*
來設定。
盒子模型在 Web 頁面佈局中扮演著重要的角色,可以用來控制元素的尺寸、位置、邊距和內邊距等方面的表現。預設情況下,元素的尺寸是指內容區域的大小,但是通過設定 box-sizing
屬性可以改變元素尺寸的計算方式,使其包括內邊距和邊框
will-change
CSS 屬性 will-change
為 web 開發者提供了一種告知瀏覽器該元素會有哪些變化的方法,這樣瀏覽器可以在元素屬性真正發生變化之前提前做好對應的優化準備工作。這種優化可以將一部分複雜的計算工作提前準備好,使頁面的反應更為快速靈敏
連結:https://developer.mozilla.org/zh-CN/docs/Web/CSS/will-change
垂直居中佈局
translate是基於自身還是父元素
translate()
是一種 CSS transform
函式,用於在元素上進行平移變換。它是相對於元素自身的位置進行平移,而不是相對於父元素或其他元素。
具體來說,translate()
函式接受兩個引數:translate(x, y)
,其中 x
和 y
分別代表在水平和垂直方向上的平移距離。如果只給出一個引數,則預設為水平方向上的平移。
translate()
函式的平移距離是相對於元素自身的初始位置進行計算的,而不受到父元素的影響。因此,如果我們想要實現相對於父元素進行平移,可以考慮使用 position
屬性和 top
、bottom
、left
、right
屬性來控制元素的位置
移動端相容問題你是如何解決的
移動端相容問題是前端開發中非常重要的一環,以下是我通常採用的一些解決方法:
- 使用移動端適配方案:移動端裝置的螢幕尺寸和畫素密度差異較大,因此需要使用適配方案來保證頁面在不同螢幕上顯示效果一致。常見的適配方案有:rem、vw/vh、flexible.js 等。
- 使用 CSS3 和 ES6 功能時需要進行字首處理:移動端的瀏覽器相容性不如 PC 端,因此在使用 CSS3 和 ES6 功能時需要進行字首處理,例如
-webkit-
、-moz-
、-ms-
、-o-
等。 - 避免使用過多的圖片和動畫效果:移動裝置的網路環境和硬體效能相對較弱,因此需要儘量減少頁面中的圖片數量和動畫效果,以提高頁面的載入速度和流暢性。
- 使用移動端專用的 UI 元件和互動方式:移動裝置的操作方式和 PC 端有較大差異,因此需要使用移動端專用的 UI 元件和互動方式,例如滑動、輕掃、長按等。
- 針對不同裝置的瀏覽器進行測試:移動裝置的瀏覽器種類繁多,不同瀏覽器在相容性上也有所不同,因此需要在開發完成後對不同裝置的瀏覽器進行測試,以確保頁面在各種瀏覽器上的相容性。
除此之外,還可以使用一些工具來幫助解決移動端相容問題,例如 Autoprefixer 可以自動新增 CSS3 字首,FastClick 可以解決移動端點選事件的延遲等
css的相對單位有哪些
在 CSS 中,相對單位有以下幾種:
em
:相對於父元素的字型大小。例如,如果父元素的字型大小為 16px,子元素的font-size
設為 1.5em,則子元素的字型大小為 24px。rem
:相對於根元素的字型大小。例如,如果根元素的字型大小為 16px,元素的font-size
設為 1.5rem,則元素的字型大小為 24px。與em
不同的是,rem
取決於根元素的字型大小,而不是父元素的字型大小。vw
和vh
:相對於視口寬度和高度的百分比。例如,如果視口寬度為 1000px,元素的width
設為 50vw,則元素的寬度為 500px。vmin
和vmax
:相對於視口寬度和高度中較小或較大的那個值的百分比。例如,如果視口寬度為 1000px,視口高度為 800px,元素的width
設為 50vmin,則元素的寬度為 400px(因為視口寬度為較大的值,所以按照視口寬度計算)。%
:相對於父元素的寬度或高度的百分比。例如,如果父元素的寬度為 1000px,元素的width
設為 50%,則元素的寬度為 500px。
相對單位與絕對單位(如畫素、英寸等)相比,具有更好的響應式特性,可以根據不同的螢幕尺寸和裝置型別自適應地調整大小,因此在響應式設計中得到廣泛應用
計算機網路
輸入URL
渲染程序
js
普通資料型別儲存在哪裡?堆還是棧
在 JavaScript 中,普通資料型別的值通常儲存在棧記憶體中。棧是一種後進先出的資料結構,可以高效地管理函式呼叫和區域性變數。
當我們宣告一個變數並賦值時,JavaScript 引擎會為該變數分配一段棧記憶體,並將變數的值儲存在其中。當該變數不再使用時,這段棧記憶體也會被釋放,變成可用的空間。
常見的普通資料型別包括數字、字串、布林值、null 和 undefined 等。這些型別的值都比較簡單,不需要過多的記憶體空間來儲存,因此通常儲存在棧記憶體中。
與普通資料型別不同,引用資料型別(如物件、陣列、函式等)的值儲存在堆記憶體中。堆記憶體是一種動態分配的記憶體空間,可以儲存複雜的資料結構和物件。
當我們宣告一個引用型別的變數時,JavaScript 引擎會為該變數分配一段棧記憶體,並將其指向堆記憶體中的實際值。因為引用型別的值通常比較複雜,包含大量的屬性和方法,因此需要較大的記憶體空間來儲存
深拷貝和淺拷貝的區別。 讓你實現一個深拷貝的思路
深拷貝和淺拷貝都是對於複雜資料型別進行復制的操作,區別在於複製的方式不同。
淺拷貝是指建立一個新物件,這個新物件有著原始物件屬性值的一份精確拷貝,如果屬性是基本型別,拷貝的就是基本型別的值,如果屬性是引用型別,拷貝的就是記憶體地址,所以如果其中一個物件改變了這個地址,就會影響到另一個物件。
深拷貝是指建立一個新物件,這個新物件的值和原始物件的值完全沒有關聯,即便原始物件中有引用型別的屬性,新物件也會開闢新的記憶體地址,完全拷貝一份新的物件,修改一個物件不會影響到另一個物件。
一個實現深拷貝的思路是:
-
首先判斷需要拷貝的物件是否是引用型別,如果是基本型別則直接返回該值。
-
如果是引用型別,則建立一個新的空物件或陣列(取決於原始物件的型別)。
-
遍歷原始物件的所有屬性或元素,將它們的值遞迴地拷貝到新物件中,這個遞迴過程需要注意以下幾點:
- 如果屬性或元素的值是基本型別,則直接複製該值;
- 如果屬性或元素的值是引用型別,則遞迴呼叫深拷貝函式,並將結果賦值給新物件的相應屬性或元素。
-
返回新物件或陣列
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
newObj[key] = deepCopy(obj[key]);
}
return newObj;
}
除了這個方法你還有其他的思路嗎?json如果來做深拷貝存在哪些問題
除了遞迴拷貝之外,還有其他實現深拷貝的思路:
- 使用Object.assign()方法實現淺拷貝,然後對於每個屬性值是引用型別的屬性,再遞迴呼叫深拷貝函式。
- 使用ES6的展開運算子(…)實現淺拷貝,然後對於每個屬性值是引用型別的屬性,再遞迴呼叫深拷貝函式。
- 使用第三方庫,如Lodash的_.cloneDeep()方法,該方法能夠遞迴地深拷貝一個物件。
使用JSON.stringify()和JSON.parse()方法進行深拷貝是一種常見的錯誤做法。雖然這種方法能夠將一個物件序列化為JSON字串,再將JSON字串解析為一個新物件,但是存在以下幾個問題:
- 該方法只能序列化物件中的可列舉屬性,不能序列化物件的原型鏈和方法。
- 如果物件中有迴圈引用(即一個物件引用了自身),則該方法會丟擲錯誤。
- 該方法不能序列化RegExp、Date、Map、Set等特殊型別的物件,會將其序列化為字串或空物件。
因此,使用JSON.stringify()和JSON.parse()方法進行深拷貝並不可靠,建議使用其他方法實現深拷貝
箭頭函式的作用,箭頭函式和普通函式的區別
箭頭函式是ES6中新增的一種函式定義方式,主要有以下幾個作用:
- 簡化函式定義:箭頭函式可以使用更短的語法定義函式,省略了function關鍵字和return語句。
- 更簡潔的this指向:箭頭函式沒有自己的this,它的this指向最近的一層非箭頭函式作用域的this,可以避免this指向混亂的問題。
- 更簡潔的程式碼結構:箭頭函式通常可以使程式碼更加簡潔易懂,特別是當需要傳遞迴調函式或者進行函數語言程式設計時,箭頭函式可以使程式碼更加簡潔易讀。
與普通函式相比,箭頭函式的主要區別在於this的指向和函式定義的語法結構:
- this指向:箭頭函式的this指向在定義時就已經確定了,指向最近的一層非箭頭函式作用域的this。而普通函式的this指向在執行時才能確定,可能會受到呼叫方式、繫結方式等多種因素的影響。
- 語法結構:箭頭函式省略了function關鍵字和return語句,更加簡潔明瞭。同時,箭頭函式的引數只有一個時可以省略括號,而普通函式的引數需要用括號括起來。
需要注意的是,由於箭頭函式的this指向與普通函式不同,因此在某些場景下可能會出現錯誤的結果。此外,箭頭函式也不能作為建構函式使用,因為它沒有自己的this。因此,需要根據實際情況選擇合適的函式定義方式
箭頭函式的this指向哪裡?它的this可以被改變嗎
箭頭函式的this指向在函式定義時就已經確定了,它的this指向的是定義時所在的作用域中的this,而不是在呼叫時所在的作用域。
具體來說,箭頭函式的this指向最近的一層非箭頭函式作用域的this。如果箭頭函式本身沒有定義作用域,則指向全域性物件。這與普通函式不同,普通函式的this指向在呼叫時才能確定,可能會受到呼叫方式、繫結方式等多種因素的影響。
由於箭頭函式的this指向在定義時就已經確定,因此它的this不能被改變。即使使用apply、call等方法來改變this指向,也無法改變箭頭函式的this指向。
需要注意的是,箭頭函式的this指向是靜態的,不能動態改變,因此在某些場景下可能會出現錯誤的結果。在這種情況下,可以使用普通函式來替代箭頭函式,或者使用bind方法來繫結this指向
typeof檢測null
使用typeof檢測null的結果是”object
“。
這是因為在JavaScript中,null被認為是一個空物件引用。雖然它不是物件,但typeof檢測null的結果是”object”,這是一個歷史遺留問題。在ES6中,通過Symbol.hasInstance方法可以正確地檢測null,但它並不常用。如果需要判斷一個值是否為null,可以直接使用嚴格等於(===)運算子,因為null只等於它本身,不等於任何其他值
瞭解微前端嗎?微前端目前業內的解決方案 阿里的乾坤瞭解嗎
微前端是一種將前端應用程式拆分為更小、更容易管理的部分的架構風格,每個部分可以獨立地開發、測試、部署和擴充套件。它的主要目的是解決單體應用程式的複雜性和可維護性問題,以及不同應用程式之間的耦合問題。
在業內,目前有許多微前端的解決方案,包括Single-SPA、qiankun、Mosaic、Piral、Luigi等等。這些解決方案都有各自的優缺點和適用場景,可以根據實際需求進行選擇。
阿里的qiankun是一種在React、Vue、Angular等前端框架上實現微前端的解決方案。它使用了主應用和子應用的概念,主應用負責整體框架的搭建和管理子應用,子應用則可以使用不同的前端框架進行開發。qiankun提供了統一的路由、狀態管理、樣式隔離等功能,可以有效地實現微前端架構
微前端的好處
- 技術棧無關性:不同的團隊可以使用不同的技術棧來開發不同的微前端應用,而這些應用可以無縫地整合到一個統一的應用中,不會出現技術棧不一致的問題。
- 模組化開發:每個微前端應用都是獨立開發的,可以根據需求進行拆分成多個小模組,每個模組可以獨立開發、測試、部署和升級,從而提高了開發效率和程式碼質量。
- 獨立部署:每個微前端應用都是獨立部署的,可以快速部署新的功能和修復bug,而不需要整個應用重新部署,從而提高了部署效率和靈活性。
- 高可維護性:由於每個微前端應用都是獨立開發、測試、部署和升級的,因此可以更容易地維護和更新每個應用,從而提高了整個應用的可維護性。
- 更好的擴充套件性:微前端應用可以在需要時獨立開發和擴充套件,可以更好地滿足業務需求,同時也可以更容易地擴充套件到新的平臺和裝置
陣列如何進行扁平化的處理?給你幾個多維陣列,將其平展開來
陣列扁平化可以將多維陣列轉化為一維陣列,常用的方法有遞迴方法和非遞迴方法。以下是一些實現方法:
遞迴方法:
``` flatten(arr) { var result = []; arr.forEach(function(item) { if (Array.isArray(item)) { result = result.concat(flatten(item)); } else { result.push(item); } }); return result; }
// 示例 var arr1 = [1, 2, [3, 4], 5, [6, 7, [8, 9]]]; console.log(flatten(arr1)); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ```
非遞迴方法:
``` flatten(arr) { var result = []; var stack = [...arr]; while (stack.length) { var item = stack.pop(); if (Array.isArray(item)) { stack.push(...item); } else { result.unshift(item); } } return result; }
// 示例 var arr2 = [1, 2, [3, 4], 5, [6, 7, [8, 9]]]; console.log(flatten(arr2)); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ```
ES6方法:
``` flatten(arr) { return arr.flat(Infinity); }
// 示例 var arr3 = [1, 2, [3, 4], 5, [6, 7, [8, 9]]]; console.log(flatten(arr3)); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ```
需要注意的是,如果陣列元素中包含了物件、函式等複雜型別,則需要根據具體情況進行處理
flat參考連結:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
場景題
實現一個功能,我在頁面A點選了一個按鈕,進入頁面B,這個時候我在頁面B進行了一個操作,這時候如何讓A進行一個重新整理,也就是如何實現兩個程序的通訊
實現兩個程序的通訊可以通過以下幾種方式:
- 使用瀏覽器的
localStorage
或者sessionStorage
來儲存需要傳遞的資料,然後在另一個頁面中讀取儲存的資料並進行處理。需要注意的是,儲存的資料型別必須是字串,因此需要使用JSON.stringify
和JSON.parse
進行資料的轉換。
示例程式碼:
在頁面A中:
localStorage.setItem('data', JSON.stringify({ name: 'John', age: 25 }));
// 跳轉到頁面B
window.location.href = 'pageB.html';
在頁面B中:
// 從localStorage中讀取資料
const data = JSON.parse(localStorage.getItem('data'));
// 處理資料
console.log(data.name, data.age); // John 25
// 刪除localStorage中的資料
localStorage.removeItem('data');
// 觸發頁面A的重新整理
window.location.reload();
- 使用瀏覽器的
window.postMessage
方法進行跨視窗通訊。該方法可以在不同視窗之間傳遞資料,並且不同視窗可以處於不同的域名和協議下。
示例程式碼:
在頁面A中:
``` // 在A頁面中註冊message事件的監聽器 window.addEventListener('message', (event) => { if (event.origin !== 'http://localhost:3000') { // 如果不是指定的來源,不予處理 return; } // 處理接收到的資料 console.log(event.data); // 觸發頁面A的重新整理 window.location.reload(); });
// 在A頁面中向B頁面傳送資料 window.open('pageB.html'); ```
在頁面B中:
// 在B頁面中向A頁面傳送資料
window.opener.postMessage({ name: 'John', age: 25 }, 'http://localhost:3000');
需要注意的是,該方法存在跨站點指令碼攻擊(XSS)的風險,因此需要在處理訊息時進行資料的合法性檢驗,確保訊息的來源和內容都是可信的。
- 使用第三方的庫來實現程序間通訊,例如 SignalR、Socket.IO 等。這些庫提供了更高階的通訊功能,並且支援實時通訊、廣播訊息等特性,但也需要相應的伺服器支援。
需要根據具體的場景和需求來選擇合適的通訊方式
vue的響應式系統。我分了vue2和vue3來講
在 Vue 2 中,Vue 通過 Object.defineProperty() 來實現響應式系統。當一個物件被傳入 Vue 例項進行響應式處理時,Vue 會遍歷這個物件的每一個屬性,並使用 Object.defineProperty() 把這個屬性轉換成 getter 和 setter。當這個屬性被讀取時,getter 會被觸發,這個屬性就會被新增到依賴中;當這個屬性被修改時,setter 會被觸發,這個屬性的依賴就會被通知,並執行相應的更新操作。這樣,當資料被修改時,所有依賴這個資料的地方都會自動更新。
但是,Vue 2 的響應式系統存在一些問題。首先,它只能監聽物件的屬性,而不能監聽新增的屬性和刪除的屬性;其次,它無法監聽陣列的變化,只能監聽陣列的索引變化,即當使用陣列的 push、pop、shift、unshift、splice 等方法時才能觸發更新。
在 Vue 3 中,Vue 引入了 Proxy 物件來實現響應式系統。當一個物件被傳入 Vue 例項進行響應式處理時,Vue 會使用 Proxy 物件對這個物件進行代理,這樣就可以監聽新增的屬性和刪除的屬性,同時也可以監聽陣列的變化。當一個屬性被讀取或修改時,Proxy 物件的 get 和 set 方法會被觸發,這樣就可以實現響應式更新。
Vue 3 的響應式系統還有一個優點,就是它支援了多個根節點,也就是 Fragment。這樣可以在不需要新增額外的 DOM 節點的情況下,返回多個元素。
總體來說,Vue 3 的響應式系統更加靈活和高效,能夠更好地應對複雜的應用場景
vue2是如何解決陣列檢測的問題
在 Vue 2 中,對於陣列的檢測是通過對陣列的原型進行改寫來實現的。Vue 2 中通過 Object.defineProperty()
方法對陣列原型上的7個變異方法進行重寫,分別是 push()
、pop()
、shift()
、unshift()
、splice()
、sort()
、reverse()
。在這些方法被呼叫時,除了執行它們本身的操作外,還會通知依賴更新。
當資料是陣列型別時,Vue 會先判斷該陣列是否具有 __ob__
屬性(Observer 物件),如果有則說明已經被觀測過,直接返回該 __ob__
物件;如果沒有,則會建立一個 Observer
物件來觀測該陣列,然後返回該 __ob__
物件。
雖然這種方式可以監聽陣列的變化,但是存在以下問題:
- 監聽不到索引值的變化,比如
arr[1] = newValue
。 - 物件的新增和刪除也需要進行額外處理。
- 遍歷陣列時會將陣列中的每一項都進行依賴收集,造成效能問題。
Vue 提供了 $set
方法,可以用來給陣列新增新元素或者修改已有元素的值,使得這些修改也能夠被 Vue 監聽到並更新檢視。例如:
this.$set(this.array, 1, 'new value')
這行程式碼會將 array
陣列中索引為 1
的元素的值改為 'new value'
,並通知 Vue 去更新檢視。
除了 $set
方法,Vue 還提供了 $delete
方法來刪除陣列中的元素
為了解決這些問題,Vue 3 採用了更加高效的響應式系統
vue2的缺陷,效能問題。如果data裡的層次很深的話,進行多層次的監聽開銷是很大的
Vue 2 在進行響應式處理時,會遞迴遍歷資料物件中的每一個屬性,並將這些屬性轉化為 getter 和 setter。當資料層次比較深時,這種遞迴遍歷的開銷就會非常大,會導致頁面渲染效能下降。
除此之外,Vue 2 還存在以下效能問題:
- 每個元件都會建立自己的觀察者 Watcher 例項,當元件數量較多時,會導致大量的記憶體開銷和效能問題;
- 每次資料變化都會導致重新渲染整個元件,即使資料變化的影響僅限於某個子元件;
- 在大型列表渲染時,使用 v-for 進行迴圈渲染會產生大量的 DOM 操作,影響渲染效能
vue的模版解析過程
在 Vue 中,模板是由 HTML 程式碼和 Vue 特定的模板語法組成的。Vue 的模板編譯器會將模板編譯成渲染函式,然後再生成 Virtual DOM,最終進行渲染。
下面是 Vue 的模板解析過程:
- 解析模板,生成 AST 抽象語法樹:Vue 的編譯器會將模板轉換為 AST 抽象語法樹,這是一個樹形結構,代表了模板的結構,包括元素節點、文字節點、指令等。
- 優化 AST:在生成 AST 之後,Vue 的編譯器會對其進行優化,例如靜態節點提取、靜態根節點提取等優化操作,以提高渲染效能。
- 生成程式碼:最後,Vue 的編譯器會將 AST 轉換為渲染函式的程式碼。這個渲染函式是一個 JavaScript 函式,接收一個引數 h,返回一個 Virtual DOM 節點。
- 生成 Virtual DOM:渲染函式生成後,Vue 會使用它來生成 Virtual DOM,然後對比新舊 Virtual DOM,計算出需要更新的部分,最終只更新需要更新的部分。
- 渲染:最後,Vue 將更新後的 Virtual DOM 渲染到真實的 DOM 中。
這個過程是 Vue 的模板編譯過程的核心,也是 Vue 能夠高效渲染頁面的重要原因
反問
- 你對我的面試表現怎麼樣
挺好的 基礎問題基本上都回答的出來,前端的廣度還不夠 挺好的
- 還會有二面嗎?
這個我們得和後面的小組商量一下
- 同程旅行的技術棧是什麼?
蘇州這邊是vue2為主,做一個低程式碼平臺的元件化
同程旅行二面
二面是業務部門負責人+HRBP面,很輕鬆愉快的一次面試
基本上就是閒聊,問我對前端的一些看法,還有實習、到時候答辯會怎麼解決之類的,順便問了我為什麼掘金ID叫八歲小孩學程式設計,是八歲就開始了嗎😂我連忙回答說是亂取的一個名字,然後就是很愉快的交流了
後續
成功OC了,打算去蘇州實習咯,這邊也和輔導員商量好了,等正式offer郵件下來就去打印出來,和學校申請去實習了,我的三月份面試也在這裡畫上了一個還算完美的句號了,謝謝同程的收留哈哈哈,也謝謝除丁香園以外的每個面試官😽