新出爐大廠面試題100道整理(原題 + 精講 1.2萬字)(一)
大家喜歡的可以給筆者點個贊,花費了很長時間整理,這些面試題更適合中級前端,和需要進(jìn)階的高級前端的小伙伴,查缺補漏,為金九銀十保駕護(hù)航??????
html篇

. 問題一:Meta標(biāo)簽常用屬性值的寫法和作用
答:
meta 標(biāo)簽提供關(guān)于HTML文檔的元數(shù)據(jù)。元數(shù)據(jù)不會顯示在頁面上,但是對于機(jī)器是可讀的。它可用于瀏覽器(如何顯示內(nèi)容或重新加載頁面),搜索引擎(關(guān)鍵詞),或其他 web 服務(wù)。
必要屬性
name:屬性名
content:屬性內(nèi)容
charset: charset為HTML5中新增的,用來聲明字符編碼;
http-equiv:屬性在HTML4中有很多值,在HTML5中只有refresh、default-style、content-type可用
name的值和說明
application name ? ?當(dāng)前頁所屬Web應(yīng)用系統(tǒng)的名稱
keywords ? ? ? ? ? ?描述網(wǎng)站內(nèi)容的關(guān)鍵詞,以逗號隔開,用于SEO搜索
description ? ? ? ? ? ?當(dāng)前頁的說明
author ? ? ? ? ? ? ? ?當(dāng)前頁的作者名
copyright ? ? ? ? ? ?版權(quán)信息
renderer ? ? ? ? ? ?renderer是為雙核瀏覽器準(zhǔn)備的,用于指定雙核瀏覽器默認(rèn)以何種方式渲染頁面
viewreport ? ? ? ? ? ?它提供有關(guān)視口初始大小的提示,僅供移動設(shè)備使用
viewreport
meta標(biāo)簽的name屬性值為viewreport時的視口的大小
1.content內(nèi)容為空時,默認(rèn)視口寬度為980
2.content設(shè)置width,不設(shè)置initail-scale時,視口寬度為設(shè)置的width值
3.content不設(shè)置width,只設(shè)置initail-scale時,是可以根據(jù)initail-scale的值計算出視口的寬度
initail-scale = 屏幕寬度 / 視口寬度
4.content同時設(shè)置width和initail-scale時,視口寬度為width的值,頁面顯示按照initail-scale比率進(jìn)行縮放
5.一般都是進(jìn)行如下設(shè)置,來實現(xiàn)視口寬等于設(shè)備寬,布局完成后屏幕顯示也不進(jìn)行縮放
<meta?name="viewport"?content="width=device-width,?initial-scale=1.0">
聲明字符編碼
charset屬性為HTML5新增的屬性,用于聲明字符編碼,以下兩種寫法效果一樣
<meta?charset="utf-8">?//HTML5
模擬http標(biāo)頭字段
http-equiv屬性與content屬性結(jié)合使用, http-equiv屬性為指定所要模擬的標(biāo)頭字段的名稱,content屬性用來提供值。
<meta?http-equiv="參數(shù)"?content="具體的描述">
content-Type 聲明網(wǎng)頁字符編碼:
<meta?http-equiv="content-Type"?content="text/html?charset=UTF-8">
refresh 指定一個時間間隔(以秒為單位),在此時間過去之后從服務(wù)器重新載入當(dāng)前頁面,也可以另外指定一個頁面.
<meta?http-equiv="refresh"?content="2;URL=http://www.baidu.com">//2秒后在當(dāng)前頁跳轉(zhuǎn)到百度
X-UA-Compatible 瀏覽器采取何種版本渲染當(dāng)前頁面
<meta?http-equiv="X-UA-Compatible"?content="IE=edge,chrome=1">?//指定IE和Chrome使用最新版本渲染當(dāng)前頁面
expires 用于設(shè)定網(wǎng)頁的到期時間,過期后網(wǎng)頁必須到服務(wù)器上重新傳輸
<meta?http-equiv="expires"?content="Sunday?22?July?2016?16:30?GMT">
catch-control 用于指定所有緩存機(jī)制在整個請求/響應(yīng)鏈中必須服從的指令
<meta?http-equiv="cache-control"?content="no-cache">
js篇

. 問題1: 給對象加上iterator接口,使之能被 for of遍歷
ES6 規(guī)定,默認(rèn)的 Iterator 接口部署在數(shù)據(jù)結(jié)構(gòu)的 Symbol.iterator 屬性,或者說,一個數(shù)據(jù)結(jié)構(gòu)只要具有Symbol.iterator屬性,就可以認(rèn)為是“可遍歷的”(iterable)。Symbol.iterator 屬性本身是一個函數(shù),就是當(dāng)前數(shù)據(jù)結(jié)構(gòu)默認(rèn)的遍歷器生成函數(shù)
因為object 沒有 Symbol.iterator 屬性,所以不能被 for of 遍歷。
const?text?=?{
???a:?1,
???b:?2,
???c:3
}
for(let?i?of?text){
????console.log(i)?//報錯:Uncaught TypeError: text is not iterable
}
所以,object想要被 for … of遍歷 ,必須在原來的基礎(chǔ)上加上 Symbol.iterator 接口屬性。
const?text?=?{
???a:?1,
???b:?2,
???c:3
}
text[Symbol.iterator]?=?function?(){
????const?_this?=?this
????return?{
????????index:-1,
????????next(){
????????????const?arr?=?Object.keys(?_this?)
????????????if(this.index?????????????????this.index++
????????????????return?{
????????????????????value:_this[arr[this.index]],
????????????????????done:false
????????????????}
????????????}else{
????????????????return?{
????????????????????value:undefined,
????????????????????done:true
????????????????}
????????????}
????????}
????}
}
for(let?i?of?text){
????console.log(i)?//?1?2?3?undefined
}
. 問題2:統(tǒng)計按鈕一秒鐘點擊次數(shù)?
這道題是變相考對防抖函數(shù)+閉包的理解
<script>
const?button?=?document.getElementById('button')
function?debounce(fn,?time)?{
????let?number?=?0?,?timer??=?null
????return?function(...arg)?{?
????????fn.apply(this,?arg)
????????number++
????????if?(timer)?return
????????timer?=?setTimeout(()?=>?{
???????????console.log(?'點擊'+?number?+?'次'?)
???????????number?=?0
???????????timer?=?null
????????},?time)
????}
}
button.onclick?=?debounce(()=>{
????console.log(1111)
},1000)
script>
效果如下


. 問題3:單頁面應(yīng)用路由的原理
無論我們用vue還是react構(gòu)建單頁面應(yīng)用,都離不開路由的概念,路由跳轉(zhuǎn)監(jiān)聽url改變,根據(jù)路由的改變來決定渲染的頁面。
hash模式
改變路由
const?path?=?"home"
window.location.hash?=?path
監(jiān)聽路由
window.addEventListener('hashchange',function(e){
??//路由發(fā)生改變時切換渲染組件...
});
histroy模式
改變路由
var?path?=?"home"
history.pushState(null,null,'?='+path);
監(jiān)聽路由
window.addEventListener('popstate',function(e){
???//路由發(fā)生改變時切換渲染組件...
})
. 問題4:如下打印結(jié)果
執(zhí)行如下代碼,回發(fā)生什么?
function?fn1(){
????console.log(a)
}
var?a?=?1
function?fn2(){
????console.log(a)
????let?a?=?2;
????console.log(a)
????fn1()
}
fn2()
結(jié)果://Uncaught ReferenceError: Cannot access 'a' before initialization
let 暫時性死區(qū)
let/const是使用區(qū)塊作用域;var是使用函數(shù)作用域
在let/const聲明之前就訪問對應(yīng)的變量與常量,會拋出ReferenceError錯誤;但在var聲明之前就訪問對應(yīng)的變量,則會得到undefined
console.log(a)?//?undefined
console.log(b)?//?causes?ReferenceError:?b?is?not?defined
var?a?=?1
let?b?=?2?
. 問題5:apply和call,bind的區(qū)別
apply,call和bind都是 用來改變this的指向
apply和call會讓當(dāng)前函數(shù)立即執(zhí)行,而bind會返回一個函數(shù),后續(xù)需要的時候再調(diào)用執(zhí)行
apply,call的區(qū)別實參數(shù)不同
const?foot?={
????apple:'蘋果'
}
function?eat(a,b){
???console.log(a,b,this)
}
const?bindEat?=?eat.bind(foot)
eat(1,2)?//?1?2?Window
bindEat(1,2)?//1?2??foot?
eat.call(foot,1,2)?//1?2??foot
/*?apply參數(shù)已數(shù)組形式傳遞??*/
eat.apply(foot,[1,2])?//1?2??foot
. 問題6:開發(fā)過程中遇到的內(nèi)存泄露情況,如何解決的?
javascript內(nèi)存泄漏幾種情況
1 意外的全局變量
function?foo(arg)?{?
????bar?=?"this?is?a?hidden?global?variable";?
}
另一種意外的全局變量可能由 this 創(chuàng)建:
function?foo()?{?
????this.variable?=?"potential?accidental?global";?
}?
//?Foo?調(diào)用自己,this?指向了全局對象(window)?
//?而不是?undefined?
foo();
盡管我們討論了一些意外的全局變量,但是仍有一些明確的全局變量產(chǎn)生的垃圾。它們被定義為不可回收(除非定義為空或重新分配)。尤其當(dāng)全局變量用于 臨時存儲和處理大量信息時,需要多加小心。如果必須使用全局變量存儲大量數(shù)據(jù)時,確保用完以后把它設(shè)置為 null 或者重新定義。與全局變量相關(guān)的增加內(nèi)存消耗的一個主因是緩存。緩存數(shù)據(jù)是為了重用,緩存必須有一個大小上限才有用。高內(nèi)存消耗導(dǎo)致緩存突破上限,因為緩 存內(nèi)容無法被回收。
2被遺忘的計時器或回調(diào)函數(shù)
var?someResource?=?getData();?
setInterval(function()?{?
????var?node?=?document.getElementById('Node');?
????if(node)?{?
????????//?處理?node?和?someResource?
????????node.innerHTML?=?JSON.stringify(someResource));?
????}?
},?1000);
與節(jié)點或數(shù)據(jù)關(guān)聯(lián)的計時器不再需要,node 對象可以刪除,整個回調(diào)函數(shù)也不需要了??墒牵嫊r器回調(diào)函數(shù)仍然沒被回收(計時器停止才會被回收)。同時,someResource 如果存儲了大量的數(shù)據(jù),也是無法被回收的。
3脫離 DOM 的引用
var?elements?=?{?
????button:?document.getElementById('button'),?
????image:?document.getElementById('image'),?
????text:?document.getElementById('text')?
};?
function?doStuff()?{?
????image.src?=?'http://some.url/image';?
????button.click();?
????console.log(text.innerHTML);?
????//?更多邏輯?
}?
function?removeButton()?{?
????//?按鈕是?body?的后代元素?
????document.body.removeChild(document.getElementById('button'));?
????//?此時,仍舊存在一個全局的?#button?的引用?
????// elements 字典。button 元素仍舊在內(nèi)存中,不能被 GC 回收。?
}
4 閉包的錯誤使用
var?theThing?=?null;?
var?replaceThing?=?function?()?{?
??var?originalThing?=?theThing;?
??var?unused?=?function?()?{?
????if?(originalThing)?
??????console.log("hi");?
??};?
??theThing?=?{?
????longStr:?new?Array(1000000).join('*'),?
????someMethod:?function?()?{?
??????console.log(someMessage);?
????}?
??};?
};?
setInterval(replaceThing,?1000);?
每次調(diào)用 replaceThing ,theThing 得到一個包含一個大數(shù)組和一個新閉包(someMethod)的新對象。同時,變量 unused 是一個引用 originalThing 的閉包(先前的 replaceThing 又調(diào)用了 theThing )。思緒混亂了嗎?最重要的事情是,閉包的作用域一旦創(chuàng)建,它們有同樣的父級作用域,作用域是共享的。someMethod 可以通過 theThing 使用,someMethod 與 unused 分享閉包作用域,盡管 unused從未使用,它引用的 originalThing 迫使它保留在內(nèi)存中(防止被回收)。當(dāng)這段代碼反復(fù)運行,就會看到內(nèi)存占用不斷上升,垃圾回收器(GC)并無法降低內(nèi)存占用。本質(zhì)上,閉包的鏈表已經(jīng)創(chuàng)建,每一個閉包作用域攜帶一個指向大數(shù)組的間接的引用,造成嚴(yán)重的內(nèi)存泄露。
解決內(nèi)存泄漏方式
1 使用嚴(yán)格模式,合理聲明變量。使用嚴(yán)格模式可以避免第一種情況的發(fā)生。
2 及時清理定時器,延時器,對于不需要的定時器和延時器,一定要及時清除。
3 合理應(yīng)用閉包,合理的應(yīng)用閉包,避免閉包函數(shù)反復(fù)執(zhí)行導(dǎo)致內(nèi)存無法及時釋放。
. 問題7:介紹一下proto和prototype
proto和prototype 是我們在平時工作中容易忽略的問題,對象原型 和 原型鏈 的概念也容易混淆。
proto是每個對象都有的屬性 ,在JS里,萬物皆對象。方法(Function)是對象,方法的原型(Function.prototype)是對象。因此,它們都會具有對象共有的特點。即:對象具有屬性proto,可稱為隱式原型,一個對象的隱式原型指向構(gòu)造該對象的構(gòu)造函數(shù)的原型,這也保證了實例能夠訪問在構(gòu)造函數(shù)原型中定義的屬性和方法。
prototypee是函數(shù)才有的屬性,方法(Function)方法這個特殊的對象,除了和其他對象一樣有上述proto屬性之外,還有自己特有的屬性——原型屬性(prototype),這個屬性是一個指針,指向一個對象,這個對象的用途就是包含所有實例共享的屬性和方法就是該實例的proto。原型對象也有一個屬性,叫做constructor,這個屬性包含了一個指針,指回原構(gòu)造函數(shù)。
?let?a?=?function?(){
?}
?a.prototype.eat?=?function(){?console.log(111)?}
?const?na?=?new?a()
?console.log(?na.__proto__?===?a.prototype?)?//?ture
. 問題8:說一下arguments對象
在函數(shù)調(diào)用的時候,瀏覽器每次都會傳遞進(jìn)arguments對象,arguments 對象實際上是所在函數(shù)的一個內(nèi)置類數(shù)組對象,arguments對象不是一個 Array 。它類似于Array,但除了length屬性和索引元素之外沒有任何Array屬性。例如,它沒有 pop 方法。但是它可以被轉(zhuǎn)換為一個真正的Array.typeof參數(shù)返回 'object'。
屬性
arguments.callee指向參數(shù)所屬的當(dāng)前執(zhí)行的函數(shù)。指向調(diào)用當(dāng)前函數(shù)的函數(shù)。
arguments.length傳遞給函數(shù)的參數(shù)數(shù)量。
arguments[@@iterator]返回一個新的Array 迭代器 對象,該對象包含參數(shù)中每個索引的值。
. 問題9:怎么實現(xiàn)一個隊列的數(shù)據(jù)結(jié)構(gòu)?
介紹
隊列也是一種線性表。它允許在表的一端插入數(shù)據(jù),在另一端刪除元素。插入元素的這一端稱之為隊尾。刪除元素的這一端我們稱之為隊首。
特性
1 在隊尾插入元素,在隊首刪除元素。
2 FIFO(先進(jìn)先出),就向排隊取票一樣。
簡單js實現(xiàn)
class?Queue?{
????constructor(){
????????this.queue?=?[]
????}
????/*?進(jìn)入隊列?*/
????enqueue(item){
????????this.queue.push(item)
????}
????/*?移除隊列?*/
????dequeue(){
????????this.queue.shift()
????}
????/*?獲取隊列長度?*/
????size(){
????????return?this.queue.length
????}
????/*?判斷是否為空?*/
}
css

. 問題一:什么是vw+vh布局?有哪些有優(yōu)點?有哪些缺陷?
解答:
1什么是什么是vw/vh
css3中引入了一個新的單位vw/vh,與視圖窗口有關(guān),vw表示相對于視圖窗口的寬度,vh表示相對于視圖窗口高度,除了vw和vh外,還有vmin和vmax兩個相關(guān)的單位。各個單位具體的含義如下:
單位含義vw相對于視窗的寬度,視窗寬度是100vwvh相對于視窗的高度,視窗高度是100vhvminvw和vh中的較小值vmaxvw和vh中的較大值;
這里我們發(fā)現(xiàn)視窗寬高都是100vw/100vh,那么vw或者vh,下簡稱vw,很類似百分比單位。vw和%的區(qū)別為:
單位含義%大部分相對于祖先元素,也有相對于自身的情況比如(border-radius、translate等)vw/vh相對于視窗的尺寸
從對比中我們可以發(fā)現(xiàn),vw單位與百分比類似,單確有區(qū)別,前面我們介紹了百分比單位的換算困難,這里的vw更像"理想的百分比單位"。任意層級元素,在使用vw單位的情況下,1vw都等于視圖寬度的百分之一。
2vw單位換算
同樣的,如果要將px換算成vw單位,很簡單,只要確定視圖的窗口大小(布局視口),如果我們將布局視口設(shè)置成分辨率大小,比如對于iphone6/7 375*667的分辨率,那么px可以通過如下方式換算成vw:
1px?=?(1/375)*100?vw
3缺陷
1 絕大多數(shù)的瀏覽器支持vw單位,但是ie9-11不支持vmin和vmax,考慮到vmin和vmax單位不常用,vw單位在絕大部分高版本瀏覽器內(nèi)的支持性很好,但是opera瀏覽器整體不支持vw單位,如果需要兼容opera瀏覽器的布局,不推薦使用vw。
2 由于是相對手機(jī)窗口,針對不同的手機(jī)視圖大小不同,所以需要對單位進(jìn)行換算處理。
. 問題二:什么是rem布局?rem布局的缺陷。
答:
rem布局
rem是一個靈活的、可擴(kuò)展的單位,由瀏覽器轉(zhuǎn)化像素并顯示。與em單位不同,rem單位無論嵌套層級如何,都只相對于瀏覽器的根元素(HTML元素)的font-size。默認(rèn)情況下,html元素的font-size為16px,所以:
1 rem = 12px
為了計算方便,通??梢詫tml的font-size設(shè)置成:
html{?font-size:?62.5%?}
這種情況下:
1 rem = 10px
rem單位都是相對于根元素html的font-size來決定大小的,根元素的font-size相當(dāng)于提供了一個基準(zhǔn),當(dāng)頁面的size發(fā)生變化時,只需要改變font-size的值,那么以rem為固定單位的元素的大小也會發(fā)生響應(yīng)的變化。因此,如果通過rem來實現(xiàn)響應(yīng)式的布局,只需要根據(jù)視圖容器的大小,動態(tài)的改變font-size即可。
rem布局缺陷
1 在響應(yīng)式布局中,必須通過js來動態(tài)控制根元素font-size的大小。
1 css樣式和js代碼有一定的耦合性。且必須將改變font-size的代碼放在css樣式之前
. 問題三:怎么讓Chrome支持小于12px 的文字
谷歌瀏覽器默認(rèn)最小字體為12px,若想讓chorme支持12px字體,只需要用css3屬性transform就可以
??例子:在谷歌瀏覽器寫出10px字體
transform:scale(0.5);
font-size:20px;
搞定收工~~~
. 問題四:透明度opacity和rgba的區(qū)別
opacity
取值在0到1之間,0表示完全透明,1表示完全不透明。
.box{opacity:?0.5;}
rgba
rgba中的R表示紅色,G表示綠色,B表示藍(lán)色,三種顏色的值都可以是正整數(shù)或百分?jǐn)?shù)。A表示Alpha透明度。取值0~1之間,類似opacity。
.box{background:?rgba(255,0,0,0.5);}
rgba和opacity的區(qū)別
rgba()和opacity都能實現(xiàn)透明效果,但最大的不同是opacity作用于元素,以及元素內(nèi)的所有內(nèi)容的透明度,而rgba()只作用于元素的顏色或其背景色。
. 問題五:position的屬性值有哪些?
absolute ? ?
生成絕對定位的元素,相對于 static 定位以外的第一個父元素進(jìn)行定位。
元素的位置通過 "left", "top", "right" 以及 "bottom" 屬性進(jìn)行規(guī)定。
fixed ? ?
生成絕對定位的元素,相對于瀏覽器窗口進(jìn)行定位。
元素的位置通過 "left", "top", "right" 以及 "bottom" 屬性進(jìn)行規(guī)定。
relative ? ?
生成相對定位的元素,相對于其正常位置進(jìn)行定位。因此,"left:20" 會向元素的 LEFT 位置添加 20 像素。
static
默認(rèn)值。沒有定位,元素出現(xiàn)在正常的流中(忽略 top, bottom, left, right 或者 z-index 聲明)。
inherit ? ?
規(guī)定應(yīng)該從父元素繼承 position 屬性的值。
. 問題六:display的屬性值有哪些?
none(元素不會被顯示);
block(元素將顯示為塊級元素,元素前后會帶有換行符);
inline(元素會被顯示為內(nèi)聯(lián)元素,元素前后沒有換行符);
inline-block(行內(nèi)塊元素。CSS2.1新增的值);
table(元素會作為塊級表格來顯示,類似table,表格前后帶有換行符);
table-row(元素會作為一個表格行顯示,類似tr);
table-cell(元素會作為一個表格單元格顯示,類似td和th)
flex 彈性盒結(jié)構(gòu)
gird 網(wǎng)格結(jié)構(gòu)
. 問題七:垂直水平居中的方案有哪些(盡量說全面一些)
position absolute 50% + 負(fù)margin -50%
.container?{
????position:?relative;
}
.box?{
????position:?absolute;
????top:?50%;
????left:?50%;
????margin-top:?-50%;
????margin-left:?-50%;
}
position absolute ?50% + transform -50%
.container?{
????position:?relative;
}
.box?{
????position:?absolute;
????top:?50%;
????left:?50%;
????transform:?translate(-50%,?-50%);
}
css-table
/*?此處引用上面的公共代碼?*/
.container?{
????display:?table-cell;
????text-align:?center;
????vertical-align:?middle;
}
.box?{
????display:?inline-block;
}
flex
/*?此處引用上面的公共代碼?*/
.container?{
????display:?flex;
????justify-content:?center;
????align-items:?center;
}
.box-center?{
????text-align:?center;
}
grid
/*?此處引用上面的公共代碼?*/
.container?{
????display:?grid;
????justify-items:?center;
????align-items:?center;
}
.box-center?{
????text-align:?center;
}
vue 篇

. 問題1:vue各個生命周期及其作用
1.初始化
beforeCreate:大vue已經(jīng)初始化,只是數(shù)據(jù)初始化與事件系統(tǒng)構(gòu)建尚未形成,不能獲取DOM節(jié)點(沒有data,沒有el)
使用場景:因為此時data和methods都拿不到,所以通常在實例以外使用。
created:實例已經(jīng)創(chuàng)建,仍然不能獲取DOM節(jié)點(有data,沒有el)
使用場景:模板渲染成html前調(diào)用,此時可以獲取data和methods, 可以初始化進(jìn)行數(shù)據(jù)請求,得到渲染數(shù)據(jù),,異步操作可以放在這里
2 掛載
beforeMount是個過渡階段,此時依然獲取不到具體的DOM節(jié)點,但是vue掛載的根節(jié)點已經(jīng)創(chuàng)建(有data,有el)
mounted:組件掛載完成,數(shù)據(jù)和DOM都已經(jīng)被渲染出來了
使用場景:模板渲染成html后調(diào)用,通常是初始化頁面完成后再對數(shù)據(jù)和DOM做一些操作,需要操作DOM的方法可以放在這里
3.更新
beforeUpdate:檢測到數(shù)據(jù)更新時,但在DOM更新前執(zhí)行
updated:更新結(jié)束后執(zhí)行
使用場景:需要對數(shù)據(jù)更新做統(tǒng)一處理的;如果需要區(qū)分不同的數(shù)據(jù)更新操作可以使用$nextTick
4.銷毀
beforeDestroy:當(dāng)要銷毀vue實例時,在銷毀前執(zhí)行
destroyed:銷毀vue實例時執(zhí)行
父子組件mounted和destroyed順序
beforeMount執(zhí)行順序 先父后子
mounted執(zhí)行順序,先子后父
beforeDestroy 執(zhí)行順序,先父后子
destroyed 執(zhí)行順序 , 先子后父
. 問題2:vue3的雙向綁定原理,與vue2.0比起來有那些優(yōu)勢?
vue3.0 的數(shù)據(jù)綁定原理 proxy 對象
vue3.0 于 Proxy 的 observer 實現(xiàn), 代替了Vue 2 系列中基于 Object.defineProperty 做為響應(yīng)式原理
感興趣的同學(xué)可以看一下筆者的vue3.0響應(yīng)式原理詳解
傳送門:vue3.0 響應(yīng)式原理(超詳細(xì))
優(yōu)勢:
1 對屬性的添加、刪除動作的監(jiān)測;
2 對數(shù)組基于下標(biāo)的修改、對于 .length 修改的監(jiān)測;
3 對 Map、Set、WeakMap 和 WeakSet 的支持;;
4 vue3 對依賴收集用的是weaMap,WeakSet,保持了對鍵名所引用的對象的弱引用,即垃圾回收機(jī)制不將該引用考慮在內(nèi),一旦不再需要,WeakMap 里面的鍵名對象和所對應(yīng)的鍵值對會自動消失,不用手動刪除引用。
5 vue2.0 初始化data時候,對于對象等引用數(shù)據(jù)類型,進(jìn)行了遞歸處理,也就是對于一些掛載在data上屬性,但是并沒有用到的屬性,也同樣做了響應(yīng)式處理,而vue3.0之后訪問到父級屬性之后,在進(jìn)行下一層track,也就是說初始化data時候無需把大量性能浪費在遞歸上。
. 問題3:vue路由衛(wèi)士?
vue中路由守衛(wèi)一共有三種,一個全局路由守衛(wèi),一個是組件內(nèi)路由守衛(wèi),一個是router獨享守衛(wèi)。
一、全局路由守衛(wèi)
只要全局路由變化,就會觸發(fā)全局的路由守衛(wèi)。
全局路由守衛(wèi)有個兩個:一個是全局前置守衛(wèi),一個是全局后置守衛(wèi)。
router.beforeEach((to,?from,?next)?=>?{
????console.log(to)?=>?//?到哪個頁面去?
????console.log(from)?=>?//?從哪個頁面來?
????next()?=>?//?一個回調(diào)函數(shù)
}
router.afterEach(to,from)?=?{}
next(false): 中斷當(dāng)前的導(dǎo)航。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到 from 路由對應(yīng)的地址,next('/') 或者 next({ path: '/' }): 跳轉(zhuǎn)到一個不同的地址。當(dāng)前的導(dǎo)航被中斷,然后進(jìn)行一個新的導(dǎo)航。你可以向 next 傳遞任意位置對象,且允許設(shè)置諸如 replace: true、name: 'home' 之類的選項以及任何用在 router-link 的 to prop 或 router.push 中的選項.
二、組件路由守衛(wèi)
//?跟methods:?{}等同級別書寫,組件路由守衛(wèi)是寫在每個單獨的vue文件里面的路由守衛(wèi)
beforeRouteEnter?(to,?from,?next)?{
????//?注意,在路由進(jìn)入之前,組件實例還未渲染,所以無法獲取this實例,只能通過vm來訪問組件實例
????next(vm?=>?{})
}
beforeRouteUpdate?(to,?from,?next)?{
????//?同一頁面,刷新不同數(shù)據(jù)時調(diào)用,
}
beforeRouteLeave?(to,?from,?next)?{
????//?離開當(dāng)前路由頁面時調(diào)用
}
三、路由獨享守衛(wèi)
路由獨享守衛(wèi)是在路由配置頁面單獨給路由配置的一個守衛(wèi)
export?default?new?VueRouter({
????routes:?[
????????{
????????????path:?'/',
????????????name:?'home',
????????????component:?'Home',
????????????beforeEnter:?(to,?from,?next)?=>?{
???????????????//?...
????????????}
????????}
????]
})
. 問題4:vue 中watch和computed區(qū)別?
watch側(cè)重點是對數(shù)據(jù)更新所產(chǎn)生的依賴追蹤,而computed側(cè)重點是對數(shù)據(jù)的緩存與處理引用,這就是watch和computed本質(zhì)的區(qū)別 ,computed可以看作一種特殊的data數(shù)據(jù)類型 ,它內(nèi)部進(jìn)行了二次依賴收集 ,第一次依賴收集是引用computed屬性值,而進(jìn)行的依賴收集 ,第二次是對computed內(nèi)部是否關(guān)聯(lián)data或者props的屬性,而又進(jìn)行的一次依賴收集。
下面我們按照vue3.0兩個例子,來分別兩者之前在流程上有什么區(qū)別。
watch
<div?id="app">
???<p>{{?count?}}p>
???<button?@input="add"?>addbutton>
div>
<script>
const?{?reactive,?watch,?toRefs?}?=?Vue
Vue.createApp({
??setup(){
????const?state?=?reactive({
???????count:1,
????})
????const?add?=?()?=>?state.count++
????watch(state.count,(count,?prevCount)?=>?{
???????console.log('新的count='?,?count?)
????})
????return?{
??????...toRefs(state),
??????add
????}
??}
}).mount('#app')
script>
從上述例子我們看出,當(dāng)點擊add后count變化 ,而是 watch作用就是,追蹤到count變化 ,而促使回調(diào)函數(shù)執(zhí)行。我們用一張流程圖來解析整個流程。

computed
<div?id="app">
???<p>{{?plusOne?}}p>
???<button?@input="add"?>addbutton>
div>
<script>
Vue.createApp({
??data:?()?=>?({
????number:?1
??}),
??computed:?{
????plusOne()?{
??????return?this.number?+?1
????}??
??},
??methods:?{
????add(){
??????this.number++
????}
??}
}).mount('#app')
script>
當(dāng)我們點擊add改變的是number,但是引用過this.number的computed也更新了新的值 ,頁面更新,我們可以看出 plusOne計算屬性可以看作一個對number緩存的數(shù)據(jù)類型,data下的number收集了plusOne依賴項,同樣plusOne也收集了{(lán){ plusOne }}的依賴促使更新視圖,我們用一張流程圖來解析整個流程。

如果想要看原理解析請看筆者的文章
傳送門 vue3.0 watch 和 computed源碼解析(舉例圖解)
. 問題5:vue中data為什么要是個函數(shù)?
vue中data必須是一個函數(shù)是和js本身特性有關(guān)。
我們做vue項目的時候,所有的vue組件都是基于大vue實例化的,我們可以用一個簡單例子來解釋一下:
function?Vue(){
}
Vue.prototype.data?=?{
????name:'jack',
????age:22,
}
var?componentA?=?new?Vue();
var?componentB?=?new?Vue();
componentA.data.age=55;
console.log(componentA,componentB)
此時,componentA 和 componentB data之間指向了同一個內(nèi)存地址,age 都變成了 55, 導(dǎo)致了問題!
接下來很好解釋為什么 vue 組件需要 function 了:
function?Vue(){
?this.data?=?this.data()
}
Vue.prototype.data?=?function?(){
????return?{
????name:'jack',
????age:22,
}
}
var?componentA?=?new?Vue();
var?componentB?=?new?Vue();
componentA.data.age=55;
console.log(componentA,componentB)
componentA 和 componentB data之間相互獨立, age 分別是 55 和 22
當(dāng)data是一個方法的時候,每一個實例化組件都會形成一個獨立的data對象,相互之間沒有影響。
react 篇

. 問題1:setState是同步的還是異步的?
對于這個問題,筆者自己總結(jié)了一下:對于setState是同步還是異步,對于整個react代碼執(zhí)行上下文來說,setState是同步的,但是setState觸發(fā)以后,并不一定得到新的數(shù)據(jù),這里有一個react有一個batchUpdate批量更新的概念。
我們來看一個例子??:
class?Example?extends?React.Component?{
??constructor()?{
????super();
????this.state?=?{
??????val:?0
????};
??}
??componentDidMount()?{
????this.setState({val:?this.state.val?+?1});
????console.log(this.state.val);????//?第?1?次?log
????this.setState({val:?this.state.val?+?1});
????console.log(this.state.val);????//?第?2?次?log
????setTimeout(()?=>?{
??????this.setState({val:?this.state.val?+?1});
??????console.log(this.state.val);??//?第?3?次?log
??????this.setState({val:?this.state.val?+?1});
??????console.log(this.state.val);??//?第?4?次?log
????},?0);
??}
??render()?{
????return?null;
??}
};
答案是:0 0 2 3
在React的setState函數(shù)實現(xiàn)中,會根據(jù)一個變量isBatchingUpdates判斷是直接更新this.state還是放到隊列中回頭再說,而isBatchingUpdates默認(rèn)是false,也就表示setState會同步更新this.state,但是,有一個函數(shù)batchedUpdates,這個函數(shù)會把isBatchingUpdates修改為true,而當(dāng)React在調(diào)用事件處理函數(shù)之前就會調(diào)用這個batchedUpdates,造成的后果,就是由React控制的事件處理過程setState不會同步更新this.state 。
. 問題2:介紹一下 react-hooks API及其如何使用?
usestate 無狀態(tài)組件的state
useCallback,useMemo 性能優(yōu)化利器.
useContext 可以使用操縱react context.
useEffect ,useLayoutEffect 副作用鉤子 可以替代class聲明組件中的聲明周期 .useLayoutEffect 在瀏覽器渲染之前 , effect在瀏覽器渲染之后
useReducer 功能可以參考redux
useRef 可以獲取元素和組件實例,還可以緩存數(shù)據(jù)
詳細(xì)的react-hooks使用可以戳??
傳送門:react-hooks如何使用?
webpack篇

. 問題一:webpack性能優(yōu)化?
打包的時間和打包之后文件的體積是影響webpack性能的主要因素。所以我們可以從這兩個方面入手,來優(yōu)化webpack性能。
1合理使用loader
用 include 或 exclude 來幫我們避免不必要的轉(zhuǎn)譯,優(yōu)化loader的管轄范圍。
2緩存babel編譯過的文件
loader: 'babel-loader?cacheDirectory=true'
如上,我們只需要為 loader 增加相應(yīng)的參數(shù)設(shè)定。選擇開啟緩存將轉(zhuǎn)譯結(jié)果緩存至文件系統(tǒng),可以提交babel-loader的工作效率。
3 ?DllPlugin類庫引入
DllPlugin 是基于 Windows 動態(tài)鏈接庫(dll)的思想被創(chuàng)作出來的。這個插件會把第三方庫單獨打包到一個文件中,這個文件就是一個單純的依賴庫。這個依賴庫不會跟著你的業(yè)務(wù)代碼一起被重新打包,只有當(dāng)依賴自身發(fā)生版本變化時才會重新打包。
4 happypack多進(jìn)程編譯
我們都知道nodejs是單線程。無法一次性執(zhí)行多個任務(wù)。這樣會使得所有任務(wù)都排隊執(zhí)行。happypack可以根據(jù)cpu核數(shù)優(yōu)勢,建立子進(jìn)程child_process,充分利用多核優(yōu)勢解決這個問題。提高了打包的效率。
const?HappyPack?=?require('happypack')
//?手動創(chuàng)建進(jìn)程池
const?happyThreadPool?=??HappyPack.ThreadPool({?size:?os.cpus().length?})
module.exports?=?{
??module:?{
????rules:?[
??????...
??????{
????????test:?/\.js$/,
????????//?問號后面的查詢參數(shù)指定了處理這類文件的HappyPack實例的名字
????????loader:?'happypack/loader?id=happyBabel',
????????...
??????},
????],
??},
??plugins:?[
????...
????new?HappyPack({
??????//?這個HappyPack的“名字”就叫做happyBabel,和樓上的查詢參數(shù)遙相呼應(yīng)
??????id:?'happyBabel',
??????//?指定進(jìn)程池
??????threadPool:?happyThreadPool,
??????loaders:?['babel-loader?cacheDirectory']
????})
??],
}
`
happypack成功,啟動了三個進(jìn)程編譯。加快了loader的加載速度。
5 scope Hoisting
scope Hoisting的作用是分析模塊之前的依賴關(guān)系 , 把打包之后的公共模塊合到同一個函數(shù)中去。它會代碼體積更小,因為函數(shù)申明語句會產(chǎn)生大量代碼;代碼在運行時因為創(chuàng)建的函數(shù)作用域更少了,內(nèi)存開銷也隨之變小。
const?ModuleConcatenationPlugin?=?require('webpack/lib/optimize/ModuleConcatenationPlugin');
module.exports?=?{
??resolve:?{
????//?針對?Npm?中的第三方模塊優(yōu)先采用?jsnext:main?中指向的?ES6?模塊化語法的文件
????mainFields:?['jsnext:main',?'browser',?'main']
??},
??plugins:?[
????//?開啟?Scope?Hoisting
????new?ModuleConcatenationPlugin(),
??],
};
6 ?tree Shaking 刪除冗余代碼
Tree-Shaking可以通過分析出import/exports依賴關(guān)系。對于沒有使用的代碼。可以自動刪除。這樣就減少了項目的體積。
舉個例子??:
import { a, b } from './pages'
a()
pages 文件里,我雖然導(dǎo)出了兩個頁面:
export const a = ()=>{ console.log(666) }
export const b = ()=>{ console.log(666) }
所以打包的結(jié)果會保留這部分:
export const a = ()=>{ console.log(666) }
b方法直接刪掉,這就是 Tree-Shaking 幫我們做的事情。刪掉了沒有用到的代碼。
7 按需加載
像vue 和 react spa應(yīng)用,首次加載的過程中,由于初始化要加載很多路由,加載很多組件頁面。會導(dǎo)致 首屏?xí)r間 非常長。一定程度上會影響到用戶體驗。所以我們需要換一種按需加載的方式。一次只加載想要看到的內(nèi)容
require.ensure 形式
const?getComponent?=>?(location,?cb)?{
??require.ensure([],?(require)?=>?{
????cb(null,?require('../pages/AComponent').default)
??},?'a')
}
"/a"?getComponent={getComponent}>
import形式
import?B?from?'@/pages/business/b.vue'
//按需加載變成了:
const?B?=?()?=>?import('@/pages/business/b.vue')
8 按需引用
不知道大家有沒有體會到,當(dāng)我們用antd等這種UI組件庫的時候。明明只想要用其中的一兩個組件,卻要把整個組件庫連同樣式庫一起引進(jìn)來,就會造成打包后的體積突然增加了好幾倍。為了解決這個問題,我們可以采取按需引入的方式。
拿antd為例,需要我們在.babelrc文件中這樣聲明,
{
"presets":?[
???[
????"@babel/preset-env",
????{
??????"targets":?{
??????????"chrome":?"67"
??????},
????"useBuiltIns":?"usage",
?????"corejs":?2
????}
???],
????"@babel/preset-react"
?],
??"plugins":?[
??[
???"@babel/plugin-transform-runtime",
??],
??//重點按需引入antd里面的style
??[??"import",?{
???"libraryName":?"antd",
???"libraryDirectory":?"es",
???"style":?true
??}]
?]
}
經(jīng)過如上配置之后,我們會發(fā)現(xiàn)體積比沒有處理的要小很多。
. 問題二:webpack怎么配置多頁面應(yīng)用?
實際這個問題變相再問webpack,配置多入口和多個html對應(yīng)
entry 應(yīng)該這么配 ,entry支持 string array object
{
??//入口文件配置?string?|?array?|?object
??entry:?{
????index:?'./src/index.js',
????list:?'./src/list.js',
????detail:?'./src/detail.js'
??},
}
html輸出配置 輸出html需要webpack插件 html-webpack-plugin
{
????plugins:?[
????new?htmlWebpackPulgin({
??????title:?'hello?我是首頁',
??????template:?'./index.html',
??????inject:?'head',
??????filename:?'index.html',
??????chunks:?['index']?//對應(yīng)?index.js
????}),
????new?htmlWebpackPulgin({
??????title:?'hello?我是列表',
??????template:?'./index.html',
??????inject:?'body',
??????filename:?'list.html',
??????chunks:?['list']?//?對應(yīng)?list.js
????}),
????new?htmlWebpackPulgin({
??????title:?'hello?我是詳情',
??????template:?'./index.html',
??????inject:?'body',
??????filename:?'detail.html',
??????chunks:?['detail']??//對應(yīng)detail.js
????})
??]
}
http篇

. 問題1:URL請求頁面之后瀏覽器的解析過程
1.用戶輸入網(wǎng)址,瀏覽器發(fā)起DNS查詢請求,域名解析。
2.三次握手
3.建立tcp連接,發(fā)送http請求
4.服務(wù)器接受到請求,并相應(yīng)http請求
5.瀏覽器對返回的html進(jìn)行解析,在這期間可能繼續(xù)請求css,js等文件,瀏覽器渲染、構(gòu)建網(wǎng)頁
6.斷開連接,四次揮手
7.瀏覽器對頁面進(jìn)行渲染呈現(xiàn)給用戶
. 問題2:javascript的同源策略
同源策略是一個重要的安全策略,它用于限制一個origin的文檔或者它加載的腳本如何能與另一個源的資源進(jìn)行交互。它能幫助阻隔惡意文檔,減少可能被攻擊的媒介。
如果兩個 URL 的 protocol、port (如果有指定的話)和 host 都相同的話,則這兩個 URL 是同源。這個方案也被稱為“協(xié)議/主機(jī)/端口元組”,或者直接是 “元組”。(“元組” 是指一組項目構(gòu)成的整體,雙重/三重/四重/五重/等的通用形式)。
源的繼承
在頁面中通過 about:blank 或 javascript: URL 執(zhí)行的腳本會繼承打開該 URL 的文檔的源,因為這些類型的 URLs 沒有包含源服務(wù)器的相關(guān)信息。
源的修改
滿足某些限制條件的情況下,頁面是可以修改它的源。腳本可以將 document.domain 的值設(shè)置為其當(dāng)前域或其當(dāng)前域的父域。如果將其設(shè)置為其當(dāng)前域的父域,則這個較短的父域?qū)⒂糜诤罄m(xù)源檢查。
. 問題3:怎么解決ie瀏覽器對get請求的緩存?
不知道大家有沒有過一種情況,在低版本ie瀏覽器下,在短暫的時間內(nèi)發(fā)出相同的get情況(比如相同時間請求一個數(shù)據(jù)列表多次),就會發(fā)現(xiàn)請求只發(fā)送了一次,其他的請求都被瀏覽器緩存了,對于這種緩存ajax情況,我們可以在url拼上時間戳,這樣瀏覽器就不會認(rèn)為這是相同的情況,就不會緩存get情況。
以aixos為例,我們可以在每次發(fā)起請求的時候?qū)et請求加以攔截
/*?攔截器?*/
axios.interceptors.request.use(
????(config)?=>?{
????????const?method???=?config.method?||?'get'
????????if?(method.toLowerCase()?===?'get')?{??/*?防止瀏覽器緩存?*/
????????????const?url??=?config.url?||?''
????????????const?t?=?new?Date().getTime()
????????????config.url?=?`${url}${url.indexOf('?')?===?-1???'?'?:?'&'}t=${t}`
????????}else?if(method.toLowerCase()?===?'post'){?/*?設(shè)置不同請求頭?*/
????????????//...
????????}
????????return?config
????},
????(error)?=>?{?
????}
)
如上,就完美解決了ajax被ie瀏覽器緩存的問題。
參考文檔
布局常用解決方案對比(媒體查詢、百分比、rem和vw/vh)
css中的多種垂直水平居中
JavaScript內(nèi)存泄露的4種方式及如何避免
vue路由守衛(wèi)哪幾種?
Vue組件為什么data必須是一個函數(shù)?
深入 setState 機(jī)制
總結(jié)
希望看到后覺的不錯的小伙伴關(guān)注一波公眾號,定期分享好的技術(shù)文章。
一大波連載好文章正在路上。。。
公眾號ID:前端公蝦米
冰淇淋里有夏天的味道

