手機端網(wǎng)站開發(fā)長沙網(wǎng)站seo方法
目錄
- 前言
- 一面
- git 常見命令
- 跨窗口通信
- vue 響應(yīng)式原理
- 發(fā)布訂閱模式
- 翻轉(zhuǎn)二叉樹
- Promise.all()
- 扁平化數(shù)組
- 面試官建議
- 二面
- Event Loop 原理
- Promise 相關(guān)
- css 描邊方式
- requestAnimation
- React 18 新特性
- JSX 相關(guān)
- react 輸出兩次
- 函數(shù)式編程
- React 批處理機制
- http請求頭有哪些
- 本地存儲
- 性能優(yōu)化方面
- webgl 的優(yōu)化手段
- 著色器優(yōu)化相關(guān)
- 緩沖區(qū)和深度測試
- 異步
- 原型和原型鏈
- 倒序輸出
- 漢語表達
- 次數(shù)最少字符
- 面試復(fù)盤
前言
即時設(shè)計 - 可實時協(xié)作的專業(yè) UI 設(shè)計工具
即時設(shè)計是一款可在線編輯的專業(yè)UI設(shè)計工具,支持原型、交互、智能動畫、切圖等功能,滿足產(chǎn)品創(chuàng)造和交付的全流程體驗。
面試題目源碼:前端面經(jīng)
一面
git 常見命令
初始化倉庫:git init
遠程倉庫地址:git clone / git remote add origin
查看關(guān)聯(lián)的遠程倉庫列表 git remote -v
顯示當(dāng)前工作目錄和暫存區(qū)的狀態(tài) git status
添加到暫存區(qū) git add .
提交本地倉庫 git commit -m "提交日志"
提交日志信息 git log
推送遠程倉庫 git push origin <本地分支名>
創(chuàng)建分支 git branch 分支名稱
切換分支 git checkout 分支名稱
合并分支 git merge 分支名稱
跨窗口通信
https://juejin.cn/post/7306040473542213644
- 可以借助 web scoket 通過后端這個載體,進行跨頁面通信
- Broadcast Channel:遵循瀏覽器的同源策略,基于發(fā)布-訂閱模式,允許一個窗口發(fā)送消息,并由其他窗口接收,本質(zhì)上是一個數(shù)據(jù)共享池子
- SharedWorker:是 HTML5 中提供的一種多線程解決方案,它可以在多個瀏覽器 TAB 頁面之間共享一個后臺線程,從而實現(xiàn)跨頁面通信
- 本地化存儲 API:利用本地存儲實現(xiàn)同域下的數(shù)據(jù)共享,window 監(jiān)聽 storage 數(shù)據(jù)變化執(zhí)行對應(yīng)的方法回調(diào)
vue 響應(yīng)式原理
-
vue2 使用 Object.defineProperty實現(xiàn),是 es6 引入的方法,可以通過配置精確添加或修改對象的屬性
-
不能利用 get/set 對額外添加的屬性進行監(jiān)聽
-
不能監(jiān)聽自身數(shù)組下標(biāo)修改元素的變化,直接定義數(shù)組可以,但是不常用
-
vue3 使用 Proxy 實現(xiàn),能夠支持對象添加或修改屬性的變化
-
通過reactive() 函數(shù)給每一個對象都包一層 proxy,從而實現(xiàn)對數(shù)據(jù)的監(jiān)控
-
支持改變數(shù)組的方法,push、pop、shift、unshift、splice、sort、reverse
-
Reflect 是 es6 引入的新特性,提供一種新的方式操作對象
-
使用 WeakMap 作為緩存區(qū)防止對象被重復(fù)代理,主要是避免內(nèi)存泄漏,以及隱藏內(nèi)部實現(xiàn)細節(jié)
發(fā)布訂閱模式
class PubSub {constructor() {this.events = {}}subscribe(event, callback) {if (!this.events[event]) this.events[event] = []this.events[event].push(callback)}unSunscribe(event, callback) {if (!this.events[event]) returnthis.events[event] = this.events[event].filter((cb) => cb !== callback)}publish(event, data) {if (!this.events[event]) returnfor (let item of this.events[event]) item(data)}
}const pubsub = new PubSub()
function callback1(data) {console.log('觸發(fā)訂閱事件 1', data)
}function callback2(data) {console.log('觸發(fā)訂閱事件 2', data)
}pubsub.subscribe('myEvent', callback1)
pubsub.subscribe('myEvent', callback2)pubsub.publish('myEvent', '11111')
翻轉(zhuǎn)二叉樹
/*** 實現(xiàn)翻轉(zhuǎn)二叉樹* 例如* a* / \* b c* 轉(zhuǎn)為* a* / |* c b*/
const invertTree = function (root) {if (root === null) return rootlet temp = root.leftroot.left = invertTree(root.right)root.right = invertTree(temp)return root
}const ensure = (output, expect, message) => {if (JSON.stringify(output) === JSON.stringify(expect)) {console.log(`${message} ok`)} else {console.log(`${message} fail`)}
}const test = function () {const input = {val: 1,left: {val: 2,left: {val: 3,left: null,right: null,},right: {val: 4,left: null,right: null,},},right: {val: 5,left: {val: 6,left: null,right: null,},right: {val: 7,left: null,right: null,},},}const expect = {val: 1,left: {val: 5,left: {val: 7,left: null,right: null,},right: {val: 6,left: null,right: null,},},right: {val: 2,left: {val: 4,left: null,right: null,},right: {val: 3,left: null,right: null,},},}const output = invertTree(input)ensure(output, expect, 'test')
}// 輸出 test ok表示測試成功
test()
Promise.all()
Promise.myAll = function (promises) {let result = []return new Promise((resolve, reject) => {promises.forEach((item, index) => {Promise.resolve(item).then((res) => {result[index] = resif (Object.keys(result).length === promises.length) resolve(result)}).catch((err) => {reject(err)})})})
}const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) => {setTimeout(() => {resolve('p2 延時一秒')}, 3000)
})
const p3 = new Promise((resolve, reject) => {setTimeout(() => {resolve('p3 延時兩秒')}, 2000)
})const p4 = Promise.reject(4)Promise.myAll([p4, p2, p3]).then((res) => console.log(res)).catch((err) => console.log(err)) // 2秒后打印 [const promise = new Promise((resolve, reject) => {})
console.log(promise)
扁平化數(shù)組
// https://juejin.cn/post/7273693216372916283console.log('\n --- 一:原生方法 --- \n')
let arr = [1, [2, 3], [4, [5, 6, [7, 8]]]]
console.log(arr.flat()) // 默認解析一層,可以指定參數(shù)
console.log(arr.flat(Infinity)) // Infinity完全解析console.log('\n--- 二、遞歸解析 ---\n')function flatten(arr, depth) {let result = []for (let i = 0; i < arr.length; i++) {if (Array.isArray(arr[i]) && depth > 0) {result = result.concat(flatten(arr[i], depth - 1))} else {result.push(arr[i])}}return result
}console.log(flatten(arr, 1))
console.log(flatten(arr, 2))
console.log(flatten(arr, 3))console.log('\n--- 三、reduce解析 ---\n')
function reduceFlatten(arr) {return arr.reduce((acc, cur) => {if (Array.isArray(cur)) {return acc.concat(reduceFlatten(cur))} else {return acc.concat(cur)}}, [])
}console.log(reduceFlatten(arr))
面試官建議
- 面試回答:先總結(jié),然后分點并結(jié)合自己的實際項目進行講解
- 應(yīng)屆生沒有太多的項目/業(yè)務(wù)經(jīng)驗,面試官可能會重點問一下基礎(chǔ)、框架原理、數(shù)據(jù)結(jié)構(gòu)與算法、計算機網(wǎng)絡(luò)這些知識點
一面忘記錄音了,有一些問題面試后沒有回想起來
二面
Event Loop 原理
事件循環(huán)(Event Loop)是 JavaScript 中實現(xiàn)異步編程的一種機制。
- 執(zhí)行同步任務(wù),放入調(diào)用棧,結(jié)果輸出到控制臺
- 異步任務(wù)放到web apis,這里分為微任務(wù)、宏任務(wù)
- 同步代碼執(zhí)行完畢(即調(diào)用棧清空)啟動事件循環(huán)機制(Event Loop)
- 依次處理執(zhí)行微任務(wù)、宏任務(wù)隊列,放入調(diào)用棧執(zhí)行
Promise 相關(guān)
同步:任務(wù)按照次序依次執(zhí)行,每個任務(wù)必須完成后才能執(zhí)行下一個任務(wù)。
異步:異步任務(wù)是非阻塞的,可以同時執(zhí)行多個任務(wù),不需要等待任務(wù)的完成。
- 執(zhí)行 async 函數(shù),返回的一定是 Promise 對象
- await 相當(dāng)于 Promise 的 then
- try…catch 可捕獲異常,代替了 Promise 的 catch
- promise.all 接收 promises 數(shù)組,所有為 resolve 才輸出,有一個 reject 就中斷輸出報
- promise.allSettled 接收 promises 數(shù)組,不論結(jié)果是什么,都會輸出每一個 promise 的狀態(tài)
css 描邊方式
- border 普通邊框
- outline 不占用空間和影響元素大小
- box-shadow 設(shè)置陰影描邊效果
requestAnimation
- setTimeout:快速滾動白屏,原因不同設(shè)備刷新率不同,setTimout 只是一個固定的時間間隔。只有主線程執(zhí)行完畢才會檢查事件隊列的任務(wù),執(zhí)行時間會變晚
- requestAnimationFrame:由系統(tǒng)決定回調(diào)函數(shù)的執(zhí)行時機,不會引起丟幀現(xiàn)象,1/60 1/75
React 18 新特性
https://juejin.cn/post/7071861718573383716
- 并發(fā)渲染機制,優(yōu)先處理用戶最關(guān)心的事情
- 批處理優(yōu)化,多個狀態(tài)合并成一個批次處理,這樣可以減少不必要的渲染次數(shù)
- 服務(wù)端渲染的改進,引入新的 api createRoot 創(chuàng)建 root 節(jié)點
- 移除對 IE 支持,繼續(xù)使用需要 17版本
JSX 相關(guān)
- js 是一種動態(tài)類型的腳本語言
- jsx 是 react 的擴展語法,主要用于定義組件UI的結(jié)構(gòu)和外觀,最后需要通過構(gòu)建工具轉(zhuǎn)換為普通的js代碼
- JSX是React的一個語法擴展,它允許你在JavaScript代碼中寫類似HTML的標(biāo)記。Babel會將JSX代碼轉(zhuǎn)換成普通的JavaScript代碼,所有的JSX元素都會被轉(zhuǎn)換成React.createElement函數(shù)調(diào)用
react 輸出兩次
- 組件被開發(fā)模式下的嚴(yán)格模式包裹,組件渲染或副作用函數(shù)會執(zhí)行兩次,不影響生產(chǎn)模式
- 函數(shù)式編程純函數(shù)的概念,用于幫助開發(fā)者發(fā)現(xiàn)和修復(fù)潛在的問題
函數(shù)式編程
總結(jié)一下: 函數(shù)式編程有兩個核心概念。
- 數(shù)據(jù)不可變(無副作用): 它要求你所有的數(shù)據(jù)都是不可變的,這意味著如果你想修改一個對象,那你應(yīng)該創(chuàng)建一個新的對象用來修改,而不是修改已有的對象。
- 無狀態(tài): 主要是強調(diào)對于一個函數(shù),不管你何時運行,它都應(yīng)該像第一次運行一樣,給定相同的輸入,給出相同的輸出,完全不依賴外部狀態(tài)的變化。
純函數(shù)帶來的意義。
- 便于測試和優(yōu)化:這個意義在實際項目開發(fā)中意義非常大,由于純函數(shù)對于相同的輸入永遠會返回相同的結(jié)果,因此我們可以輕松斷言函數(shù)的執(zhí)行結(jié)果,同時也可以保證函數(shù)的優(yōu)化不會影響其他代碼的執(zhí)行。
- 可緩存性:因為相同的輸入總是可以返回相同的輸出,因此,我們可以提前緩存函數(shù)的執(zhí)行結(jié)果。
- 更少的 Bug:使用純函數(shù)意味著你的函數(shù)中不存在指向不明的 this,不存在對全局變量的引用,不存在對參數(shù)的修改,這些共享狀態(tài)往往是絕大多數(shù) bug 的源頭。
React 批處理機制
- 在事件處理函數(shù)的更新不是一個個重新渲染處理的,而是批處理多個狀態(tài),將更新操作放入一個隊列中,下一段時間一次性執(zhí)行更新操作
- 狀態(tài)更新完成前,一般是無法打印狀態(tài)的最新值的,只能等到下一次
- React 的狀態(tài)更新是異步處理的,可以使用回調(diào)函數(shù)進行更新
http請求頭有哪些
常見請求頭:Accept 接受的內(nèi)容、字符集、編碼方式,Content 請求體的類型、長度,緩存機制(強制緩存、協(xié)商緩存標(biāo)識)
本地存儲
- Cookie是在客戶端存儲數(shù)據(jù)的機制,用于存儲少量用戶信息(4KB),可以設(shè)置過期時間,基于瀏覽器。
- Session是在服務(wù)器端存儲用戶狀態(tài)的機制,用于存儲用戶的登錄狀態(tài)和其他相關(guān)信息,占用服務(wù)器資源,基于服務(wù)器。
- Token是一種無狀態(tài)的身份驗證機制,用于驗證用戶身份,可以包含用戶信息,不占用服務(wù)器資源,可以存儲在客戶端或服務(wù)器。
- IndexedDB 是一個運行在瀏覽器上的非關(guān)系型數(shù)據(jù)庫系統(tǒng),它允許你存儲大量數(shù)據(jù),包括文件和二進制數(shù)據(jù)
性能優(yōu)化方面
代碼層面
- 使用防抖和節(jié)流
- 減少頁面的重排和重繪,要統(tǒng)一調(diào)整樣式、多次修改 DOM 先脫離標(biāo)準(zhǔn)流
- 事件觸發(fā)使用事件委托機制
- 按需加載,如單頁面應(yīng)用的路由懶加載
構(gòu)建方面
- 開啟 gzip 壓縮代碼,vite 使用 vite-plugin-compression 插件,后端開啟 gzip 支持
- 常用的第三庫使用 CDN 服務(wù),就是部署多個服務(wù)器節(jié)點,用戶請求將定位到離用戶最近的節(jié)點
其他
- 優(yōu)先使用緩存減少HTTP請求,將多個細小請求可以合并大文件
- 圖?優(yōu)化:對圖片進?壓縮和優(yōu)化,減少圖片的??和請求次數(shù)
- 使?字體圖標(biāo):使?字體圖標(biāo)可以減少圖片的請求,提?頁面性能
- 服務(wù)端渲染
用戶體驗
- 增加骨架屏、loading加載動畫效果
webgl 的優(yōu)化手段
- 監(jiān)視渲染幀率、控制臺lighthouse性能報告
- 良好的編碼規(guī)則,將場景中不需要的對象通過 remove dispose 進行清除廢置
- 使用廉價的燈光,如環(huán)境光和平行光
- 陰影方面:優(yōu)化陰影貼圖范圍,降低分辨率貼圖尺寸
- 貼圖、模型等資源進行壓縮
- 謹(jǐn)慎使用抗鋸齒和后期處理通道
- 著色器指定精度、保持代碼簡單、使用貼圖紋理表示噪波
- 盡可能在頂點著色器進行計算,并將結(jié)果發(fā)送到片元著色器
著色器優(yōu)化相關(guān)
- 著色器指定精度、保持代碼簡單、使用貼圖紋理表示噪波
- 盡可能在頂點著色器進行計算,并將結(jié)果發(fā)送到片元著色器
緩沖區(qū)和深度測試
概述:幾何體由一個個頂點粒子構(gòu)成,每個粒子包含了位置、uv貼圖(3d=>2d坐標(biāo)對應(yīng))等,三個頂點形成一個面,在three.js中。物體由一個個三角形面構(gòu)成。
BufferGeometry:是面片、線或點幾何體的有效表述。包括頂點位置,面片索引、法相量、顏色值、UV 坐標(biāo)和自定義緩存屬性值。使用 BufferGeometry 可以有效減少向 GPU 傳輸上述數(shù)據(jù)所需的開銷。目前,three.js的物體都用上了Buffer,可以直接調(diào)用,一些頂點可以使用這個。
當(dāng)WebGL繪制粒子時,WebGL會測試正在繪制的粒子哪個更靠前,在其后面的粒子不會被繪制,在其前面的粒子會被繪制,這被稱為深度測試,可以通過alphaTest停用depth testing。
異步
setTimeout(function () {console.log(1)
}, 0)new Promise(function (resolve, reject) {console.log(2)for (var i = 0; i < 10000; i++) {if (i === 10) console.log(10)i == 9999 && resolve()}console.log(3)
}).then(function () {console.log(4)
})console.log(5)//2 10 3 5 4 1
- 計時器宏任務(wù),放入宏任務(wù)隊列
- new Promise 立即執(zhí)行函數(shù),輸出 2
- promise 微任務(wù),放入微任務(wù)隊列
- 進入 for 循環(huán),輸出10 3
- 同步任務(wù),輸出 5
- 微任務(wù),輸出 4
- 宏任務(wù),輸出 1
原型和原型鏈
function Foo() {getName = function () {console.log(1)}return this
}Foo.getName = function () {console.log(2)
}
Foo.prototype.getName = function () {console.log(3)
}
var getName = function () {console.log(4)
}
function getName() {console.log(5)
}Foo.getName() //2
getName() //4
Foo().getName() //1
new Foo().getName() //3
- Foo.getName 綁定到類的方法 2
- var 存在變量提升,function fn1(){} 定義的函數(shù)也會變量提升,結(jié)果如下
var getName
function getName() {console.log(5)
}getName = function () {console.log(4)
}
//node 環(huán)境運行報錯、let 聲明不可用
- Foo() 返回 this ,指向window ,但是進行 Foo()函數(shù),getName 被重新賦值了,所以是 1,以后也是1
- new Foo() 指向當(dāng)前實例,內(nèi)部的方法 ,返回3
倒序輸出
// 輸入:1->2->3->4->5->NULL
// 輸出:5->4->3->2->1->NULLfunction traverseStr(str) {const array = str.split('->').slice(0, -1)return array.reverse().join('->') + '->NULL'
}const target = '1->2->3->4->5->NULL'
const result = traverseStr(target)
console.log(result)
漢語表達
次數(shù)最少字符
const target = 'aaabbbcceeff'
function removeStr(target) {let map = new Map(),result = []for (let ch of target) map.set(ch, (map.get(ch) || 0) + 1)let minCount = Math.min(...map.values())for (let ch of target) {if (map.get(ch) !== minCount) result.push(ch)}return result.join('')
}const result = removeStr(target)
console.log(result)
面試復(fù)盤
- 之前面試的一些八股文、總結(jié)大部分都在自己內(nèi)部的語雀文檔,后續(xù)逐步整理一下發(fā)出來,希望能夠幫助到面試的小伙伴
- 大概是12月12號一面,第二天的二面,但是第二輪面試掛了。數(shù)字轉(zhuǎn)為漢字那題沒寫出來,只是說了一下大概的思路(后續(xù)源碼整理到算法倉庫里面),去除出現(xiàn)最少字符雖然寫出來了,可能代碼邏輯還有待優(yōu)化的地方
- webgl 和著色器優(yōu)化那部分答的一般