1、 HTML語(yǔ)義化
根據(jù)內(nèi)容的結(jié)構(gòu)化(內(nèi)容語(yǔ)義化),選擇合適的標(biāo)簽(代碼語(yǔ)義化)便于開(kāi)發(fā)者閱讀和寫(xiě)出更優(yōu)雅的代碼的同時(shí)讓瀏覽器的爬蟲(chóng)和機(jī)器很好地解析。
常見(jiàn)的語(yǔ)義化標(biāo)簽:
1. title:頁(yè)面主體內(nèi)容。
2. hn:h1~h6,分級(jí)標(biāo)題,h1 與 title協(xié)調(diào)有利于搜索引擎優(yōu)化。
3. ul:無(wú)序列表。
4. ol:有序列表。
5. header:頁(yè)眉通常包括網(wǎng)站標(biāo)志、主導(dǎo)航、全站鏈接以及搜索框。
6. nav:標(biāo)記導(dǎo)航,僅對(duì)文檔中重要的鏈接群使用。
7. main:頁(yè)面主要內(nèi)容,一個(gè)頁(yè)面只能使用一次。如果是web應(yīng)用,則包圍其主要功能。
8. article:定義外部的內(nèi)容,其中的內(nèi)容獨(dú)立于文檔的其余部分。
9. section:定義文檔中的節(jié)(section、區(qū)段)。比如章節(jié)、頁(yè)眉、頁(yè)腳或文檔中的其他部分。
10. aside:定義其所處內(nèi)容之外的內(nèi)容。如側(cè)欄、文章的一組鏈接、廣告、友情鏈接、相關(guān)產(chǎn)品列表等。
11. footer:頁(yè)腳,只有當(dāng)父級(jí)是body時(shí),才是整個(gè)頁(yè)面的頁(yè)腳。
12. small:呈現(xiàn)小號(hào)字體效果,指定細(xì)則,輸入免責(zé)聲明、注解、署名、版權(quán)。
13. strong:和 em 標(biāo)簽一樣,用于強(qiáng)調(diào)文本,但它強(qiáng)調(diào)的程度更強(qiáng)一些。
14. em:將其中的文本表示為強(qiáng)調(diào)的內(nèi)容,表現(xiàn)為斜體。
15. mark:使用黃色突出顯示部分文本。
16. figure:規(guī)定獨(dú)立的流內(nèi)容(圖像、圖表、照片、代碼等等)(默認(rèn)有40px左右margin)。
17. figcaption:定義 figure 元素的標(biāo)題,應(yīng)該被置于 figure 元素的第一個(gè)或最后一個(gè)子元素的位置。
18. cite:表示所包含的文本對(duì)某個(gè)參考文獻(xiàn)的引用,比如書(shū)籍或者雜志的標(biāo)題。
19. blockquoto:定義塊引用,塊引用擁有它們自己的空間。
20. q:短的引述(跨瀏覽器問(wèn)題,盡量避免使用)。
21. time:datetime屬性遵循特定格式,如果忽略此屬性,文本內(nèi)容必須是合法的日期或者時(shí)間格式。
22. abbr:簡(jiǎn)稱或縮寫(xiě)。
23. dfn:定義術(shù)語(yǔ)元素,與定義必須緊挨著,可以在描述列表dl元素中使用。
24. del:移除的內(nèi)容。
25. ins:添加的內(nèi)容。
26. code:標(biāo)記代碼。
27. meter:定義已知范圍或分?jǐn)?shù)值內(nèi)的標(biāo)量測(cè)量。(Internet Explorer 不支持 meter 標(biāo)簽)
28. progress:定義運(yùn)行中的進(jìn)度(進(jìn)程)。
2、盒模型
頁(yè)面渲染時(shí),dom 元素所采用的 布局模型??赏ㄟ^(guò)box-sizing進(jìn)行設(shè)置,分為W3C盒子模型(標(biāo)準(zhǔn)盒模型)和IE盒子模型(怪異盒模型)</br>區(qū)別:</br>標(biāo)準(zhǔn)盒模型:width/height 只是內(nèi)容高度,不包含 padding 和 border 值 </br>IE盒子模型: width/height 包含了 padding 和 border 值
// 設(shè)置標(biāo)準(zhǔn)模型
box-sizing: content-box;
// 設(shè)置IE模型
box-sizing: border-box;
3、BFC
塊級(jí)格式化上下文(Block Formatting Context),是一個(gè)獨(dú)立的渲染區(qū)域,讓處于 BFC 內(nèi)部的元素與外部的元素相互隔離,使內(nèi)外元素的定位不會(huì)相互影響。</br>IE下為 Layout,可通過(guò) zoom:1 觸發(fā)
觸發(fā)條件:
1. 根元素
2. position: absolute/fixed
3. display: inline-block / table
4. float 元素
5. ovevflow !== visible
規(guī)則:
1. 屬于同一個(gè) BFC 的兩個(gè)相鄰 Box 垂直排列
2. 屬于同一個(gè) BFC 的兩個(gè)相鄰 Box 的 margin 會(huì)發(fā)生重疊
3. BFC 中子元素的 margin box 的左邊, 與包含塊 (BFC) border box的左邊相接觸 (子元素 absolute 除外)
4. BFC 的區(qū)域不會(huì)與 float 的元素區(qū)域重疊
5. 計(jì)算 BFC 的高度時(shí),浮動(dòng)子元素也參與計(jì)算
6. 文字層不會(huì)被浮動(dòng)層覆蓋,環(huán)繞于周?chē)?br>
應(yīng)用:
1. 阻止margin重疊
2. 可以包含浮動(dòng)元素 —— 清除內(nèi)部浮動(dòng)(清除浮動(dòng)的原理是兩個(gè)div都位于同一個(gè) BFC 區(qū)域之中)
3. 自適應(yīng)兩欄布局
4. 可以阻止元素被浮動(dòng)元素覆蓋
4、 居中布局
水平居中
1. 行內(nèi)元素: text-align: center
2. 塊級(jí)元素: margin: 0 auto
3. absolute + transform
4. flex + justify-content: center
垂直居中
1. line-height: height
2. absolute + transform
3. flex + align-items: center
4. table
水平垂直居中
1. absolute + transform
2. flex + justify-content + align-items
3. absolute+負(fù)margin
4. absolute + auto margin
5. absolute + calc
6. table
7. flex
8. grid
5. 選擇器優(yōu)先級(jí)
!important > 行內(nèi)樣式 > #id > .class > tag > * > 繼承 > 默認(rèn)
6、HTTP 狀態(tài)碼
第一個(gè)數(shù)字定義了響應(yīng)的類別,且有五種可能取值。
1xx:指示信息--表示請(qǐng)求已接收,繼續(xù)處理。
2xx:成功--表示請(qǐng)求已被成功接收、理解、接受。
3xx:重定向--要完成請(qǐng)求必須進(jìn)行更進(jìn)一步的操作。
4xx:客戶端錯(cuò)誤--請(qǐng)求有語(yǔ)法錯(cuò)誤或請(qǐng)求無(wú)法實(shí)現(xiàn)。
5xx:服務(wù)器端錯(cuò)誤--服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求。
常見(jiàn)狀態(tài)代碼、狀態(tài)描述的說(shuō)明如下。
200 OK:客戶端請(qǐng)求成功。
400 Bad Request:客戶端請(qǐng)求有語(yǔ)法錯(cuò)誤,不能被服務(wù)器所理解。
401 Unauthorized:請(qǐng)求未經(jīng)授權(quán),這個(gè)狀態(tài)代碼必須和WWW-Authenticate報(bào)頭域一起使用。
403 Forbidden:服務(wù)器收到請(qǐng)求,但是拒絕提供服務(wù)。
404 Not Found:請(qǐng)求資源不存在,舉個(gè)例子:輸入了錯(cuò)誤的URL。
500 Internal Server Error:服務(wù)器發(fā)生不可預(yù)期的錯(cuò)誤。
503 Server Unavailable:服務(wù)器當(dāng)前不能處理客戶端的請(qǐng)求,一段時(shí)間后可能恢復(fù)正常,舉個(gè)例子:HTTP/1.1 200 OK(CRLF)。
7、瀏覽器輸入 url 之后發(fā)生了什么
瀏覽器向 DNS 服務(wù)器請(qǐng)求解析該 URL 中的域名所對(duì)應(yīng)的 IP 地址;
建立TCP連接(三次握手);
瀏覽器發(fā)出讀取文件(URL 中域名后面部分對(duì)應(yīng)的文件)的HTTP 請(qǐng)求,該請(qǐng)求報(bào)文作為 TCP 三次握手的第三個(gè)報(bào)文的數(shù)據(jù)發(fā)送給服務(wù)器;
服務(wù)器對(duì)瀏覽器請(qǐng)求作出響應(yīng),并把對(duì)應(yīng)的 html 文本發(fā)送給瀏覽器;
釋放 TCP連接(四次揮手);
瀏覽器將該 html 文本并顯示內(nèi)容;
8、同源策略和跨域通信
同源策略限制了從同一個(gè)源加載的文檔或腳本如何與來(lái)自另一個(gè)源的資源進(jìn)行交互。這是一個(gè)用于隔離潛在惡意文件的重要安全機(jī)制。</br>協(xié)議、域名、端口只要有一個(gè)不一樣,就是不同的源。
JSONP:在頁(yè)面上引入不同域上的js腳本文件不受同源策略限制。因此在js文件載入完畢之后,觸發(fā)回調(diào),可以將需要的data作為參數(shù)傳入。
優(yōu)點(diǎn):兼容性好(兼容低版本IE)
缺點(diǎn):JSONP只支持GET請(qǐng)求
CORS:根據(jù)請(qǐng)求頭的Origin值和響應(yīng)頭的Access-Control-Request-Headers和Access-Control-Request-Method的值進(jìn)行比對(duì),通過(guò)了就可以請(qǐng)求成功,沒(méi)通過(guò)就請(qǐng)求失敗
postMessage (H5中新增的),window.postMessage(message,targetOrigin) 方法是html5新引進(jìn)的特性,可以使用它來(lái)向其它的window對(duì)象發(fā)送消息,無(wú)論這個(gè)window對(duì)象是屬于同源或不同源
node反向代理:如果我們用的是node起的前端服務(wù),那我們可以使用node來(lái)直接進(jìn)行反向代理,引入一個(gè)處理代理的插件,然后配置一個(gè)target
Nginx反向代理:主要就是用了nginx.conf內(nèi)的proxy_pass http://xxx.xxx.xxx,會(huì)把所有請(qǐng)求代理到那個(gè)域名
WebSocket
Hash(window.location.hash + iframe)
7、Cookie、sessionStorage、localStorage區(qū)別
共同點(diǎn):都是保存在瀏覽器端,且同源的。
區(qū)別:
(1)cookie數(shù)據(jù)始終在同源的http請(qǐng)求中攜帶,即cookie在瀏覽器和服務(wù)器間來(lái)回傳遞。
sessionStorage和localStorage不會(huì)自動(dòng)把數(shù)據(jù)發(fā)給服務(wù)器,僅在本地保存。
(2)cookie數(shù)據(jù)不能超過(guò)4k(適合保存小數(shù)據(jù))。
sessionStorage和localStorage容量較大,
(3)數(shù)據(jù)有效期不同,sessionStorage:僅在當(dāng)前瀏覽器窗口關(guān)閉前有效。
localStorage:始終有效,窗口或?yàn)g覽器關(guān)閉也一直保存,需手動(dòng)清除;
cookie只在設(shè)置的cookie過(guò)期時(shí)間之前一直有效,即使窗口或?yàn)g覽器關(guān)閉。
(4)作用域不同。 sessionStorage不在不同的瀏覽器窗口中共享;
localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。
應(yīng)用場(chǎng)景:
localStorage:常用于長(zhǎng)期登錄(+判斷用戶是否已登錄),適合長(zhǎng)期保存在本地的數(shù)據(jù)。
sessionStorage :敏感賬號(hào)一次性登錄;
cookies與服務(wù)器交互。
8、GET和POST的區(qū)別
簡(jiǎn)單來(lái)說(shuō):GET產(chǎn)生一個(gè)TCP數(shù)據(jù)包,POST產(chǎn)生兩個(gè)TCP數(shù)據(jù)包
嚴(yán)格的說(shuō):對(duì)于GET方式的請(qǐng)求,瀏覽器會(huì)把http header和data一并發(fā)送出去,服務(wù)器響應(yīng)200(返回?cái)?shù)據(jù));
而對(duì)于POST請(qǐng)求。瀏覽器先發(fā)送header,服務(wù)器響應(yīng)100 continue,瀏覽器再發(fā)送data,服務(wù)器響應(yīng)200 ok(返回?cái)?shù)據(jù))
GET請(qǐng)求的參數(shù)是放在請(qǐng)求的URL中,而POST方法是放在請(qǐng)求體中
GET請(qǐng)求在URL中傳遞參數(shù)時(shí)會(huì)有長(zhǎng)度限制,而POST無(wú)限制(不是絕對(duì)的,只是相對(duì)來(lái)說(shuō))
GET請(qǐng)求會(huì)被瀏覽器主動(dòng)緩存,而POST不會(huì)
GET請(qǐng)求的參數(shù)會(huì)保存在瀏覽器中,而POST的參數(shù)不會(huì)保存在瀏覽器中
9、JS有幾種數(shù)據(jù)類型,其中基本數(shù)據(jù)類型有哪些!
七種數(shù)據(jù)類型
Boolean
Null
Undefined
Number
String
Symbol (ECMAScript 6 新定義)
Object
(ES6之前)其中5種為基本類型:string,number,boolean,null,undefined,
ES6出來(lái)的Symbol也是原始數(shù)據(jù)類型 ,表示獨(dú)一無(wú)二的值
Object 為引用類型(范圍挺大),也包括數(shù)組、函數(shù)
ES2020(即 ES11),又增加了新類型:**BigInt。**這里能答出來(lái)8種類型更加加分
10、原型和原型鏈
創(chuàng)建一個(gè)函數(shù)就會(huì)為其創(chuàng)建一個(gè)prototype屬性,指向這個(gè)函數(shù)的原型對(duì)象,原型對(duì)象會(huì)自動(dòng)獲得constructor屬性,指向prototype屬性所在函數(shù)。
當(dāng)一個(gè)對(duì)象調(diào)用某個(gè)方法或者屬性的時(shí)候,先在自身查找,如果找到就調(diào)用,如果沒(méi)有就順著__proto__到原型對(duì)象中查找,如果還沒(méi)有就繼續(xù)去原型的原型中查找,一直到null,這樣形成一條鏈叫做原型鏈。如果還沒(méi)有找到就返回undefined。
11、作用域和閉包
作用域其實(shí)可理解為該上下文中聲明的 變量和聲明的作用范圍。</br>閉包屬于一種特殊的作用域,稱為 靜態(tài)作用域。它的定義可以理解為: 父函數(shù)被銷(xiāo)毀 的情況下,返回出的子函數(shù)仍然可以繼續(xù)訪問(wèn)到父級(jí)的變量對(duì)象,這樣的函數(shù)稱為閉包。
閉包會(huì)產(chǎn)生一個(gè)很經(jīng)典的問(wèn)題:
多個(gè)子函數(shù)的[[scope]]都是同時(shí)指向父級(jí),是完全共享的。因此當(dāng)父級(jí)的變量對(duì)象被修改時(shí),所有子函數(shù)都受到影響。
解決:
變量可以通過(guò) 函數(shù)參數(shù)的形式 傳入,避免使用默認(rèn)的[[scope]]向上查找
使用setTimeout包裹,通過(guò)第三個(gè)參數(shù)傳入
使用 塊級(jí)作用域,讓變量成為自己上下文的屬性,避免共享
12、call和apply、bind
首先要了解this的指向問(wèn)題:
(1)this的指向不是在函數(shù)定義時(shí)確定的,而是在函數(shù)調(diào)用時(shí)確定,this默認(rèn)情況下指向window,嚴(yán)格模式下為undefined
(2)使用new 調(diào)用構(gòu)造函數(shù)時(shí),構(gòu)造函數(shù)內(nèi)的this 指向新創(chuàng)建的對(duì)象
(3)通過(guò) 出call/apply/bind方法顯式調(diào)用函數(shù)時(shí),函數(shù)內(nèi)this 指向指定的對(duì)象(第一個(gè)參數(shù))
(4)通過(guò)上下文對(duì)象A調(diào)用函數(shù)時(shí),函數(shù)內(nèi)this指向?qū)ο驛
var obj = {
? name: '橘子君',
? fn: function () {
? ? console.log(this);
? }
}
obj.fn();// 輸出:{name: "橘子君", fn: ?}
(5)箭頭函數(shù)本身并不存在this,箭頭函數(shù)的this的指向由它的外層作用域來(lái)決定的(指向外層作用域的this)
再來(lái)看call和apply、bind</br>作用:在函數(shù)調(diào)用時(shí)改變函數(shù)的執(zhí)行上下文也就是this的值指向</br>區(qū)別:call采用不定長(zhǎng)的參數(shù)列表,而apply使用一個(gè)參數(shù)數(shù)組。
call: fn.call(target, 1, 2)
apply: fn.apply(target, [1, 2])
bind: fn.bind(target)(1,2)
13、為什么 JavaScript 是單線程
JavaScript語(yǔ)言的一大特點(diǎn)就是單線程,也就是說(shuō),同一個(gè)時(shí)間只能做一件事。那么,為什么?
JavaScript不能有多個(gè)線程呢?這樣能提高效率啊。
JavaScript的單線程,與它的用途有關(guān)。作為瀏覽器腳本語(yǔ)言,JavaScript的主要用途是與用戶互動(dòng),以及操作DOM。這決定了它只能是單線程,否則會(huì)帶來(lái)很復(fù)雜的同步問(wèn)題。比如,假定JavaScript同時(shí)有兩個(gè)線程,一個(gè)線程在某個(gè)DOM節(jié)點(diǎn)上添加內(nèi)容,另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器應(yīng)該以哪個(gè)線程為準(zhǔn)?
所以,為了避免復(fù)雜性,從一誕生,JavaScript就是單線程,這已經(jīng)成了這門(mén)語(yǔ)言的核心特征,將來(lái)也不會(huì)改變。
為了利用多核CPU的計(jì)算能力,HTML5提出Web Worker標(biāo)準(zhǔn),允許JavaScript腳本創(chuàng)建多個(gè)線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個(gè)新標(biāo)準(zhǔn)并沒(méi)有改變JavaScript單線程的本質(zhì)。
14、淺拷貝、深拷貝
深拷貝和淺拷貝是只針對(duì)Object和Array這樣的引用數(shù)據(jù)類型的。基本類型不存在這個(gè)問(wèn)題。</br>簡(jiǎn)單來(lái)說(shuō),有兩個(gè)對(duì)象 A 和 B,B = A,當(dāng)你修改 A的屬性或者方法 時(shí),B 的值也跟著發(fā)生了變化,這時(shí)候就叫淺拷貝。如果不發(fā)生變化,就叫深拷貝。在引用數(shù)據(jù)類型中,會(huì)產(chǎn)生淺拷貝的問(wèn)題。
如何實(shí)現(xiàn)深拷貝?
(1)使用遞歸的方式實(shí)現(xiàn)深拷貝
(2)用**JSON.parse( JSON.stringify(obj) )**來(lái)完成深拷貝,但是該方法不能解決屬性為函數(shù),undefined,循環(huán)引用的的情況
(3)用Object.assign()來(lái)完成深拷貝,newObj = Object.assign({}, obj),這種方法對(duì)于一層對(duì)象來(lái)說(shuō)是沒(méi)有問(wèn)題的,但是如果對(duì)象的屬性對(duì)應(yīng)的還是對(duì)象或者數(shù)組時(shí),就是淺拷貝。
var obj = { a: {a: "kobe", b: 39} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "wade";
console.log(obj.a.a); // wade 原對(duì)象也改變了,是淺拷貝
一層的情況
let obj = {
? username: 'kobe'
};
let obj2 = Object.assign({},obj);
obj2.username = 'wade';
console.log(obj);//{username: "kobe"} 原對(duì)象沒(méi)有被改變,是深拷貝
15、數(shù)組去重
方法1:定義一個(gè)新數(shù)組,并存放原數(shù)組的第一個(gè)元素,然后將元素組一一和新數(shù)組的元素對(duì)比,若不同則存放在新數(shù)組中</br>方法2:先將原數(shù)組排序,在與相鄰的進(jìn)行比較,2如果不同則存入新數(shù)組。</br>方法3:利用對(duì)象屬性存在的特性,如果沒(méi)有該屬性則存入新數(shù)組。
Array.prototype.unique = function () {
? ? var arr = this, obj = {}, result = [];
? ? for (var i = 0; i < arr.length; i++) {
? ? ? ? if (!obj[arr[i]]) { //如果能查找到,證明數(shù)組元素重復(fù)了
? ? ? ? ? ? obj[arr[i]] = 1;
? ? ? ? ? ? result.push(arr[i]);
? ? ? ? }
? ? }
? ? return result;
};
var a = [1, 2, 3, 1, 2, 3];
var b = a.unique();
console.log(b); //打印結(jié)果:(3) [1, 2, 3]
方法4(最常用):使用es6 set,Set數(shù)據(jù)結(jié)構(gòu),它類似于數(shù)組,其成員的值都是唯一的
let arr= [1, 2, 3, 3, 5, 7, 2, 6, 8];
console.log([...new Set(arr)]);
方法5:使用filter過(guò)濾函數(shù)去重。
var arr = [1, 2, 3, 1, 2, 3];
console.log(arr.filter((v, i, arr) => arr.indexOf(v) === i))//打印結(jié)果:(3) [1, 2, 3]
15、防抖、節(jié)流
防抖:觸發(fā)高頻函數(shù)事件后,n秒內(nèi)函數(shù)只能執(zhí)行一次,如果在n秒內(nèi)這個(gè)事件再次被觸發(fā)的話,那么會(huì)重新計(jì)算時(shí)間。通常使用的場(chǎng)景是:用戶輸入,只需再輸入完成后做一次輸入校驗(yàn)即可。</br>節(jié)流:所謂節(jié)流,就是指連續(xù)觸發(fā)事件但是在 n 秒中只執(zhí)行一次函數(shù)。節(jié)流會(huì)稀釋函數(shù)的執(zhí)行頻率。通常使用場(chǎng)景: 滾動(dòng)條事件 或者 resize 事件,通常每隔 100~500 ms執(zhí)行一次即可
//防抖
function debounce(func, wait){
? let timeout;
? return function(){
? ? if(timeout){
? ? ? clearTimeout(timeout)
? ? }
? ? timeout = setTimeout(() => {
? ? ? func.apply(this, arguments)
? ? }, wait)
? }}
//節(jié)流
function throttle(func, wait){
? let timeout;
? return function(){
? ? if(!timeout){
? ? ? timeout = setTimeout(() => {
? ? ? ? timeout = null;
? ? ? ? func.apply(this, arguments)
? ? ? }, wait)
? ? }
? }}
16、數(shù)組操作
map【常用】: 遍歷數(shù)組,返回回調(diào)返回值組成的新數(shù)組
forEach【常用】: 無(wú)法break,可以用try/catch中throw new Error來(lái)停止
filter【常用】: 過(guò)濾
some: 有一項(xiàng)返回true,則整體為true
every: 有一項(xiàng)返回false,則整體為false
join【常用】: 通過(guò)指定連接符生成字符串
push / pop: 末尾推入和彈出,改變?cè)瓟?shù)組, push 返回?cái)?shù)組長(zhǎng)度, pop 返回原數(shù)組最后一項(xiàng);
unshift / shift: 頭部推入和彈出,改變?cè)瓟?shù)組,unshift 返回?cái)?shù)組長(zhǎng)度,shift 返回原數(shù)組第一項(xiàng) ;
sort(fn) / reverse【常用】: 排序與反轉(zhuǎn),改變?cè)瓟?shù)組
concat【常用】: 連接數(shù)組,不影響原數(shù)組, 淺拷貝
slice(start, end): 返回截?cái)嗪蟮男聰?shù)組,不改變?cè)瓟?shù)組
splice(start, number, value...)【常用】: 返回刪除元素組成的數(shù)組,value 為插入項(xiàng),改變?cè)瓟?shù)組
indexOf / lastIndexOf(value, fromIndex): 查找數(shù)組項(xiàng),返回對(duì)應(yīng)的下標(biāo)
reduce / reduceRight(fn(prev, cur), defaultPrev): 兩兩執(zhí)行,prev 為上次化簡(jiǎn)函數(shù)的return值,cur 為當(dāng)前值
當(dāng)傳入 defaultPrev 時(shí),從第一項(xiàng)開(kāi)始;
當(dāng)未傳入時(shí),則為第二項(xiàng)
16、解釋一下變量提升
所有的聲明都會(huì)提升到作用域的最頂上去。</br>函數(shù)聲明的優(yōu)先級(jí)高于變量聲明的優(yōu)先級(jí),并且函數(shù)聲明和函數(shù)定義的部分一起被提升。
舉例說(shuō)明一下:
(1)變量提升
console.log(a);? //undefined
var a = 123;
因?yàn)樽兞縜的聲明被提到了作用域頂端。上面代碼編譯后應(yīng)該是下面這個(gè)樣子
var a;
console.log(a)
a = 123
//所以輸出內(nèi)容為 undeifend
(2)函數(shù)提升
具名函數(shù)的聲明有兩種方式:1. 函數(shù)聲明式 2. 函數(shù)字面量式
//函數(shù)聲明式
function bar () {}
//變量形式聲明;
var foo = function () {}
函數(shù) 變量形式聲明 和普通變量一樣 提升的 只是一個(gè)沒(méi)有值的變量。
函數(shù)聲明式的提升現(xiàn)象和變量提升略有不同,函數(shù)聲明式會(huì)提升到作用域最前邊,并且將聲明內(nèi)容一起提升到最上邊。
看例子:
bar()
var bar = function() {
? console.log(1);
}
// 報(bào)錯(cuò):TypeError: bar is not a function
bar()
function bar() {
? console.log(1);
}
//輸出結(jié)果1
17、ES6+增加了哪些新特性
聲明 let / const
解構(gòu)賦值
class / extend: 類聲明與繼承
Set / Map: 新的數(shù)據(jù)結(jié)構(gòu)
箭頭函數(shù)
promise
async/await
數(shù)組的擴(kuò)展
對(duì)象的擴(kuò)展
18、let和const區(qū)別
let / const: 塊級(jí)作用域、不存在變量提升、暫時(shí)性死區(qū)、不允許重復(fù)聲明
const: 聲明常量,無(wú)法修改(基本類型不可修改,引用類型如對(duì)象和數(shù)組,可以修改)
19、async和await的用途?
所謂Promise,簡(jiǎn)單說(shuō)就是一個(gè)容器,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果。從語(yǔ)法上說(shuō),Promise 是一個(gè)對(duì)象,從它可以獲取異步操作的消息。Promise提供統(tǒng)一的API,各種異步操作都可以用同樣的方法進(jìn)行處理。
有三個(gè)狀態(tài):
等待中(pending)
完成了(resolved)
拒絕了(rejected)
Promise的缺點(diǎn):
首先,無(wú)法取消Promise,一旦新建它就會(huì)立即執(zhí)行,無(wú)法中途取消。
其次,如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯(cuò)誤,不會(huì)反應(yīng)到外部。
第三,當(dāng)處于pending狀態(tài)時(shí),無(wú)法得知目前進(jìn)展到哪一個(gè)階段(剛剛開(kāi)始還是即將完成)。
解決了什么問(wèn)題:
Promise 的出現(xiàn)解決了 之前的回調(diào)地獄問(wèn)題,并且Promise 實(shí)現(xiàn)了鏈?zhǔn)秸{(diào)用,也就是說(shuō)每次調(diào)用 then 之后返回的都是一個(gè) Promise , 并且是一個(gè)全新的Promise 。是因?yàn)镻romise 的狀態(tài)不可變。如果你在then中使用了return ,那么 return 的值會(huì)被 Promise .resolve 包裝。
//newPromise.js
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class NewPromise {
? ? constructor(executor) {
? ? ? ? executor(this.resolve,this.reject)
? ? }
? ? //promise狀態(tài)
? ? status = PENDING
? ? value = undefined
? ? reason = undefined
? ? //值默認(rèn)沒(méi)有
? ? resolve = value => {
? ? ? ? //如果狀態(tài)不是等待,阻止程序向下執(zhí)行
? ? ? ? if(this.status !== PENDING)return
? ? ? ? this.status = FULFILLED
? ? ? ? this.value = value
? ? }
? ? //使用箭頭函數(shù)的原因:直接調(diào)用一個(gè)普通函數(shù),函數(shù)里面的this指向是undefined的
? ? reject = reason => {
? ? ? ? if(this.status !== PENDING)return
? ? ? ? //將狀態(tài)更改為失敗
? ? ? ? this.status = REJECTED
? ? ? ? this.reason = reason
? ? }
? ? //判斷promise的狀態(tài),返回回調(diào)函數(shù),需要傳遞value和reason
? ? then(successCallback,failCallback){
? ? ? ? //判斷狀態(tài)
? ? ? ? if(thi2s.status === FULFILLED){
? ? ? ? ? ? successCallback(this.value)
? ? ? ? }else if(this.status === REJECTED){
? ? ? ? ? ? failCallback(this.reason)
? ? ? ? }
? ? }
}
module.exports = NewPromise
20、1、SPA是什么,有什么優(yōu)缺點(diǎn)
SPA( single-page application )僅在 Web 頁(yè)面初始化時(shí)加載相應(yīng)的 HTML、JavaScript 和 CSS。一旦頁(yè)面加載完成,SPA 不會(huì)因?yàn)橛脩舻牟僮鞫M(jìn)行頁(yè)面的重新加載或跳轉(zhuǎn);取而代之的是利用路由機(jī)制實(shí)現(xiàn) HTML 內(nèi)容的變換,UI 與用戶的交互,避免頁(yè)面的重新加載。
優(yōu)點(diǎn):
用戶體驗(yàn)好、快,內(nèi)容的改變不需要重新加載整個(gè)頁(yè)面,避免了不必要的跳轉(zhuǎn)和重復(fù)渲染;
基于上面一點(diǎn),SPA 相對(duì)對(duì)服務(wù)器壓力小;
前后端職責(zé)分離,架構(gòu)清晰,前端進(jìn)行交互邏輯,后端負(fù)責(zé)數(shù)據(jù)處理;
缺點(diǎn):
初次加載耗時(shí)多:為實(shí)現(xiàn)單頁(yè) Web 應(yīng)用功能及顯示效果,需要在加載頁(yè)面的時(shí)候?qū)?JavaScript、CSS 統(tǒng)一加載,部分頁(yè)面按需加載;
前進(jìn)后退路由管理:由于單頁(yè)應(yīng)用在一個(gè)頁(yè)面中顯示所有的內(nèi)容,所以不能使用瀏覽器的前進(jìn)后退功能,所有的頁(yè)面切換需要自己建立堆棧管理;
SEO 難度較大:由于所有的內(nèi)容都在一個(gè)頁(yè)面中動(dòng)態(tài)替換顯示,所以在 SEO 上其有著天然的弱勢(shì)。
21、談?wù)剬?duì)MVVM的理解
MVVM 是Model-View-ModelView的縮寫(xiě),是一種脫胎于 MVC 模式的設(shè)計(jì)模式。
Model 代表數(shù)據(jù)層,負(fù)責(zé)存放業(yè)務(wù)相關(guān)的數(shù)據(jù);
View 代表視圖層,負(fù)責(zé)在頁(yè)面上展示數(shù)據(jù);
ViewModel 是的作用是同步 View 和 Model 之間的關(guān)聯(lián)。數(shù)據(jù)會(huì)綁定到viewModel層并自動(dòng)將數(shù)據(jù)渲染到頁(yè)面中,視圖變化的時(shí)候會(huì)通知viewModel層更新數(shù)據(jù)。
viewModel 是由前端開(kāi)發(fā)人員組織生成和維護(hù)的視圖數(shù)據(jù)層。在這一層,前端開(kāi)發(fā)者對(duì)從后端獲取的 Model 數(shù)據(jù)進(jìn)行轉(zhuǎn)換處理,做二次封裝,以生成符合 View 層使用預(yù)期的視圖數(shù)據(jù)模型。需要注意的是 ViewModel 所封裝出來(lái)的數(shù)據(jù)模型包括視圖的狀態(tài)和行為兩部分,而 Model 層的數(shù)據(jù)模型是只包含狀態(tài)的,比如頁(yè)面的這一塊展示什么,而頁(yè)面加載進(jìn)來(lái)時(shí)發(fā)生什么,點(diǎn)擊這一塊發(fā)生什么,這一塊滾動(dòng)時(shí)發(fā)生什么這些都屬于視圖行為(交互),視圖狀態(tài)和行為都封裝在了 ViewModel 里。這樣的封裝使得 ViewModel 可以完整地去描述 View 層。
MVVM 框架實(shí)現(xiàn)了雙向綁定,這樣 ViewModel 的內(nèi)容會(huì)實(shí)時(shí)展現(xiàn)在 View *層,前端開(kāi)發(fā)者再也不必低效又麻煩地通過(guò)操縱 DOM 去更新視圖,MVVM 框架已經(jīng)把最臟最累的一塊做好了,我們只需要處理和維護(hù) ViewModel,視圖就會(huì)自動(dòng)得到相應(yīng)更新。這樣 View 層展現(xiàn)的不是 Model 層的數(shù)據(jù),而是 ViewModel處理后 的數(shù)據(jù),由 /ViewModel 負(fù)責(zé)與 Model 層交互,這就完全解耦了 View 層和 Model 層,這個(gè)解耦是至關(guān)重要的,它是前后端分離方案實(shí)施的重要一環(huán)。
22、Vue的生命周期
beforeCreate(創(chuàng)建前) 在數(shù)據(jù)觀測(cè)和初始化事件還未開(kāi)始
created(創(chuàng)建后) 完成數(shù)據(jù)觀測(cè),屬性和方法的運(yùn)算,初始化事件,$el屬性還沒(méi)有顯示出來(lái)
beforeMount(載入前) 在掛載開(kāi)始之前被調(diào)用,相關(guān)的render函數(shù)首次被調(diào)用。實(shí)例已完成以下的配置:編譯模板,把data里面的數(shù)據(jù)和模板生成html。注意此時(shí)還沒(méi)有掛載html到頁(yè)面上。
mounted(載入后) 在el 被新創(chuàng)建的 vm.$el 替換,并掛載到實(shí)例上去之后調(diào)用。實(shí)例已完成以下的配置:用上面編譯好的html內(nèi)容替換el屬性指向的DOM對(duì)象。完成模板中的html渲染到html頁(yè)面中。此過(guò)程中進(jìn)行ajax交互。
beforeUpdate(更新前) 在數(shù)據(jù)更新之前調(diào)用,發(fā)生在虛擬DOM重新渲染和打補(bǔ)丁之前??梢栽谠撱^子中進(jìn)一步地更改狀態(tài),不會(huì)觸發(fā)附加的重渲染過(guò)程。
updated(更新后) 在由于數(shù)據(jù)更改導(dǎo)致的虛擬DOM重新渲染和打補(bǔ)丁之后調(diào)用。調(diào)用時(shí),組件DOM已經(jīng)更新,所以可以執(zhí)行依賴于DOM的操作。然而在大多數(shù)情況下,應(yīng)該避免在此期間更改狀態(tài),因?yàn)檫@可能會(huì)導(dǎo)致更新無(wú)限循環(huán)。該鉤子在服務(wù)器端渲染期間不被調(diào)用。
beforeDestroy(銷(xiāo)毀前) 在實(shí)例銷(xiāo)毀之前調(diào)用。實(shí)例仍然完全可用。
destroyed(銷(xiāo)毀后) 在實(shí)例銷(xiāo)毀之后調(diào)用。調(diào)用后,所有的事件監(jiān)聽(tīng)器會(huì)被移除,所有的子實(shí)例也會(huì)被銷(xiāo)毀。該鉤子在服務(wù)器端渲染期間不被調(diào)用。
23、如何動(dòng)態(tài)更新對(duì)象或數(shù)組的值?
由于Object.defineProperty() 只能對(duì)屬性進(jìn)行數(shù)據(jù)劫持,Vue 無(wú)法監(jiān)聽(tīng)到對(duì)象或數(shù)組內(nèi)部某個(gè)屬性值的變化,因此在直接設(shè)置以上兩類數(shù)據(jù)的值時(shí),頁(yè)面不會(huì)實(shí)時(shí)更新。此時(shí)可以通過(guò) this.$set 方法來(lái)解決。
this.$set(數(shù)組/對(duì)象,要改變的index/屬性名,要改成的value)
除了$set外,對(duì)于數(shù)組Vue提供了一些可以觸發(fā)視圖更新的方法
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
23、介紹一下 Vuex 吧
Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。每一個(gè) Vuex 應(yīng)用的核心就是 store(倉(cāng)庫(kù))。store 基本上就是一個(gè)容器,它包含著你的應(yīng)用中大部分的狀態(tài) ( state )。
(1)Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時(shí)候,若 store 中的狀態(tài)發(fā)生變化,那么相應(yīng)的組件也會(huì)相應(yīng)地得到高效更新。
(2)改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個(gè)狀態(tài)的變化。
主要包括以下幾個(gè)模塊:
State:定義了應(yīng)用狀態(tài)的數(shù)據(jù)結(jié)構(gòu),可以在這里設(shè)置默認(rèn)的初始狀態(tài)。
Getter:允許組件從 Store 中獲取數(shù)據(jù),mapGetters 輔助函數(shù)僅僅是將 store 中的 getter 映射到局部計(jì)算屬性。
Mutation:是唯一更改 store 中狀態(tài)的方法,且必須是同步函數(shù)。
Action:用于提交 mutation,而不是直接變更狀態(tài),可以包含任意異步操作。
Module:允許將單一的 Store 拆分為多個(gè) store 且同時(shí)保存在單一的狀態(tài)樹(shù)中。
24、為什么vue組件中的 data 必須是一個(gè)函數(shù),return 一個(gè)對(duì)象
因?yàn)榻M件是用來(lái)復(fù)用的,且 JS 里對(duì)象是引用類型,如果組件中 data 是一個(gè)對(duì)象,子組件中的 data 屬性值會(huì)相互影響,如果組件中 data 選項(xiàng)是一個(gè)函數(shù),每個(gè)實(shí)例可以維護(hù)一份被返回對(duì)象的獨(dú)立拷貝,組件實(shí)例之間的 data 屬性值不會(huì)互相影響
追問(wèn):new Vue 實(shí)例里,data 可以直接是一個(gè)對(duì)象?
new Vue() 是根組件,是不會(huì)被復(fù)用的,因此不存在引用對(duì)象的問(wèn)題。
25、v-if和v-show的用途和區(qū)別
v-if 是真正的條件渲染,因?yàn)樗鼤?huì)確保在切換過(guò)程中條件塊內(nèi)的事件監(jiān)聽(tīng)器和子組件適當(dāng)?shù)乇讳N(xiāo)毀和重建;也是惰性的:如果在初始渲染時(shí)條件為假,則什么也不做——直到條件第一次變?yōu)檎鏁r(shí),才會(huì)開(kāi)始渲染條件塊。
v-show 就簡(jiǎn)單得多——不管初始條件是什么,元素總是會(huì)被渲染,并且只是簡(jiǎn)單地基于 CSS 的 “display” 屬性進(jìn)行切換。
所以,v-if 適用于在運(yùn)行時(shí)很少改變條件,不需要頻繁切換條件的場(chǎng)景;v-show 則適用于需要非常頻繁切換條件的場(chǎng)景。
26、v-for循環(huán)為什么要加key值,key用什么合適
key特殊 attribute 主要用在 Vue 的虛擬 DOM 算法,在新舊 nodes 對(duì)比時(shí)辨識(shí) VNodes。如果不使用 key,Vue 會(huì)使用一種最大限度減少動(dòng)態(tài)元素并且盡可能的嘗試就地修改/復(fù)用相同類型元素的算法。而使用 key 時(shí),它會(huì)基于 key 的變化重新排列元素順序,并且會(huì)移除 key 不存在的元素。
有相同父元素的子元素必須有獨(dú)特的 key。重復(fù)的 key 會(huì)造成渲染錯(cuò)誤。
總結(jié)一下:key 是為Vue 中 vnode 的唯一標(biāo)記,通過(guò)這個(gè) key,我們的 diff 操作可以更準(zhǔn)確、更快速。
在列表中一般使用當(dāng)前項(xiàng)的id作為key,而不要使用index,因?yàn)榱斜碓鰟h操作是導(dǎo)致index值的變化,增加了跟新dom的數(shù)量,性能低下。
27、計(jì)算屬性computed 和偵聽(tīng)器watch 的區(qū)別和運(yùn)用的場(chǎng)景?
計(jì)算屬性 computed:
(1)支持緩存,只有依賴數(shù)據(jù)發(fā)生變化時(shí),才會(huì)重新進(jìn)行計(jì)算函數(shù);
(2)計(jì)算屬性內(nèi)不支持異步操作;
(3)計(jì)算屬性的函數(shù)中都有一個(gè) get(默認(rèn)具有,獲取計(jì)算屬性)和 set(手動(dòng)添加,設(shè)置計(jì)算屬性)方法;
(4)計(jì)算屬性是自動(dòng)監(jiān)聽(tīng)依賴值的變化,從而動(dòng)態(tài)返回內(nèi)容。
偵聽(tīng)屬性 watch:
(1)不支持緩存,只要數(shù)據(jù)發(fā)生變化,就會(huì)執(zhí)行偵聽(tīng)函數(shù);
(2)偵聽(tīng)屬性內(nèi)支持異步操作;
(3)偵聽(tīng)屬性的值可以是一個(gè)對(duì)象,接收 handler 回調(diào),deep,immediate 三個(gè)屬性;
(3)監(jiān)聽(tīng)是一個(gè)過(guò)程,在監(jiān)聽(tīng)的值變化時(shí),可以觸發(fā)一個(gè)回調(diào),并做一些其他事情。
運(yùn)用場(chǎng)景:
1. 當(dāng)我們需要進(jìn)行數(shù)值計(jì)算,并且依賴于其它數(shù)據(jù)時(shí),應(yīng)該使用 computed,因?yàn)榭梢岳?computed 的緩存特性,避免每次獲取值時(shí),都要重新計(jì)算;
2. 當(dāng)我們需要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開(kāi)銷(xiāo)較大的操作時(shí),應(yīng)該使用 watch,使用 watch 選項(xiàng)允許我們執(zhí)行異步操作 ( 訪問(wèn)一個(gè) API ),限制我們執(zhí)行該操作的頻率,并在我們得到最終結(jié)果前,設(shè)置中間狀態(tài)。這些都是計(jì)算屬性無(wú)法做到的。
28、組件之間的通信(父子組件、兄弟組件、跨級(jí)組件)
下面我們分別介紹每種通信方式:
(1)props / $emit 適用 父子組件通信
父?jìng)髯觩rops,子傳父 $on、$emit。
(2)ref 與 $parent / $children 適用 父子組件通信
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子組件上,引用就指向組件實(shí)例
$parent / $children:訪問(wèn)父 / 子實(shí)例
(3)EventBus ($emit / $on) 適用于 父子、隔代、兄弟組件通信
這種方法通過(guò)一個(gè)空的 Vue 實(shí)例作為中央事件總線(事件中心),用它來(lái)觸發(fā)事件和監(jiān)聽(tīng)事件,從而實(shí)現(xiàn)任何組件間的通信,包括父子、隔代、兄弟組件。
(4)$attrs/$listeners 適用于 隔代組件通信
$attrs:包含了父作用域中不被 prop 所識(shí)別 (且獲取) 的特性綁定 ( class 和 style 除外 )。當(dāng)一個(gè)組件沒(méi)有聲明任何 prop 時(shí),這里會(huì)包含所有父作用域的綁定 ( class 和 style 除外 ),并且可以通過(guò) v-bind="$attrs" 傳入內(nèi)部組件。通常配合 inheritAttrs 選項(xiàng)一起使用。
$listeners:包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監(jiān)聽(tīng)器。它可以通過(guò) v-on="$listeners" 傳入內(nèi)部組件
(5)provide / inject 適用于 隔代組件通信
祖先組件中通過(guò) provider 來(lái)提供變量,然后在子孫組件中通過(guò) inject 來(lái)注入變量。 provide / inject API 主要解決了跨級(jí)組件間的通信問(wèn)題,不過(guò)它的使用場(chǎng)景,主要是子組件獲取上級(jí)組件的狀態(tài),跨級(jí)組件間建立了一種主動(dòng)提供與依賴注入的關(guān)系。
(6)? vuex:終極解決方案,適用于 父子、隔代、兄弟組件通信
29、SSR是什么?
SSR也就是服務(wù)端渲染,也就是將Vue在客戶端把標(biāo)簽渲染成HTML的工作放在服務(wù)端完成,然后再把html直接返回給客戶端。
SSR有著更好的SEO、并且首屏加載速度更快等優(yōu)點(diǎn)。不過(guò)它也有一些缺點(diǎn),比如我們的開(kāi)發(fā)條件會(huì)受到限制,服務(wù)器端渲染只支持beforeCreate和created兩個(gè)鉤子,當(dāng)我們需要一些外部擴(kuò)展庫(kù)時(shí)需要特殊處理,服務(wù)端渲染應(yīng)用程序也需要處于Node.js的運(yùn)行環(huán)境。還有就是服務(wù)器會(huì)有更大的負(fù)載需求。
30、vue-router 中 hash 和 history 路由模式實(shí)現(xiàn)原理
(1)hash 模式的實(shí)現(xiàn)原理
實(shí)現(xiàn)原理很簡(jiǎn)單,location.hash 的值就是 URL 中 # 后面的內(nèi)容。比如下面這個(gè)網(wǎng)站,它的 location.hash 的值為 '#search':
https://www.demo.com#search
hash 路由模式的實(shí)現(xiàn)主要是基于下面幾個(gè)特性:
URL 中 hash 值只是客戶端的一種狀態(tài),當(dāng)向服務(wù)器端發(fā)出請(qǐng)求時(shí),hash 部分不會(huì)被發(fā)送;
hash 值的改變,都會(huì)在瀏覽器的訪問(wèn)歷史中增加一個(gè)記錄。因此我們能通過(guò)瀏覽器的回退、前進(jìn)按鈕控制hash 的切換;
可以通過(guò) a 標(biāo)簽,并設(shè)置 href 屬性,當(dāng)用戶點(diǎn)擊這個(gè)標(biāo)簽后,URL 的 hash 值會(huì)發(fā)生改變;或者使用 JavaScript 來(lái)對(duì) loaction.hash 進(jìn)行賦值,改變 URL 的 hash 值;
我們可以使用 hashchange 事件來(lái)監(jiān)聽(tīng) hash 值的變化,從而對(duì)頁(yè)面進(jìn)行跳轉(zhuǎn)(渲染)。
(2)history 模式的實(shí)現(xiàn)原理
history 模式使用了HTML5 提供了 History API 來(lái)實(shí)現(xiàn) URL 的變化。其中做最主要的 API 有以下兩個(gè):history.pushState() 和 history.repalceState()。這兩個(gè) API 可以在不進(jìn)行刷新的情況下,操作瀏覽器的歷史紀(jì)錄。不同的是,前者是新增一個(gè)歷史記錄,后者是直接替換當(dāng)前的歷史記錄,如下所示:
pushState(state, title, url)
repalceState(state, title, url)
參數(shù)說(shuō)明
state: 可以通過(guò)history.state讀取
title: 可選參數(shù),暫時(shí)沒(méi)有用,建議傳個(gè)短標(biāo)題
url: 改變過(guò)后的url地址、
history 路由模式的實(shí)現(xiàn)主要基于存在下面幾個(gè)特性:
pushState 和 repalceState 兩個(gè) API 來(lái)操作實(shí)現(xiàn) URL 的變化 ;
我們可以使用 popstate 事件來(lái)監(jiān)聽(tīng) url 的變化,從而對(duì)頁(yè)面進(jìn)行跳轉(zhuǎn)(渲染);
history.pushState() 或 history.replaceState() 不會(huì)觸發(fā) popstate 事件,這時(shí)我們需要手v動(dòng)觸發(fā)頁(yè)面跳轉(zhuǎn)(渲染)。
31、keep-alive是什么
keep-alive 是 Vue 內(nèi)置的一個(gè)組件,可以使被包含的組件保留狀態(tài),避免重新渲染 ,其有以下特性:
一般結(jié)合路由和動(dòng)態(tài)組件一起使用,用于緩存組件;
提供 include 和 exclude 屬性,兩者都支持字符串或正則表達(dá)式, include 表示只有名稱匹配的組件會(huì)被緩存,exclude 表示任何名稱匹配的組件都不會(huì)被緩存 ,其中 exclude 的優(yōu)先級(jí)比 include 高;
對(duì)應(yīng)兩個(gè)鉤子函數(shù) activated 和 deactivated ,當(dāng)組件被激活時(shí),觸發(fā)鉤子函數(shù) activated,當(dāng)組件被移除時(shí),觸發(fā)鉤子函數(shù) deactivated。
32、在哪個(gè)生命周期內(nèi)調(diào)用異步請(qǐng)求?
可以在鉤子函數(shù) created、beforeMount、mounted 中進(jìn)行調(diào)用,因?yàn)樵谶@三個(gè)鉤子函數(shù)中,data 已經(jīng)創(chuàng)建,可以將服務(wù)端端返回的數(shù)據(jù)進(jìn)行賦值。
SSR服務(wù)器端渲染只支持beforeCreate和created兩個(gè)鉤子,所以SSR的異步請(qǐng)求要放在created鉤子中
33、介紹一下Vue中的Diff算法
首先,對(duì)比節(jié)點(diǎn)本身,判斷是否為同一節(jié)點(diǎn),如果不為相同節(jié)點(diǎn),則刪除該節(jié)點(diǎn)重新創(chuàng)建節(jié)點(diǎn)進(jìn)行替換。
如果為相同節(jié)點(diǎn),進(jìn)行patchVnode,判斷如何對(duì)該節(jié)點(diǎn)的子節(jié)點(diǎn)進(jìn)行處理,先判斷一方有子節(jié)點(diǎn)一方?jīng)]有子節(jié)點(diǎn)的情況(如果新的children沒(méi)有子節(jié)點(diǎn),將舊的子節(jié)點(diǎn)移除)。
比較如果都有子節(jié)點(diǎn),則進(jìn)行updateChildren,判斷如何對(duì)這些新老節(jié)點(diǎn)的子節(jié)點(diǎn)進(jìn)行操作(diff核心)。
匹配時(shí),找到相同的子節(jié)點(diǎn),遞歸比較子節(jié)點(diǎn)。
在diff中,只對(duì)同層的子節(jié)點(diǎn)進(jìn)行比較,放棄跨級(jí)的節(jié)點(diǎn)比較,使得時(shí)間復(fù)雜從O(n^3)降低值O(n),也就是說(shuō),只有當(dāng)新舊children都為多個(gè)子節(jié)點(diǎn)時(shí)才需要用核心的Diff算法進(jìn)行同層級(jí)比較。
34、Vue模版編譯原理
Vue的編譯過(guò)程就是將template轉(zhuǎn)化為render函數(shù)的過(guò)程。會(huì)經(jīng)歷以下階段:
生成AST樹(shù)
優(yōu)化
codegen
首先解析模版,生成AST語(yǔ)法樹(shù)(一種用JavaScript對(duì)象的形式來(lái)描述整個(gè)模板)。
使用大量的正則表達(dá)式對(duì)模板進(jìn)行解析,遇到標(biāo)簽、文本的時(shí)候都會(huì)執(zhí)行對(duì)應(yīng)的鉤子進(jìn)行相關(guān)處理。
Vue的數(shù)據(jù)是響應(yīng)式的,但其實(shí)模板中并不是所有的數(shù)據(jù)都是響應(yīng)式的。有一些數(shù)據(jù)首次渲染后就不會(huì)再變化,對(duì)應(yīng)的DOM也不會(huì)變化。那么優(yōu)化過(guò)程就是深度遍歷AST樹(shù),按照相關(guān)條件對(duì)樹(shù)節(jié)點(diǎn)進(jìn)行標(biāo)記。這些被標(biāo)記的節(jié)點(diǎn)(靜態(tài)節(jié)點(diǎn))我們就可以跳過(guò)對(duì)它們的比對(duì),對(duì)運(yùn)行時(shí)的模板起到很大的優(yōu)化作用。
編譯的最后一步是將優(yōu)化后的AST樹(shù)轉(zhuǎn)換為可執(zhí)行的代碼。
35、重排、重繪
重排(Reflow)是什么?
定義:DOM中各個(gè)元素都有自己的盒子模型,需要瀏覽器根據(jù)樣式進(jìn)行計(jì)算,并根據(jù)計(jì)算結(jié)果將元素放到特定位置,這就是Reflow
觸發(fā)Reflow的條件
增、刪、改、移DOM
修改CSS樣式
Resize窗口
頁(yè)面滾動(dòng)
修改網(wǎng)頁(yè)的默認(rèn)字體重繪(回流)(Repaint)是什么?
定義:當(dāng)各種盒子的位置、大小以及其他屬性改變時(shí),瀏覽器需要把這些元素都按照各自的特性繪制一遍,這個(gè)過(guò)程稱為Repaint。
觸發(fā)Repaint的條件:
DOM改動(dòng)
CSS改動(dòng)
如何減少重繪、避免重排?
DOM層面:DocumentFragment本質(zhì)上是一個(gè)占位符,真正插入頁(yè)面的是它的所有子孫節(jié)點(diǎn),所以,將需要變動(dòng)的DOM節(jié)點(diǎn)先匯總到DocumentFragment,然后一次性插入,可以減少DOM操作的次數(shù)。
CSS層面:操作多個(gè)樣式時(shí),可以先匯總到一個(gè)類中,然后一次性修改
36、css3動(dòng)畫(huà)常用的屬性
transition動(dòng)畫(huà):</br>
transition-property: 參與過(guò)渡的屬性</br>
transition-duration: 過(guò)渡的持續(xù)時(shí)間</br>
transition-delay: 延遲過(guò)渡的時(shí)間</br>
transition-timing-function:過(guò)渡的動(dòng)畫(huà)類型
復(fù)合寫(xiě)法:
transition: 屬性值1? 屬性值2? 屬性值3? 屬性值4
animation關(guān)鍵幀動(dòng)畫(huà):</br>
animation-name:設(shè)置對(duì)象所應(yīng)用的動(dòng)畫(huà)名稱</br>
animation-duration:設(shè)置對(duì)象動(dòng)畫(huà)的持續(xù)時(shí)間</br>
animation-timing-function:設(shè)置對(duì)象動(dòng)畫(huà)的過(guò)渡類型</br>
animation-delay:設(shè)置對(duì)象動(dòng)畫(huà)延遲的時(shí)間</br>
animation-iteration-count:設(shè)置對(duì)象動(dòng)畫(huà)的循環(huán)次數(shù)(默認(rèn)情況下循環(huán)1次)</br>
animation-direction:設(shè)置對(duì)象動(dòng)畫(huà)在循環(huán)中是否反向運(yùn)動(dòng)</br>
animation-play-state:設(shè)置對(duì)象動(dòng)畫(huà)的狀態(tài) </br>
常常結(jié)合transform使用:
? 平移:transfrom:translate(X,Y)</br>
? 旋轉(zhuǎn):transform: rotate()</br>
? 斜切:ransform:skew(X,Y)</br>
? 縮放:transform:scale(X,Y)</br>
例如:
.box{
? ? height:200px;
? ? width: 200px;
? ? position: relative;
? ? margin:200px auto;
? ? transform-style:preserve-3d;
? ? -webkit-transform-style:preserve-3d;
? ? transform:rotateX(20deg) rotateY(20deg);
? ? animation:one 5s linear infinite;
? ? -webkit-animation:one 5s linear infinite;
}
@keyframes one{
? ? from{
? ? ? ? transform:rotateX(-20deg) rotateY(0deg) ;
? ? }
? ? to{
? ? ? ? transform:rotateX(340deg) rotateY(360deg) ;
? ? }
}
37、事件委托
事件捕獲和冒泡允許我們實(shí)現(xiàn)一種被稱為 事件委托 的強(qiáng)大的事件處理模式。
這個(gè)想法是,如果我們有許多以類似方式處理的元素,那么就不必為每個(gè)元素分配一個(gè)處理程序 —— 而是將單個(gè)處理程序放在它們的共同祖先上。
在處理程序中,我們獲取 event.target 以查看事件實(shí)際發(fā)生的位置并進(jìn)行處理。
38、JS哪些操作會(huì)造成內(nèi)存泄露
內(nèi)存泄漏是指一塊被分配的內(nèi)存既不能使用,也不能回收,直到瀏覽器進(jìn)程結(jié)束。
意外的全局變量
閉包
沒(méi)有清理的dom元素 dom元素賦值給變量,又通過(guò)removeChild移除dom元素。但是dom元素的引用還在內(nèi)存中
被遺忘的定時(shí)器或者回調(diào)
39、說(shuō)幾條寫(xiě)JavaScript的基本規(guī)范?
1、不要在同一行聲明多個(gè)變量
2、使用===或!==來(lái)比較
3、使用字面量的方式來(lái)創(chuàng)建對(duì)象、數(shù)組,替代new Array這種形式
4、不要使用全局函數(shù)
5、switch語(yǔ)句必須要帶default分支
6、函數(shù)不應(yīng)該有的時(shí)候有return,有的時(shí)候沒(méi)有return
7、fon-in循環(huán)中的變量,用var關(guān)鍵字說(shuō)明作用域,防止變量污染
8、變量的聲明遵循駝峰命名法,聲明構(gòu)造函數(shù)時(shí)首字母大寫(xiě),定義常量的時(shí)候盡量用大寫(xiě)字母,用_分割
9、三元表達(dá)式可以替代if語(yǔ)句
10、&&和||是可以短路的,使用&&時(shí)如果前面一個(gè)值是錯(cuò)的,那么后面的值不用判斷,使用||時(shí),如果前面一個(gè)值是對(duì)的,那么后面的值不用判斷
11、比較數(shù)據(jù)類型以下6種情況是false,其他都是true:false、""、0、null、NaN、undefined
12、數(shù)據(jù)類型檢測(cè)用typeof,對(duì)象類型檢測(cè)用instanceof
13、異步加載第三方的內(nèi)容
14、單行注釋//,多行注釋/**/
15、使用命名空間解決變量名沖突
16、多人協(xié)作開(kāi)發(fā),新建一個(gè)js文件,const聲明常量,在js文件中引用,用常量名替代方法名,這樣做可以防止命名沖突