【原創(chuàng)】前端面試知識體系(一)
我的博客來源:https://1024bibi.com/2018/01/01/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E7%9F%A5%E8%AF%86%E4%BD%93%E7%B3%BB%EF%BC%88%E4%B8%80%EF%BC%89/
防抖和節(jié)流有什么區(qū)別,分別用于什么場景- 節(jié)流:限制執(zhí)行頻率,有節(jié)奏的執(zhí)行;
- 防抖:限制執(zhí)行次數(shù),多次密集的觸發(fā)只執(zhí)行一次;
防抖 debounce
function?debounce(fn,?delay?=?200)?{
?let?timer?=?0
?
?return?function?()?{
??if?(timer)?clearTimeout(timer);
??
??timer?=?setTimeout(()?=>?{
???fn.apply(this,?arguments);?//?透傳?this?和參數(shù)
???timer?=?0
??},?delay)
?}
}
const?input?=?document.getElementById('input')
input.addEventListener('keyup',?debounce(()?=>?{
?console.log('發(fā)起搜索',?input.value)
}),?300)
節(jié)流 throttle
例如:drag或scroll期間觸發(fā)某個回調(diào),要設(shè)置一個時間間隔
px % em rem vw/vh 有什么區(qū)別px 和 %
- px基本單位,絕對單位(固定的),其他的都是相對單位
- % 相對于父元素的寬度比例
- 元素內(nèi)的垂直居中
<style>
?#container?{
??width:?200px;
??height:?200px;
??position:?relative;
??background-color:?#ccc;
?}
?#box?{
??width:?100px;
??height:?100px;
??position:?absolute;
??left:?50%;
??top:?50%;
??margin-top:?-50px;
??margin-left:?-50px;
??background-color:?blue;
?}
</style>
<div?id="container">
?<div?id="box"></div>
</div>
em 和 rem
- em 相對于當前元素的 font-size
- rem 相對于根節(jié)點的 font-size
<div?style="font-size:?20px;">
?<p?style="text-indent:?2em;?font-size:?40px;">首行縮進</p>?//?font-size:?40px;?text-indent:?80px;
?<p?style="text-indent:?2em;">哪吒,?算法貓叔</p>?//?font-size:?20px;?text-indent:?40px;
</div>
<style>
?@media?only?screen?and?(max-width:?374px)?{
??//?iphone5?或者更小的尺寸,以?iphone5?的寬度(320px)比例設(shè)置?font-size
??html?{
???font-size:?86px;
??}
?}
?@media?only?screen?and?(min-width:?375px)?and?(max-width:?413px)?{
??//?iphone6/7/8?和?iphone?x
??html?{
???font-size:?100px;
??}
?}
?@media?only?screen?and?(min-width:?414px)?{
??//?iphone6p?或者更大的尺寸,以?iphone6p?的寬度(414px)比例設(shè)置?font-size
??html?{
???font-size:?110px;
??}
?}
?p?{
??font-size:?.16rem;
?}
</style>
vw / vh
- vw 屏幕寬度的 1%
- vh 屏幕高度的 1%
- vmin 兩者的最小值,vmax 兩者的最大值
<div?id="div1">?div1?</div>
<div?id="div2">?div2?</div>
<div?id="div3">?div3?</div>
<style>
?div?{
??border:?1px?solid?#ccc;
??margin-top:?20px;
?}
?#div1?{
??width:?10vw;
??height:?10vh;
?}
?#div2?{
??width:?10vmax;
??height:?10vmax;
?}
?#div3?{
??width:?10vmin;
??height:?10vmin;
?}
</style>
什么時候不能使用箭頭函數(shù)
箭頭函數(shù)有什么缺點?
- 沒有 arguments
- 無法通過 apply call bind 改變 this
- 某些箭頭函數(shù)代碼難以閱讀
什么時候不能使用箭頭函數(shù)?
- 不適用-對象方法
const?obj?=?{
?name:?'哪吒,B站,算法貓叔',
?getName:?()?=>?{
??return?this.name
?}
}
console.log(obj.getName())
- 不適用-原型方法
const?obj?=?{
?name:?'哪吒,B站,算法貓叔'
}
obj.__proto__.getName?=?()?=>?{
?return?this.name
}
console.log(?obj.getName()?)
- 不適用-構(gòu)造函數(shù)
const?Foo?=?(name,?age)?=>?{
?this.name?=?name
?this.age?=?age
}
const?f?=?new?Foo('張三',?20)
//?報錯?Foo?is?not?a?constructor
- 不適用-動態(tài)上下文中的回調(diào)函數(shù)
const?btn1?=?document.getElementById('btn1')
btn1.addEventListener('click',?()?=>?{
?//?console.log(this?===?window)
?this.innerHTMl?=?'clicked'
})
- 不適用-Vue生命周期和method : vue組件本質(zhì)上一個 JS 對象
- React 組件(非Hooks)本質(zhì)上是一個 es6 class,class里面適用箭頭函數(shù)沒問題
{
?data()?{?return?{?name:??'哪吒,B站:算法貓叔'?}?},
?methods:?{
??getName:?()?=>?{
???//?報錯?Cannot?read?properties?of?undefined?(reading?'name')
??return?this.name
??},
??//?getName()?{
??//??return?this.name?//?正常
??//?}
?},
?mounted:?()?=>?{
??//?報錯
?},
?//?mounted()?{
?//??正常
?//?}
}
JS中for-in和for-of有什么區(qū)別
for of 去遍歷可以generator
const?arr?=?[10,?20,?30]
for?(let?val?of?arr)?{
?console.log(val);?//?值
}
const?str?=?'abc'
for?(let?c?of?str)?{
?console.log(c);
}
function?fn()?{
?for?(let?arg?of?arguments)?{
??console.log(arg)
?}
}
fn(100,?200,?'aaa')
const?pList??=?document.getElementsByTagName('p')
//?querySelectorAll('p')
for?(let?p?of?pList)?{
?console.log(p)
}
- 遍歷對象: for ... in 可以,for ... of 不可以
- 遍歷Map Set:for...of 可以,for...in 不可以
- 遍歷generator:for...of 可以,for ... in 不可以
對象,數(shù)組,字符串可枚舉的,就可以使用for?...?in?循環(huán)
const?obj1?=?{?x:?100?}
Object.getOwnPropertyDescriptors(obj1)
x:
configurable:?true
enumerable:?true
value:?100
writeable:?true
- 可枚舉 vs 可迭代
for ... in 用于可枚舉數(shù)據(jù),如對象,數(shù)組,字符串,得到key
for ... of 用于可迭代數(shù)據(jù),如數(shù)組,字符串,Map,Set,得到value
for-await-of有什么作用for await...of 用于遍歷多個Promise
function?createPromise(val)?{
?return?new?Promise((resolve)?=>?{
??setTimeout(()?=>?{
???resolve(val)
??},?1000)
?})
}
(async?function?()?{
?const?p1?=?createPromise(100)
?const?p2?=?createPromise(200)
?const?p3?=?createPromise(300)
?//?const?res1?=?await?p1
?//?console.log(res1)
?//?const?res2?=?await?p2
?//?console.log(res2)
?//?const?res3?=?await?p3
?//?console.log(res3)
?const?list?=?[p1,?p2,?p3]
?//?Promise.all(list).then(res?=>?console.log(res))
?for?await?(let?res?of?list)?{
??console.log(res)
?}
})()
offsetHeight - scrollHeight - clientHeight區(qū)別
盒子模型: width, height, padding, border, margin, box-sizing
offsetHeight offsetWidth : border + padding + content
clientHeight clientWidth: padding + content
scrollHeight scrollWidth: padding + 實際內(nèi)容尺寸
HTMLCollection和NodeList有什么區(qū)- DOM是一顆樹,所有節(jié)點都是Node
- Node是Element的基類
- Element是其他HTML元素的基類,如HTMLDivElement
const?p1?=?document.getElementById('p1')
class?Node?{}
//?document
class?Document?extends?Node?{}
class?DocumentFragment?extends?Node?{}
//?文本和注釋
class?CharacterData?extends?Node?{}
class?Comment?extends?CharacterData?{}
class?Text?extends?CharacterData?{}
//?elem
class?Element?extends?Node?{}
class?HTMLElement?extends?Element?{}
class?HTMLDivElement?extends?HTMLElement?{}
class?HTMLInputElement?extends?HTMLElement?{}
- HTMLCollection 是 Element 的集合
- NodeList 是 Node 集合
<p?id="p1">
?<b>node</b>?vs?<em>element</em>
</p>
<script>
?const?p1?=?document.getElementById('p1')
?console.log(p1.children)
?
?console.log(p1.childNodes)
?//?[b,text,em,comment]
</script>
劃重點:
- 獲取 Node 和 Element 的返回結(jié)果可能不一樣
- 如 elem.childNodes 和 elem.children 不一樣
- 前者包含Text和Comment節(jié)點,后者不會
類數(shù)組 變成 數(shù)組
const?arr1?=?Array.from(list)
const?arr2?=?Array.prototype.slice.call(list)
const?arr3?=?[...list]
Vue中computed和watch有什么區(qū)別
- computed 用于計算產(chǎn)生新的數(shù)據(jù)
- watch 用于監(jiān)聽現(xiàn)有數(shù)據(jù)
watch:?{
?name(newValue,?oldValue)?{
??console.log('watch?name',?newValue,?oldValue)
?}
},
computed:?{
?userInfo()?{
??return?this.name?+?this.city
?}
}
computed有緩存 watch沒有緩存
Vue組件通訊有幾種方式
props和$emit
$parent
自定義事件
$refs
$attr
provide/inject
vuex
---
$attrs?$listeners
vue3?移除?$listeners
上一級沒有接收到的
props:?['x'],?//?$attrs
emits:?['getX'],?//?$listeners
Object.keys(this.$attrs)
<l3?v-bind="$attrs"></l3>
dom結(jié)點:?inheritAttrs:?false
---
this.$parent
this.$refs
provide:?{
?info:?'aaa'
}
provide()?{
?return?{
??info:?computed(()?=>?this.name)
?}
}
---
父子組件
上下級組件(跨多級)通訊
全局組件
Vuex中action和mutation有什么區(qū)別
mutation: 原子操作,必須同步代碼
action: 可包含多個mutation;可包含異步代碼
JS嚴格模式有什么特點
'use?strict'?//?全局開啟
function?fn()?{
?'use?strict'?//?某個函數(shù)開啟
}
- 全局變量必須先聲明
- 禁止使用 with
- 創(chuàng)建eval作用域
- 禁止this指向window
- 函數(shù)參數(shù)不能重名
垃圾回收 GC
什么是垃圾回收?
function?fn1()?{
?const?a?=?'aa'
?console.log(a)
?
?const?obj?=?{?x:?100?}
?console.log(obj)
}
fn1()
function?fn2()?{
?const?obj?=?{?x:?100?}
?window.obj?=?obj
}
fn2()
function?getDataFns()?{
?const?data?=?{}?//?閉包
?return?{
??get(key)?{
???return?data[key]
??},
??set(key,?value)?{
???data[key]?=?value
??}
?}
}
const?{?get,?set?}?=?getDataFns()
set('x',?100)
get('x')
引用計數(shù)(之前)
//?對象被?a?引用
let?a?=?{?x:?100?}
let?a1?=?a
a?=?10
a1?=?null
//?循環(huán)引用
function?fn3()?{
?const?obj1?=?{}
?const?obj2?=?{}
?obj1.a?=?obj2
?obj2.a?=?obj1
}
fn3()
//?ie6-7?內(nèi)存泄漏的?bug
var?div1?=?document.getElementById('div1')
div1.a?=?div1
div1.someBigData?=?{}
標記清除(現(xiàn)代)
//?JS?根?window
JS閉包是內(nèi)存泄漏嗎
閉包的數(shù)據(jù)是不可以被垃圾回收的
如何檢測JS內(nèi)存泄漏檢測內(nèi)存變化
const?arr?=?[]
for?(let?i?=?0;?i?<?10?*?10000;?i++)?{
?arr.push(i)
}
function?bind()?{
?//?模擬一個比較大的數(shù)據(jù)
?const?obj?=?{
??str:?JSON.stringify(arr)?//?簡單的拷貝
?}
?window.addEventListener('resize',?()?=>?{
??console.log(obj)
?})
}
let?n?=?0
function?start()?{
?setTimeout(()?=>?{
??bind()
??n++
??
??//?執(zhí)行?50?次
??if?(n?<?50)?{
???start()
??}?else?{
???alert('done')
??}
?},?200)
}
document.getElementById('btn1').addEventListener('click',?()?=>?{
?start()
})
JS內(nèi)存泄漏的場景有哪些
- 被全局變量,函數(shù)引用,組件銷毀時未清除
- 被全局事件,定時器引用,組件銷毀時未清除
- 被自定義事件引用,組件銷毀時未清除
//?標記清除算法
const?data?=?{}
function?fn1()?{
?const?obj?=?{?x:?100?}
?data.obj?=?obj
}
fn1()
const?map?=?new?Map()
function?fn1()?{
?const?obj?=?{?x:?100?}
?map.set('a',?obj)
}
fn1()
//?WeakMap?WeakSet?弱引用
<script>
?const?wMap?=?new?WeakMap();?//?弱引用
?function?fn1()?{
??const?obj?=?{?x:?100?}
??wMap.set(obj,?100)?//?WeakMap的key,只能是引用類型
?}
?fn1()
?//?WeakSet
</script>
Ajax-Fetch-Axios三者有什么區(qū)別
- Ajax(Asynchronous Javascript and XML),一種技術(shù)統(tǒng)稱
- Fetch,一個具體的原生API 瀏覽器原生API,用于網(wǎng)絡(luò)請求 和XMLHttpRequest一個級別 Fetch語法更加簡潔,易用,支持Promise
- Axios,是一個第三方庫 最常用的網(wǎng)絡(luò)請求lib 內(nèi)部可用XMLHttpRequest和Fetch來實現(xiàn)
- lib(庫)和API(原生的函數(shù))的區(qū)別
- fetch 和 XMLHttpRequest 全局的API
function?ajax1(url,?sucessFn)?{
?const?xhr?=?new?XMLHttpRequest();
?xhr.open("GET",?url,?false);
?xhr.onreadystatechange?=?function?()?{
??//?這里的函數(shù)異步執(zhí)行
??if?(xhr.readyState?==?4)?{
???if?(xhr.status?==?200)?{
????successFn(xhr.responseText);
???}
??}
?}
?xhr.send(null);
}
function?ajax2(url)?{
?return?fetch(url).then(res?=>?res.json());
}
請描述TPC三次握手和四次揮手
建立TCP連接
- 先建立連接(確保雙方都有收發(fā)消息的能力)
- 再傳輸內(nèi)容(如發(fā)送給一個get請求)
- 網(wǎng)絡(luò)連接是TCP協(xié)議,傳輸內(nèi)容是HTTP協(xié)議
- SYN SYN+ACK ACK
四次揮手-關(guān)閉連接
1.?FIN?->
2.?ACK?<-
3.?FIN?<-
4.?ACK?->
HTTP跨域時為何要發(fā)送options請求
跨域請求
- 瀏覽器同源策略
- 同源策略一般限制Ajax網(wǎng)絡(luò)請求,不能跨域請求server
-
不會限制
<link> <img> <script> <iframe>加載第三方資源
JSONP
//?www.aaa.com網(wǎng)頁
<script>
?window.onSuccess?=?function(data)?{
??console.log(data)
?}
</script>
<script?src="https://www.bbb.com/api/getData"></script>
//?https://www.bbb.com...?返回了一段字符串
'onSuccess({?errno:?0,?data:?{}?})'
//?CORS?配置允許跨域(服務(wù)端)
response.setHeader("Access-Control-Allow-Origin",?"http://localhost:8011")?//?或者"*"
response.setHeader("Access-Control-Allow-Headers",?"X-Requested-With")
response.setHeader("Access-Control-Allow-Methods",?"PUT,POST,GET,DELETE,OPTIONS")
response.setHeader("Access-Control-Allow-Credentials",?"true")?//?允許跨域接收?cookie
options請求,是跨域請求之前的預檢查;瀏覽器自行發(fā)起的,無需我們干預,不會影響實際的功能
瀏覽器和nodejs事件循環(huán)(EventLoop)有什么單線程和異步
- JS是單線程的(無論在瀏覽器還是nodejs)
- 瀏覽器中JS執(zhí)行和DOM渲染共用一個線程
- 異步 宏任務(wù) 和 微任務(wù) 宏任務(wù),如 setTimeout setInterval 網(wǎng)絡(luò)請求 微任務(wù),如 promise async / await 微任務(wù)在下一輪DOM渲染之前執(zhí)行,宏任務(wù)在之后執(zhí)行 微任務(wù): MutationObserver 監(jiān)聽DOM樹的變化,Mutation observer 是用于代替 Mutation ?events 作為觀察DOM樹結(jié)構(gòu)發(fā)生變化時,做出相應(yīng)處理的API MutationObserver接口提供了監(jiān)視對DOM樹所做更改的能力。它被設(shè)計為舊的Mutation Events功能的替代品,該功能是DOM3 Events規(guī)范的一部分。
const?p?=?document.createElement('p')
p.innerHTML?=?'new?paragraph'
document.body.appendChild(p)
const?list?=?document.getElementsByTagName('p')
console.log('length---',?listh.length)
console.log('start')
//?渲染之后
setTimeout(()?=>?{
?const?list?=?document.getElementsByTagName('p')
?console.log('length?on?timeout---',?list.length)?//?2
?alert('阻塞?timeout')
})
//?渲染之前
Promise.resolve().then(()?=>?{
?const?list?=?document.getElementsByTagName('p')
?console.log('length?on?promise.then---',?list.length)?//?2
?alert('阻塞?promise')
})
console.log('end')
//?同步任務(wù)?->?異步任務(wù)?->?宏任務(wù)
//?微任務(wù)要比宏任務(wù)要快
//?Event?Loop
<script>
?console.log('start')
?setTimeout(()?=>?{
??console.log('timeout')
?})
?Promise.resolve().then(()?=>?{
??console.log('promise?then')
?))
?console.log('end')
?//?ajax(url,?fn)?//?300ms
?
?//?Event?Loop?繼續(xù)監(jiān)聽
?//?宏任務(wù)?MarcoTask?Queue
?//?()?=>?{
?//???console.log('timeout')
?//?}
?//?fn
?
?//?DOM?渲染
?//?微任務(wù)?MicroTask?Queue
?//?()?=>?{
?//???console.log('promise?then')
?//?}
</script>
Nodejs異步
- Nodejs同樣使用ES語法,也是單線程,也需要異步
- 異步任務(wù)也分:宏任務(wù)+微任務(wù)
- 但是,它的宏任務(wù)和微任務(wù),分不同類型,有不同優(yōu)先級
虛擬DOM(vdom)真的很快嗎
- vdom: Virtual DOM,虛擬DOM 用JS對象模擬DOM節(jié)點數(shù)據(jù)
- 組件化 數(shù)據(jù)視圖分離,數(shù)據(jù)驅(qū)動視圖 只關(guān)注業(yè)務(wù)數(shù)據(jù),而不用再關(guān)心DOM變化
- vdom并不快,js直接操作dom才是最快的 但”數(shù)據(jù)驅(qū)動視圖“要有合適的技術(shù)方案,不能全部dom重建 vdom就是目前最合適的技術(shù)方案(并不是因為它快,而是合適)
遍歷一個數(shù)組用for和forEach哪個更快
- for更快
- forEach每次都要創(chuàng)建一個函數(shù)來調(diào)用,而for不會創(chuàng)建函數(shù)
- 函數(shù)需要獨立的作用域,會有額外的開銷
nodejs如何開啟多進程,進程如何通訊-進程和線程的
進程 process vs 線程 thread 進程,OS 進行資源分配和調(diào)度的最小單位,有獨立內(nèi)存空間 線程,OS 進行運算調(diào)度的最小單位,共享進程內(nèi)存空間 JS是單線程的,但可以開啟多進程執(zhí)行,如WebWorker js 不可以開啟一個線程
為何需要多進程?
- 多核CPU,更適合處理多進程
- 內(nèi)存較大,多個進程才能更好的利用(單進程有內(nèi)存上限)
- 總之,“壓榨”機器資源,更快,更節(jié)省 單個進程內(nèi)存2G左右
nodejs如何開啟多進程
//?console.info(process.pid)
const?http?=?require('http')
const?server?=?http.createServer()
server.listen(3000,?()?=>?{
?console.log('localhost:?3000')
})
console.info(process.pid)
//?WebWorker?進程
//?fork
const?http?=?require('http')
const?server?=?http.createServer((req,?res)?=>?{
?if?(req.url?===?'/get-sum')?{
???console.info('主進程?id',?process.id)
???
???res.end('hello')
?}
})
server.listen(3000,?()?=>?{
?console.info('localhost:?3000')
})
//?cluster?進程
//?子進程,計算
function?getSum()?{
?let?sum?=?0
?for?(let?i?=?0;?i?<?10000;?i++)?{
??sum?+=?i
?}
?return?sum
}
process.on('message',?data?=>?{
?console.log('子進程?id',?process.pid)
?console.log(‘子進程接受到的信息:',?data)
?const?sum?=?getSum()
?//?發(fā)送消息給主進程
?process.send(sum)
})
const?http?=?require('http')
const?fork?=?require('child_process').fork
const?server?=?http.createServer((req,?res)?=>?{
?if?(req.url?===?'/get-sum')?{
??console.info('主進程?id',?process.pid)
???//?開啟子進程
??const??computeProcess?=?fork('./compute.js')
??computeProcess.send('開始計算')
??computeProcess.on('message',?data?=>?{
????console.info('主進程接受到的信息:',?data)
????res.end('sum?is'?+?data)
??})
??computeProcess.on('close',?()?=>?{
????console.info('子進程因報錯而退出')
????computeProcess.kill()
????res.end('error')
??})
?}
})
server.listen(3000,?()?=>?{
?console.info('localhost:?3000')
})
const?http?=?require('http')
const?cpuCoreLength?=?require('os').cpus().length
const?cluster?=?require('cluster')
if?(cluster.isMaster)?{
?for?(let?i?=?0;?i?<?cpuCoreLength;?i++)?{
??cluster.fork()?//?開啟子進程
?}
?cluster.on('exit',?worker?=>?{
??console.log('子進程退出')
??cluster.fork()?//?進程守護
?})
}?else?{
?//?多個子進程會共享一個?TCP?連接,提供一份網(wǎng)絡(luò)服務(wù)
??const?server?=?http.createServer((req,?res)?=>?{
??res.writeHead(200)
??res.end('done')
?})
?server.listen(3000)
}
開啟子進程 child_process.fork 和 cluster.fork 使用 send 和 on 傳遞消息
請描述js-bridge的實現(xiàn)原理- JS無法直接調(diào)用 native API
- 需要通過一些特定的“格式”來調(diào)用
-
JS Bridge的常見實現(xiàn)方式
- 注冊全局API
- URL Scheme
//?封裝?JS-bridge
const?sdk?=?{
?invoke(url,?data?=?{},?onSuccess,?onError)?{
???const?iframe?=?document.createElement('iframe')
???iframe.style.visibility?=?'hidden'
???document.body.appendChild(iframe)
???
???iframe.onload?=?()?=>?{
????const?content?=?iframe1.contentWindow.document.body.innerHTML
???}
?}
}
requestIdleCallback和request
由React fiber引起的關(guān)注
- 組建樹轉(zhuǎn)換為鏈表,可分段渲染
- 渲染時可以暫停,去執(zhí)行其他高優(yōu)任務(wù),空閑時再繼續(xù)渲染
- 如何判斷空閑? - requestIdleCallback
區(qū)別
- requestAnimationFrame 每次渲染完都會執(zhí)行,高優(yōu)
- requestIdleCallback 空閑時才執(zhí)行,低優(yōu)
<p>requestAnimationFrame</p>
<button?id="btn1">change</button>
<div?id="box"></div>
<script>
const?box?=?document.getElementById('box')
document.getElementById('btn1').addEventListener('click',?()?=>?{
?let?curWidth?=?100
?const?maxWidth?=?400
?function?addWidth()?{
??curWidth?=?curWidth?+?3
??box.style.width?=?`${curWidth}px`
??if?(curWidth?<?maxWidth)?{
???window.requestIdleCallback(addWidth)?//?時間不用自己控制
??}
?}
})
</script>
start
end
timeout
requestAnimationFrame
requestIdleCallback
window.onload?=?()?=>?{
?console.info('start')
?setTimeout(()?=>?{
??console.info('timeout')
?})
?//?宏任務(wù),順序交換也一樣
?//?高優(yōu)
?window.requestAnimationFrame(()?=>?{
??console.info('requestAnimationFrame')
?})
?//?低優(yōu)
?window.requestIdleCallback(()?=>?{
??console.info('requestIdleCallback')
?})
?console.info('end')
}
Vue每個生命周期都做了什么
-
beforeCreate 創(chuàng)建一個空白的Vue實例 data method 尚未被初始化,不可使用
-
created vue實例初始化完成,完成響應(yīng)式綁定 data method都已經(jīng)初始化完成,可調(diào)用 尚未開始渲染模板
-
beforeMount 編譯模版,調(diào)用render生成vdom 還沒有開始渲染DOM
$el null element沒有
-
mounted 完成DOM渲染 組件創(chuàng)建完成 開始由“創(chuàng)建階段”進入“運行階段”
-
beforeUpdate data發(fā)生變化之后 準備更新DOM(尚未更新DOM)
-
updated data發(fā)生變化,且DOM更新完成 (不要在updated中修改data,可能會導致死循環(huán))
-
beforeUnmount 組件進入銷毀階段(尚未銷毀,可正常使用) 可移除,解綁一些全局事件,自定義事件
-
unmounted 組件被銷毀了 所有子組件也都被銷毀了
-
keep-alive組件 onActivated 緩存組件被激活 onDeactivated 緩存組件被隱藏
<keep-alive>
?<Child1?v-if="num?===?1"></Child1>
?<Child2?v-else></Child2>
</keep-alive>
//?Child1?2
created()?{
?console.log()?//?keep-alive?中只創(chuàng)建
}
activated()?{}
deactivated()?{}
//?創(chuàng)建一次被緩存
child1?created
child1?activated
child2?created
child1?deactivated
child2?activated
child2?deactivated
child1?activated
-
vue什么時候操作DOM比較合適 mounted和updated都不能保證子組件全部掛載完成 使用$nextTick渲染DOM
只有nextTick操作DOM才是最安全的
$nextTick
mounted()?{
?this.$nextTick(function?()?{
??//?僅在整個視圖都被渲染之后才會運行的代碼
?})
}
- ajax 應(yīng)該在那個生命周期? 有兩個選擇:created 和 mounted 推薦:mounted
- vue3 Composition API 生命周期有何區(qū)別? 用setup代替了beforeCreate和created 使用Hooks函數(shù)的形式,如mounted改為onMounted()
import?{?onUpdated,?onMounted?}?from?'vue'
export?default?{
?setup()?{
??onMounted(()?=>?{
?
??})
??onUpdated(()?=>?{
??
??})
?}
}
Vue2和Vue3和React三者的diff算法有什么
介紹diff算法 diff算法很早就有
tree diff優(yōu)化 只比較同一層級,不跨級比較 tag 不同則刪掉重建(不再去比較內(nèi)部的細節(jié)) 子節(jié)點通過key區(qū)分(key的重要性)
vue3最長遞增子序列 vue2 雙端比較 React 僅右移
Vue-router的MemoryHistory是什么Hash, WebHistory, MemoryHistory( v4 之前叫做 abstract history)
移動端H5點擊有300ms延遲,該如何解決FastClick原理 監(jiān)聽touchend事件(touchstart touchend會先于click觸發(fā)) 使用自定義DOM事件模擬一個click事件 把默認的click事件(300ms之后觸發(fā))禁止掉
現(xiàn)代瀏覽器的改進
<head>
?<meta?charset="UTF-8">
?<meta?name="viewport"?content="width=device-width,?initial-scale=1.0">
?<meta?http-equiv="X-UA-Compatible"?content="ie=edge">
</head>
HTTP請求中token和cookie有什么區(qū)別-cookie
- cookie HTTP無狀態(tài),每次請求都要帶cookie,以幫助識別身份 服務(wù)端也可以向客戶端set-cookie,cookie大小限制4kb 默認有跨域限制:不可跨域共享、傳遞cookie
- withCredentials 前端設(shè)置跨域cookie共享 cookie本地存儲 HTML5之前cookie常被用于本地存儲 HTML5之后推薦使用localStorage和sessionStorage
- 現(xiàn)在瀏覽器開始禁止第三方cookie 和跨域限制不同。這里是:禁止網(wǎng)頁引入的第三方JS設(shè)置cookie 打擊第三方廣告,保護用戶隱私 新增屬性 SameSite: Strict/Lax/None;值可自己選擇
- 瀏覽器的Cookie新增加了一個SameSite屬性,用來防止CSRF攻擊和用戶追蹤
- cookie和session cookie用于登錄驗證,存儲用戶標識 session在服務(wù)端,存儲用戶詳細信息,和cookie信息一一對應(yīng) cookie和session是常見登錄驗證解決方案
- token vs cookie cookie是HTTP規(guī)范,而token是自定義傳遞 cookie會默認被瀏覽器存儲,而token需自己存儲 token默認沒有跨域限制
- jwt (json web token) 可以取代 cookie和session 前端發(fā)起登錄,后端驗證成功之后,返回一個加密的token 前端自行存儲這個token(其中包含了用戶信息,加密了) 以后訪問服務(wù)端接口,都帶著這個token,作為用戶信息
- cookie:HTTP標準;跨域限制;配合session使用
- token:無標準;無跨域限制;用于JWT
- session缺點 占用服務(wù)端內(nèi)存,硬件成本高 多進程,多服務(wù)器時,不好同步,需使用第三方緩存,如redis 默認有跨域限制
- session優(yōu)點 原理簡單,易于學習 用戶信息存儲在服務(wù)端,可快速禁某個用戶
- jwt 優(yōu)點 不占用服務(wù)端內(nèi)存 多進程,多服務(wù)器 不受影響 沒有跨域限制
- jwt 缺點 用戶信息存儲在客戶端,無法快速封禁某用戶 萬一服務(wù)端秘鑰被泄漏,則用戶信息全部丟失 token體積一般大于cookie,會增加請求的數(shù)據(jù)量
如有嚴格管理用戶信息的需求(保密,快速封禁)推薦session 如沒有特殊要求,則使用jwt
如何實現(xiàn)SSO單點登錄- 基于cookie cookie默認不可跨域共享,但有些情況下可設(shè)置為共享 主域名相同,如www.baidu.com image.baidu.com 設(shè)置cookie domain為主域名,即可共享cookie
- sso 主域名完全不同,則cookie無法共享 可使用sso技術(shù)方案
網(wǎng)絡(luò)協(xié)議 HTT P協(xié)議在應(yīng)用層 TCP UDP 協(xié)議再傳輸層 嚴格來說,應(yīng)該拿TCP和UDP進行比較
OSI的體系結(jié)構(gòu)
7.?應(yīng)用層
6.?表示層
5.?會話層
4.?運輸層
3.?網(wǎng)絡(luò)層
2.?數(shù)據(jù)鏈路層
1.?物理層
TCP/IP的體系結(jié)構(gòu)
1.?應(yīng)用層(各種應(yīng)用層協(xié)議,如DNS,HTTP,SMTP等)
2.?運輸層(TCP或UDP)
3.?網(wǎng)際層(IP)
4.?網(wǎng)絡(luò)接口層
TCP協(xié)議
- 有連接(三次握手)
- 有斷開(四次揮手)
- 穩(wěn)定傳輸
UDP協(xié)議
- 無連接,無斷開
- 不穩(wěn)定傳輸,但效率高
- 如視頻會議,語音通話
HTTP是應(yīng)用層,TCP UDP是傳輸層 TCP有連接,有斷開,穩(wěn)定傳輸 UDP無連接,無斷開,不穩(wěn)定傳輸,但效率高
HTTP協(xié)議1.0和1.1和2.0有什么區(qū)別- HTTP1.0 棄用 最基礎(chǔ)的HTTP協(xié)議 支持基本的GET,POST的方法
- HTTP1.1 緩存策略 cache-control E-tag等 支持長連接Connection: keep-alive,一次TCP連接多次請求 斷點續(xù)傳,狀態(tài)碼 206 支持新的方法PUT DELETE等,可用于Restful API
- HTTP2.0 可壓縮header,減少體積 多路復用,一次TCP連接中可以多個HTTP并行請求 服務(wù)端推送
HTTP S 加密傳輸 HTTP明文傳輸 HTTP S 加密傳輸 HTTP+TLS/SSL
script標簽的defer和async有什么區(qū)別
<script src="xxx.js" async 或 defer></script>
無:HTML暫停解析,下載JS,執(zhí)行JS,再繼續(xù)解析HTMl defer:HTML繼續(xù)解析,并行下載JS,HTML解析完再執(zhí)行JS async: HTML繼續(xù)解析,并行下載JS,執(zhí)行JS,再解析HTM L
prefetch和dns-prefetch分別preload和prefetch preload資源在當前頁面使用,會優(yōu)先加載 prefetch資源在未來頁面使用,空閑時加載
<head>
<link?rel="preload"?href="style.css"?as="style">
<link?rel="preload"?href="main.js"?as="script">
<link?rel="prefetch"?href="other.js"?as="script">
<link?rel="stylesheet"?href="style.css">
</head>
<body>
<script?src="main.js"?defer></script>
</body>
dns-prefetch 和 preconnect dns-prefetch即DNS預查詢 preconnect即DNS預連接
多個域名時,當前已經(jīng)解析完,預查詢,預連接
<link?rel="dns-prefetch"?href="域名">
<link?rel="dns-preconnect"?href=""?crossorigin></link>
prefetch 是資源預獲?。ê蚿reload相關(guān)) dns-prefetch 是DNS預查詢(和preconnect相關(guān))
前端攻擊手段有哪些,該如何預防- xss Cross Site Script 跨站腳本攻擊 手段:黑客將JS代碼插入到網(wǎng)頁內(nèi)容中,渲染時執(zhí)行JS代碼 預防:特殊字符替換(前端或者后端)
const newStr = str.replaceAll('<', '<').replaceAll('>', '>')
- Vue React 默認屏蔽xss攻擊 除了用 vue v-html react dangerouslySetInnerHTML
- CSRF Cross Site Request Forgery 跨站請求偽造 手段:黑客誘導用戶去訪問另一個網(wǎng)站的接口,偽造請求 預防:嚴格的跨站限制 + 驗證碼機制
- CSRF詳細過程 用戶登錄了A網(wǎng)站,有了cookie 黑客誘導用戶到B網(wǎng)站,并發(fā)起A網(wǎng)站的請求 A網(wǎng)站的API發(fā)現(xiàn)有cookie,認為是用戶自己操作的
- CSRF預防手段 嚴格的跨域請求限制,如判斷referrer(請求來源) 為cookie設(shè)置SameSite,禁止跨域傳遞cookie 關(guān)鍵接口使用短信驗證碼
- 點擊劫持 Click Jacking 手段:誘導界面上蒙一個通明的iframe,誘導用戶點擊 預防:讓iframe不能跨域加載
- 點擊劫持 預防
if?(top.location.hostname?!==?self.location.hostname)?{
?alert("您正在訪問不安全的頁面,即將跳轉(zhuǎn)到安全頁面!“)
?top.location.href?=?self.location.href
}
hearders
X-Frame-Options:?sameorigin
- DDoS DIstribute denial-of-service分布式拒絕服務(wù) 手段:分布式的,大規(guī)模的流量訪問,使服務(wù)器癱瘓 預防:軟件層不好做,需硬件預防(如阿里云WAF)
- SQL注入 手段:黑客提交內(nèi)容時寫入SQL語句,破壞數(shù)據(jù)庫 預防:處理輸入的內(nèi)容,替換特殊字符
- xss, ddos, csrf, sql注入, 點擊劫持
- WebSocket 支持端對端通訊 可以由client發(fā)起,也可以由server發(fā)起 用于:消息通知,直播間討論區(qū),聊天室,協(xié)同編輯
npm?init?-y
npm?install?ws?--save
npm?install?nodemon?--save-dev
const?{?WebSocketServer?}?=?require('ws')
const?wsServer?=?new?WebSocketServer({?port:?3000?})
wsServer.on('connection',?ws?=>?{
?console.info('connected')
?
?ws.on('message',?msg?=>?{
??console.info('收到了信息',?msg.toString)
??
??//?服務(wù)端向客戶端發(fā)送信息
??setTimeout(()?=>?{
???ws.send('服務(wù)端已經(jīng)收到了信息:'?+?msg.toString())
??},?2000)
?})
})
<button?id="btn-send">發(fā)送消息</button>
const?ws?=?new?WebSocket('ws://127..0.0.1:3000')
ws.onopen?=?()?=>?{
?console.info('opened')
?ws.send('client?opened')
}
ws.onmessage?=?event?=?{
?console.info('收到了信息',?event.data)
}
const?btnSend?=?document.getElementById('btn-send')
btnSend.addEventListener('click',?()?=>?{
?console.info('clicked')
?ws.send('當前時間'?+?Date.now())
})
WebSocket 連接過程 先發(fā)起一個 HTTP 請求 成功之后再升級到 WebSocket 協(xié)議,再通訊
WebSocket 和 HTTP 區(qū)別 WebSocket 協(xié)議名是 ws:// , 可雙端發(fā)起請求 WebSocket 沒有跨域限制 通過send和onmessage通訊(HTTP通過req和res)
ws可升級為?wss?(像https)
import?{?createServer?}?from?'https'
import?{?readFileSync?}?from?'fs'
import?{?WebSocketServer?}?from?'ws'
const?server?=?createServer?({
?cert:?readFileSync('/path/to/cert.pem'),
?key:?readFileSync('/path/to/key.pem')
})
const?wss?=?new?WebSocketServer({?server?})
擴展:實際項目推薦socket.io,?api更簡潔
io.on('connection',?socket?=>?{
?socket.emit('request',?/*...*/)
?io.emit('broadcast',?...)
?socket.on('reply',?()?=>?{})
})
const?{?WebSocketServer?}?=?require('ws')
const?wsServer?=?new?WebSocketServer({?port:?3000?})
const?list?=?new?Set()
wsServer.on('connection',?curWs?=>?{
?console.info('connected')
?list.add(curWs)
?curWs.on('message',?msg?=>?{
??console.info('received?message',?msg.toString())
??
??//?傳遞給其他客戶端
??list.forEach(ws?=>?{
???if?(ws?===?curWs)?return
???ws.send(msg.toString())
??})
?})
})
WebSocket和HTTP長輪詢的區(qū)別
- 區(qū)別 HTTP長輪詢:客戶端發(fā)起請求,服務(wù)端等待,不會立即返回 WebSocket:客戶端可發(fā)起請求,服務(wù)端也可發(fā)起請求
- 注意: HTTP長輪詢,需處理timeout,即 timeout 之后重新發(fā)請求
步驟: 網(wǎng)絡(luò)請求: DNS查詢(得到IP),建立TCP連接(三次握手) 瀏覽器發(fā)起HTTP請求 收到請求響應(yīng),得到HTML源代碼
繼續(xù)請求靜態(tài)資源 解析HTML過程中,遇到靜態(tài)資源還會繼續(xù)發(fā)起網(wǎng)絡(luò)請求 JS CSS 圖片 視頻等 注意:靜態(tài)資源可能有強緩存,此時不必請求
解析:字符串 -> 結(jié)構(gòu)化數(shù)據(jù) HTML構(gòu)建DOM樹 CSS構(gòu)建CSSOM樹(style tree) 兩者結(jié)合,形成 render tree
渲染
解析過程很復雜
CSS 可能來自 <style> <link>JS 可能內(nèi)嵌,或外鏈,還有 defer async 邏輯
img 可能內(nèi)嵌(base64),可能外鏈
優(yōu)化解析
CSS放在<head>中,不要異步加載CSS
JS放在<body>最下面(或合理使用 defer async)<img> 提前定義 width height
渲染:Render Tree 繪制到頁面 計算各個DOM的尺寸,定位,最后繪制到頁面 遇到JS可能會執(zhí)行(參考 defer async) 異步CSS,圖片加載,可能會觸發(fā)重新渲染
網(wǎng)絡(luò)請求:DNS解析,HTTP請求 解析:DOM樹,CSSOM樹,Render Tree 渲染:計算,繪制,同時執(zhí)行JS
網(wǎng)頁重繪repaint和重排reflow有什么- 網(wǎng)頁動畫
- Modal Dialog 彈窗
- 增加/刪除一個元素,顯示/隱藏一個元素
重繪 repaint
- 元素外觀改變,如顏色,背景色
- 但元素的尺寸,定位不變,不會影響其他元素的位置
重排 reflow
- 重新計算尺寸和布局,可能會影響其他元素的位置
- 如元素高度增加,可能會使相鄰元素位置下移
區(qū)別
- 重排比重繪要影響更大,消耗也更大
- 所以,要盡量避免無意義的重排
減少重排的方法 1/2
- 集中修改樣式,或直接切換 css class
- 修改之前先設(shè)置 display: none, 脫離文檔流
- 使用bfc特性,不影響其他元素位置
減少重排的方法 2/2
- 頻繁觸發(fā)(resize scroll)使用節(jié)流和防抖
- 使用 createDocumentFragment 批量操作 DOM
- 優(yōu)化動畫,使用 CSS3 和 requestAnimationFrame
擴展:BFC
- Block Format Context 塊級格式化上下文
- 內(nèi)部的元素無論如何改動,都不會影響其他元素的位置
觸發(fā) BFC 的條件 1/2
-
根節(jié)點
<html> - float: left/right
- overflow: auto/scroll/hidden;
觸發(fā) BFC 的條件 2/2
- display: inline-block / table / table-row / table-cell;
- display: flex/grid; 的直接子元素
- position: absolute / fixed;
使用 WebSocket
- 無跨域限制
- 需要服務(wù)端支持,成本高
通過 localStorage 通訊
- 同域的 A 和 B 兩個頁面
- A頁面設(shè)置 localStorage
- B頁面可監(jiān)聽到 localStorage 值的修改
通過 SharedWorker 通訊
- SharedWorker 是 WebWorker 的一種
- WebWorker 可開啟子進程執(zhí)行 JS,但不能操作 DOM
- SharedWorker 可單獨開啟一個進程,用于同域頁面通訊
