本專題按照如下幾個方便進行整理:javascript
適合初次全面複習的同窗,查缺補漏,知識面比較全,複習完成後,再按照本人整理的面試高頻題配合複習,使得找工做事半功倍,必定要理解,不要死記硬背,對於一些概念性的和原理的內容要深刻理解。css
「你從頭讀,儘可能往下讀,直到你一竅不通時,再從頭開始,這樣堅持往下讀,直到你徹底讀懂爲止。」html
HTTP協議定義Web客戶端如何從Web服務器請求Web頁面,以及服務器如何把Web頁面傳送給客戶端。客戶端向服務器發送一個請求報文,服務器以一個狀態行做爲響應。前端
請求報文{ 請求行、請求頭、空行、請求體 } 請求行:{http方法、頁面地址、http協議、http版本} 響應報文{ 狀態行、響應頭、空行、響應體 }vue
Request Header:java
Response Header:node
http: 是互聯網上應用最爲普遍的一種網絡協議,是一個客戶端和服務器端請求和應答的標準(TCP),用於從 WWW 服務器傳輸超文本到本地瀏覽器的超文本傳輸協議。
https:是以安全爲目標的 HTTP 通道,即 HTTP 下加入 SSL 層進行加密。react
https 協議的主要做用:創建一個信息安全通道,來確保數據的傳輸,確保網站的真實性。webpack
客戶端在使用 HTTPS 方式與 Web 服務器通訊時有如下幾個步驟,如圖所示。nginx
使用 HTTPS 協議可認證用戶和服務器,確保數據發送到正確的客戶機和服務器;
HTTPS 協議要比 http 協議安全,可防止數據在傳輸過程當中不被竊取、改變,確保數據的完整性。 HTTPS 是現行架構下最安全的解決方案,雖然不是絕對安全,但它大幅增長了中間人攻擊的成本。
費時
,會使頁面加載時間延長 50%,增長 10%~20%的耗電。緩存
不如 http 高效,會增長數據開銷。證書費
用越高。IP
,不能再同一個 ip 上綁定多個域名,ipv4 資源支持不了這種消耗。首先在瀏覽器中輸入URL
查找緩存:瀏覽器先查看瀏覽器緩存-系統緩存-路由緩存中是否有該地址頁面,若是有則顯示頁面內容。若是沒有則進行下一步。
DNS域名解析:瀏覽器向DNS服務器發起請求,解析該URL中的域名對應的IP地址
創建TCP鏈接:解析出IP地址後,根據IP地址和默認80端口,和服務器創建TCP鏈接
發起HTTP請求:瀏覽器發起讀取文件的HTTP請求,,該請求報文做爲TCP三次握手的第三次數據發送給服務器
服務器響應請求並返回結果:服務器對瀏覽器請求作出響應,並把對應的html文件發送給瀏覽器
關閉TCP鏈接:經過四次揮手釋放TCP鏈接
瀏覽器渲染:客戶端(瀏覽器)解析HTML內容並渲染出來,瀏覽器接收到數據包後的解析流程爲:
JS引擎解析過程:調用JS引擎執行JS代碼(JS的解釋階段,預處理階段,執行階段生成執行上下文,VO,做用域鏈、回收機制等等)
建立window對象:window對象也叫全局執行環境,當頁面產生時就被建立,全部的全局變量和函數都屬於window的屬性和方法,而DOM Tree也會映射在window的doucment對象上。當關閉網頁或者關閉瀏覽器時,全局執行環境會被銷燬。
加載文件:完成js引擎分析它的語法與詞法是否合法,若是合法進入預編譯
預編譯:在預編譯的過程當中,瀏覽器會尋找全局變量聲明,把它做爲window的屬性加入到window對象中,並給變量賦值爲'undefined';尋找全局函數聲明,把它做爲window的方法加入到window對象中,並將函數體賦值給他(匿名函數是不參與預編譯的,由於它是變量)。而變量提高做爲不合理的地方在ES6中已經解決了,函數提高還存在。
解釋執行:執行到變量就賦值,若是變量沒有被定義,也就沒有被預編譯直接賦值,在ES5非嚴格模式下這個變量會成爲window的一個屬性,也就是成爲全局變量。string、int這樣的值就是直接把值放在變量的存儲空間裏,object對象就是把指針指向變量的存儲空間。函數執行,就將函數的環境推入一個環境的棧中,執行完成後再彈出,控制權交還給以前的環境。JS做用域其實就是這樣的執行流機制實現的。
DOM
的變化影響了元素的幾何信息(DOM
對象的位置和尺寸大小),瀏覽器須要從新計算元素的幾何屬性,將其安放在界面中的正確位置,這個過程叫作重排。觸發緣由:
1. 頁面初始化的時候;
2. 操做DOM元素時;
3. 某些元素的尺寸改變了;
4. 若是 CSS 的屬性發生變化了。
複製代碼
觸發緣由:改變元素的 color、background、box-shadow 等屬性
Reflow要比Repaint更花費時間,也就更影響性能。因此要儘可能避免過多的Reflow。
減小 reflow/repaint
fixed
或 absoult
的 position
,那麼修改他們的 CSS 是不會 reflow 的。position:absolute
或fixed
元素,對其餘元素影響不大GPU
加速,translate
使用3D
變化第一次握手:創建鏈接時,客戶端發送syn包(syn=j)到服務器,並進入SYN_SENT狀態,等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP鏈接成功)狀態,完成三次握手。
握手過程當中傳送的包裏不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。
複製代碼
客戶端進程發出鏈接釋放報文
,而且中止發送數據。釋放數據報文首部,FIN=1,其序列號爲seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即便不攜帶數據,也要消耗一個序號。2)服務器收到鏈接釋放報文,發出確認報文
,ACK=1,ack=u+1,而且帶上本身的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,可是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。
3)客戶端收到服務器的確認請求後,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送鏈接釋放報文(在這以前還須要接受服務器發送的最 後的數據)。
4)服務器將最後的數據發送完畢後,就向客戶端發送鏈接釋放報文
,FIN=1,ack=u+1,因爲在半關閉狀態,服務器極可能又發送了一些數據,假定此時的序列號爲seq=w,此時,服務器就進入了LAST-ACK(最後確認)狀態,等待客戶端的確認。
5)客戶端收到服務器的鏈接釋放報文後,必須發出確認
,ACK=1,ack=w+1,而本身的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP鏈接尚未釋放,必須通過2∗∗MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED狀態。
6)服務器只要收到了客戶端發出的確認,當即進入CLOSED狀態。一樣,撤銷TCB後,就結束了此次的TCP鏈接。能夠看到,服務器結束TCP鏈接的時間要比客戶端早一些。
創建Socket鏈接至少須要一對套接字,其中一個運行於客戶端,稱爲ClientSocket ,另外一個運行於服務器端,稱爲ServerSocket 。
套接字之間的鏈接過程分爲三個步驟:服務器監聽,客戶端請求,鏈接確認。
一、服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待鏈接的狀態,實時監控網絡狀態,等待客戶端的鏈接請求。
二、客戶端請求:指客戶端的套接字提出鏈接請求,要鏈接的目標是服務器端的套接字。
爲此,客戶端的套接字必須首先描述它要鏈接的服務器的套接字,指出服務器端套接字的地址和端口號,而後就向服務器端套接字提出鏈接請求。
三、鏈接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的鏈接請求時,就響應客戶端套接字的請求,創建一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,雙方就正式創建鏈接。
而服務器端套接字繼續處於監聽狀態,繼續接收其餘客戶端套接字的鏈接請求。
TCP是面向連接
的,而UDP是面向無鏈接的。
TCP僅支持單播傳輸
,UDP 提供了單播,多播,廣播的功能。
TCP的三次握手保證了鏈接的可靠性
; UDP是無鏈接的、不可靠的一種數據傳輸協議,首先不可靠性體如今無鏈接上,通訊都不須要創建鏈接,對接收到的數據也不發送確認信號,發送端不知道數據是否會正確接收。
UDP的頭部開銷
比TCP的更小,數據傳輸速率更高
,實時性更好
。
1.瀏覽器回退表現不一樣 GET在瀏覽器回退時時無害的,而POST會再次提交請求
2.瀏覽器對請求地址的處理不一樣 GET請求地址會被瀏覽器主動緩存,而POST不會,除非手動設置
3.瀏覽器對響應的處理不一樣GET請求參數會被完整的保留在瀏覽器歷史記錄裏,而POST中的參數不會被保留
4.參數大小不一樣. GET請求在URL中傳送的參數是有長度的限制,而POST沒有限制
5.安全性不一樣. GET參數經過URL傳遞,會暴露,不安全;POST放在Requset Body中,相對更安全
6.針對數據操做的類型不一樣.GET對數據進行查詢,POST主要對數據進行增刪改!簡單說,GET是隻讀,POST是寫。
跨域的原理
跨域,是指瀏覽器不能執行其餘網站的腳本。它是由瀏覽器的同源策略
形成的。
同源策略,是瀏覽器對 JavaScript 實施的安全限制,只要協議、域名、端口
有任何一個不一樣,都被看成是不一樣的域。
跨域原理,便是經過各類方式,避開瀏覽器的安全限制
。
解決方案
最初作項目的時候,使用的是jsonp,但存在一些問題,使用get請求不安全,攜帶數據較小,後來也用過iframe,但只有主域相同才行,也是存在些問題,後來經過了解和學習發現使用代理和proxy代理配合起來使用比較方便,就引導後臺按這種方式作下服務器配置,在開發中使用proxy,在服務器上使用nginx代理,這樣開發過程當中彼此都方便,效率也高;如今h5新特性還有 windows.postMessage()
JSONP:
ajax 請求受同源策略影響,不容許進行跨域請求,而 script 標籤 src 屬性中的鏈 接卻能夠訪問跨域的 js 腳本,利用這個特性,服務端再也不返回 JSON 格式的數據,而是 返回一段調用某個函數的 js 代碼,在 src 中進行了調用,這樣實現了跨域。
步驟:
//動態建立 script
var script = document.createElement('script');
// 設置回調函數
function getData(data) {
console.log(data);
}
//設置 script 的 src 屬性,並設置請求地址
script.src = 'http://localhost:3000/?callback=getData';
// 讓 script 生效
document.body.appendChild(script);
複製代碼
JSONP 的缺點:
JSON 只支持 get,由於 script 標籤只能使用 get 請求; JSONP 須要後端配合返回指定格式的數據。
document.domain 基礎域名相同 子域名不一樣
window.name 利用在一個瀏覽器窗口內,載入全部的域名都是共享一個window.name
CORS CORS(Cross-origin resource sharing)跨域資源共享 服務器設置對CORS的支持原理:服務器設置Access-Control-Allow-Origin HTTP響應頭以後,瀏覽器將會容許跨域請求
proxy代理
window.postMessage() 利用h5新特性window.postMessage()
Websocket
ajax 輪詢 實現原理:ajax 輪詢指客戶端每間隔一段時間向服務端發起請求,保持數據的同步。
優勢:可實現基礎(指間隔時間較短)的數據更新。
缺點:這種方法也只是儘可能的模擬即時傳輸,但並不是真正意義上的即時通信,頗有可能出現客戶端請求時,服務端數據並未更新。或者服務端數據已更新,但客戶端未發起請求。致使屢次請求資源浪費,效率低下。【數據更新不及時,效率低下
】
long poll 長輪詢
實現原理: long poll 指的是客戶端發送請求以後,若是沒有數據返回,服務端會將請求掛起放入隊列(不斷開鏈接)處理其餘請求,直到有數據返回給客戶端。而後客戶端再次發起請求,以此輪詢。在 HTTP1.0 中客戶端能夠設置請求頭 Connection:keep-alive,服務端收到該請求頭以後知道這是一個長鏈接,在響應報文頭中也添加 Connection:keep-alive。客戶端收到以後表示長鏈接創建完成,能夠繼續發送其餘的請求。在 HTTP1.1 中默認使用了 Connection:keep-alive 長鏈接。
優勢:減小客戶端的請求,下降無效的網絡傳輸,保證每次請求都有數據返回,不會一直佔用線程。
缺點:沒法處理高併發,當客戶端請求量大,請求頻繁時對服務器的處理能力要求較高。服務器一直保持鏈接會消耗資源,須要同時維護多個線程,服務器所能承載的 TCP 鏈接數是有上限的,這種輪詢很容易把鏈接數頂滿。每次通信都須要客戶端發起,服務端不能主動推送。【沒法處理高併發,消耗服務器資源嚴重,服務端不能主動推送
】
iframe 長鏈接
實現原理:
在網頁上嵌入一個 iframe 標籤,該標籤的 src 屬性指向一個長鏈接請求。這樣服務端就能夠源源不斷地給客戶端傳輸信息。保障信息實時更新。
優勢:消息及時傳輸。
缺點:消耗服務器資源
。
WebSocket
實現原理: Websocket 實現了客戶端與服務端的雙向通訊,只須要鏈接一次,就能夠相互傳輸數據,很適合實時通信、數據實時更新等場景。
Websocket 協議與 HTTP 協議沒有關係,它是一個創建在 TCP 協議上的全新協議,爲了兼容 HTTP 握手規範,在握手階段依然使用 HTTP 協議,握手完成以後,數據經過 TCP 通道進行傳輸。
Websoket 數據傳輸是經過 frame 形式,一個消息能夠分紅幾個片斷傳輸。這樣大數據能夠分紅一些小片斷進行傳輸,不用考慮因爲數據量大致使標誌位不夠的狀況。也能夠邊生成數據邊傳遞消息,提升傳輸效率。
優勢: 雙向通訊。客戶端和服務端雙方均可以主動發起通信。 沒有同源限制。客戶端能夠與任意服務端通訊,不存在跨域問題。 數據量輕。第一次鏈接時須要攜帶請求頭,後面數據通訊都不須要帶請求頭,減小了請求頭的負荷。 傳輸效率高。由於只須要一次鏈接,因此數據傳輸效率高。
缺點: 長鏈接須要後端處理業務的代碼更穩定,推送消息相對複雜;
長鏈接受網絡限制比較大,須要處理好重連。
兼容性,WebSocket 只支持 IE10 及其以上版本。
服務器長期維護長鏈接須要必定的成本,各個瀏覽器支持程度不一;
成熟的 HTTP 生態下有大量的組件能夠複用,WebSocket 則沒有,遇到異常問題難以快速定位快速解決。【須要後端代碼穩定,受網絡限制大,兼容性差,維護成本高,生態圈小】
相同點:
不一樣點:
概念
DTD(document type definition,文檔類型定義)聲明於文檔最前面,用來定義XML或(X)HTML的文件類型。瀏覽器會使用它來判斷文檔類型,並根據這個判斷決定用什麼引擎來解析和渲染他們。
解析引擎的兩種模式
解析引擎有嚴格模式和混雜模式。嚴格模式的排版和 JS 運做模式是 以該瀏覽器支持的最高標準運行。混雜模式,向後兼容,模擬老式瀏覽器,防止瀏覽器沒法兼容頁面。
DOCTYPE的做用
DOCTYPE是用來聲明文檔類型和DTD規範的,其做用一是文件的合法性驗證。若是文件代碼不合法,那麼瀏覽器解析時會出一些錯誤。二是瀏覽器會使用它來判斷文檔類型,並根據這個判斷決定用什麼引擎來解析和渲染他們。
概念:
HTML5的語義化指的是合理正確的使用語義化的標籤來建立頁面結構
。【正確的標籤作正確的事】
語義化標籤:
header nav main article section aside footer
語義化的優勢:
沒CSS樣式的狀況下,頁面總體也會呈現很好的結構效果
代碼結構清晰
,易於閱讀,利於開發和維護
方便其餘設備解析(如屏幕閱讀器)根據語義渲染網頁。有利於搜索引擎優化(SEO)
,搜索引擎爬蟲會根據不一樣的標籤來賦予不一樣的權重漸進加強(Progressive Enhancement): 一開始就針對低版本瀏覽器進行構建頁面,完成基本的功能,而後再針對高級瀏覽器進行效果、交互、追加功能達到更好的體驗。
優雅降級(Graceful Degradation): 一開始就構建站點的完整功能,而後針對瀏覽器測試和修復。好比一開始使用 CSS3 的特性構建了一個應用,而後逐步針對各大瀏覽器進行 hack 使其能夠在低版本瀏覽器上正常瀏覽。 二者區別 一、廣義: 其實要定義一個基準線,在此之上的加強叫作漸進加強,在此之下的兼容叫優雅降級 二、狹義: 漸進加強通常說的是使用CSS3技術,在不影響老瀏覽器的正常顯示與使用情形下來加強體驗,而優雅降級則是體現html標籤的語義,以便在js/css的加載失敗/被禁用時,也不影響用戶的相應功能。
/* 例子 */
.transition { /*漸進加強寫法*/
-webkit-transition: all .5s;
-moz-transition: all .5s;
-o-transition: all .5s;
transition: all .5s;
}
.transition { /*優雅降級寫法*/
transition: all .5s;
-o-transition: all .5s;
-moz-transition: all .5s;
-webkit-transition: all .5s;
}
複製代碼
不一樣瀏覽器的標籤默認的margin和padding不同。*{margin:0;padding:0;}
IE6雙邊距bug:塊屬性標籤float後,又有橫行的margin狀況下,在IE6顯示margin比設置的大。hack:display:inline;將其轉化爲行內屬性。
設置較小高度標籤(通常小於10px),在IE6,IE7中高度超出本身設置高度。hack:給超出高度的標籤設置overflow:hidden;或者設置行高line-height 小於你設置的高度。
Chrome 中文界面下默認會將小於 12px 的文本強制按照 12px 顯示,可經過加入 CSS 屬性 -webkit-text-size-adjust: none; 解決。
超連接訪問事後hover樣式就不出現了,被點擊訪問過的超連接樣式再也不具備hover和active了。解決方法是改變CSS屬性的排列順序:L-V-H-A ( love hate ): a:link {} a:visited {} a:hover {} a:active {}
選擇器
優先級
帶!important 標記的樣式屬性優先級最高; 樣式表的來源不一樣時,優先級順序爲:內聯樣式> 內部樣式 > 外部樣式 > 瀏覽器用戶 自定義樣式 > 瀏覽器默認樣式
樣式表的來源相同時:!important > 行內樣式>ID選擇器 > 類選擇器 > 標籤 > 通配符 > 繼承 > 瀏覽器默認屬性
CSS 盒模型本質上是一個盒子,它包括:邊距,邊框,填充和實際內容。CSS 中的盒子模型包括 IE 盒子模型和標準的 W3C 盒子模型。在標準的盒子模型中,width 指 content 部分的寬度,在 IE 盒子模型中,width 表示 content+padding+border 這三個部分的寬度,故在計算盒子的寬度時存在差別:
標準盒模型: 一個塊的總寬度=width+margin(左右)+padding(左右)+border(左右)
怪異盒模型: 一個塊的總寬度=width+margin(左右)(既 width 已經包含了 padding 和 border 值)
BFC的概念
BFC
是 Block Formatting Context
的縮寫,即塊級格式化上下文。BFC
是CSS佈局的一個概念,是一個獨立的渲染區域,規定了內部box如何佈局, 而且這個區域的子元素不會影響到外面的元素,其中比較重要的佈局規則有內部 box 垂直放置,計算 BFC 的高度的時候,浮動元素也參與計算。
BFC的原理佈局規則
垂直方向
,一個接一個地放置垂直方向的距離由margin決定
。屬於同一個BFC的兩個相鄰Box的margin會發生重疊不會與float box重疊
子元素不會影響到外面的元素
浮動元素也參與計算高度
display屬性,決定了這個Box的類型
。不一樣類型的Box會參與不一樣的Formatting Context
。如何建立BFC?
BFC的使用場景
/*全部屬性從原始值到制定值的一個過渡,運動曲線ease,運動時間0.5秒*/
transition:all,.5s
複製代碼
//animation:動畫名稱,一個週期花費時間,運動曲線(默認ease),動畫延遲(默認0),播放次數(默認1),是否反向播放動畫(默認normal),是否暫停動畫(默認running)
/*執行一次logo2-line動畫,運動時間2秒,運動曲線爲 linear*/
animation: logo2-line 2s linear;
複製代碼
//transform:適用於2D或3D轉換的元素
//transform-origin:轉換元素的位置(圍繞那個點進行轉換)。默認(x,y,z):(50%,50%,0)
transform:translate(30px,30px);
transform:rotate(30deg);
transform:scale(.8);
複製代碼
選擇器:nth-of-type()
陰影 文字陰影: text-shadow: 2px 2px 2px #000;(水平陰影,垂直陰影,模糊距離,陰影顏色) 盒子陰影: box-shadow: 10px 10px 5px #999
邊框 border-image: url(border.png);
背景
文字
漸變
Filter(濾鏡)
彈性佈局、柵格佈局、多列布局
媒體查詢
固定定位 fixed: 元素的位置相對於瀏覽器窗口是固定位置,即便窗口是滾動的它也不會移動。Fixed 定 位使元素的位置與文檔流無關,所以不佔據空間。 Fixed 定位的元素和其餘元素重疊。
相對定位 relative: 若是對一個元素進行相對定位,它將出如今它所在的位置上。而後,能夠經過設置垂直 或水平位置,讓這個元素「相對於」它的起點進行移動。 在使用相對定位時,不管是 否進行移動,元素仍然佔據原來的空間。所以,移動元素會致使它覆蓋其它框。
絕對定位 absolute: 絕對定位的元素的位置相對於最近的已定位父元素,若是元素沒有已定位的父元素,那 麼它的位置相對於。absolute 定位使元素的位置與文檔流無關,所以不佔據空間。 absolute 定位的元素和其餘元素重疊。
粘性定位 sticky: 元素先按照普通文檔流定位,而後相對於該元素在流中的 flow root(BFC)和 containing block(最近的塊級祖先元素)定位。然後,元素定位表現爲在跨越特定閾值前爲相對定 位,以後爲固定定位。
默認定位 Static: 默認值。沒有定位,元素出如今正常的流中(忽略 top, bottom, left, right 或者 z-index 聲 明)。 inherit: 規定應該從父元素繼承 position 屬性的值。
box-sizing 規定兩個並排的帶邊框的框,語法爲 box-sizing:content-box/border-box/inherit
content-box:寬度和高度分別應用到元素的內容框,在寬度和高度以外繪製元素的內邊距和邊框。【標準盒子模型】
border-box:爲元素設定的寬度和高度決定了元素的邊框盒。【IE 盒子模型】
inherit:繼承父元素的 box-sizing 值。
水平居中
對於 行內元素 : text-align: center
;
對於肯定寬度的塊級元素:
(1)width和margin實現。margin: 0 auto
;
(2)絕對定位和margin-left: -width/2, 前提是父元素position: relative
對於寬度未知的塊級元素
(1)table標籤配合margin左右auto實現水平居中
。使用table標籤(或直接將塊級元素設值爲 display:table),再經過給該標籤添加左右margin爲auto。
(2)inline-block實現水平居中方法。display:inline-block和text-align:center實現水平居中。
(3)絕對定位+transform
,translateX能夠移動自己元素的50%。
(4)flex佈局使用justify-content:center
垂直居中
line-height
實現居中,這種方法適合純文字類絕對定位
,標籤經過margin實現自適應居中table 佈局
,父級經過轉換成表格形式,而後子級設置 vertical-align 實現
。(須要注意的是:vertical-align: middle使用的前提條件是內聯元素以及display值爲table-cell的元素)。佈局的傳統解決方案,基於盒狀模型,依賴 display 屬性 + position 屬性 + float 屬性。它對於那些特殊佈局很是不方便,好比,垂直居中就不容易實現。
Flex 是 Flexible Box 的縮寫,意爲"彈性佈局",用來爲盒狀模型提供最大的靈活性。指定容器 display: flex 便可。 簡單的分爲容器屬性和元素屬性。
容器的屬性:
項目的屬性(元素的屬性):
首先 Rem 相對於根(html)的 font-size 大小來計算。簡單的說它就是一個相對單例 如:font-size:10px;,那麼(1rem = 10px)瞭解計算原理後首先解決怎麼在不一樣設備上設置 html 的 font-size 大小。其實 rem 佈局的本質是等比縮放,通常是基於寬度。
優勢:能夠快速適用移動端佈局,字體,圖片高度
缺點:
①目前 ie 不支持,對 pc 頁面來說使用次數很少;
②數據量大:全部的圖片,盒子都須要咱們去給一個準確的值;才能保證不一樣機型的適配;
③在響應式佈局中,必須經過 js 來動態控制根元素 font-size 的大小。也就是說 css 樣式和 js 代碼有必定的耦合性。且必須將改變 font-size 的代碼放在 css 樣式以前。
經過百分比單位 " % " 來實現響應式的效果。經過百分比單位能夠使得瀏覽器中的組件的寬和高隨着瀏覽器的變化而變化,從而實現響應式的效果。 直觀的理解,咱們可能會認爲子元素的百分比徹底相對於直接父元素,height 百分比相 對於 height,width 百分比相對於 width。 padding、border、margin 等等不管是垂直方向仍是水平方向,都相對於直接父元素的 width。 除了 border-radius 外,還有好比 translate、background-size 等都是相對於自身的。
缺點:
(1)計算困難
(2)各個屬性中若是使用百分比,相對父元素的屬性並非惟一的。形成咱們使用百分比單位容易使佈局問題變得複雜。
浮動佈局:當元素浮動之後能夠向左或向右移動,直到它的外邊緣碰到包含它的框或者另一個浮動元素的邊框爲止。元素浮動之後會脫離正常的文檔流,因此文檔的普通流中的框就變的好像浮動元素不存在同樣。
優勢
這樣作的優勢就是在圖文混排的時候能夠很好的使文字環繞在圖片周圍。另外當元素浮動了起來以後,它有着塊級元素的一些性質例如能夠設置寬高等,但它與inline-block仍是有一些區別的,第一個就是關於橫向排序的時候,float能夠設置方向而inline-block方向是固定的;還有一個就是inline-block在使用時有時會有空白間隙的問題
缺點
最明顯的缺點就是浮動元素一旦脫離了文檔流,就沒法撐起父元素,會形成父級元素高度塌陷
。
<div class="parent">
//添加額外標籤而且添加clear屬性
<div style="clear:both"></div>
//也能夠加一個br標籤
複製代碼
//在css中添加:after僞元素
.parent:after{
/* 設置添加子元素的內容是空 */
content: '';
/* 設置添加子元素爲塊級元素 */
display: block;
/* 設置添加的子元素的高度0 */
height: 0;
/* 設置添加子元素看不見 */
visibility: hidden;
/* 設置clear:both */
clear: both;
}
複製代碼
什麼事CSS預處理器?
CSS預處理器是一種語言用來爲CSS增長一些變成的特性,無需考慮瀏覽器兼容問題,例如你能夠在CSS中使用變量,簡單的程序邏輯、函數等在編程語言中的一些基本技巧,可讓CSS更加簡潔,適應性更強,代碼更直觀等諸多好處 基本語法區別
Sass是以.sass爲擴展名,Less是以.less爲擴展名,Stylus是以.styl爲擴展名 變量的區別
Sass 變量必須是以$
開頭的,而後變量和值之間使用冒號(:)隔開,和css屬性是同樣的。 Less 變量是以@
開頭的,其他sass都是同樣的。 Stylus 對變量是沒有任何設定的,能夠是以$開頭或者任意字符,並且變量之間能夠冒號,空格隔開,可是在stylus中不能用@開頭 三種預處理器都有:嵌套、運算符、顏色函數、導入、繼承、混入。Stylus還有一些高級特性。例如循環、判斷等
1.opacity:0
,該元素隱藏起來了,但不會改變頁面佈局,而且,若是該元素已經綁定 一些事件,如click 事件,那麼點擊該區域,也能觸發點擊事件的
2.visibility:hidden
,該元素隱藏起來了,但不會改變頁面佈局,可是不會觸發該元素已 經綁定的事件 ,隱藏對應元素,在文檔佈局中仍保留原來的空間(重繪)
3.display:none
,把元素隱藏起來,而且會改變頁面佈局,能夠理解成在頁面中把該元素。 不顯示對應的元素,在文檔佈局中再也不分配空間(迴流+重繪)
包括值類型(基本對象類型)和引用類型(複雜對象類型)
基本類型(值類型): Number(數字),String(字符串),Boolean(布爾),Symbol(符號),null(空),undefined(未定義)在內存中佔據固定大小,保存在棧內存中
引用類型(複雜數據類型): Object(對象)、Function(函數)。其餘還有Array(數組)、Date(日期)、RegExp(正則表達式)、特殊的基本包裝類型(String、Number、Boolean) 以及單體內置對象(Global、Math)等 引用類型的值是對象 保存在堆內存中,棧內存存儲的是對象的變量標識符以及對象在堆內存中的存儲地址。
Symbol 是 ES6 的新增屬性,表明用給定名稱做爲惟一標識,這種類型的值能夠這樣建立,let id=symbol(「id」);Symbl 確保惟一,即便採用相同的名稱,也會產生不一樣的值,有內置方法 Object.getOwnPropertySymbols(obj)能夠得到全部的 symbol。 也有一個方法 Reflect.ownKeys(obj)返回對象全部的鍵,包括 symbol。
undefined是訪問一個未初始化的變量時返回的值,而null是訪問一個還沒有存在的對象時所返回的值。undefined看做是空的變量,而null看做是空的對象
console.log(typeof 1); // number
console.log(typeof true); // boolean
console.log(typeof 'mc'); // string
console.log(typeof function(){}); // function
console.log(typeof console.log()); // function
console.log(typeof []); // object
console.log(typeof {}); // object
console.log(typeof null); // object
console.log(typeof undefined); // undefined
複製代碼
優勢:可以快速區分基本數據類型
缺點:不能將Object、Array和Null區分,都返回object
console.log(1 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
複製代碼
優勢:可以區分Array、Object和Function,適合用於判斷自定義的類實例對象
缺點:Number,Boolean,String基本數據類型不能判斷
var toString = Object.prototype.toString;
console.log(toString.call(1)); //[object Number]
console.log(toString.call(true)); //[object Boolean]
console.log(toString.call('mc')); //[object String]
console.log(toString.call([])); //[object Array]
console.log(toString.call({})); //[object Object]
console.log(toString.call(function(){})); //[object Function]
console.log(toString.call(undefined)); //[object Undefined]
console.log(toString.call(null)); //[object Null]
複製代碼
優勢:精準判斷數據類型
缺點:寫法繁瑣不容易記,推薦進行封裝後使用
強制類型轉換
// 1.字符串拼接
var a = 100 + 10 // 110
var b = 100 + '10' // '10010'
// 2.==運算符
100 = '100' // true
0 == '' // true
null = undefined // true [nullundefined都會轉換成false]
// 3.if語句
let a = true
if(a){
//
}
let b = 100
if(b){
// 會將b強制轉換成boolean類型
}
let c = ''
if(c){
// 空字符串會被強制轉換成boolean
}
//4.邏輯運算符
console. Log (10 && 0) // 0
console. log('' || 'abc') // 'abc'
console. log(!window.abc) // true
//判斷一個變量會被當作true仍是false,使用雙非判斷
var a = 100
console.log(!!a)
複製代碼
ES6以前建立變量用的是var,以後建立變量用的是let/const
三者區別:
var定義的變量,沒有塊的概念,能夠跨塊訪問
, 不能跨函數訪問。
let定義的變量,只能在塊做用域裏訪問,不能跨塊訪問,也不能跨函數訪問。
const用來定義常量,使用時必須初始化(即必須賦值),只能在塊做用域裏訪問,且不能修改。
var能夠先使用,後聲明
,由於存在變量提高;let必須先聲明後使用。
var是容許在相同做用域內重複聲明同一個變量
的,而let與const不容許這一現象。
在全局上下文中,基於let聲明的全局變量和全局對象GO(window)沒有任何關係 ;
var聲明的變量會和GO有映射關係;
解決暫時性死區
:
暫時性死區是瀏覽器的bug:檢測一個未被聲明的變量類型時,不會報錯,會返回undefined
如:console.log(typeof a) //undefined
而:console.log(typeof a)//未聲明以前不能使用
let a
當瀏覽器開闢出供代碼執行的棧內存後,代碼並無自上而下當即執行,而是繼續作了一些事情:把當前做用域中全部帶var、function關鍵字的進行提早的聲明和定義 =>變量提高機制 【預解析】
a=3
表示給全局設置window.a=3
和在全局做用域下var a=3
是同樣的;在變量提高階段,遇到大括號
、判斷體
等,不論條件是否成立,都要進行變量提高,而在高版本瀏覽器中,函數只聲明、不賦值。
什麼是單線程
單線程即同一時間只有一個線程,只能作一件事
緣由:避免DOM渲染的衝突
解決方案:異步
實現方式:event-loop
JS的 同步任務/異步任務
同步任務:在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務
異步:不進入主線程、而進入"任務隊列
"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行
JavaScript爲何須要異步
若是在JS代碼執行過程當中,某段代碼執行太久,後面的代碼遲遲不能執行,產生阻塞(即卡死),會影響用戶體驗。
JavaScript怎麼實現異步
JS 實現異步時經過 事件循環(Event Loop),是JS異步的解決方案。 JS實現異步的具體解決方案
一、同步代碼,直接執行
二、異步代碼先放在 異步隊列
中
三、待同步函數執行完畢,輪詢執行異步隊列 中的函數
目前JS解決異步的方案有哪些
Promise
Async/Await
JS內存空間分爲棧(stack) 、堆(heap) 、池(通常也會歸類爲棧中) 。 其中棧存放變量,堆存放複雜對象,池存放常量,因此也叫常量池。
棧
內存(不包含閉包中的變量)堆
內存棧內存(Stack):瀏覽器在計算機內存中分配出一塊內存供代碼執行的環境棧(ECStack),也稱棧內存 ;
基本數據類型都是存到棧裏面的。 引用數據類型指針存到棧內存。
堆內存(Heap):瀏覽器會把內置的屬性和方法放到一個單獨的內存中,
引用數據類型是先開闢一個堆內存,一個16進制的地址,按照鍵、值分別存放,最後把地址放到棧中供代碼關聯使用;
js 中存在多種做用域(全局,函數私有的,塊級私有的),引擎在編譯執行代碼的過程當中,首先會建立一個執行棧,也就是棧內存(ECStack
=> 執行環境棧),而後執行代碼。
代碼執行前首先會造成本身的EC
(執行上下文),執行上下文分爲全局執行上下文(EC(G)
)和函數執行上下文(EC(...)
),其中函數的執行上下文是私有的。
建立執行上下文的過程當中,可能會建立:
GO(Global Object)
:全局對象 瀏覽器端,會把GO賦值給windowVO(Varible Object)
:變量對象,存儲當前上下文中的變量。AO(Active Object)
:活動對象而後把上下文壓縮進棧,進棧後,在當前上下文再依次執行代碼; 全局執行器上下文(EC(G))進棧(ECStack)執行,執行完代碼就會把造成的上下文釋放(出棧),執行後一些沒用的上下文也將出棧,有用的上下文會壓縮到棧底(閉包)。棧底永遠是全局執行上下文,棧頂則永遠是當前執行上下文。當頁面關閉全局上下文出棧。
VO 變量對象:每個執行上下文都會有本身的一個VO變量對象,用來存放在當前上下文中建立的變量和函數。(函數私有上下文叫 AO 活躍對象,但也是變量對象)。
GO 全局對象:他是一個堆內存(存儲的都是瀏覽器內置的 api 屬性方法),在瀏覽器端,讓 window 指向它
VO(G)全局變量對象:全局上下文中用來存儲全局變量的空間,他不是 GO=》只不過某些狀況下 VO(G)中的東西會和 GO 中的東西有所關聯而已;
函數執行:
ECStack
:引擎在編譯執行代碼的過程當中,首先會建立一個執行棧EC(FN)
:函數執行的時候,造成一個全新的私有上下文EC(FN)
,供字符串代碼執行GO
項目中,若是存在大量不被釋放的內存(堆/棧/上下文),頁面性能會變得很慢。當某些代碼操做不能被合理釋放,就會形成內存泄漏。咱們儘量減小使用閉包,由於它會消耗內存。
瀏覽器垃圾回收機制/內存回收機制:
瀏覽器的
Javascript
具備自動垃圾回收機制(GC:Garbage Collecation
),垃圾收集器會按期(週期性)找出那些不在繼續使用的變量,而後釋放其內存。
標記清除:在js
中,最經常使用的垃圾回收機制是標記清除:當變量進入執行環境時,被標記爲「進入環境」,當變量離開執行環境時,會被標記爲「離開環境」。垃圾回收器會銷燬那些帶標記的值並回收它們所佔用的內存空間。
谷歌瀏覽器:「查找引用」,瀏覽器不定時去查找當前內存的引用,若是沒有被佔用了,瀏覽器會回收它;若是被佔用,就不能回收。
IE瀏覽器:「引用計數法」,當前內存被佔用一次,計數累加1次,移除佔用就減1,減到0時,瀏覽器就回收它。
優化手段:內存優化 ; 手動釋放:取消內存的佔用便可。
(1)堆內存:fn = null 【null:空指針對象】
(2)棧內存:把上下文中,被外部佔用的堆的佔用取消便可。
內存泄漏
在 JS 中,常見的內存泄露主要有 4 種,全局變量、閉包、DOM 元素的引用、定時器
建立函數的時候,已經聲明瞭當前函數的做用域==>當前建立函數所處的上下文
。若是是在全局下建立的函數就是[[scope]]:EC(G)
,函數執行的時候,造成一個全新的私有上下文EC(FN)
,供字符串代碼執行(進棧執行)
定義:簡單來講做用域就是變量與函數的可訪問範圍,由當前環境與上層環境的一系列變量對象組成
1.全局做用域:代碼在程序的任何地方都能被訪問,window 對象的內置屬性都擁有全局做用域。
2.函數做用域:在固定的代碼片斷才能被訪問
做用:做用域最大的用處就是隔離變量
,不一樣做用域下同名變量不會有衝突。
做用域鏈參考連接通常狀況下,變量到 建立該變量 的函數的做用域中取值。可是若是在當前做用域中沒有查到,就會向上級做用域去查,直到查到全局做用域,這麼一個查找過程造成的鏈條就叫作做用域鏈。
閉包的概念
函數執行時造成的私有上下文EC(FN),正常狀況下,代碼執行完會出棧後釋放;可是特殊狀況下,若是當前私有上下文中的某個東西被上下文之外的事物佔用了,則上下文不會出棧釋放,從而造成不銷燬的上下文。 函數執行函數執行過程當中,會造成一個全新的私有上下文,可能會被釋放,可能不會被釋放,不論釋放與否,他的做用是:
(1)保護:劃分一個獨立的代碼執行區域,在這個區域中有本身私有變量存儲的空間,保護本身的私有變量不受外界干擾(操做本身的私有變量和外界沒有關係);
(2)保存:若是當前上下文不被釋放【只要上下文中的某個東西被外部佔用便可】,則存儲的這些私有變量也不會被釋放,能夠供其下級上下文中調取使用,至關於把一些值保存起來了;
咱們把函數執行造成私有上下文,來保護和保存私有變量機制稱爲閉包
。
閉包是指有權訪問另外一個函數做用域中的變量的函數--《JavaScript高級程序設計》
稍全面的回答: 在js中變量的做用域屬於函數做用域, 在函數執行完後,做用域就會被清理,內存也會隨之被回收,可是因爲閉包函數是創建在函數內部的子函數, 因爲其可訪問上級做用域,即便上級函數執行完, 做用域也不會隨之銷燬, 這時的子函數(也就是閉包),便擁有了訪問上級做用域中變量的權限,即便上級函數執行完後做用域內的值也不會被銷燬。
閉包的特性:
一、內部函數能夠訪問定義他們外部函數的參數和變量。(做用域鏈的向上查找,把外圍的做用域中的變量值存儲在內存中而不是在函數調用完畢後銷燬)設計私有的方法和變量,避免全局變量的污染。
1.1.閉包是密閉的容器,,相似於set、map容器,存儲數據的
1.2.閉包是一個對象,存放數據的格式爲 key-value 形式
二、函數嵌套函數
三、本質是將函數內部和外部鏈接起來。優勢是能夠讀取函數內部的變量,讓這些變量的值始終保存在內存中,不會在函數被調用以後自動清除
閉包造成的條件:
閉包的用途:
閉包應用場景
閉包的兩個場景,閉包的兩大做用:保存/保護
。 在開發中, 其實咱們隨處可見閉包的身影, 大部分前端JavaScript 代碼都是「事件驅動」的,即一個事件綁定的回調方法; 發送ajax請求成功|失敗的回調;setTimeout的延時回調;或者一個函數內部返回另外一個匿名函數,這些都是閉包的應用。
閉包的優勢:延長局部變量的生命週期
閉包缺點:會致使函數的變量一直保存在內存中,過多的閉包可能會致使內存泄漏
this
指向window
。this
就會指向該對象
。this
指向返回的這個對象
。this
綁定看的是this所在函數定義在哪一個對象下
,就綁定哪一個對象。若是有嵌套的狀況,則this綁定到最近的一層對象上。apply 、 call 和 bind
調用模式,這三個方法均可以顯示的指定調用函數的 this 指向。apply
接收參數的是數組,call
接受參數列表,`` bind方法經過傳入一個對象,返回一個
this 綁定了傳入對象的新函數。這個函數的
this指向除了使用
new `時會被改變,其餘狀況下都不會改變。若爲空默認是指向全局對象window。// 第一種:字面量
var o1 = {name: "o1"}
var o2 = new Object({name: "o2"})
// 第二種:經過構造函數
var M = function(name){this.name = name}
var o3 = new M("o3")
// 第三種:Object.create()
var p = {name: "p"}
var o4 = Object.create(p)
複製代碼
ES6提供了更接近面向對象(注意:javascript本質上是基於對象的語言)語言的寫法,引入了Class(類)這個概念,做爲對象的模板。經過class關鍵字,能夠定義類。 類的建立(es5):new 一個 function,在這個 function 的 prototype 裏面增長屬性和 方法。
5條原型規則,是學習理解原型鏈的基礎。
1.全部的引用類型(數組、對象、函數),都具備對象特性
,便可自由擴展屬性(除了「nul」意外)
2.全部的引用類型(數組、對象、函數),都有一個_proto_(隱式原型)屬性
,屬性值是一個普通的對象
3.全部的函數,都有一個prototype(顯式原型)屬性
,屬性值也是一個普通的對象
4.全部的引用類型(數組、對象、函數),__ proto __ 屬性值指向它的構造函數的" __ prototype __ "屬性值
5.當試圖獲得一個對象的某個屬性時,若是這個對象自己沒 有這個屬性,那麼會去它的__proto__(即它的構造函數的 prototype)中尋找
。
var obj = {}; obj.a = 100;//符合第一條能夠自由擴展屬性
var arr = []; arr.a = 100;
function fn(){}
fn.a=100;
console. log(obj.__proto__)
console. log(arr.__proto__)
console. log(fn.__proto__)
console. log(fn.prototype)
console.log(obj.__proto__===Object. prototype)
複製代碼
示例:
// 構造函數
function Foo(name, age) {
this.name = name;
}
Foo.prototype. alertName = function(){
alert(this.name);
}
//建立示例
var f = new Foo('zhangsan');
f.printName = function (){
console.log(this. name);
}
// 測試
f.printName();
f.alertName();
f.toString() //要去f._proto_._proto_中查找 原型鏈
複製代碼
原型鏈就是我從個人實例對象網上找構造這個實例相關聯的對象,而後這個關聯的對象再往上找它又有創造它的原型對象以此類推,一直到Object.prototype原型對象終止。Object.prototype原型對象是整個原型鏈的頂端,到這就截止了。
原型關係:
基於原型的執行規則:即原型鏈
原型: 在 JS 中,每當定義一個對象(函數也是對象)時,對象中都會包含一些預約義的屬性。其中每一個函數對象都有一個prototype
屬性,這個屬性指向函數的原型對象。
原型鏈:函數的原型鏈對象constructor默認指向函數自己,原型對象除了有原型屬性外,爲了實現繼承,還有一個原型鏈指針__proto__,該指針是指向上一層的原型對象,而上一層的原型對象的結構依然相似。所以能夠利用__proto__一直指向Object的原型對象上,而Object原型對象用Object.prototype.proto=null表示原型鏈頂端。如此造成了js的原型鏈繼承。同時全部的js對象都有Object的基本防範
特色: JavaScript
對象是經過引用來傳遞的,咱們建立的每一個新對象實體中並無一份屬於本身的原型副本。當咱們修改原型時,與之相關的對象也會繼承這一改變。
空對象
設置原型
,將對象的原型設置爲函數的prototype
對象。this
指向這個對象,執行構造函數的代碼(爲這個新對象添加屬性)(1)第一種是以原型鏈的方式來實現繼承
,可是這種實現方式存在的缺點是,在包含有引用類型的數據時,會被全部的實例對象所共享,容易形成修改的混亂。還有就是在建立子類型的時候不能向超類型傳遞參數。
(2)第二種方式是使用借用構造函數
的方式,這種方式是經過在子類型的函數中調用超類型的構造函數來實現的,這一種方法解決了不能向超類型傳遞參數的缺點,可是它存在的一個問題就是沒法實現函數方法的複用,而且超類型原型定義的方法子類型也沒有辦法訪問到。
(3)第三種方式是組合繼承
,組合繼承是將原型鏈和借用構造函數組合起來使用的一種方式。經過借用構造函數的方式來實現類型的屬性的繼承,經過將子類型的原型設置爲超類型的實例來實現方法的繼承。這種方式解決了上面的兩種模式單獨使用時的問題,可是因爲咱們是以超類型的實例來做爲子類型的原型,因此調用了兩次超類的構造函數,形成了子類型的原型中多了不少沒必要要的屬性。
(4)第四種方式是原型式繼承
,原型式繼承的主要思路就是基於已有的對象來建立新的對象,實現的原理是,向函數中傳入一個對象,而後返回一個以這個對象爲原型的對象。這種繼承的思路主要不是爲了實現創造一種新的類型,只是對某個對象實現一種簡單繼承,ES5 中定義的 Object.create() 方法就是原型式繼承的實現。缺點與原型鏈方式相同。
(5)第五種方式是寄生式繼承
,寄生式繼承的思路是建立一個用於封裝繼承過程的函數,經過傳入一個對象,而後複製一個對象的副本,而後對象進行擴展,最後返回這個對象。這個擴展的過程就能夠理解是一種繼承。這種繼承的優勢就是對一個簡單對象實現繼承,若是這個對象不是咱們的自定義類型時。缺點是沒有辦法實現函數的複用。
(6)第六種方式是寄生式組合繼承
,組合繼承的缺點就是使用超類型的實例作爲子類型的原型,致使添加了沒必要要的原型屬性。寄生式組合繼承的方式是使用超類型的原型的副原本做爲子類型的原型,這樣就避免了建立沒必要要的屬性。
最簡單的方法就是JSON.parse(JSON.stringify()),可是這種拷貝方法不能夠拷貝一些特殊的屬性(例如正則表達式,undefine,function)
//對象深度克隆的簡單實現
var shallowCopy = function(obj) { // 只拷貝對象
if (typeof obj !== 'object') return;
// 根據 obj 的類型判斷是新建一個數組仍是對象
var newObj = obj instanceof Array ? [] : {};
// 遍歷 obj,而且判斷是 obj 的屬性才拷貝
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
複製代碼
//方法 1
Object.assign(target, ...sources)
//方法 2
function simpleClone(obj) {
var result = {};
for (var i in obj) {
result[i] = obj[i];
}
return result;
}
複製代碼
複製代碼
JS
是單線程的,爲了防止一個函數執行時間過長阻塞後面的代碼,因此會先將同步代碼壓入執行棧中,依次執行,將異步代碼推入異步隊列,異步隊列又分爲宏任務隊列和微任務隊列,由於宏任務隊列的執行時間較長,因此微任務隊列要優先於宏任務隊列。微任務隊列的表明就是,Promise.then
,MutationObserver
,宏任務的話就是setImmediate setTimeout setInterval
JS運行的環境。通常爲瀏覽器或者Node。 在瀏覽器環境中,有JS 引擎線程和渲染線程,且兩個線程互斥。 Node環境中,只有JS 線程。 不一樣環境執行機制有差別,不一樣任務進入不一樣Event Queue隊列。 當主程結束,先執行準備好微任務,而後再執行準備好的宏任務,一個輪詢結束。
事件環的運行機制是,先會執行棧中的內容,棧中的內容執行後執行微任務,微任務清空後再執行宏任務,先取出一個宏任務,再去執行微任務,而後在取宏任務清微任務這樣不停的循環。
eventLoop 是由JS的宿主環境(瀏覽器)來實現的;
事件循環能夠簡單的描述爲如下四個步驟:
瀏覽器中的任務源(task):
宏任務(macrotask)
:
宿主環境提供的,好比瀏覽器
ajax、setTimeout、setInterval、setTmmediate(只兼容ie)、script、requestAnimationFrame、messageChannel、UI渲染、一些瀏覽器api
微任務(microtask)
:
語言自己提供的,好比promise.then
then、queueMicrotask(基於then)、mutationObserver(瀏覽器提供)、messageChannel 、mutationObersve
Node
是基於V8引擎的運行在服務端的JavaScript
運行環境,在處理高併發、I/O密集(文件操做、網絡操做、數據庫操做等)場景有明顯的優點。雖然用到也是V8引擎,但因爲服務目的和環境不一樣,致使了它的API與原生JS有些區別,其Event Loop還要處理一些I/O,好比新的網絡鏈接等,因此Node的Event Loop(事件環機制)與瀏覽器的是不太同樣。
執行順序以下:
timers
: 計時器,執行setTimeout和setInterval的回調pending callbacks
: 執行延遲到下一個循環迭代的 I/O 回調idle, prepare
: 隊列的移動,僅系統內部使用poll輪詢
: 檢索新的 I/O 事件;執行與 I/O 相關的回調。事實上除了其餘幾個階段處理的事情,其餘幾乎全部的異步都在這個階段處理。check
: 執行setImmediate
回調,setImmediate在這裏執行close callbacks
: 執行close
事件的callback
,一些關閉的回調函數,如:socket.on('close', ...)回調有四個問題
new Promise(
/*執行器executor */
function (resolve, reject){
//段耗時很長的異步操做
resolve();//數據處理完成
reject(); //數據處理出錯
}
.then(function A(){
//成功,下一步
},function B(){
//失敗,作相應處理
}
複製代碼
Promise的內部是如何實現異步執行的呢?
經過查看Promise的源碼實現,發現其異步執行是經過asap這個庫來實現的。
asap是as soon as possible的簡稱,在Node和瀏覽器環境下,能將回調函數以高優先級任務來執行(下一個事件循環以前),即把任務放在微任務隊列中執行。
宏任務(macro-task)和微任務(micro-task)表示異步任務的兩種分類。在掛起任務時, js 引擎會將全部任務按照類別分到這兩個隊列中,首先在 macrotask 的隊列(這個隊列也被叫作 task queue)中取出第一個任務,執行完畢後取出 microtask 隊列中的全部任務順序執行;以後再取 macrotask 任務,周而復始,直至兩個隊列的任務都取完。
// asap 用法
asap(function () {
// ...
});
複製代碼
pending
|【待定】初始狀態fulfilled
|【實現】操做成功rejected
【被否決】操做失敗Promise狀態發生改變,就會觸發.then()裏的響應函數處理後 續步驟。 Promise狀態一經改變,不會再變。 Promise實例一經建立,執行器當即執行。
.then()接受兩個函數做爲參數,分別表明fulilled
和rejected .then()
返回一個新的Promise實例,因此它能夠鏈式調用 .當前面的Promise狀態改變時,.then()根據其最終狀態,選擇特定 的狀態響應函數執行 .狀態響應函數能夠返回新的Promise,或其它值 .若是返回新的Promise,那麼下一級.then()會在新Promise狀態改變以後執行 .若是返回其它任何值,則會馬上執行下一級.then()
由於.then()返回的仍是Promise實例。 會等裏面的.then()執行完,在執行外面的。 對於咱們來講,此時最好將其展開,會更好讀。
Promise會自動捕獲內部異常,並交給rejected響應函數處理。 錯誤處理的兩種作法: reject('錯誤信息).then(null,message =>{}) throw new Error('錯誤信息')catch(message=>{}) 推薦使用第二種,更加清晰好讀,而且能夠捕獲前面的錯誤。
返回一個rejected的Promise實例。 Promise.reject()其餘特性同Promise.resolve(),但不認thenable
相似Promise.all(),區別在於它有任意一個完成就算完成。 場景用法: 把異步操做和定時器放在一塊兒 若是定時器先觸發,就認爲超時,告知用戶
把回調包裝成Promise最爲常見。它有兩個顯而易見的好處: 可讀性更好 返回的結果能夠加入任何Promise隊列
c。
ajax是一種異步通訊的方法,從服務端獲取數據,達到局部刷新頁面的效果。 過程:
XMLHttpRequest
對象;open
方法傳入三個參數 請求方式(GET/POST)、url、同步異步(true/false)
;onreadystatechange
事件,當readystate
等於4時返回responseText
;JS高階編程技巧:惰性函數/柯理化函數/高階函數 constructor構造函數模式 類和實例 call/apply/bind DOM/BOM的核心操做 DOM2級事件的核心運行機制 事件對象 發佈訂閱設計模式 瀏覽器底層渲染機制和DOM的迴流重繪 事件傳播機制和事件代理
ES6/ES7的核心知識 解頭函數ArrowFunction 解構聚值和拓展運算符 JS底層運行機制:單線程和同步 Set/Map數據結構 異步編程 Gonerator生成器函數 Intorator選代器和for of循環
AJAX/HTTP先後端數據交互
前端性能優化彙總(包含強緩存和弱緩存)
什麼是MVVM?
視圖模型雙向綁定
,是Model-View-ViewModel
的縮寫,也就是把MVC
中的Controller
演變成ViewModel。Model
層表明數據模型,View
表明UI組件,ViewModel
是View
和Model
層的橋樑,數據會綁定到viewModel
層並自動將數據渲染到頁面中,視圖變化的時候會通知viewModel
層更新數據。之前是操做DOM結構更新視圖,如今是數據驅動視圖
。
MVVM的優勢:
1.低耦合
。視圖(View)能夠獨立於Model變化和修改,一個Model能夠綁定到不一樣的View上,當View變化的時候Model能夠不變化,當Model變化的時候View也能夠不變;
2.可重用性
。你能夠把一些視圖邏輯放在一個Model裏面,讓不少View重用這段視圖邏輯。
3.獨立開發
。開發人員能夠專一於業務邏輯和數據的開發(ViewModel),設計人員能夠專一於頁面設計。
4.可測試
。
Vue做爲MVVM模式的實現庫的2種技術
a. 模板解析
b. 數據綁定
模板解析:實現初始化顯示
a. 解析大括號表達式
b. 解析指令
數據綁定:實現更新顯示
a. 經過數據劫持實現
建立了兩種對象Observer和complie,先建立的Observer,後建立的complie,observer是爲了監視/劫持data中全部層次的屬性,同時還爲每一種屬性建立了另一種對象dep,dep與data中的屬性一一對應,complie做用是用來編譯模版,初始化界面,調用update對象,complie還爲每一個表達式建立了對應的watcher同時指定了更新節點的回調函數,將watcher添加到全部對應的dep中,
Vue 是一個構建數據驅動的漸進性框架,它的目標是經過 API 實現響應數據綁定和視圖更新。
優勢:
一、數據驅動視圖
,對真實 dom 進行抽象出 virtual dom, 並配合 diff 算法、響應式和觀察者、異步隊列等手段以最小代價更新 dom,渲染頁面
二、組件化
,組件用單文件的形式進行代碼的組織編寫,使得咱們能夠在一個文 件裏編寫 html\css\js 而且配合 Vue-loader 以後,支持更強大的預處理器等功能
三、強大且豐富的 API
提供一系列的 api 能知足業務開發中各種需求
四、因爲採用虛擬 dom
,但讓 Vue ssr 先天不足
五、生命週期鉤子函數
,選項式的代碼組織方式,寫熟了仍是蠻順暢的,但仍然 有優化空間(Vue3 composition-api)
六、生態好
,社區活躍
缺點:
一、因爲底層基於 Object.defineProperty 實現響應式
,而這個 api 自己不支持 IE8 及如下瀏覽器
二、csr 的先天不足,首屏性能問題(白屏)
三、seo 不友好
什麼是虛擬dom
說白了就是以js對象的形式去添加dom元素
本質上是優化了diff算法
虛擬dom自己也有本身的缺陷他更適合批量修改dom
儘可能不要跨層級修改dom
設置key能夠最大的利用節點,避免重複渲染
1、什麼是vdom?
Virtual DOM 就是用JS對象來模擬真實DOM結構,而後用這個樹構建一個真正的 DOM 樹, 插到文檔當中。當狀態變動的時候,從新構造一棵新的對象樹。而後用新的樹和舊的樹 進行比較,記錄兩棵樹差別 把所記錄的差別應用到所構建的真正的 DOM 樹上,視圖就更新了。Virtual DOM 本質上就是在 JS 和 DOM 之間作了一個緩存。【版本1.1】
創建一個與 dom 樹對應的虛擬 dom 對象( js 對象),以對象嵌套的方式來表示 dom 樹,那麼每次 dom 的更改就變成了 js 對象的屬性的更改,這樣一來就能查找 js 對象 的屬性變化要比查詢 dom 樹的性能開銷小。【版本1.2】
總結起來就兩點,1、用JS對象來模擬真實的DOM結構,用這個對象構建真正的DOM樹,當狀態變動時,從新構建一個新的對象樹,而後新舊樹進行對比,記錄兩者差別並應用到所構建的真正的樹上,視圖也就更新了。2、每次變動時由原來的操做真實的DOM變成了查找js對象的屬性變化,直接在內存中操做js對象,性能開銷更小,效率更高。【版本2】
總結起來就是,Virtual DOM 就是用JS對象來模擬真實DOM結構,而後用JS對象樹構建真正的DOM樹。當狀態變動時,從新構建一棵新的對象樹,而後新舊樹經過diff算法進行比較,若存在差別則將差別應用到所構建的真正的樹上,視圖就更新了。這個比較過程,由原來的查詢真實DOM樹變成查找js對象屬性,性能開銷小了,效率也就高了。Virtual DOM 本質上就是在 JS 和 DOM 之間作了一個緩存。【版本3】
2、爲什麼要用vdom?
虛擬dom就是爲了解決操做真是dom帶來的性能問題而出現的,將DOM對比操做放在JS層,提升效率
DOM結構的對比,放在JS層來作(圖靈完備語言:能實現邏輯代碼的語言)操做內存中的js顯然效率更高
3、vdom核心函數有哪些
核心函數:
h('標籤名', {...屬性名...}, [...子元素...])
h('標籤名', {...屬性名...}, '.........')
patch(container, vnode)
patch(vnode, newVnode)
vue.js是採用數據劫持結合發佈者-訂閱者模式的方式,經過Object.defineProperty()來劫持各個屬性的setter和getter,在數據變更時發佈消息給訂閱者,觸發相應的監聽回調
Vue是一個典型的MVVM框架,模型(Model)只是普通的javascript對象,修改它則試圖(View)會自動更新。這種設計讓狀態管理變得很是簡單而直觀
Observer(數據監聽器): Observer的核心是經過Object.defineProprtty()來監聽數據的變更,這個函數內部能夠定義setter和getter,每當數據發生變化,就會觸發setter。這時候Observer就要通知訂閱者,訂閱者就是Watcher
Watcher(訂閱者): Watcher訂閱者做爲Observer和Compile之間通訊的橋樑,主要作的事情是:
Compile(指令解析器): Compile主要作的事情是解析模板指令,將模板中變量替換成數據,而後初始化渲染頁面視圖,並將每一個指令對應的節點綁定更新函數,添加鑑定數據的訂閱者,一旦數據有變更,收到通知,更新試圖
參考回答: 一則語法糖,至關於 v-bind:value="xxx" 和 @input,意思是綁定了一個 value 屬性的值, 子組件可對 value 屬性監聽,經過$emit('input', xxx)的方式給父組件通信。本身實現 v-model 方式的組件也是這樣的思路。
vue-loader就是一個加載器,能把vue組件轉化成javascript模塊 爲何要轉譯vue組件? 能夠動態的渲染一些數據,對三個標籤template(結構)、style(表現)、script(行爲)都作了優化,script中能夠直接使用es6 style 也默承認以使用sass而且還給你提供做用域的選擇,另外開發階段還給你提供熱加載 還能夠以下使用:
<template src="../hello.vue"></template>
複製代碼
導航鉤子翻譯過來就是路由的生命週期函數(vue-router) 他其實主要分爲兩種全局和局部
全局的鉤子函數
beforeEach:在路由切換開始時調用
afterEach:在路由切換離開是調用
局部到單個路由 beforeEnter
組件的鉤子函數
beforeRouterEnter,
beforeRouterUpdate,
beforeRouterLeave
to:即將進入的目標對象
from:當前導航要高開的導航對象
next:是一個函數調用resolve執行下一步
每一個Vue
實例在建立時都會通過一系列的初始化過程,vue
的生命週期鉤子,就是說在達到某一階段或條件時去觸發的函數,目的就是爲了完成一些動做或者事件
create階段
:vue實例被建立beforeCreate
: 建立前,此時data和methods中的數據都尚未初始化created
: 建立完畢,data中有值,未掛載mount階段
: vue實例被掛載到真實DOM節點beforeMount
:能夠發起服務端請求,去數據mounted
: 此時能夠操做DOMupdate階段
:當vue實例裏面的data數據變化時,觸發組件的從新渲染 beforeUpdate
:更新前updated
:更新後destroy階段
:vue實例被銷燬 beforeDestroy
:實例被銷燬前,此時能夠手動銷燬一些方法 destroyed
:銷燬後生命週期(父子組件) 父組件beforeCreate --> 父組件created --> 父組件beforeMount --> 子組件beforeCreate --> 子組件created --> 子組件beforeMount --> 子組件 mounted --> 父組件mounted -->父組件beforeUpdate -->子組件beforeDestroy--> 子組件destroyed --> 父組件updated
加載渲染過程 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
掛載階段 父created->子created->子mounted->父mounted
父組件更新階段 父beforeUpdate->父updated
子組件更新階段 父beforeUpdate->子beforeUpdate->子updated->父updated
銷燬階段 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
computed與watch
通俗來說,既能用 computed 實現又能夠用 watch 監聽來實現的功能,推薦用 computed, 重點在於 computed 的緩存功能 computed 計算屬性是用來聲明式的描述一個值依賴了其它的值,當所依賴的值或者變量 改變時,計算屬性也會跟着改變; watch 監聽的是已經在 data 中定義的變量,當該變量變化時,會觸發 watch 中的方法。
watch 屬性監聽 是一個對象,鍵是須要觀察的屬性,值是對應回調函數,主要用來監聽某些特定數據的變化,從而進行某些具體的業務邏輯操做,監聽屬性的變化,須要在數據變化時執行異步或開銷較大的操做時使用
computed 計算屬性 屬性的結果會被緩存
,當computed
中的函數所依賴的屬性沒有發生改變的時候,那麼調用當前函數的時候結果會從緩存中讀取。除非依賴的響應式屬性變化時纔會從新計算,主要當作屬性來使用 computed
中的函數必須用return
返回最終的結果 computed
更高效,優先使用。data 不改變,computed 不更新。
使用場景 computed
:當一個屬性受多個屬性影響的時候使用,例:購物車商品結算功能 watch
:當一條數據影響多條數據的時候使用,例:搜索數據
1.一個組件被複用屢次的話,也就會建立多個實例。本質上,這些實例用的都是同一個構造函數。 2.若是data是對象的話,對象屬於引用類型,會影響到全部的實例。因此爲了保證組件不一樣的實例之間data不衝突,data必須是一個函數。
1.當 v-for 和 v-if 處於同一個節點時,v-for 的優先級比 v-if 更高,這意味着 v-if 將分別重複運行於每一個 v-for 循環中。若是要遍歷的數組很大,而真正要展現的數據不多時,這將形成很大的性能浪費 2.這種場景建議使用 computed,先對數據進行過濾
Vuex中全部的狀態更新的惟一途徑都是mutation,異步操做經過 Action 來提交 mutation實現,這樣使得咱們能夠方便地跟蹤每個狀態的變化,從而讓咱們可以實現一些工具幫助咱們更好地瞭解咱們的應用。 每一個mutation執行完成後都會對應到一個新的狀態變動,這樣devtools就能夠打個快照存下來,而後就能夠實現 time-travel 了。若是mutation支持異步操做,就沒有辦法知道狀態是什麼時候更新的,沒法很好的進行狀態的追蹤,給調試帶來困難。
當 Vue.js 用 v-for
更新已渲染過的元素列表時,它默認用「就地複用」策略。若是數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序,而是簡單複用此處每一個元素,而且確保它在特定索引下顯示已被渲染過的每一個元素。重複的key會形成渲染錯誤。
key
的做用主要是爲了讓vue能夠區分元素,更高效的對比更新虛擬DOM;
Vue在patch
過程當中判斷兩個節點是不是相同節點,key是一個必要條件,是惟一標識,如不定義key,Vue
只能認爲比較的兩個節點是同一個,這致使了頻繁更新元素,使得整個patch
過程比較低效,影響性能;
從源碼中能夠知道,Vue判斷兩個節點是否相同時主要判斷二者的key和元素類型等,所以若是不設置key,它的值就是undefined
,則可能永遠認爲這是兩個相同的節點,只能去作更新操做,這形成了大量的dom更新操做,明顯是不可取的。
父子組件通訊
父->子props
,子->父 $on、$emit
獲取父子組件實例 parent、children
Ref
獲取實例的方式調用組件的屬性或者方法 父->子孫 Provide、inject
官方不推薦使用,可是寫組件庫時很經常使用
兄弟組件通訊
Event Bus
實現跨組件通訊 Vue.prototype.$bus = new Vue()
自定義事件
跨級組件通訊
Vuex、$attrs、$listeners
Provide、inject
當一個Vue實例建立時,Vue會遍歷data選項的屬性,用 Object.defineProperty 將它們轉爲 getter/setter而且在內部追蹤相關依賴,在屬性被訪問和修改時通知變化。每一個組件實例都有相應的 watcher 程序實例,它會在組件渲染的過程當中把屬性記錄爲依賴,以後當依賴項的 setter 被調用時,會通知 watcher從新計算,從而導致它關聯的組件得以更新。
vue
中雙向綁定是一個指令v-model
,能夠綁定一個動態值到視圖,同時視圖中變化能改變該值。v-model
是語法糖,默認狀況下相於:value和@input
。v-model
能夠減小大量繁瑣的事件處理代碼,提升開發效率,代碼可讀性也更好v-model
v-model
,自定義組件上若是要使用它須要在組件內綁定value並處理輸入事件v-model
模板的組件渲染函數,發現它會被轉換爲value屬性的綁定以及一個事件監聽,事件回調函數中會作相應變量更新操做,這說明神奇魔法其實是vue的編譯器完成的。nextTick
是Vue
提供的一個全局API
,是在下次DOM
更新循環結束以後執行延遲迴調,在修改數據以後使用$nextTick
,則能夠在回調中獲取更新後的DOM
;Vue
將開啓1個隊列,並緩衝在同一事件循環中發生的全部數據變動。若是同一個watcher
被屢次觸發,只會被推入到隊列中-次。這種在緩衝時去除重複數據對於避免沒必要要的計算和DOM
操做是很是重要的。nextTick
方法會在隊列中加入一個回調函數,確保該函數在前面的dom操做完成後才調用;nextTick
實現,它會在callbacks
裏面加入咱們傳入的函數,而後用timerFunc
異步方式調用它們,首選的異步方式會是Promise
。這讓我明白了爲何能夠在nextTick
中看到dom
操做結果。在下次 DOM 更新循環結束以後執行延遲迴調,在修改數據以後當即使用 nextTick 來獲取更新後的 DOM。 nextTick主要使用了宏任務和微任務。 根據執行環境分別嘗試採用Promise、MutationObserver、setImmediate,若是以上都不行則採用setTimeout定義了一個異步方法,屢次調用nextTick會將方法存入隊列中,經過這個異步方法清空當前隊列。
vue中的插槽是一個很是好用的東西slot說白了就是一個佔位的 在vue當中插槽包含三種一種是默認插槽(匿名)一種是具名插槽還有一種就是做用域插槽 匿名插槽就是沒有名字的只要默認的都填到這裏具名插槽指的是具備名字的
做用:實現組件緩存,保持這些組件的狀態,以免反覆渲染致使的性能問題。 須要緩存組件 頻繁切換,不須要重複渲染
場景:tabs標籤頁 後臺導航,vue性能優化
原理:Vue.js
內部將DOM
節點抽象成了一個個的VNode
節點,keep-alive
組件的緩存也是基於VNode
節點的而不是直接存儲DOM
結構。它將知足條件(pruneCache與pruneCache)
的組件在cache
對象中緩存起來,在須要從新渲染的時候再將vnode
節點從cache
對象中取出並渲染。
mixin 項目變得複雜的時候,多個組件間有重複的邏輯就會用到mixin
多個組件有相同的邏輯,抽離出來
mixin並非完美的解決方案,會有一些問題
vue3提出的Composition API旨在解決這些問題【追求完美是要消耗必定的成本的,如開發成本】
場景:PC端新聞列表和詳情頁同樣的右側欄目,能夠使用mixin進行混合
劣勢:1.變量來源不明確,不利於閱讀 2.多mixin可能會形成命名衝突 3.mixin和組件可能出現多對多的關係,使得項目複雜度變高
複製代碼
狀態管理庫,相似 React 中的 Rudux
關於vuex vuex是一個專門爲vue構建的狀態集管理,主要是爲了解決組件間狀態共享的問題,強調的是數據的集中式管理,說白了主要是便於維護便於解耦因此不是全部的項目都適合使用vuex,若是你不是構建大型項目使用vuex反而使你的項目代碼繁瑣多餘
vuex的核心: state mutations getters actions modules
Vuex 是一個專爲 Vue 應用程序開發的狀態管理模式。每個 Vuex 應用的核心就是 store(倉庫)。
若 store 中的狀態發生變化,那麼相應的組件也會相應地獲得高效更新 2. 改變 store 中的狀態的惟一途徑就是顯式地提交 (commit) mutation, 這樣使得咱們能夠方便地跟蹤每個狀態的變化 Vuex主要包括如下幾個核心模塊:
就像計算屬性同樣,getter 的返回值會根據它的依賴被緩存起來, 且只有當它的依賴值發生了改變纔會被從新計算 3. Mutation:是惟一更改 store 中狀態的方法,且必須是同步函數 4. Action:用於提交 mutation,而不是直接變動狀態,能夠包含任意異步操做 5. Module:容許將單一的 Store 拆分爲多個 store 且同時保存在單一的狀態樹中
1)對Vuex基本理解1)是什麼:Vuex是一個專爲Vue.js應用程序開發的狀態管理的vue插件2)做用:集中式管理vue多個組件共享的狀態和從後臺獲取的數據states幫助組件管理狀態的,基於state的還有一個計算屬性數據getters,getters是從state中讀取數據並計算的,他們兩個的數據都是給組件去讀,組件中讀取state狀態數據使用 store.getters和mapGetters();更新狀態數據涉及到actions和mutations,經過$store.dispatch或mapAction()觸發action的調用,而後actions會經過commit()觸發mutations調用,mutations則直接更新狀態;actions還能夠同後臺API進行雙向通訊。
「單向數據流」理念的極簡示意:
簡單的單向數據流(unidirectional data flow)是指用戶訪問View,View發出用戶交互的Action,在Action裏對state進行相應更新。state更新後會觸發View更新頁面的過程。這樣數據老是清晰的單向進行流動,便於維護而且能夠預測
問題:渲染真實的DOM開銷是很大的,修改了某個數據,若是直接渲染到真實dom上會引發整個DOM樹的重繪和重排。 Virtual Dom 根據真實DOM生成的一個Virtual DOM,當Virtual DOM某個節點的數據改變後生成一個新的Vnode,而後Vnode和oldVnode做對比,發現有不同的地方就直接修改在真實的DOM上,而後使oldVnode的值爲Vnode. 注意:在採起diff算法比較的時候,只會在同層級進行,不會跨層級比較。 當數據發生改變時,set方法會讓調用Dep.notify()方法通知全部訂閱者Watcher,訂閱者就會調用patch函數給真實的DOM打補丁,更新響應的試圖。
更小巧、更快速 支持自定義渲染器 支持搖樹優化:一種在打包時去除無用代碼的優化手段 支持Fragments和跨組件渲染
模板語法99%保持不變 原生支持基於class的組件,而且無需藉助任何編譯及各類stage階段的特性 在設計時也考慮TypeScript的類型推斷特性 重寫虛擬DOM
能夠期待更多的編譯時提示來減小運行時的開銷 優化插槽生成
能夠單獨渲染父組件和子組件 靜態樹提高
下降渲染成本 基於Proxy的觀察者機制
節省內存開銷
檢測機制
更加全面、精準、高效,更具可調試式的響應跟蹤
a. 生成 Block tree Vue.js 2.x 的數據更新並觸發從新渲染的粒度是組件級的,單個組件內部 須要遍歷該組 件的整個 vnode 樹。在 2.0 裏,渲染效率的快慢與組件大小成正相關:組件越大,渲染 效率越慢。而且,對於一些靜態節點,又無數據更新,這些遍歷都是性能浪費。 Vue.js 3.0 作到了經過編譯階段對靜態模板的分析,編譯生成了 Block tree。 Block tree 是一個將模版基於動態節點指令切割的嵌套區塊,每一個 區塊內部的節點結構是固定的, 每一個區塊只須要追蹤自身包含的動態節點。因此,在 3.0 裏,渲染效率再也不與模板大小 成正相關,而是與模板中動態節點的數量成正相關。
b. slot 編譯優化 Vue.js 2.x 中,若是有一個組件傳入了 slot,那麼每次父組件更新的時候,會強制使子組 件 update,形成性能的浪費。 Vue.js 3.0 優化了 slot 的生成,使得非動態 slot 中屬性的更新只會觸發子組件的更新。 動態 slot 指的是在 slot 上面使用 v-if,v-for,動態 slot 名字等會致使 slot 產生運行時動 態變化可是又沒法被子組件 track 的操做。 c. diff 算法優化
a. diff 方法優化 Vue2.x 中的虛擬 dom 是進行全量的對比。 Vue3.0 中新增了靜態標記(PatchFlag):在與上次虛擬結點進行對比的時候,值對比 帶有 patch flag 的節點,而且能夠經過 flag 的信息得知當前節點要對比的具體內容化。 b. hoistStatic 靜態提高 Vue2.x : 不管元素是否參與更新,每次都會從新建立。 Vue3.0 : 對不參與更新的元素,只會被建立一次,以後會在每次渲染時候被不停的複用。 c. cacheHandlers 事件偵聽器緩存 默認狀況下 onClick 會被視爲動態綁定,因此每次都會去追蹤它的變化可是由於是同一 個函數,因此沒有追蹤變化,直接緩存起來複用便可。 原做者姓名: 歐陽呀
2.0存在的問題 1.對原始數據進行克隆一份 2.須要分別給對象中的每一個屬性設置監聽 3.0裏面使用的是proxy監聽對象中的全部的屬性
Composition API 與 React.js 中 Hooks 的異同點
a. React.js 中的 Hooks 基本使用 React Hooks 容許你 "勾入" 諸如組件狀態和反作用處理等 React 功能中。Hooks 只能 用在函數組件中,並容許咱們在不須要建立類的狀況下將狀態、反作用處理和更多東西 帶入組件中。 React 核心團隊奉上的採納策略是不反對類組件,因此你能夠升級 React 版本、在新組 件中開始嘗試 Hooks,並保持既有組件不作任何更改。 案例: useState 和 useEffect 是 React Hooks 中的一些例子,使得函數組件中也能增長狀態和 運行反作用。 咱們也能夠自定義一個 Hooks,它打開了代碼複用性和擴展性的新大門。
b. Vue Composition API 基本使用 Vue Composition API 圍繞一個新的組件選項 setup 而建立。setup() 爲 Vue 組件提供了 狀態、計算值、watcher 和生命週期鉤子。 並無讓原來的 API(Options-based API)消失。容許開發者 結合使用新舊兩種 API (向下兼容)。
c. 原理 React hook 底層是基於鏈表實現,調用的條件是每次組件被 render 的時候都會順序執行 全部的 hooks。 Vue hook 只會被註冊調用一次,Vue 能避開這些麻煩的問題,緣由在於它對數據的響 應是基於 proxy 的,對數據直接代理觀察。(這種場景下,只要任何一個更改 data 的地 方,相關的 function 或者 template 都會被從新計算,所以避開了 React 可能遇到的性能 上的問題)。 React 中,數據更改的時候,會致使從新 render,從新 render 又會從新把 hooks 從新注 冊一次,因此 React 複雜程度會高一些。 m
編碼階段
儘可能減小data中的數據及層次結構,data中的數據都會增長getter和setter,會收集對應的watcher v-if和v-for不能連用 若是須要使用v-for給每項元素綁定事件時使用事件代理 SPA 頁面採用keep-alive緩存組件 在更多的狀況下,使用v-if替代v-show key保證惟一 使用路由懶加載、異步組件 防抖、節流 第三方模塊按需導入 長列表滾動到可視區域動態加載 圖片懶加載
SEO優化
預渲染 服務端渲染SSR 打包優化 壓縮代碼 Tree Shaking/Scope Hoisting 使用cdn加載第三方模塊 多線程打包happypack splitChunks抽離公共文件 sourceMap優化
用戶體驗
骨架屏 PWA 還能夠使用緩存(客戶端緩存、服務端緩存)優化、服務端開啓gzip壓縮等。
相同點: 1.都是組件化開發和虛擬DOM(Virtual Dom) 2.都支持經過props進行父子組件間數據通訊 3.都支持數據驅動視圖,不直接操做DOM,更新狀態數據界面就自動更新 4.都支持服務端渲染 5.都支持native的方案,React的 React Native, Vue 的Weex
不一樣點: 1.數據綁定:vue實現了數據的雙向綁定,react的數據流動是單向的 2.組件的寫法不同,React推薦的是JSX語法,也就是把HTML和CSS都寫進JavaScript,即"all in js";vue推薦的作法是webpack+vue+loader的單文件組件格式,即html,css,js寫在同一個文件中; 3.數據狀態管理不一樣,state對象在react應用中是不可變的,須要使用setState方法更新狀態;在vue中state對象不是必須的,數據由data屬性在vue對象中管理 4.Virtual Dom不同,vue會跟蹤每一個組件的依賴關係,不須要從新渲染整個組件樹; 而對於react而言,每當應用的狀態改變時,所有的組件都會被渲染,因此react中會須要shouldComponentUpdate這個生命週期函數方法來進行控制 5.React嚴格上只針對MVC的View層,Vue則是MVVM模式
使用公共組件進行狀態提高
react中父子組件中參數互傳,子傳父是先在父組件上綁定屬性設置爲一個函數,當子組件須要給父組件傳值的時候,則經過props調用該函數將參數傳入到該函數當中,此時就能夠在父組件中的函數中接收到該參數了,這個參數則爲子組件傳過來的值
父傳子是在父組件中直接綁定一個正常的屬性,這個屬性就是指具體的值,在子組件中,用props就能夠獲取到這個值
1.能夠new一個 Vue 的 EventBus,進行事件監聽,一邊執行監聽,一邊執行新增 VUE的eventBus 就是發佈訂閱模式,是能夠在React中使用的;
2.使用pubsub-js
3.redux
setState 既存在異步狀況也存在同步狀況
1.異步狀況 在React事件當中是異步操做
import React,{ Component } from "react";
class Count extends Component{
constructor(props){
super(props);
this.state = {
count:0
}
}
render(){
return (
<> <p>count:{this.state.count}</p> <button onClick={this.btnAction}>增長</button> </>
)
}
btnAction = ()=>{
//不能直接修改state,須要經過setState進行修改
this.setState({
count: this.state.count + 1
});
console.log(this.state.count);
}
}
export default Count;
複製代碼
2.同步狀況 若是是在setTimeout事件或者自定義的dom事件中,都是同步的
import React,{ Component } from "react";
class Count extends Component{
constructor(props){
super(props);
this.state = {
count:0
}
}
render(){
return (
<> <p>count:{this.state.count}</p> <button onClick={this.btnAction}>增長</button> </>
)
}
btnAction = ()=>{
//不能直接修改state,須要經過setState進行修改
//同步
setTimeout(()=>{
this.setState({
count: this.state.count + 1
});
console.log(this.state.count);
})
}
}
export default Count;
複製代碼
3.同步狀況 自定義dom事件
import React,{ Component } from "react";
class Count extends Component{
constructor(props){
super(props);
this.state = {
count:0
}
}
render(){
return (
<> <p>count:{this.state.count}</p> <button id="btn">綁定點擊事件</button> </>
)
}
componentDidMount(){
//自定義dom事件,也是同步修改
document.querySelector('#btn').addEventListener('click',()=>{
this.setState({
count: this.state.count + 1
});
console.log(this.state.count);
});
}
}
export default Count;
複製代碼
屢次的異步setState,更新前會進行合併
import React,{ Component } from "react";
class Count extends Component{
constructor(props){
super(props);
this.state = {
count:0
}
}
render(){
return (
<> <p>count:{this.state.count}</p> <button onClick={this.btnAction}>增長</button> </>
)
}
btnAction = ()=>{
//不能直接修改state,須要經過setState進行修改
this.setState({
message:'hi',
count: this.state.count + 1
},()=>{
console.log('a:',this.state.count);
});
this.setState({
count: this.state.count + 1
},()=>{
console.log('b:',this.state.count);
});
this.setState({
count: this.state.count + 1
},()=>{
console.log('c:',this.state.count);
});
this.setState({
count: this.state.count + 1
},()=>{
console.log('d:',this.state.count);
});
//會輸出:a:2 b:2 c:2 d:2
}
}
export default Count;
複製代碼
屢次的異步setState,更新前不進行合併
btnAction = ()=>{
//不能直接修改state,須要經過setState進行修改
this.setState((preState,props)=>{
return {
count: preState.count + 1
}
},()=>{
console.log('a:',this.state.count);
});
this.setState((preState,props)=>{
return {
count: preState.count + 1
}
},()=>{
console.log('b:',this.state.count);
});
this.setState((preState,props)=>{
return {
count: preState.count + 1
}
},()=>{
console.log('c:',this.state.count);
});
this.setState((preState,props)=>{
return {
count: preState.count + 1
}
},()=>{
console.log('d:',this.state.count);
});
//會輸出:a:1 b:2 c:3 d:4
}
複製代碼
安裝
當組件的實例被建立並插入到 DOM 中時,這些方法按如下順序調用:
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
更新中
更新可能由道具或狀態的更改引發。當從新渲染組件時,這些方法按如下順序調用:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
卸載
當組件從 DOM 中移除時調用此方法:
componentWillUnmount()
複製代碼
Portals 提供了一種一流的方式來將子組件渲染到存在於父組件的 DOM 層次結構以外的 DOM 節點中。結構不受外界的控制的狀況下就能夠使用portals進行建立
// 異步懶加載
const Box = lazy(()=>import('./components/Box'));
// 使用組件的時候要用suspense進行包裹
<Suspense fallback={<div>loading...</div>}> {show && <Box/>} </Suspense>
複製代碼
immutable內部提供的全部數據類型,對其數據進行任意操做,操做獲得的結果是修改後的值 而且修改後的值是一個新的對象,原來的對象沒有發生任何變化。 immutable.js文檔
const map1 = Map({a:1,b:2,c:3});
const map2 = map1.set('b',50);
console.log(map1);
console.log(map2);
複製代碼
1,解釋 節流:事件觸發後,規定時間內,事件處理函數不能再次被調用。也就是說在規定的時間內,函數只能被調用一次,且是最早被觸發調用的那次。
防抖:屢次觸發事件,事件處理函數只能執行一次,而且是在觸發操做結束時執行。也就是說,當一個事件被觸發準備執行事件函數前,會等待必定的時間(這時間是碼農本身去定義的,好比 1 秒),若是沒有再次被觸發,那麼就執行,若是被觸發了,那就本次做廢,從新重新觸發的時間開始計算,並再次等待 1 秒,直到能最終執行!
2,使用場景: 節流:滾動加載更多、搜索框搜的索聯想功能、高頻點擊、表單重複提交…… 防抖:搜索框搜索輸入,並在輸入完之後自動搜索、手機號,郵箱驗證輸入檢測、窗口大小 resize 變化後,再從新渲染。
head
標籤中,而腳本文件在body
結束前,這樣能夠防止阻塞的方式。class
,或者csstext
屬性,一次性地改變樣式。transform
來作形變和位移Document Fragment
對象,完成後再把這個對象加入DOM。再好比,使用cloneNode()
方法,在克隆的節點上進行操做,而後再用克隆的節點替換原始節點。display: none
(須要1次重排和重繪),而後對這個節點進行100次操做,最後再恢復顯示(須要1次重排和重繪)。這樣一來,你就用兩次從新渲染,取代了可能高達100次的從新渲染。position
屬性爲absolute
或fixed
的元素,重排的開銷會比較小,由於不用考慮它對其餘元素的影響。