> 聲明位於HTML文檔中的第一行,處於html標籤以前,告知瀏覽器的解析器用什麼文檔標準解析這個文檔。DOCTYPE不存在或格式不正確會致使文檔以兼容模式呈現。 標準模式的排版和js運做模式都是以該瀏覽器支持的最高標準運行。在兼容模式中,頁面以寬鬆的向後兼容的方式顯示,模擬老式瀏覽器以防止站點沒法工做過。
> 主要分紅兩部分:渲染引擎(layout engine 或 rendering engine)和js引擎 渲染引擎:負責取得網頁的內容(HTML、XML、圖像等等)、整理訊息(例如加入css等),以及計算網頁的顯示方式,而後會輸出至顯示器或打印機。瀏覽器的內核的不一樣對於網頁的語法解釋會有不一樣,因此渲染的效果也不相同。全部網頁瀏覽器、電子郵件客戶端以及其它須要編輯、顯示網格內容的應用程序都須要內核 js引擎:解析和執行javascript來實現網頁的動態效果 最開始渲染引擎和js引擎並無區分很明確,後來js引擎愈來愈獨立 - 瀏覽器組成: 從原理構成上分爲七個模塊,分別是 User Interface(用戶界面)、 Browser engine(瀏覽器引擎) 、 Rendering engine(渲染引擎) 、 Networking(網絡) 、 JavaScript Interpreter(js解釋器) 、 UI Backend(UI後端) 、Date Persistence(數據持久化存儲) 其中,最重要的是渲染引擎(內核)和 JavaScript 解釋器(JavaScript引擎) 瀏覽器內核主要負責 HTML 、CSS 的解析,頁面佈局、渲染與複合層合成; JavaScript 引擎負責 JavaScript 代碼的解釋與執行
- Trident內核:IE、MaxThon、TT、The World、360、搜狗瀏覽器等。[又稱MSHTML] - Gecko內核:Netscape6及以上版本、FF、MozillaSuite、SeaMonkey等 - Presto內核:Opera7及以上。[Opera內核原爲Presto,現爲Blink] - Webkit內核:Safari、Chrome等。[Chrome的Blink(webkit的分支)]
- 加強了圖形渲染(canvas) - 影音(video,audio) - 數據存儲(sessionStorage/localStorage) - 語義化更好的元素,好比article、footer、header、nav、section - 表單控件,calendar、date、time、email、url、search - 新的技術,webworker(專用線程)、websocket(全雙工通訊)、Geolocation地理定位
> IE6/7/8支持經過document.createElement方法產生的標籤,能夠利用這一特性讓這些瀏覽器支持HTML5新標籤,瀏覽器支持新標籤後,還須要添加標籤默認的樣式,最好的方式是直接使用成熟的框架,使用最多的是html5shiv框架 <!--[if it IE 9]><script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
- 用正確的標籤作正確的事情 - html語義化讓頁面的內容結構化,結構更清晰,便於對瀏覽器、搜索引擎解析 - 即便在沒有樣式css狀況下也以一種文檔格式顯示,而且是容易閱讀的 - 搜索引擎的爬蟲也依賴於HTML標記來肯定上下文和各個關鍵字的權重,利於SEO - 使閱讀源代碼的人對網站更容易將網站分塊,便於閱讀維護理解
- 相同點:都存儲在客戶端 - 不一樣點: - 存儲大小: - cookie數據大小不能超過4k - sessionStorage和localStorage雖然也有存儲大小的限制,但比cookie大的多,能夠達到5M或更大,就是爲了解決cookie存儲空間不足而誕生的 - 有限時間: - localStorage存儲持久數據,瀏覽器關閉後數據不丟失除非主動刪除數據 - sessionStorage數據在當前瀏覽器窗口關閉後自動刪除 - cookie設置的cookie過時時間以前一直有效,即便窗口或瀏覽器關閉 - 數據域服務器之間的交互方式 - cookie的數據會自動的傳遞到服務器,服務器端也能夠寫cookie到客戶端 - sessionStorage和localStorage不會自動把數據發給服務器,僅在本地保存
- iframe會阻塞主頁面的onload事件 - 搜索引擎的檢索程序沒法解讀這種頁面,不利於seo - iframe和主頁面共享鏈接池,而瀏覽器對相同域的連接有限制,因此會影響頁面的並行加載 > 使用iframe以前須要考慮這些缺點,若是須要使用iframe,最好經過JavaScript動態給iframe添加src屬性值
> 是爲了防止一些人使用軟件惡意註冊、發帖等行爲而設的,它的存在是爲了確保登陸網站的是一個坐在電腦面前的真人,而不是一個自動登陸的軟件
- 若是userAgent中包含MSIE,說明必定是IE瀏覽器 - 若是userAgent中包含Trident卻不包含MSIE,說明必定是IE11瀏覽器 - 若是userAgent中包含Firefox,說明必定是Firefox瀏覽器 - 若是userAgent中包含OPR,說明必定是Opera瀏覽器 - 若是userAgent中包含Edge,說明必定是Edge瀏覽器 - 若是userAgent中包含Edge,說明必定是Chrome瀏覽器 - 若是userAgent中包含Edge,說明必定是Edge瀏覽器
垃圾回收機制就是間歇的不按期的尋找到再也不使用的變量, 並釋放掉它們所指向的內存; 主要爲了以防內存泄漏, (內存泄漏: 當已經不須要某塊內存時這塊內存還存在着), JS有兩種變量: 全局變量和在函數中產生的局部變量。 局部變量的生命週期在函數執行事後就結束了, 此時即可將它引用的內存釋放(即垃圾回收); 但全局變量生命週期會持續到瀏覽器關閉頁面。 JS執行環境中的垃圾回收器有兩種方式: 標記清除(mark and sweep)、 引用計數(reference counting)。 標記清除: 垃圾收集器給內存中的全部變量都加上標記, 而後去掉環境中的變量以及被環境中的變量引用的變量的標記。 在此以後再被加上的標記的變量即爲須要回收的變量, 由於環境中的變量已經沒法訪問到這些變量。 引用計數(reference counting): 這種方式經常會引發內存泄漏, 低版本的IE使用這種方式。機制就是跟蹤一個值的引用次數, 當聲明一個變量並將一個引用類型賦值給該變量時該值引用次數加1, 當這個變量指向其餘一個時該值的引用次數便減一。 當該值引用次數爲0時就會被回收。
> 漸進式渲染是用於提升網頁性能(尤爲是提升用戶感知的加載速度),以儘快呈現頁面的技術。 在之前互聯網帶寬較小的時期,這種技術更爲廣泛。現在,移動終端的盛行,而移動網絡每每不穩定,漸進式渲染在現代前端開發中仍然有用武之地。 一些舉例: - 圖片懶加載——頁面上的圖片不會一次性所有加載。當用戶滾動頁面到圖片部分時,JavaScript 將加載並顯示圖像。 肯定顯示內容的優先級(分層次渲染)——爲了儘快將頁面呈現給用戶,頁面只包含基本的最少許的 CSS、腳本和內容,而後能夠使- 用延遲加載腳本或監聽DOMContentLoaded/load事件加載其餘資源和內容。 - 異步加載 HTML 片斷——當頁面經過後臺渲染時,把 HTML 拆分,經過異步請求,分塊發送給瀏覽器。更多相關細節能夠在這裏找到。
> Viewport :字面意思爲視圖窗口,在移動web開發中使用。表示將設備瀏覽器寬度虛擬成一個特定的值(或計算得出),這樣利於移動web站點跨設備顯示效果基本一致。移動版的 Safari 瀏覽器最新引進了 viewport 這個 meta tag,讓網頁開發者來控制 viewport 的大小和縮放,其餘手機瀏覽器也基本支持。 在移動端瀏覽器當中,存在着兩種視口,一種是可見視口(也就是咱們說的設備大小),另外一種是視窗視口(網頁的寬度是多少)。舉個例子:若是咱們的屏幕是320像素 * 480像素的大小(iPhone4),假設在瀏覽器中,320像素的屏幕寬度可以展現980像素寬度的內容。那麼320像素的寬度就是可見視口的寬度,而可以顯示的980像素的寬度就是視窗視口的寬度。 爲了顯示更多的內容,大多數的瀏覽器會把本身的視窗視口擴大,簡易的理解,就是讓本來320像素的屏幕寬度可以容下980像素甚至更寬的內容(將網頁等比例縮小)。 - Viewport屬性值 - width 設置layout viewport 的寬度,爲一個正整數,或字符串"width-device" - initial-scale 設置頁面的初始縮放值,爲一個數字,能夠帶小數 - minimum-scale 容許用戶的最小縮放值,爲一個數字,能夠帶小數 - maximum-scale 容許用戶的最大縮放值,爲一個數字,能夠帶小數 - height 設置layout viewport 的高度,這個屬性對咱們並不重要,不多使用 - user-scalable 是否容許用戶進行縮放,值爲"no"或"yes", no 表明不容許,yes表明容許這些屬性能夠同時使用,也能夠單獨使用或混合使用,多個屬性同時使用時用逗號隔開就好了。
- img的alt屬性 若是沒法顯示圖像,瀏覽器將顯示alt指定的內容 - 元素title屬性 在鼠標移到元素上時顯示title的內容
- href href標識超文本引用,用在link和a等元素上,href是引用和頁面關聯,是在當前元素和引用資源之間創建聯繫 若在文檔中添加href ,瀏覽器會識別該文檔爲 CSS 文件,就會並行下載資源而且不會中止對當前文檔的處理。這也是爲何建議使用 link 方式加載 CSS,而不是使用 @import 方式。 - src src表示引用資源,替換當前元素,用在img,script,iframe上,src是頁面內容不可缺乏的一部分。 當瀏覽器解析到src ,會暫停其餘資源的下載和處理(圖片不會暫停其餘資源下載),直到將該資源加載、編譯、執行完畢,相似於將所指向資源應用到當前內容。這也是爲何建議把 js 腳本放在底部而不是頭部的緣由。
- 行內元素 一個行內元素只佔據它對應標籤的邊框所包含的空間 通常狀況下,行內元素只能包含數據和其餘行內元素 b, big, i, small, tt abbr, acronym, cite, code, dfn, em, kbd, strong, samp, var a, bdo, br, img, map, object, q, script, span, sub, sup button, input, label, select, textarea - 塊級元素 佔據一整行,高度、行高、內邊距和外邊距均可以改變,能夠容納塊級標籤和其餘行內標籤 header,form,ul,ol,table,article,div,hr,aside,figure,canvas,video,audio,footer
> label 標籤一般是寫在表單內,它關聯一個控件,使用 label 能夠實現點擊文字選取對應的控件。 <input type="checkbox" id="test"> <label for="test" >test</label>
> 將不想要自動完成的 form 或 input 設置爲 autocomplete=off
- DOM: Document Object Model,文檔對象模型 DOM 是爲了操做文檔出現的 API,document 是其的一個對象 DOM和文檔有關,這裏的文檔指的是網頁,也就是html文檔。DOM和瀏覽器無關,他關注的是網頁自己的內容。 - BOM: Browser Object Model,瀏覽器對象模型 BOM 是爲了操做瀏覽器出現的 API,window 是其的一個對象 window 對象既爲 javascript 訪問瀏覽器提供API,同時在 ECMAScript 中充當 Global 對象
- 把<link>放在<head>中 把<link>標籤放在<head></head>之間是規範要求的內容。此外,這種作法可讓頁面逐步呈現,提升了用戶體驗。將樣式表放在文檔底部附近,會使許多瀏覽器(包括 Internet Explorer)不能逐步呈現頁面。一些瀏覽器會阻止渲染,以免在頁面樣式發生變化時,從新繪製頁面中的元素。這種作法能夠防止呈現給用戶空白的頁面或沒有樣式的內容。 - 把<script>標籤剛好放在</body>以前 腳本在下載和執行期間會阻止 HTML 解析。把<script>標籤放在底部,保證 HTML 首先完成解析,將頁面儘早呈現給用戶。 例外狀況是當你的腳本里包含document.write()時。可是如今,document.write()不推薦使用。同時,將<script>標籤放在底部,意味着瀏覽器不能開始下載腳本,直到整個文檔(document)被解析。也許,對此比較好的作法是,<script>使用defer屬性,放在<head>中。
- 語義化html標籤; - 合理的title, description, keywords; - 重要的html代碼放前面; - 少用iframe, 搜索引擎不會抓取iframe中的內容 - 圖片加上alt
學習計算機網絡時咱們通常採用折中的辦法,也就是中和 OSI 和 TCP/IP 的優勢,採用一種只有五層協議的體系結構,這樣既簡潔又能將概念闡述清楚。 #### 應用層 應用層(application-layer)的任務是經過應用進程間的交互來完成特定網絡應用。應用層協議定義的是應用進程(進程:主機中正在運行的程序)間的通訊和交互的規則。對於不一樣的網絡應用須要不一樣的應用層協議。在互聯網中應用層協議不少,如域名系統 DNS,支持萬維網應用的 HTTP 協議,支持電子郵件的 SMTP 協議等等。咱們把應用層交互的數據單元稱爲報文。 **域名系統** - 域名系統(Domain Name System 縮寫 DNS,Domain Name 被譯爲域名)是因特網的一項核心服務,它做爲能夠將域名和 IP 地址相互映射的一個分佈式數據庫,可以令人更方便的訪問互聯網,而不用去記住可以被機器直接讀取的 IP 數串。(百度百科)例如:一個公司的 Web 網站可看做是它在網上的門戶,而域名就至關於其門牌地址,一般域名都使用該公司的名稱或簡稱。例如上面提到的微軟公司的域名,相似的還有:IBM 公司的域名是 www.ibm.com、Oracle 公司的域名是 www.oracle.com、Cisco 公司的域名是 www.cisco.com 等。 **HTTP 協議** - 超文本傳輸協議(HTTP,HyperText Transfer Protocol)是互聯網上應用最爲普遍的一種網絡協議。全部的 WWW(萬維網) 文件都必須遵照這個標準。設計 HTTP 最初的目的是爲了提供一種發佈和接收 HTML 頁面的方法。(百度百科) #### 運輸層 運輸層(transport layer)的主要任務就是負責向兩臺主機進程之間的通訊提供通用的數據傳輸服務。應用進程利用該服務傳送應用層報文。「通用的」是指並不針對某一個特定的網絡應用,而是多種應用能夠使用同一個運輸層服務。因爲一臺主機可同時運行多個線程,所以運輸層有複用和分用的功能。所謂複用就是指多個應用層進程可同時使用下面運輸層的服務,分用和複用相反,是運輸層把收到的信息分別交付上面應用層中的相應進程。 **運輸層主要使用如下兩種協議** - 傳輸控制協議 TCP(Transmisson Control Protocol)--提供面向鏈接的,可靠的數據傳輸服務。 - 用戶數據協議 UDP(User Datagram Protocol)--提供無鏈接的,盡最大努力的數據傳輸服務(不保證數據傳輸的可靠性)。 **UDP 的主要特色** - UDP 是無鏈接的; - UDP 使用盡最大努力交付,即不保證可靠交付,所以主機不須要維持複雜的連接狀態(這裏面有許多參數); - UDP 是面向報文的; - UDP 沒有擁塞控制,所以網絡出現擁塞不會使源主機的發送速率下降(對實時應用頗有用,如 直播,實時視頻會議等); - UDP 支持一對1、一對多、多對一和多對多的交互通訊; - UDP 的首部開銷小,只有 8 個字節,比 TCP 的 20 個字節的首部要短。 **TCP 的主要特色** - TCP 是面向鏈接的。(就好像打電話同樣,通話前須要先撥號創建鏈接,通話結束後要掛機釋放鏈接); - 每一條 TCP 鏈接只能有兩個端點,每一條 TCP 鏈接只能是點對點的(一對一); - TCP 提供可靠交付的服務。經過 TCP 鏈接傳送的數據,無差錯、不丟失、不重複、而且按序到達; - TCP 提供全雙工通訊。TCP 容許通訊雙方的應用進程在任什麼時候候都能發送數據。TCP 鏈接的兩端都設有發送緩存和接收緩存,用來臨時存放雙方通訊的數據; - 面向字節流。TCP 中的「流」(Stream)指的是流入進程或從進程流出的字節序列。「面向字節流」的含義是:雖然應用程序和 TCP 的交互是一次一個數據塊(大小不等),但 TCP 把應用程序交下來的數據僅僅當作是一連串的無結構的字節流。 #### 網絡層 在 計算機網絡中進行通訊的兩個計算機之間可能會通過不少個數據鏈路,也可能還要通過不少通訊子網。網絡層的任務就是選擇合適的網間路由和交換結點, 確保數據及時傳送。 在發送數據時,網絡層把運輸層產生的報文段或用戶數據報封裝成分組和包進行傳送。在 TCP/IP 體系結構中,因爲網絡層使用 IP 協議,所以分組也叫 IP 數據報 ,簡稱 數據報。 這裏要注意:不要把運輸層的「用戶數據報 UDP 」和網絡層的「 IP 數據報」弄混。另外,不管是哪一層的數據單元,均可籠統地用「分組」來表示。 這裏強調指出,網絡層中的「網絡」二字已經不是咱們一般談到的具體網絡,而是指計算機網絡體系結構模型中第三層的名稱. 互聯網是由大量的異構(heterogeneous)網絡經過路由器(router)相互鏈接起來的。互聯網使用的網絡層協議是無鏈接的網際協議(Intert Prococol)和許多路由選擇協議,所以互聯網的網絡層也叫作網際層或 IP 層。 #### 數據鏈路層 數據鏈路層(data link layer)一般簡稱爲鏈路層。兩臺主機之間的數據傳輸,老是在一段一段的鏈路上傳送的,這就須要使用專門的鏈路層的協議。 在兩個相鄰節點之間傳送數據時,數據鏈路層將網絡層交下來的 IP 數據報組裝程幀,在兩個相鄰節點間的鏈路上傳送幀。每一幀包括數據和必要的控制信息(如同步信息,地址信息,差錯控制等)。 在接收數據時,控制信息使接收端可以知道一個幀從哪一個比特開始和到哪一個比特結束。這樣,數據鏈路層在收到一個幀後,就可從中提出數據部分,上交給網絡層。 控制信息還使接收端可以檢測到所收到的幀中有偏差錯。若是發現差錯,數據鏈路層就簡單地丟棄這個出了差錯的幀,以免繼續在網絡中傳送下去白白浪費網絡資源。若是須要改正數據在鏈路層傳輸時出現差錯(這就是說,數據鏈路層不只要檢錯,並且還要糾錯),那麼就要採用可靠性傳輸協議來糾正出現的差錯。這種方法會使鏈路層的協議複雜些。 #### 物理層 在物理層上所傳送的數據單位是比特。 物理層(physical layer)的做用是實現相鄰計算機節點之間比特流的透明傳送,儘量屏蔽掉具體傳輸介質和物理設備的差別。 使其上面的數據鏈路層沒必要考慮網絡的具體傳輸介質是什麼。「透明傳送比特流」表示經實際電路傳送後的比特流沒有發生變化,對傳送的比特流來講,這個電路好像是看不見的。 在互聯網使用的各類協中最重要和最著名的就是 TCP/IP 兩個協議。如今人們常常提到的 TCP/IP 並不必定單指 TCP 和 IP 這兩個具體的協議,而每每表示互聯網所使用的整個 TCP/IP 協議族。
TCP 提供一種面向鏈接的、可靠的字節流服務。其中,面向鏈接意味着兩個使用 TCP 的應用(一般是一個客戶和一個服務器)在彼此交換數據以前必須先創建一個 TCP 鏈接。在一個 TCP 鏈接中,僅有兩方進行彼此通訊;而字節流服務意味着兩個應用程序經過 TCP 連接交換 8bit 字節構成的字節流,TCP 不在字節流中插入記錄標識符。 對於可靠性,TCP 經過如下方式進行保證: - 數據包校驗:目的是檢測數據在傳輸過程當中的任何變化,若校驗出包有錯,則丟棄報文段而且不給出響應,這時 TCP 發送數據端超時後會重發數據; - 對失序數據包重排序:既然 TCP 報文段做爲 IP 數據報來傳輸,而 IP 數據報的到達可能會失序,所以 TCP 報文段的到達也可能會失序。TCP 將對失序數據進行從新排序,而後才交給應用層; - 丟棄重複數據:對於重複數據,可以丟棄重複數據; - 應答機制:當 TCP 收到發自 TCP 鏈接另外一端的數據,它將發送一個確認。這個確認不是當即發送,一般將推遲幾分之一秒; - 超時重發:當 TCP 發出一個段後,它啓動一個定時器,等待目的端確認收到這個報文段。若是不能及時收到一個確認,將重發這個報文段; - 流量控制:TCP 鏈接的每一方都有固定大小的緩衝空間。TCP 的接收端只容許另外一端發送接收端緩衝區所能接納的數據,這能夠防止較快主機導致較慢主機的緩衝區溢出,這就是流量控制。TCP 使用的流量控制協議是可變大小的滑動窗口協議。
這一步包括 DNS 具體的查找過程,包括:瀏覽器緩存->系統緩存->路由器緩存... - 瀏覽器搜索本身的 DNS 緩存(維護一張域名與 IP 地址的對應表); - 搜索操做系統中的 DNS 緩存(維護一張域名與 IP 地址的對應表); - 搜索操做系統的 hosts 文件( Windows 環境下,維護一張域名與 IP 地址的對應表); - 操做系統將域名發送至 LDNS(本地區域名服務器),LDNS 查詢 本身的 DNS 緩存(通常查找成功率在 80% 左右),查找成功則返回結果,失敗則發起一個迭代 DNS 解析請求: - LDNS 向 Root Name Server (根域名服務器,如 com、net、org 等的解析的頂級域名服務器的地址)發起請求,此處,Root Name Server 返回 com 域的頂級域名服務器的地址; - LDNS 向 com 域的頂級域名服務器發起請求,返回 baidu.com 域名服務器地址; - LDNS 向 baidu.com 域名服務器發起請求,獲得 www.baidu.com 的 IP 地址; - LDNS 將獲得的 IP 地址返回給操做系統,同時本身也將 IP 地址緩存起來; - 操做系統將 IP 地址返回給瀏覽器,同時本身也將 IP 地址緩存起來;
首先明確兩者沒有區別!兩個 IP 地址的角度不同,127.0.0.1 是從 IETF(因特爾工程任務組)規定看,是保留給本機使用的 IP 地址,全部的計算機默認都是相同的。而 192.168.0.1 其實只是 IETF 在 c 類網址中,專門留出給專用網絡用的一個網段中的一個 IP 而已,該網段包含了 192.168.0.1 到 192.168.255.255 中全部的 IP 地址。 ### 五類 ip 地址的範圍 IP 地址分爲 A,B,C,D,E 五類。 網絡號:用於識別主機所在的網絡; 主機號:用於識別該網絡中的主機。 其中 A 類分配給政府機關使用,B 類地址給大中型企業使用,C 類地址給我的使用。這三種是主要的。 IP 地址分爲五類,A 類保留給政府機構,B 類分配給中等規模的公司,C 類分配給任何須要的人,D 類用於組播,E 類用於實驗,各種可容納的地址數目不一樣。 其中 A 類、B 類、和 C 類這三類地址用於 TCP/IP 節點,其它兩類 D 類和 E 類被用於特殊用途。 A、B、C 三類 IP 地址的特徵:當將 IP 地址寫成二進制形式時,A 類地址的第一位老是 O,B 類地址的前兩位老是 10,C 類地址的前三位老是 110。 A 類地址 - ⑴ A 類地址第 1 字節爲網絡地址,其它 3 個字節爲主機地址。 - ⑵ A 類地址範圍:1.0.0.1—126.155.255.254 - ⑶ A 類地址中的私有地址和保留地址: - ① 10.X.X.X 是私有地址(所謂的私有地址就是在互聯網上不使用,而被用在局域網絡中的地址)。 - ② 127.X.X.X 是保留地址,用作循環測試用的。 B 類地址 - ⑴ B 類地址第 1 字節和第 2 字節爲網絡地址,其它 2 個字節爲主機地址。 - ⑵ B 類地址範圍:128.0.0.1—191.255.255.254。 - ⑶ B 類地址的私有地址和保留地址 - ① 172.16.0.0—172.31.255.255 是私有地址 - ② 169.254.X.X 是保留地址。若是你的 IP 地址是自動獲取 IP 地址,而你在網絡上又沒有找到可用的 DHCP 服務器。就會獲得其中一個 IP。 C 類地址 - ⑴ C 類地址第 1 字節、第 2 字節和第 3 個字節爲網絡地址,第 4 個個字節爲主機地址。另外第 1 個字節的前三位固定爲 110。 - ⑵ C 類地址範圍:192.0.0.1—223.255.255.254。 - ⑶ C 類地址中的私有地址: - 192.168.X.X 是私有地址。 D 類地址 - ⑴ D 類地址不分網絡地址和主機地址,它的第 1 個字節的前四位固定爲 1110。 - ⑵ D 類地址範圍:224.0.0.1—239.255.255.254 E 類地址 - ⑴ E 類地址也不分網絡地址和主機地址,它的第 1 個字節的前五位固定爲 11110。 - ⑵ E 類地址範圍:240.0.0.1—255.255.255.254
在 HTTP/1.0 中默認使用短鏈接。也就是說,客戶端和服務器每進行一次 HTTP 操做,就創建一次鏈接,任務結束就中斷鏈接。當客戶端瀏覽器訪問的某個 HTML 或其餘類型的 Web 頁中包含有其餘的 Web 資源(如 JavaScript 文件、圖像文件、CSS 文件等),每遇到這樣一個 Web 資源,瀏覽器就會從新創建一個 HTTP 會話。 而從 HTTP/1.1 起,默認使用長鏈接,用以保持鏈接特性。使用長鏈接的 HTTP 協議,會在響應頭加入這行代碼:Connection:keep-alive 在使用長鏈接的狀況下,當一個網頁打開完成後,客戶端和服務器之間用於傳輸 HTTP 數據的 TCP 鏈接不會關閉,客戶端再次訪問這個服務器時,會繼續使用這一條已經創建的鏈接。Keep-Alive 不會永久保持鏈接,它有一個保持時間,能夠在不一樣的服務器軟件(如 Apache)中設定這個時間。實現長鏈接須要客戶端和服務端都支持長鏈接。 HTTP 協議的長鏈接和短鏈接,實質上是 TCP 協議的長鏈接和短鏈接
> 例如一個 100kb 的 HTML 文檔須要傳送到另一臺計算機,並不會整個文檔直接傳送過去,可能會切割成幾個部分,好比四個分別爲 25kb 的數據段。而每一個數據段再加上一個 TCP 首部,就組成了 TCP 報文。 TCP 報文 (Segment),包括首部和數據部分。 首部: - 源端口 source port - 目的端口 destination port - 序號 sequence number - 確認號 acknowledgment number - 數據偏移 offset - 保留 reserved - 標誌位 tcp flags - 窗口大小 window size - 檢驗和 checksum - 緊急指針 urgent pointer - 選項 tcp options
- cookie+setInterval - localStorage - webSocket - SharedWorker
1.瀏覽器經過DNS將url地址解析爲ip(若是有緩存直接返回緩存,不然遞歸解析) 2.經過DNS解析獲得了目標服務器的ip地址後,與服務器創建TCP鏈接。 - ip協議:選擇傳輸路線,負責找到 - tcp協議:三次握手,分片,可靠傳輸,從新發送的機制 3.瀏覽器經過http協議發送請求(增長http的報文信息)頭 體 行 4.服務器接收請求後,查庫,讀文件,拼接好返回的http響應 5.瀏覽器收到html,開始渲染 6.解析html爲dom,解析css爲css-tree,最終生成render-tree阻塞渲染 7.遍歷渲染樹開始佈局,計算每一個節點的位置大小信息 8.將渲染樹每一個節點繪製到屏幕 9.加載js文件,運行js腳本 10.relow(樣式)和repaint(位置)
- 在通訊中,同步和異步強調的是消息的通訊機制;而在計算機操做系統中,複用了通訊裏的概念,同步(Synchronous)是指在發生一個調用時,在沒有獲得結果以前,該調用不返回;異步(Asynchronous)是指在發生一個調用時,當即返回。
- cors 服務器端對於CORS的支持,主要就是經過設置Access-Control-Allow-Origin來進行的。若是瀏覽器檢測到相應的設置,就能夠容許Ajax進行跨域的訪問。 - jsonp var script = document.createElement('script'); script.src = "http://aa.xx.com/js/*.js"; document.body.appendChild(script); - postMessage window對象新增了一個window.postMessage方法,容許跨窗口通訊,不論這兩個窗口是否同源。目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。 - window.name - location.hash - http-proxy - nginx - websocket - iframe 基於iframe實現的跨域要求兩個域具備aa.xx.com,bb.xx.com這種特色,也就是兩個頁面必須屬於同一個頂級基礎域(例如都是xxx.com,或是xxx.com.cn),使用同一協議(例如都是 http)和同一端口(例如都是80),這樣在兩個頁面中同時添加document.domain,就能夠實現父頁面調用子頁面的函數
- 200("OK")一切正常。實體主體中的文檔(若存在的話)是某資源的表示。 - 400("Bad Request")客戶端方面的問題。實體主題中的文檔(若存在的話)是一個錯誤消息。但願客戶端可以理解此錯誤消息,並改正問題。 - 500("Internal Server Error")服務期方面的問題。實體主體中的文檔(若是存在的話)是一個錯誤消息。該錯誤消息一般無濟於事,由於客戶端沒法修復服務器方面的問題。 - 301("Moved Permanently")當客戶端觸發的動做引發了資源URI的變化時發送此響應代碼。另外,當客戶端向一個資源的舊URI發送請求時,也發送此響應代碼。 - 404用於服務器端不知道客戶端要請求哪一個資源的狀況; - 410用於服務器端知道客戶端所請求的資源曾經存在,但如今已經不存在了的狀況。 - 409("Conflict")當客戶端試圖執行一個」會致使一個或多個資源處於不一致狀態「的操做時,發送此響應代碼。
- http1.0與1.1對比: - http 1.0 對於每一個鏈接都得創建一次鏈接, 一次只能傳送一個請求和響應, 請求就會關閉, http1.0 沒有 Host 字段 - 而 http1.1 在同一個鏈接中能夠傳送多個請求和響應, 多個請求能夠重疊和同時進行, http1.1 必須有 host 字段 - http1.1 中引入了 ETag 頭, 它的值 entity tag 能夠用來惟一的描述一個資源. 請求消息中能夠使用 If-None-Match 頭域來匹配資源的 entitytag 是否有變化 - http1.1 新增了 Cache-Control 頭域(消息請求和響應請求均可以使用), 它支持一個可擴展的指令子集 - http1.0 中只定義了 16 個狀態響應碼, 對錯誤或警告的提示不夠具體. http1.1 引入了一個 Warning 頭域, 增長對錯誤或警告信息的描述. 且新增了 24 個狀態響應碼 - http1.x與2.x對比: - **二進制格式**:http1.x 是文本協議,而 http2.0 是二進制以幀爲基本單位,是一個二進制協議,一幀中除了包含數據外同時還包含該幀的標識:Stream Identifier,即標識了該幀屬於哪一個 request,使得網絡傳輸變得十分靈活。 - **多路複用**: 一個很大的改進,原先 http1.x 一個鏈接一個請求的狀況有比較大的侷限性,也引起了不少問題,如創建多個鏈接的消耗以及效率問題。 - http1.x 爲了解決效率問題,可能會盡可能多的發起併發的請求去加載資源,然而瀏覽器對於同一域名下的併發請求有限制,而優化的手段通常是將請求的資源放到不一樣的域名下來突破這種限制。 - 而 http2.0 支持的多路複用能夠很好的解決這個問題,多個請求共用一個 TCP 鏈接,多個請求能夠同時在這個 TCP 鏈接上併發,一個是解決了創建多個 TCP 鏈接的消耗問題,一個也解決了效率的問題。那麼是什麼原理支撐多個請求能夠在一個 TCP 鏈接上併發呢?基本原理就是上面的二進制分幀,由於每一幀都有一個身份標識,因此多個請求的不一樣幀能夠併發的無序發送出去,在服務端會根據每一幀的身份標識,將其整理到對應的 request 中。 - **header 頭部壓縮**:主要是經過壓縮 header 來減小請求的大小,減小流量消耗,提升效率。由於以前存在一個問題是,每次請求都要帶上 header,而這個 header 中的數據一般是一層不變的。 - 支持服務端推送
> 流量控制是對一條通訊路徑上的流量進行控制,就是發送方經過獲取接收方的回饋來動態調整發送的速率,來達到控制流量的效果,其目的是保證發送者的發送速度不超過接收者的接收速度。
擁塞控制是對整個通訊子網的流量進行控制,屬於全局控制。 1. 慢開始+擁塞避免 一開始使用慢啓動,即擁塞窗口設爲 1,而後擁塞窗口指數增加到慢開始的門限值(ssthresh=16),則切換爲擁塞避免,即加法增加,這樣增加到必定程度,致使網絡擁塞,則此時會把擁塞窗口從新降爲 1,即從新慢開始,同時調整新的慢開始門限值爲 12,以後以此類推。 2. 快重傳+快恢復 - **快重傳**:上面咱們說的重傳機制都是等到超時還未收到接收方的回覆,纔開始進行重傳。而快重傳的設計思路是:若是發送方收到 3 個重複的接收方的 ACK,就能夠判斷有報文段丟失,此時就能夠當即重傳丟失的報文段,而不用等到設置的超時時間到了纔開始重傳,提升了重傳的效率。 - **快恢復**:上面的擁塞控制會在網絡擁塞時將擁塞窗口降爲 1,從新慢開始,這樣存在的一個問題就是網絡沒法很快恢復到正常狀態。快恢復就是來優化這個問題的,使用快恢復,則出現擁塞時,擁塞窗口只會下降到新的慢開始門閥值(即 12),而不會降爲 1,而後直接開始進入擁塞避免加法增加
- 創建 TCP 鏈接須要三次握手:三次握手: 首先 Client 端發送鏈接請求報文,Server 段接受鏈接後回覆 ACK 報文,併爲此次鏈接分配資源。Client 端接收到 ACK 報文後也向 Server 段發生 ACK 報文,並分配資源,這樣 TCP 鏈接就創建了。 - 第一步: 客戶機的 TCP 先向服務器的 TCP 發送一個鏈接請求報文. 這個特殊的報文中不含應用層數據, 其首部中的 SYN 標誌位被置 1. 另外, 客戶機會隨機選擇一個起始序號 seq=x(鏈接請求報文不攜帶數據,但要消耗掉一個序號) - 第二步: 服務器端的 TCP 收到鏈接請求報文後, 若贊成創建鏈接, 就向客戶機發送請求, 併爲該 TCP 鏈接分配 TCP 緩存和變量. 在確認報文中,SYN 和 ACK 位都被置爲 1, 確認號字段的值爲 x+1, 而且服務器隨機產生起始序號 seq=y(確認報文不攜帶數據, 但也要消耗掉一個序號). 確認報文一樣不包含應用層數據. - 第三步: 當客戶機收到確認報文後, 還要向服務器給出確認, 而且也要給該鏈接分配緩存和變量. 這個報文的 ACK 標誌位被置爲 1, 序號字段爲 x+1, 確認號字段爲 y+1 - 四次揮手 - 第一步: 客戶機打算關閉鏈接,就向其 TCP 發送一個鏈接釋放報文,並中止再發送數據,主動關閉 TCP 鏈接, 該報文的 FIN 標誌位被置 1, seq=u, 它等於前面已經傳送過的數據的最後一個字節的序號加 1(FIN 報文即便不攜帶數據,也要消耗掉一個序號) - 第二步: 服務器接收鏈接釋放報文後即發出確認, 確認號是 ack=u+1, 這個報文本身的序號是 v, 等於它前面已傳送過的數據的最後一個本身的序號加 1. 此時, 從客戶機到服務器這個方向的鏈接就釋放了, TCP 鏈接處於半關閉狀態. 但服務器若發送數據, 客戶機仍要接收, 即從服務器到客戶機的鏈接仍未關閉. - 第三步: 若服務器已經沒有了要向客戶機發送的數據, 就通知 TCP 釋放鏈接, 此時其發出 FIN=1 的鏈接釋放報文 - 第四步: 客戶機收到鏈接釋放報文後, 必須發出確認. 在確認報文中, ACK 字段被置爲 1, 確認號 ack=w+1, 序號 seq=u+1. 此時, TCP 鏈接尚未釋放掉, 必須通過等待計時器設置的時間 2MSL 後, A 才進入到鏈接關閉狀態.
- 對稱密鑰加密是指加密和解密使用同一個密鑰的方式,這種方式存在的最大問題就是密鑰發送問題,即如何安全地將密鑰發給對方;而非對稱加密是指使用一對非對稱密鑰,即公鑰和私鑰,公鑰能夠隨意發佈,但私鑰只有本身知道。發送密文的一方使用對方的公鑰進行加密處理,對方接收到加密信息後,使用本身的私鑰進行解密。 - 因爲非對稱加密的方式不須要發送用來解密的私鑰,因此能夠保證安全性;可是和對稱加密比起來,它很是的慢,因此咱們仍是要用對稱加密來傳送消息,但對稱加密所使用的密鑰咱們能夠經過非對稱加密的方式發送出去。
> 攻擊方僞造源地址發送 SYN 報文,服務端此時回覆 syn+ack,可是真正的 IP 地址收到這個包以後,有可能直接回復了 RST 包,可是若是不回覆 RST 包,那就更嚴重了,可能服務端會在幾十秒後才關閉這個 socket 連接(時間根據每一個系統不同)
能夠 1. 客戶端發送一個SYN包,頭部包含Fast Open選項的Cookie長度爲0 2. 服務端根據客戶端IP生成cookie,放在SYN+ACK包中一同發回客戶端 3. 客戶端收到Cookie之後緩存在本身的本地內存 4. 客戶端再次訪問服務端時,在SYN包攜帶數據,並在頭部包含上次緩存在本地的TCP cookie 5. 若是服務端校驗Cookie合法,則在客戶端回覆ACK前就能夠直接發送數據。若是Cookie不合法,則按照正常三次握手進行
> Udp接收到應用協議的某個消息(已編碼爲二進制)後,不會分包,可是要記錄當前數據包的大小(消息大小加上8個字節頭部),而後交給IP層。而TCP接收到應用層協議的消息(已編碼爲二進制)後,而後參考該計算機鏈接的網絡數據鏈路層MTU(最大傳輸單元)肯定是否要分包,而後交給IP層。 UDP是非面向鏈接的,即發送數據以前不須要創建鏈接,而TCP則是面向鏈接的,也就是說,經過TCP鏈接傳送的數據,無差錯,不丟失,不重複,且按序到達。也就是說UDP管發無論到,而TCP管發管到。所以,在安全性方面來講,TCP更具備優越性。 Udp適用場合:若是須要作實時性很高且消息小(好比小於以太網的MTU1500Byte)的通訊程序,UDP就很適合。關鍵消息重複發,不用等確認均可以,固然最終仍是須要確認。若是是內網環境,網絡很是穩定,UDP幾乎不會出問題,若是極度要求性能,能夠考慮。若是你公網網絡資源有限,而用戶之間須要傳遞大量數據,能夠考慮用UDP作NAT穿透。
打開 1 個頁面至少須要 1 個網絡進程、1 個瀏覽器進程、1 個 GPU 進程以及 1 個渲染進程,共 4 個;最新的 Chrome 瀏覽器包括:1 個瀏覽器(Browser)主進程、1 個 GPU 進程、1 個網絡(NetWork)進程、多個渲染進程和多個插件進程。 - 瀏覽器進程:主要負責界面顯示、用戶交互、子進程管理,同時提供存儲等功能。 - 渲染進程:核心任務是將 HTML、CSS 和 JavaScript 轉換爲用戶能夠與之交互的網頁,排版引擎 Blink 和 JavaScript 引擎 V8 都是運行在該進程中,默認狀況下,Chrome 會爲每一個 Tab 標籤建立一個渲染進程。出於安全考慮,渲染進程都是運行在沙箱模式下。 - GPU 進程:其實,Chrome 剛開始發佈的時候是沒有 GPU 進程的。而 GPU 的使用初衷是爲了實現 3D CSS 的效果,只是隨後網頁、Chrome 的 UI 界面都選擇採用 GPU 來繪製,這使得 GPU 成爲瀏覽器廣泛的需求。最後,Chrome 在其多進程架構上也引入了 GPU 進程。 - 網絡進程:主要負責頁面的網絡資源加載,以前是做爲一個模塊運行在瀏覽器進程裏面的,直至最近才獨立出來,成爲一個單獨的進程。 - 插件進程:主要是負責插件的運行,因插件易崩潰,因此須要經過插件進程來隔離,以保證插件進程崩潰不會對瀏覽器和頁面形成影響。
> 互聯網中的數據是經過數據包來傳輸的。數據包要在互聯網上進行傳輸,就要符合網際協議(IP),互聯網上不一樣的在線設備都有惟一的地址,地址只是一個數字,只要知道這個具體的地址,就能夠往這裏發送信息。 若是要想把一個數據包從主機 A 發送給主機 B,那麼在傳輸以前,數據包上會被附加上主機 B 的 IP 地址信息,這樣在傳輸過程當中才能正確尋址。額外地,數據包上還會附加上主機 A 自己的 IP 地址,有了這些信息主機 B 才能夠回覆信息給主機 A。這些附加的信息會被裝進一個叫 IP 頭的數據結構裏。IP 頭是 IP 數據包開頭的信息,包含 IP 版本、源 IP 地址、目標 IP 地址、生存時間等信息。 IP 是很是底層的協議,只負責把數據包傳送到對方電腦,可是對方電腦並不知道把數據包交給哪一個程序,是交給瀏覽器仍是交給王者榮耀?所以,須要基於 IP 之上開發能和應用打交道的協議,最多見的是用戶數據包協議(User Datagram Protocol),簡稱UDP和傳輸控制協議(Transmission Control Protocol),簡稱TCP. 基本傳輸過程爲: - 上層將數據包交給傳輸層 - 傳輸層會在數據包前面附加上UDP 頭,組成新的 UDP 數據包,再將新的 UDP 數據包交給網絡層 - 網絡層再將 IP 頭附加到數據包上,組成新的 IP 數據包,並交給底層 - 數據包被傳輸到主機 B 的網絡層,在這裏主機 B 拆開 IP 頭信息,並將拆開來的數據部分交給傳輸層 - 在傳輸層,數據包中的 UDP 頭會被拆開,並根據 UDP 中所提供的端口號,把數據部分交給上層的應用程序 - 最終,數據包就發送到了主機 B 上層應用程序這裏。
- DNS緩存 主要就是在瀏覽器本地把對應的 IP 和域名關聯起來,這樣在進行DNS解析的時候就很快。 - MemoryCache 是指存在內存中的緩存。從優先級上來講,它是瀏覽器最早嘗試去命中的一種緩存。從效率上來講,它是響應速度最快的一種緩存。內存緩存是快的,也是「短命」的。它和渲染進程「生死相依」,當進程結束後,也就是 tab 關閉之後,內存裏的數據也將不復存在。 - 瀏覽器緩存 瀏覽器緩存,也稱Http緩存,分爲強緩存和協商緩存。優先級較高的是強緩存,在命中強緩存失敗的狀況下,纔會走協商緩存。 - 強緩存:強緩存是利用 http 頭中的 Expires 和 Cache-Control 兩個字段來控制的。強緩存中,當請求再次發出時,瀏覽器會根據其中的 expires 和 cache-control 判斷目標資源是否「命中」強緩存,若命中則直接從緩存中獲取資源,不會再與服務端發生通訊。 實現強緩存,過去咱們一直用expires。當服務器返回響應時,在 Response Headers 中將過時時間寫入 expires 字段。像這樣 expires: Wed, 12 Sep 2019 06:12:18 GMT 能夠看到,expires 是一個時間戳,接下來若是咱們試圖再次向服務器請求資源,瀏覽器就會先對比本地時間和 expires 的時間戳,若是本地時間小於 expires 設定的過時時間,那麼就直接去緩存中取這個資源。 從這樣的描述中你們也不難猜想,expires 是有問題的,它最大的問題在於對「本地時間」的依賴。若是服務端和客戶端的時間設置可能不一樣,或者我直接手動去把客戶端的時間改掉,那麼 expires 將沒法達到咱們的預期。 考慮到 expires 的侷限性,HTTP1.1 新增了Cache-Control字段來完成 expires 的任務。expires 能作的事情,Cache-Control 都能作;expires 完成不了的事情,Cache-Control 也能作。所以,Cache-Control 能夠視做是 expires 的徹底替代方案。在當下的前端實踐裏,咱們繼續使用 expires 的惟一目的就是向下兼容。 cache-control: max-age=31536000 在 Cache-Control 中,咱們經過max-age來控制資源的有效期。max-age 不是一個時間戳,而是一個時間長度。在本例中,max-age 是 31536000 秒,它意味着該資源在 31536000 秒之內都是有效的,完美地規避了時間戳帶來的潛在問題。 Cache-Control 相對於 expires 更加準確,它的優先級也更高。當 Cache-Control 與 expires 同時出現時,咱們以 Cache-Control 爲準。 - 協商緩存:協商緩存依賴於服務端與瀏覽器之間的通訊。協商緩存機制下,瀏覽器須要向服務器去詢問緩存的相關信息,進而判斷是從新發起請求、下載完整的響應,仍是從本地獲取緩存的資源。若是服務端提示緩存資源未改動(Not Modified),資源會被重定向到瀏覽器緩存,這種狀況下網絡請求對應的狀態碼是 304。 協商緩存的實現,從 Last-Modified 到 Etag,Last-Modified 是一個時間戳,若是咱們啓用了協商緩存,它會在首次請求時隨着 Response Headers 返回: Last-Modified: Fri, 27 Oct 2017 06:35:57 GMT 隨後咱們每次請求時,會帶上一個叫 If-Modified-Since 的時間戳字段,它的值正是上一次 response 返回給它的 last-modified 值: If-Modified-Since: Fri, 27 Oct 2017 06:35:57 GMT 服務器接收到這個時間戳後,會比對該時間戳和資源在服務器上的最後修改時間是否一致,從而判斷資源是否發生了變化。若是發生了變化,就會返回一個完整的響應內容,並在 Response Headers 中添加新的 Last-Modified 值;不然,返回如上圖的 304 響應,Response Headers 不會再添加 Last-Modified 字段。 使用 Last-Modified 存在一些弊端,這其中最多見的就是這樣兩個場景: 咱們編輯了文件,但文件的內容沒有改變。服務端並不清楚咱們是否真正改變了文件,它仍然經過最後編輯時間進行判斷。所以這個資源在再次被請求時,會被當作新資源,進而引起一次完整的響應——不應從新請求的時候,也會從新請求。 當咱們修改文件的速度過快時(好比花了 100ms 完成了改動),因爲 If-Modified-Since 只能檢查到以秒爲最小計量單位的時間差,因此它是感知不到這個改動的——該從新請求的時候,反而沒有從新請求了。 這兩個場景其實指向了同一個 bug——服務器並無正確感知文件的變化。爲了解決這樣的問題,Etag 做爲 Last-Modified 的補充出現了。 Etag 是由服務器爲每一個資源生成的惟一的標識字符串,這個標識字符串能夠是基於文件內容編碼的,只要文件內容不一樣,它們對應的 Etag 就是不一樣的,反之亦然。所以 Etag 可以精準地感知文件的變化。 Etag 的生成過程須要服務器額外付出開銷,會影響服務端的性能,這是它的弊端。所以啓用 Etag 須要咱們審時度勢。正如咱們剛剛所提到的——Etag 並不能替代 Last-Modified,它只能做爲 Last-Modified 的補充和強化存在。 Etag 在感知文件變化上比 Last-Modified 更加準確,優先級也更高。當 Etag 和 Last-Modified 同時存在時,以 Etag 爲準。 - Service Worker Cache Service Worker 是一種獨立於主線程以外的 Javascript 線程。它脫離於瀏覽器窗體,所以沒法直接訪問 DOM。這樣獨立的個性使得 Service Worker 的「我的行爲」沒法干擾頁面的性能,這個「幕後工做者」能夠幫咱們實現離線緩存、消息推送和網絡代理等功能。咱們藉助 Service worker 實現的離線緩存就稱爲 Service Worker Cache。 Service Worker 的生命週期包括 install、active、working 三個階段。一旦 Service Worker 被 install,它將始終存在,只會在 active 與 working 之間切換,除非咱們主動終止它。這是它能夠用來實現離線存儲的重要先決條件. - Push Cache Push Cache 是指 HTTP2 在 server push 階段存在的緩存。這塊的知識比較新,應用也還處於萌芽階段,應用範圍有限不表明不重要——HTTP2 是趨勢、是將來。在它還未被推而廣之的此時此刻,我仍但願你們能對 Push Cache 的關鍵特性有所瞭解: - Push Cache 是緩存的最後一道防線。瀏覽器只有在 Memory Cache、HTTP Cache 和 Service Worker Cache 均未命中的狀況下才會去詢問 Push Cache。 - Push Cache 是一種存在於會話階段的緩存,當 session 終止時,緩存也隨之釋放。 - 不一樣的頁面只要共享了同一個 HTTP2 鏈接,那麼它們就能夠共享同一個 Push Cache。
- ajax是使用XMLHttpRequest對象發起的,可是用起來很麻煩,因此ES6新規範就有了fetch,fetch發一個請求不用像ajax那樣寫一大堆代碼。 - 使用fetch沒法取消一個請求,這是由於fetch基於Promise,而Promise沒法作到這一點。 - 在默認狀況下,fetch不會接受或者發送cookies - fetch沒有辦法原生監測請求的進度,而XMLHttpRequest能夠 - fetch只對網絡請求報錯,對400,500都當作成功的請求,須要封裝去處理 - fetch因爲是ES6規範,兼容性上比不上XMLHttpRequest
HTML5引入了 history.pushState() 和 history.replaceState() 方法,它們分別能夠添加和修改歷史記錄條目。 let stateObj = { foo: "bar", }; history.pushState(stateObj, "page 2", "bar.html"); 假設當前頁面爲 foo.html,執行上述代碼後會變爲 bar.html,點擊瀏覽器後退,會變爲 foo.html,但瀏覽器並不會刷新。 pushState() 須要三個參數: 一個狀態對象, 一個標題 (目前被忽略), 和 (可選的) 一個 URL. 讓咱們來解釋下這三個參數詳細內容: - 狀態對象 — 狀態對象 state 是一個 JavaScript 對象,經過 pushState () 建立新的歷史記錄條目。不管何時用戶導航到新的狀態,popstate 事件就會被觸發,且該事件的 state 屬性包含該歷史記錄條目狀態對象的副本。狀態對象能夠是能被序列化的任何東西。緣由在於 Firefox 將狀態對象保存在用戶的磁盤上,以便在用戶重啓瀏覽器時使用,咱們規定了狀態對象在序列化表示後有640k的大小限制。若是你給 pushState() 方法傳了一個序列化後大於 640k 的狀態對象,該方法會拋出異常。若是你須要更大的空間,建議使用 sessionStorage 以及 localStorage. - 標題 — Firefox 目前忽略這個參數,但將來可能會用到。傳遞一個空字符串在這裏是安全的,而在未來這是不安全的。二選一的話,你能夠爲跳轉的 state 傳遞一個短標題。 - URL — 該參數定義了新的歷史URL記錄。注意,調用 pushState() 後瀏覽器並不會當即加載這個 URL,但可能會在稍後某些狀況下加載這個 URL,好比在用戶從新打開瀏覽器時。新URL沒必要須爲絕對路徑。若是新URL是相對路徑,那麼它將被做爲相對於當前 URL 處理。新 URL 必須與當前URL同源,不然 pushState() 會拋出一個異常。該參數是可選的,缺省爲當前 URL。
REST 指的是一組架構約束條件和原則。知足這些約束條件和原則的應用程序或設計就是 RESTful。 - GET get方法在Rest中主要用於獲取資源,可以發送參數,不過有限制,且參數都會以?開頭的形 式附加在URL尾部。規範的get方法處理器應該是冪等的,也就是說對一個資源不論發送多少次get請求都不會更改數據或形成破壞。 - POST post方法在Rest請求中主要用於添加資源,參數信息存放在請求報文的消息體中相對安全,且可發送較大信息 - PUT put方法在Rest中主要用於更新資源,由於大多數瀏覽器不支持put和delete,會自動將put和delete請求轉化爲get和post. 所以爲了使用put和delete方法, 須要以post發送請求,在表單中使用隱藏域發送真正的請求。put方法的參數是同post同樣是存放在消息中的,一樣具備安全性,可發送較大信息。put方法是冪等的,對同一URL資源作出的同一數據的任意次put請求其對數據的改變都是一致的。 - DELETE Delete在Rest請求中主要用於刪除資源,由於大多數瀏覽器不支持put和delete,會自動將put和delete請求轉化爲get和post。所以爲了使用put和delete方法,須要以post發送請求,在表單中使用隱藏域發送真正的請求。Delete方法的參數同post同樣存放在消息體中,具備安全性,可發送較大信息 Delete方法是冪等的,不論對同一個資源進行多少次delete請求都不會破壞數據
- GET產生一個TCP數據包;POST產生兩個TCP數據包。 - GET在瀏覽器回退時是無害的,而POST會再次提交請求。 - GET產生的URL地址能夠被Bookmark,而POST不能夠。 - GET請求會被瀏覽器主動cache,而POST不會,除非手動設置。 - GET請求只能進行url編碼,而POST支持多種編碼方式。 - GET請求參數會被完整保留在瀏覽器歷史記錄裏,而POST中的參數不會被保留。 - GET請求在URL中傳送的參數是有長度限制的,而POST麼有。 - 對參數的數據類型,GET只接受ASCII字符,而POST沒有限制。 - GET比POST更不安全,由於參數直接暴露在URL上,因此不能用來傳遞敏感信息。 - GET參數經過URL傳遞,POST放在Request body中。
Accept 請求頭用來告知客戶端能夠處理的內容類型,這種內容類型用MIME類型來表示。服務器使用 Content-Type 應答頭通知客戶端它的選擇。 Accept: text/html Accept: image/* Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8 - Accept屬於請求頭, Content-Type屬於實體頭。 Http報頭分爲通用報頭,請求報頭,響應報頭和實體報頭。 請求方的http報頭結構:通用報頭|請求報頭|實體報頭 響應方的http報頭結構:通用報頭|響應報頭|實體報頭 - Accept表明發送端(客戶端)但願接受的數據類型。 好比:Accept:text/xml; 表明客戶端但願接受的數據類型是xml類型 Content-Type表明發送端(客戶端|服務器)發送的實體數據的數據類型。 好比:Content-Type:text/html; 表明發送端發送的數據格式是html。 兩者合起來, Accept:text/xml; Content-Type:text/html 即表明但願接受的數據類型是xml格式,本次請求發送的數據的數據格式是html。
- HTTP 的URL 以http:// 開頭,而HTTPS 的URL 以https:// 開頭 - HTTP 是不安全的,而 HTTPS 是安全的 - HTTP 標準端口是80 ,而 HTTPS 的標準端口是443 - 在OSI 網絡模型中,HTTP工做於應用層,而HTTPS 的安全傳輸機制工做在傳輸層 - HTTP 沒法加密,而HTTPS 對傳輸的數據進行加密 - HTTP無需證書,而HTTPS 須要CA機構wosign的頒發的SSL證書
> 無狀態協議對於事務處理沒有記憶能力。缺乏狀態意味着若是後續處理須要前面的信息也就是說, 當客戶端一次HTTP請求完成之後,客戶端再發送一次HTTP請求,HTTP並不知道當前客戶端是一個」老用戶「。 能夠使用Cookie來解決無狀態的問題,Cookie就至關於一個通行證,第一次訪問的時候給客戶端發送一個Cookie, 當客戶端再次來的時候,拿着Cookie(通行證),那麼服務器就知道這個是」老用戶「。
> 因爲一般狀況下 Socket 鏈接就是 TCP 鏈接,所以 Socket 鏈接一旦創建,通訊雙方便可開始相互發送數據內容,直到雙方鏈接斷開。但在實際網絡應用中,客戶端到服務器之間的通訊每每須要穿越多箇中間節點,例如路由器、網關、防火牆等,大部分防火牆默認會關閉長時間處於非活躍狀態的鏈接而致使 Socket 鏈接斷連,所以須要經過輪詢告訴網絡,該鏈接處於活躍狀態。 而 HTTP 鏈接使用的是「請求—響應」的方式,不只在請求時須要先創建鏈接,並且須要客戶端向服務器發出請求後,服務器端才能回覆數據。 不少狀況下,須要服務器端主動向客戶端推送數據,保持客戶端與服務器數據的實時與同步。此時若雙方創建的是 Socket 鏈接,服務器就能夠直接將數據傳送給客戶端;若雙方創建的是 HTTP 鏈接,則服務器須要等到客戶端發送一次請求後才能將數據傳回給客戶端,所以,客戶端定時向服務器端發送鏈接請求,不只能夠保持在線,同時也是在「詢問」服務器是否有新的數據,若是有就將數據傳給客戶端。
> 如前所述,一個 HTTP 報文包含起始行,頭域和消息體,HTTP 協議自己並無對報文中任一部分的長度作限制,也就是說,理論上一個請求 URI 能夠無限長,頭域能夠無限多,請求體能夠無限大。但在實際場景下,請求 URI 的長度會受到瀏覽器的限制,若是在瀏覽器中輸入過長的 URL,那麼瀏覽器會自動進行截斷。而服務器出於安全性和效率的考慮,也會對頭域和消息體的大小做出必定的限制。
- GET 方法:發送一個請求來取得服務器上的某一資源 - POST 方法:向 URL 指定的資源提交數據或附加新的數據 - PUT 方法:跟 POST 方法很像,也是想服務器提交數據。可是,它們之間有不一樣。PUT 指定了資源在服務器上的位置,而 POST 沒有 - HEAD 方法:只請求頁面的首部 - DELETE 方法:刪除服務器上的某資源 - OPTIONS 方法:它用於獲取當前 URL 所支持的方法。若是請求成功,會有一個 Allow 的頭包含相似「GET,POST」這樣的信息 - TRACE 方法:TRACE 方法被用於激發一個遠程的,應用層的請求消息迴路 - CONNECT 方法:把請求鏈接轉換到透明的 TCP/IP 通道
HTTP通訊機制是在一次完整的HTTP通訊過程當中,Web瀏覽器與Web服務器之間將完成下列7個步驟: - 創建TCP鏈接 在HTTP工做開始以前,Web瀏覽器首先要經過網絡與Web服務器創建鏈接,該鏈接是經過TCP來完成的,該協議與IP協議共同構建 Internet,即著名的TCP/IP協議族,所以Internet又被稱做是TCP/IP網絡。HTTP是比TCP更高層次的應用層協議,根據規則, 只有低層協議創建以後才能,才能進行更層協議的鏈接,所以,首先要創建TCP鏈接,通常TCP鏈接的端口號是80。 - Web瀏覽器向Web服務器發送請求行 一旦創建了TCP鏈接,Web瀏覽器就會向Web服務器發送請求命令。例如:GET /sample/hello.jsp HTTP/1.1。 - Web瀏覽器發送請求頭 瀏覽器發送其請求命令以後,還要以頭信息的形式向Web服務器發送一些別的信息,以後瀏覽器發送了一空白行來通知服務器,它已經結束了該頭信息的發送。 - Web服務器應答 客戶機向服務器發出請求後,服務器會客戶機回送應答, HTTP/1.1 200 OK ,應答的第一部分是協議的版本號和應答狀態碼。 - Web服務器發送應答頭 正如客戶端會隨同請求發送關於自身的信息同樣,服務器也會隨同應答向用戶發送關於它本身的數據及被請求的文檔。 - Web服務器向瀏覽器發送數據 Web服務器向瀏覽器發送頭信息後,它會發送一個空白行來表示頭信息的發送到此爲結束,接着,它就以Content-Type應答頭信息所描述的格式發送用戶所請求的實際數據。 - Web服務器關閉TCP鏈接 通常狀況下,一旦Web服務器向瀏覽器發送了請求數據,它就要關閉TCP鏈接,而後若是瀏覽器或者服務器在其頭信息加入了這行代碼: Connection:keep-alive TCP鏈接在發送後將仍然保持打開狀態,因而,瀏覽器能夠繼續經過相同的鏈接發送請求。保持鏈接節省了爲每一個請求創建新鏈接所需的時間,還節約了網絡帶寬。 創建TCP鏈接->發送請求行->發送請求頭->(到達服務器)發送狀態行->發送響應頭->發送響應數據->斷TCP鏈接
要作好防XSS、CSRF、SQL注入攻擊.DDOS攻擊。 - XSS概念: 譯爲跨站腳本攻擊,具體是指攻擊者在Web頁面裏插入惡意Script腳本,當用戶瀏覽該網頁時,Script代碼會被執行,從而進行惡意攻擊。 - XSS預防: 關鍵cookie字段設置httpOnly 輸入檢查,特殊字符 < > / &等,對其進行轉義後存儲 - CSRF概念: 本質上講,是黑客將一個http接口中須要傳遞的全部參數都預測出來,而後無論以什麼方式,他均可以根據他的目的來任意調用你的接口,對服務器實現CURD。 - CSRF 預防: 使用驗證碼,更高級用圖靈測試 - SQL概念: 一般沒有任何過濾,直接把參數存放到了SQL語句當中 - SQL預防: 根本上防止SQL注入的方法,就是參數化查詢或者作詞法分析。 - DDOS概念: 利用木桶原理,尋找利用系統應用的瓶頸;阻塞和耗盡;當前問題:用戶的帶寬小於攻擊的規模,噪聲訪問帶寬成爲木桶的短板。 - DDOS預防:用軟硬件結合的方式來防護是最有效的
1.CDN緩存更方便 2.突破瀏覽器併發限制 (通常每一個域名創建的連接不超過6個) 3.Cookieless,節省帶寬, 尤爲是上行帶寬通常比下行要慢; 4.對於UGC的內容和主站隔離, 防止沒必要要的安全問題 (上傳js竊取主站cookie之類的)。 正是這個緣由要求用戶內容的域名 必須不是本身主站的子域名, 而是一個徹底獨立的第三方域名。 5.數據作了劃分, 甚至切到了不一樣的物理集羣, 經過子域名來分流比較省事。
CDN又稱爲內容分發網絡; 本意在於 儘量避開互聯網上有可能影響數據 傳輸速度和穩定性的瓶頸和環節, 使內容傳輸的更快、更穩定。 主要目的: 解決因分佈、帶寬、服務器性能帶來的訪問延遲問題, 適用於站點加速、點播、直播等場景。 使用戶可就近取得所需內容, 解決 Internet網絡擁擠的情況, 提升用戶訪問網站的響應速度和成功率。 缺點: - 實施複雜 , 投資大; - 目前大部分的CDN還只是對靜態內容加速, 對動態加速效果很差; 而雙線對動態加速的效果跟靜態是同樣的。
- HTTP 最初的版本中,每進行一次 HTTP 通訊,就要斷開一次 TCP 鏈接(無鏈接) HTTP/1.1 增長了持久鏈接(HTTP Persistent Connections )的方法,其特色是,只要一方未明確提出斷開鏈接,則另外一方保持 TCP 鏈接狀態 - 管線化是指將多個 HTTP 請求整批發送,在發送過程當中不用等待對方響應 管線化是在持久鏈接的基礎上實現的,管線化的實現,可以同時並行發送多個請求,而不須要一個接一個的等待響應
> 我會準備兩個服務器上傳接口,前端或者原生客戶端上傳文件能夠拿到文件大小,根據文件大小,分發不一樣的對應服務器接口處理上傳,大文件能夠進行斷點續傳,原理是 md5 生成惟一的 hash 值,將分片的 hash 數組先上傳到後端,而後將文件分片上傳,對比 hash 值,相同的則丟棄。不一致的話,根據數組內容進行 buffer 拼接生成文件。 斷點續傳最核心的內容就是把文件「切片」而後再一片一片的傳給服務器,可是這看似簡單的上傳過程卻有着無數的坑。 首先是文件的識別,一個文件被分紅了若干份以後如何告訴服務器你切了多少塊,以及最終服務器應該如何把你上傳上去的文件進行合併,這都是要考慮的。 所以在文件開始上傳以前,咱們和服務器要有一個「握手」的過程,告訴服務器文件信息,而後和服務器約定切片的大小,當和服務器達成共識以後就能夠開始後續的文件傳輸了。 前臺要把每一塊的文件傳給後臺,成功以後前端和後端都要標識一下,以便後續的斷點。 當文件傳輸中斷以後用戶再次選擇文件就能夠經過標識來判斷文件是否已經上傳了一部分,若是是的話,那麼咱們能夠接着上次的進度繼續傳文件,以達到續傳的功能。有了HTML5 的 File api以後切割文件比想一想的要簡單的多的多。 只要用slice 方法就能夠了 var packet = file.slice(start, end); 參數start是開始切片的位置,end是切片結束的位置 單位都是字節。經過控制start和end 就能夠是實現文件的分塊 如 file.slice(0,1000); file.slice(1000,2000); file.slice(2000,3000); // ...... 在把文件切成片以後,接下來要作的事情就是把這些碎片傳到服務器上。若是中間掉線了,下次再傳的時候就得先從服務器獲取上一次上傳文件的位置,而後以這個位置開始上傳接下來的文件內容。
- 方案一:```a+=b;b=a-b;a-=b;``` - 方案二:```a^=b;b^=a;a^=b;``` - 方案三:```a=[a,b];b=a[0];a=a[1];``` - 方案四:```a=[b,b=a][0];``` - 方案五:```a={a:b,b:a};b=a.b;a=a.a;``` - 方案六:```[a,b]=[b,a];```
- let 和 const - Set 和 Map數據結構 - Class - 模板字符串 - 箭頭函數 - Itertor 和 for of 遍歷索引數組和類數組對象 - ... 參數加強和打散數組 - 解構 數組/對象/參數 - Promise - Symbol 基本類型 - Reflect - Proxy - Decorator 裝飾器 - es6 module es6模塊
- 閉包是函數和聲明該函數的詞法環境的組合。(MDN定義) A clousure is the combination of a function and the lexical environment within which that function was declared. 注:離散數學中是對集合間關係的一種描述 - ECMAScript支持閉包,於是js中的閉包表現爲:外層函數調用後,外層函數變量被內層函數對象的[[scope]]引用着而致使外層函數的做用域對象AO沒法釋放(垃圾回收)); - js中閉包常見做用:<1>.實現共有變量,如:函數累加器;<2>.能夠作緩存(存儲結構);<3>.屬性私有化;<4>.模塊化開發,防止污染全局變量 - js閉包的缺點:比普通函數佔有更多內存(多的是外層函數做用域對象AO始終存在),容易形成內存泄漏
> 什麼是原型鏈:只要是對象就有原型, 而且原型也是對象, 所以只要定義了一個對象, 那麼就能夠找到他的原型, 如此反覆, 就能夠構成一個對象的序列, 這個結構就被稱爲原型鏈 全部的實例有一個內部指針(prototype),指向它的原型對象,而且能夠訪問原型對象上的全部屬性和方法。
> Set 對象相似於數組,且成員的值都是惟一的。Map 對象是鍵值對集合,和 JSON 對象相似,可是 key 不只能夠是字符串還能夠是對象
- 原型鏈繼承 - 構造函數繼承 - 組合繼承 - 原型式繼承
function object(o){javascript
function F(){}; F.prototype=o; return new F();
}php
- 寄生式繼承 - 寄生組合式繼承
- 工廠模式 - 構造函數模式 - 原型模式 - 動態原型模式 - 寄生構造函數模式 - 穩妥構造函數模式
- number(NaN) - boolean - string - object(Null) - undefined - function - symbol
- '==='會首先進行類型判斷,屏蔽了自動類型轉換;而'=='會先進行自動類型轉換爲數字後再比較。'==='是屏蔽了自動類型轉換的'=='
- js底層undefined會被自動翻譯成null;undefined可看作未賦值,null指曾賦過值,可是目前沒有值;null是一個特殊關鍵字,不是標識符,不能當作變量來使用和賦值,而undefined倒是一個標識符,可看成變量來使用和賦值
- Number(null)返回0,Number(undefined)返回NaN,Number(NaN)返回NaN,可是Number(undefined)==Number(NaN)返回false
- 只能經過isNaN判斷
- isArray() es6語法 - Object.prototype.toString.call() 無兼容性問題
優先級:new綁定 > 顯示綁定 > 隱式綁定 > 默認綁定 - 默認綁定 非嚴格模式下默認綁定指向全局對象,嚴格模式下默認綁定指向undefined - 隱式綁定 存在隱式丟失現象,且對象屬性引用鏈中只有最頂層會影響調用位置 - 顯式綁定 硬綁定和api調用上下文能夠解決隱式丟失問題 - new綁定
- 建立(或者說構造)一個全新的對象 - 這個對象會被執行[[prototype]]鏈接 - 這個新對象會綁定到函數調用的this - 若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象
- pop() 返回pop出來的元素 - push() 返回新數組的長度 - shift() 返回頭部的元素 - unshift() 返回新數組的長度 - splice() 返回被刪除元素的數組 - sort() 返回新數組 - reverse() 返回新數組 - fill() 返回新數組 - copyWithin() 返回新數組
- undefined - null - false - +0、-0和NaN - ""
> undefined、function、symbol和包含循環引用的對象都不符合JSON的結構標準,JSON.stringify在對象中遇到undefined、function和symbol時會自動將其忽略,在數組中則會返回null
> 基本類型值的字符串化規則爲null轉換爲"null",undefined轉換爲"undefined",true轉換爲"true"。數字的字符串轉換遵循通用規則,極小和極大的數字使用指數形式。對普通對象來講,如無自行定義,toString()返回內部屬性[[class]]的值
- toString()方法;數值、字符串、對象、布爾;都有toString方法;這個方法惟一能作的就是返回相應的字符串;其中null和undefined沒有toString()方法; - String()屬於強制轉換, null轉換的結果爲null;undefined轉換的結果爲undefined;其他的若是有toString()方法,即調用該方法,返回相應的結果;
> true轉化爲1,false轉化爲0,undefined轉化爲NaN,null轉化爲0。對象會首先被轉化爲相應的基本類型,若是返回非數字的基本類型,則再遵循以上規則將其強制轉換爲數字
> JavaScript中的值能夠分爲能夠被強制類型轉爲false的值和其餘,其中能夠轉換爲假的值包括:undefined;null;false;+0、-0和NaN;"",其他均爲真值
> 爲了將值轉換爲相應的基本類型值,抽象操做ToPrimitive會首先經過內部操做DefaultValue,檢查該值是否有valueOf()方法。若是有返回基本類型值,使用該值進行強制類型轉換;若是沒有就使用toString()的返回值(若是存在)來進行強制類型轉換;若是valueOf()和toString()均不返回基本類型值,會產生TypeError錯誤。
- const定義的變量不能夠修改,並且必須初始化 - var定義的變量能夠修改,若是不初始化會輸出undefined,不會報錯。 - let是塊級做用域,函數內部使用let定義後,對函數外部無影響。
> 在進入一個執行上下文後,先把 var 和 function 聲明的變量前置,再去順序執行代碼。 PS:做用域分爲全局做用域和函數做用域,用var聲明的變量,只在本身所在的所用域有效。
主要區別在this指向問題 - 普通函數的this 指向調用它的那個對象,例如 obj.func ,那麼func中的this就是obj - 箭頭函數不能做爲構造函數,不能使用new,沒有this,arguments箭頭函數,箭頭函數的this永遠指向其上下文的 this ,任何方法都改變不了其指向,如 call() , bind() , apply()(或者說箭頭函數中的this指向的是定義時的this,而不是執行時的this)
> 也能夠稱之爲事件代理,給父元素綁定事件,用來監聽子元素的冒泡事件,並找到是哪一個子元素的事件。將事件委託給另外的元素,利用事件冒泡的特性,將裏層的事件委託給外層事件,將事件綁定到目標元素的父節點,根據event對象的屬性進行事件委託,改善性能。事件監聽器會分析從子元素冒泡上來的事件,找到是哪一個子元素的事件。
- 直接使用 filter、concat 來計算 var a = [1,2,3,4,5] var b = [2,4,6,8,10] //交集 var c = a.filter(function(v){ return b.indexOf(v) > -1 }) //差集 var d = a.filter(function(v){ return b.indexOf(v) == -1 }) //補集 var e = a.filter(function(v){ return !(b.indexOf(v) > -1) }) .concat(b.filter(function(v){ return !(a.indexOf(v) > -1)})) //並集 var f = a.concat(b.filter(function(v){ return !(a.indexOf(v) > -1)})); console.log("數組a:", a); console.log("數組b:", b); console.log("a與b的交集:", c); console.log("a與b的差集:", d); console.log("a與b的補集:", e); console.log("a與b的並集:", f); - 藉助擴展運算符(...)以及 Set 的特性實現相關計算,代碼也會更加簡單些 var b = [2,4,6,8,10] console.log("數組a:", a); console.log("數組b:", b); var sa = new Set(a); var sb = new Set(b); // 交集 let intersect = a.filter(x => sb.has(x)); // 差集 let minus = a.filter(x => !sb.has(x)); // 補集 let complement = [...a.filter(x => !sb.has(x)), ...b.filter(x => !sa.has(x))]; // 並集 let unionSet = Array.from(new Set([...a, ...b])); console.log("a與b的交集:", intersect); console.log("a與b的差集:", minus); console.log("a與b的補集:", complement); console.log("a與b的並集:", unionSet);
> Symbol 是一種新的、特殊的對象,能夠用做對象中唯一的屬性名。使用 Symbol 替換string 能夠避免不一樣的模塊屬性的衝突。還能夠將Symbol設置爲私有,以便尚無直接訪問Symbol權限的任何人都不能訪問它們的屬性。 Symbol 是JS新的基本數據類型。與number、string和boolean 原始類型同樣,Symbol 也有一個用於建立它們的函數。與其餘原始類型不一樣,Symbol沒有字面量語法。建立它們的惟一方法是使用如下方法中的Symbol構造函數 let symbol = Symbol();
> 原型模式會建立新的對象,而不是建立未初始化的對象,它會返回使用從原型或樣本對象複製的值進行初始化的對象。原型模式也稱爲屬性模式。 原型模式有用的一個例子是使用與數據庫中的默認值匹配的值初始化業務對象。原型對象保留默認值,這些默認值將被複制到新建立的業務對象中。 傳統語言不多使用原型模式,可是JavaScript做爲一種原型語言,在構建新對象及其原型時使用這種模式。
> 在 ES6 中,let 和const 跟 var、class和function同樣也會被提高,只是在進入做用域和被聲明之間有一段時間不能訪問它們,這段時間是臨時死區(TDZ)。
> WeakMaps 提供了一種從外部擴展對象而不影響垃圾收集的方法。當我們想要擴展一個對象,可是由於它是封閉的或者來自外部源而不能擴展時,能夠應用WeakMap。 WeakMap只適用於 ES6 或以上版本。WeakMap是鍵和值對的集合,其中鍵必須是對象。 WeakMaps的有趣之處在於,它包含了對map內部鍵的弱引用。弱引用意味着若是對象被銷燬,垃圾收集器將從WeakMap中刪除整個條目,從而釋放內存。 > 和Map的區別: 當它們的鍵/值引用的對象被刪除時,它們的行爲都不一樣,如下面的代碼爲例: var map=new Map(); var weakmap=new WeakMap(); (funciton(){ var a={ x:12 }; var b={ y:12 }; map.set(a,1); weakmap.set(b,2); })() 執行上面的 IIFE,就沒法再引用{x:12}和{y:12}。垃圾收集器繼續運行,並從 WeakMa中刪除鍵b指針,還從內存中刪除了{y:12}。 但在使用 Map的狀況下,垃圾收集器不會從Map中刪除指針,也不會從內存中刪除{x:12}。 WeakMap 容許垃圾收集器執行其回收任務,但Map不容許。對於手動編寫的 Map,數組將保留對鍵對象的引用,以防止被垃圾回收。但在WeakMap中,對鍵對象的引用被「弱」保留,這意味着在沒有其餘對象引用的狀況下,它們不會阻止垃圾回收。
> 柯里化是一種模式,其中一個具備多個參數的函數被分解成多個函數,當被串聯調用時,這些函數將一次累加一個所需的全部參數。這種技術有助於使用函數式編寫的代碼更容易閱讀和編寫。須要注意的是,要實現一個函數,它須要從一個函數開始,而後分解成一系列函數,每一個函數接受一個參數。 function curry(fn){ if(fn.length===0){ return fn; } function _curried(depth,args){ return function(newArgument){ if(depth-1===0){ return fn(...args,newArgument) } return _curried(depth-1,[...args,newArgument]); } } return _curried(fn.length,[]); } function add(a,b){ return a+b; } var curriedAdd=curry(add); var addFive=curried(5); var result=[0,1,2,3,4,5].map(addFive);
> PC 時代爲了突破瀏覽器的域名併發限制。有了域名發散。 瀏覽器有併發限制,是爲了防止DDOS攻擊。 域名收斂:就是將靜態資源放在一個域名下。減小DNS解析的開銷。 域名發散:是將靜態資源放在多個子域名下,就能夠多線程下載,提升並行度,使客戶端加載靜態資源更加迅速。 域名發散是pc端爲了利用瀏覽器的多線程並行下載能力。而域名收斂多用與移動端,提升性能,由於dns解析是是從後向前迭代解析,若是域名過多性能會降低,增長DNS的解析開銷。
- event.target 返回觸發事件的元素 - event.currentTarget 返回綁定事件的元素
- 回調函數實現 - 事件監聽 - 發佈訂閱 - Promise/A+ 和生成器函數 - async/await
- 遵循嚴格模式:"use strict"; - 將 js 腳本放在頁面底部,加快渲染頁面 - 將 js 腳本將腳本成組打包,減小請求 - 使用非阻塞方式下載 js 腳本 - 儘可能使用局部變量來保存全局變量 - 儘可能減小使用閉包 - 使用 window 對象屬性方法時,省略 window - 儘可能減小對象成員嵌套 - 緩存 DOM 節點的訪問 - 經過避免使用 eval() 和 Function() 構造器 - 給 setTimeout() 和 setInterval() 傳遞函數而不是字符串做爲參數 - 儘可能使用直接量建立對象和數組 - 最小化重繪(repaint)和迴流(reflow)
- e.getAttribute(),是標準 DOM 操做文檔元素屬性的方法,具備通用性可在任意文檔上使用,返回元素在源文件中設置的屬性 - e.propName 一般是在 HTML 文檔中訪問特定元素的特性,瀏覽器解析元素後生成對應對象(如 a 標籤生成 HTMLAnchorElement),這些對象的特性會根據特定規則結合屬性設置獲得,對於沒有對應特性的屬性,只能使用 getAttribute 進行訪問 - e.getAttribute()返回值是源文件中設置的值,類型是字符串或者 null(有的實現返回"") - e.propName 返回值多是字符串、布爾值、對象、undefined 等 - 大部分 attribute 與 property 是一一對應關係,修改其中一個會影響另外一個,如 id,title 等屬性 - 一些布爾屬性`<input hidden/>`的檢測設置須要 hasAttribute 和 removeAttribute 來完成,或者設置對應 property - 像`<a href="../index.html">link</a>`中 href 屬性,轉換成 property 的時候須要經過轉換獲得完整 URL - 一些 attribute 和 property 不是一一對應如:form 控件中`<input value="hello"/>`對應的是 defaultValue,修改或設置 value property 修改的是控件當前值,setAttribute 修改 value 屬性不會改變 value property
- offsetWidth/offsetHeight 返回值包含 content + padding + border,效果與 e.getBoundingClientRect()相同 - clientWidth/clientHeight 返回值只包含 content + padding,若是有滾動條,也不包含滾動條 - scrollWidth/scrollHeight 返回值包含 content + padding + 溢出內容的尺寸
> 在解析 HTML 生成 DOM 過程當中,js 文件的下載是並行的,不須要 DOM 處理到 script 節點。所以,script 的位置不影響首屏顯示的開始時間。瀏覽器解析 HTML 是自上而下的線性過程,script 做爲 HTML 的一部分一樣遵循這個原則。所以,script 會延遲 DomContentLoad,只顯示其上部分首屏內容,從而影響首屏顯示的完成時間
- cookie - session - url 重寫 - 隱藏 input - ip 地址
- document.write 只能重繪整個頁面 - innerHTML 能夠重繪頁面的一部分
- 將時間設爲當前時間往前一點 var date = new Date(); date.setDate(date.getDate() - 1);//真正的刪除 setDate()方法用於設置一個月的某一天 -expires 的設置 document.cookie = 'user='+ encodeURIComponent('name') + ';expires = ' + new Date(0)
- attribute 是 dom 元素在文檔中做爲 html 標籤擁有的屬性; - property 就是 dom 元素在 js 中做爲對象擁有的屬性。 - 對於 html 的標準屬性來講,attribute 和 property 是同步的,是會自動更新的 - 可是對於自定義的屬性來講,他們是不一樣步的
var a = Number(1) // 1 var b = new Number(1) // Number {[[PrimitiveValue]]: 1} typeof (a) // number typeof (b) // object a == b // true - var a = 1 是一個常量,而 Number(1)是一個函數 - new Number(1)返回的是一個對象 - a==b 爲 true 是由於因此在求值過程當中,老是會強制轉爲原始數據類型而非對象,例以下面的代碼: typeof 123 // "number" typeof new Number(123) // "object" 123 instanceof Number // false (new Number(123)) instanceof Number // true 123 === new Number(123) // false
- 單線程是指 JavaScript 在執行的時候,有且只有一個主線程來處理全部的任務。 - 目的是爲了實現與瀏覽器交互。 - 咱們設想一下,若是 JavaScript 是多線程的,如今咱們在瀏覽器中同時操做一個 DOM,一個線程要求瀏覽器在這個 DOM 中添加節點,而另外一個線程卻要求瀏覽器刪掉這個 DOM 節點,那這個時候瀏覽器就會很鬱悶,他不知道應該以哪一個線程爲準。因此爲了不此類現象的發生,下降複雜度,JavaScript 選擇只用一個主線程來執行代碼,以此來保證程序執行的一致性。
> 專門用來作動畫,不卡頓,用法和setTimeout同樣。對 rAF 的闡述 MDN 資料 定時器一直是 js 動畫的核心技術,但它們不夠精準,由於定時器時間參數是指將執行代碼放入 UI 線程隊列中等待的時間,若是前面有其餘任務隊列執行時間過長,則會致使動畫延遲,效果不精確等問題。 因此處理動畫循環的關鍵是知道延遲多長時間合適:時間要足夠短,才能讓動畫看起來比較柔滑平順,避免多餘性能損耗;時間要足夠長,才能讓瀏覽器準備好變化渲染。這個時候 rAF 就出現了,採用系統時間間隔(大多瀏覽器刷新頻率是 60Hz,至關於 1000ms/60≈16.6ms),保持最佳繪製效率,不會由於間隔時間太短,形成過分繪製,增長開銷;也不會由於間隔時間太長,使用動畫卡頓不流暢,讓各類網頁動畫效果可以有一個統一的刷新機制。而且 rAF 會把每一幀中的全部 DOM 操做集中起來,在一次重繪或迴流中就完成
- 絕對定位水平垂直居中 <div style="position: absolute; width: 500px; height: 300px; margin: auto; top: 0; left: 0; bottom: 0; right: 0; background-color: green;">水平垂直居中</div> - 水平垂直居中 <div style="position: relative; width:400px; height:200px; top: 50%; left: 50%; margin: -100px 0 0 -200px; background-color: red;">水平垂直居中</div> - 水平垂直居中 <div style="position: absolute; width:300px; height:200px; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: blue;">水平垂直居中</div> - flex 佈局居中 <div style="display: flex;align-items: center;justify-content: center;"> <div style="width: 100px;height: 100px;background-color: gray;">flex 佈局</div> </div>
- display:none 此元素不會被顯示 - display:block 此元素將顯示爲塊級元素,此元素先後會帶有換行符 - display:inline 此元素會被顯示爲內聯元素,元素先後沒有換行符 - display:inline-block 行內塊元素,css2.1新增 - display:list-item 此元素會做爲列表顯示 - display:run-in 此元素會根據上下文做爲塊級元素或內兩元素顯示 - display:compact css2.1廢除 - display:marker css2.1廢除 - display:table 此元素會做爲塊級表格來顯示,表格先後帶有換行符 - display:inline-table 此元素會做爲內聯表格來顯示,表格先後沒有換行符 - display:table-row-group 此元素會做爲一個或多個行的分組來顯示(相似<tbody>) - display:table-header-group 此元素會做爲一個或多個行的分組來顯示(相似<thead>) - display:table-footer-group 此元素會做爲一個或多個行的分組來顯示(相似<tfoot>) - display:table-row 此元素會做爲一個表格行來顯示(相似<tr>) - display:table-column-group 此元素會做爲一個或多個列的分組來顯示(相似<colgroup>) - display:table-column 此元素會做爲一個單元格列顯示(相似<col>) - display:table-cell 此元素會做爲一個表格單元格來顯示(相似<td>和<th>) - display:table-caption 此元素會做爲一個表格標題來顯示(相似<caption>) - display:inherit 規定應該從父元素繼承display屬性的值
會觸發重繪或迴流/重排的操做 - 添加、刪除元素(迴流+重繪) - 隱藏元素,display:none(迴流+重繪),visibility:hidden(只重繪,不迴流) - 移動元素,如改變top、left或移動元素到另外1個父元素中(重繪+迴流) - 改變瀏覽器大小(迴流+重繪) - 改變瀏覽器的字體大小(迴流+重繪) - 改變元素的padding、border、margin(迴流+重繪) - 改變瀏覽器的字體顏色(只重繪,不迴流) - 改變元素的背景顏色(只重繪,不迴流) 優化: - 用transform 代替 top,left ,margin-top, margin-left... 這些位移屬性 - opacity 加上 transform: translateZ/3d 這個屬性以後便不會發生迴流和重繪了 - 不要使用 js 代碼對dom 元素設置多條樣式,選擇用一個 className 代替之。 - 若是確實須要用 js 對 dom 設置多條樣式那麼能夠將這個dom 先隱藏,而後再對其設置 - 不要使用table 佈局,由於table 的每個行甚至每個單元格的樣式更新都會致使整個table 從新佈局 - 對於頻繁變化的元素應該爲其加一個 transform 屬性,對於視頻使用video 標籤
- 三者都是製做web網頁時用到的單位 - px是實際像素大小,em是相對於父元素的像素大小,rem是相對於跟玉原石的像素大小 - IE6~8不支持em和rem屬性
- 選擇器類型: - ID #id - class .class - 標籤 p - 通用 * - 屬性 [type="text"] - 僞類 :hover - 僞元素 ::first-line - 子選擇器、相鄰選擇器 - 權重計算規則: - 第一等:表明內聯樣式,如: style=」」,權值爲1000。 - 第二等:表明ID選擇器,如:#content,權值爲0100。 - 第三等:表明類,僞類和屬性選擇器,如.content,權值爲0010。 - 第四等:表明類型選擇器和僞元素選擇器,如div p,權值爲0001。 - 通配符、子選擇器、相鄰選擇器等的。如*、>、+,權值爲0000。 - 繼承的樣式沒有權值。
> Flex(Flexible Box),也就是」彈性佈局」,它能夠很靈活地實現垂直居中、多列布局等自適應問題。而任何一個容器均可以指定爲Flex佈局。設爲Flex佈局之後,子元素的float、clear和vertical-align屬性將失效
- 盒模型:內容(content)、填充(padding)、邊界(margin)、 邊框(border) - 類型: IE 盒子模型、標準 W3C 盒子模型; - 兩種盒模型的主要區別是:標準盒模型的寬高是值內容寬高(content) - 而IE盒模型的寬高是指content+padding+border。 - 設置盒模型的方式是:設置box-sizing box-sizing:content-box 標準盒模型, box-sizing:border-box IE盒模型
- 重置(Resetting):重置意味着除去全部的瀏覽器默認樣式。對於頁面全部的元素,像margin、padding、font-size這些樣式所有置成同樣。你將必須從新定義各類元素的樣式。 - 標準化(Normalizing):標準化沒有去掉全部的默認樣式,而是保留了有用的一部分,同時還糾正了一些常見錯誤。 當須要實現很是個性化的網頁設計時,我會選擇重置的方式,由於我要寫不少自定義的樣式以知足設計需求,這時候就再也不須要標準化的默認樣式了。
> 浮動(float)是 CSS 定位屬性。浮動元素從網頁的正常流動中移出,可是保持了部分的流動性,會影響其餘元素的定位(好比文字會圍繞着浮動元素)。這一點與絕對定位不一樣,絕對定位的元素徹底從文檔流中脫離。 CSS 的clear屬性經過使用left、right、both,讓該元素向下移動(清除浮動)到浮動元素下面。 若是父元素只包含浮動元素,那麼該父元素的高度將塌縮爲 0。咱們能夠經過清除(clear)從浮動元素後到父元素關閉前之間的浮動來修復這個問題。 有一種 hack 的方法,是自定義一個.clearfix類,利用僞元素選擇器::after清除浮動。另外還有一些方法,好比添加空的<div></div>和設置浮動元素父元素的overflow屬性。與這些方法不一樣的是,clearfix方法,只須要給父元素添加一個類,定義以下: .clearfix::after { content: ''; display: block; clear: both; } 值得一提的是,把父元素屬性設置爲overflow: auto或overflow: hidden,會使其內部的子元素造成塊格式化上下文(Block Formatting Context),而且父元素會擴張本身,使其可以包圍它的子元素。
> CSS 中的z-index屬性控制重疊元素的垂直疊加順序。z-index只能影響position值不是static的元素。 沒有定義z-index的值時,元素按照它們出如今 DOM 中的順序堆疊(層級越低,出現位置越靠上)。非靜態定位的元素(及其子元素)將始終覆蓋靜態定位(static)的元素,而無論 HTML 層次結構如何。 層疊上下文是包含一組圖層的元素。在一組層疊上下文中,其子元素的z-index值是相對於該父元素而不是 document root 設置的。每一個層疊上下文徹底獨立於它的兄弟元素。若是元素 B 位於元素 A 之上,則即便元素 A 的子元素 C 具備比元素 B 更高的z-index值,元素 C 也永遠不會在元素 B 之上. 每一個層疊上下文是自包含的:當元素的內容發生層疊後,整個該元素將會在父層疊上下文中按順序進行層疊。少數 CSS 屬性會觸發一個新的層疊上下文,例如opacity小於 1,filter不是none,transform不是none。
塊格式上下文(BFC)是 Web 頁面的可視化 CSS 渲染的部分,是塊級盒佈局發生的區域,也是浮動元素與其餘元素交互的區域。 一個 HTML 盒(Box)知足如下任意一條,會建立塊格式化上下文: - float的值不是none. - position的值不是static或relative. - display的值是table-cell、table-caption、inline-block、flex、或inline-flex。 - overflow的值不是visible。 在 BFC 中,每一個盒的左外邊緣都與其包含的塊的左邊緣相接。 兩個相鄰的塊級盒在垂直方向上的邊距會發生合併(collapse)。更多內容請參考邊距合併(margin collapsing)。
- 空div方法:<div style="clear:both;"></div>。 - Clearfix 方法:上文使用.clearfix類已經提到。 - overflow: auto或overflow: hidden方法:上文已經提到。 在大型項目中,我會使用 Clearfix 方法,在須要的地方使用.clearfix。設置overflow: hidden的方法可能使其子元素顯示不完整,當子元素的高度大於父元素時。
雪碧圖是把多張圖片整合到一張上的圖片。它被運用在衆多使用了不少小圖標的網站上(Gmail 在使用)。實現方法: - 使用生成器將多張圖片打包成一張雪碧圖,併爲其生成合適的 CSS。 - 每張圖片都有相應的 CSS 類,該類定義了background-image、background-position和background-size屬性。 - 使用圖片時,將相應的類添加到你的元素中。 好處: - 減小加載多張圖片的 HTTP 請求數(一張雪碧圖只須要一個請求)。可是對於 HTTP2 而言,加載多張圖片再也不是問題。 - 提早加載資源,防止在須要時纔在開始下載引起的問題,好比只出如今:hover僞類中的圖片,不會出現閃爍。
- 在肯定問題緣由和有問題的瀏覽器後,使用單獨的樣式表,僅供出現問題的瀏覽器加載。這種方法須要使用服務器端渲染。 - 使用已經處理好此類問題的庫,好比 Bootstrap。 - 使用 autoprefixer 自動生成 CSS 屬性前綴。 - 使用 Reset CSS 或 Normalize.css。
- 優雅的降級:爲現代瀏覽器構建應用,同時確保它在舊版瀏覽器中正常運行。 - Progressive enhancement - The practice of building an application for a base level of user experience, but adding functional enhancements when a browser supports it. - 漸進式加強:構建基於用戶體驗的應用,但在瀏覽器支持時添加新增功能。 - 利用 caniuse.com 檢查特性支持。 - 使用 autoprefixer 自動生成 CSS 屬性前綴。 - 使用 Modernizr進行特性檢測。
這些方法與可訪問性(a11y)有關。 - visibility: hidden:元素仍然在頁面流中,並佔用空間。 - width: 0; height: 0:使元素不佔用屏幕上的任何空間,致使不顯示它。 - position: absolute; left: -99999px:將它置於屏幕以外。 - text-indent: -9999px:這隻適用於block元素中的文本。 - Metadata:例如經過使用 Schema.org,RDF 和 JSON-LD。 - WAI-ARIA:如何增長網頁可訪問性的 W3C 技術規範。 即便 WAI-ARIA 是理想的解決方案,我也會採用絕對定位方法,由於它具備最少的注意事項,適用於大多數元素,並且使用起來很是簡單。
- all 適用於全部設備。 - print 爲了加載合適的文檔到當前使用的可視窗口. 須要提早諮詢 paged media(媒體屏幕尺寸), 以知足個別設備網頁尺寸不匹配等問題。 - screen 主要適用於彩色的電腦屏幕 - speech 解析speech這個合成器. 注意: CSS2已經有一個類似的媒體類型叫aural.
> 首先,瀏覽器從最右邊的選擇器,即關鍵選擇器(key selector),向左依次匹配。根據關鍵選擇器,瀏覽器從 DOM 中篩選出元素,而後向上遍歷被選元素的父元素,判斷是否匹配。選擇器匹配語句鏈越短,瀏覽器的匹配速度越快。避免使用標籤和通用選擇器做爲關鍵選擇器,由於它們會匹配大量的元素,瀏覽器必需要進行大量的工做,去判斷這些元素的父元素們是否匹配。 BEM (Block Element Modifier) methodology recommends that everything has a single class, and, where you need hierarchy, that gets baked into the name of the class as well, this naturally makes the selector efficient and easy to override. BEM (Block Element Modifier)原則上建議爲獨立的 CSS 類命名,而且在須要層級關係時,將關係也體如今命名中,這天然會使選擇器高效且易於覆蓋。 搞清楚哪些 CSS 屬性會觸發從新佈局(reflow)、重繪(repaint)和合成(compositing)。在寫樣式時,避免觸發從新佈局的可能。
- 優勢: - 提升 CSS 可維護性。 - 易於編寫嵌套選擇器。 - 引入變量,增添主題功能。能夠在不一樣的項目中共享主題文件。 - 經過混合(Mixins)生成重複的 CSS。 - Splitting your code into multiple files. CSS files can be split up too but doing so will require a HTTP request to download each CSS file. - 將代碼分割成多個文件。不進行預處理的 CSS,雖然也能夠分割成多個文件,但須要創建多個 HTTP 請求加載這些文件。 - 缺點: - 須要預處理工具。 - 從新編譯的時間可能會很慢
- 喜歡: - 絕大部分優勢上題以及提過。 - Less 用 JavaScript 實現,與 NodeJS 高度結合。 - 不喜歡: - 我經過node-sass使用 Sass,它用 C ++ 編寫的 LibSass 綁定。在 Node 版本切換時,我必須常常從新編譯。 - Less 中,變量名稱以@做爲前綴,容易與 CSS 關鍵字混淆,如@media、@import和@font-face。
> 使用@font-face併爲不一樣的font-weight定義font-family。
> 這部分與上面關於編寫高效的 CSS 有關。瀏覽器從最右邊的選擇器(關鍵選擇器)根據關鍵選擇器,瀏覽器從 DOM 中篩選出元素,而後向上遍歷被選元素的父元素,判斷是否匹配。選擇器匹配語句鏈越短,瀏覽器的匹配速度越快。 例如,對於形如p span的選擇器,瀏覽器首先找到全部<span>元素,並遍歷它的父元素直到根元素以找到<p>元素。對於特定的<span>,只要找到一個<p>,就知道'`已經匹配並中止繼續匹配。
CSS 僞元素是添加到選擇器的關鍵字,去選擇元素的特定部分。它們能夠用於裝飾(:first-line,:first-letter)或將元素添加到標記中(與 content:...組合),而沒必要修改標記(:before,:after)。 - :first-line和:first-letter能夠用來修飾文字。 - 上面提到的.clearfix方法中,使用clear: both來添加不佔空間的元素。 - 使用:before和after展現提示中的三角箭頭。鼓勵關注點分離,由於三角被視爲樣式的一部分,而不是真正的 DOM。若是不使用額外的 HTML 元素,只用 CSS 樣式繪製三角形是不太可能的。
- block: - 大小:填充其父容器的寬度。 - 定位:重新的一行開始,而且不容許旁邊有 HTML 元素(除非是float) - 可否設置width和height:能 - 能夠使用vertical-align對齊:不能夠 - 邊距(margin)和填充(padding): 各個方向都存在 - 浮動(float):- - inline-block: - 大小:取決於內容。 - 定位:與其餘內容一塊兒流動,並容許旁邊有其餘元素。 - 可否設置width和height:能 - 能夠使用vertical-align對齊:能夠 - 邊距(margin)和填充(padding):各個方向都存在 - 浮動(float):- - inline: - 大小:取決於內容。 - 定位:與其餘內容一塊兒流動,並容許旁邊有其餘元素。 - 可否設置width和height:不能。設置會被忽略。 - 能夠使用vertical-align對齊:能夠 - 邊距(margin)和填充(padding):只有水平方向存在。垂直方向會被忽略。儘管border和padding在content周圍,但垂直方向上的空間取決於'line-height' - 浮動(float):就像一個block元素,能夠設置垂直邊距和填充。
通過定位的元素,其position屬性值必然是relative、absolute、fixed或sticky。 - static:默認定位屬性值。該關鍵字指定元素使用正常的佈局行爲,即元素在文檔常規流中當前的佈局位置。此時 top, right, bottom, left 和 z-index 屬性無效。 - relative:該關鍵字下,元素先放置在未添加定位時的位置,再在不改變頁面佈局的前提下調整元素位置(所以會在此元素未添加定位時所在位置留下空白)。 - absolute:不爲元素預留空間,經過指定元素相對於最近的非 static 定位祖先元素的偏移,來肯定元素位置。絕對定位的元素能夠設置外邊距(margins),且不會與其餘邊距合併。 - fixed:不爲元素預留空間,而是經過指定元素相對於屏幕視口(viewport)的位置來指定元素位置。元素的位置在屏幕滾動時不會改變。打印時,元素會出如今的每頁的固定位置。fixed 屬性會建立新的層疊上下文。當元素祖先的 transform 屬性非 none 時,容器由視口改成該祖先。 - sticky:盒位置根據正常流計算(這稱爲正常流動中的位置),而後相對於該元素在流中的 flow root(BFC)和 containing block(最近的塊級祖先元素)定位。在全部狀況下(即使被定位元素爲 table 時),該元素定位均不對後續元素形成影響。當元素 B 被粘性定位時,後續元素的位置仍按照 B 未定位時的位置來肯定。position: sticky 對 table 元素的效果與 position: relative 相同。
- Bootstrap:更新週期緩慢。Bootstrap 4 已經處於 alpha 版本將近兩年了。添加了在頁面中普遍使用的微調按鈕組件。 - Semantic UI:源代碼結構使得自定義主題很難理解。很是規主題系統的使用體驗不好。外部庫的路徑須要硬編碼(hard code)配置。變量從新賦值沒有 Bootstrap 設計得好。 - Bulma:須要不少非語義的類和標記,顯得不少餘。不向後兼容,以致於升級版本後,會破壞應用的正常運行。
- Flex: flex容器中存在兩條軸, 橫軸和縱軸, 容器中的每一個單元稱爲flex item。 在容器上能夠設置6個屬性: flex-direction flex-wrap flex-flow justify-content align-items align-content 注意:當設置 flex 佈局以後,子元素的 float、clear、vertical-align 的屬性將會失效。 Flex 項目屬性 有六種屬性可運用在 item 項目上: 1. order 2. flex-basis 3. flex-grow 4. flex-shrink 5. flex 6. align-self - Grid: CSS網格佈局用於將頁面分割成數個主要區域,或者用來定義組件內部元素間大小、位置和圖層之間的關係。 像表格同樣,網格佈局讓咱們可以按行或列來對齊元素。可是,使用CSS網格可能仍是比CSS表格更容易佈局。例如,網格容器的子元素能夠本身定位,以便它們像CSS定位的元素同樣,真正的有重疊和層次。
> 響應式設計和自適應設計都以提升不一樣設備間的用戶體驗爲目標,根據視窗大小、分辨率、使用環境和控制方式等參數進行優化調整。 響應式設計的適應性原則:網站應該憑藉一份代碼,在各類設備上都有良好的顯示和使用效果。響應式網站經過使用媒體查詢,自適應柵格和響應式圖片,基於多種因素進行變化,創造出優良的用戶體驗。就像一個球經過膨脹和收縮,來適應不一樣大小的籃圈。 自適應設計更像是漸進式加強的現代解釋。與響應式設計單一地去適配不一樣,自適應設計經過檢測設備和其餘特徵,從早已定義好的一系列視窗大小和其餘特性中,選出最恰當的功能和佈局。與使用一個球去穿過各類的籃筐不一樣,自適應設計容許使用多個球,而後根據不一樣的籃筐大小,去選擇最合適的一個。
> 我傾向於使用更高分辨率的圖形(顯示尺寸的兩倍)來處理視網膜顯示。更好的方法是使用媒體查詢,像@media only screen and (min-device-pixel-ratio: 2) { ... },而後改變background-image。 對於圖標類的圖形,我會盡量使用 svg 和圖標字體,由於它們在任何分辨率下,都能被渲染得十分清晰。 還有一種方法是,在檢查了window.devicePixelRatio的值後,利用 JavaScript 將<img>的src屬性修改,用更高分辨率的版本進行替換。
> translate()是transform的一個值。改變transform或opacity不會觸發瀏覽器從新佈局(reflow)或重繪(repaint),只會觸發複合(compositions)。而改變絕對定位會觸發從新佈局,進而觸發重繪和複合。transform使瀏覽器爲元素建立一個 GPU 圖層,但改變絕對定位會使用到 CPU。所以translate()更高效,能夠縮短平滑動畫的繪製時間。 當使用translate()時,元素仍然佔據其原始空間(有點像position:relative),這與改變絕對定位不一樣。
- display:none: - 是否隱藏:是 - 是否在文檔中佔用空間:否 - 是否會觸發事件:否 - visible:hidden: - 是否隱藏:是 - 是否在文檔中佔用空間:是 - 是否會觸發事件:否 - opacity:0: - 是否隱藏:是 - 是否在文檔中佔用空間:是 - 是否會觸發事件:是
- 用途: - @import只能引入css文件 - link既能引入css,又能引入其它文件,好比:Vue腳手架惟一index.html中引入.ico圖標 - 加載順序: - 加載頁面時,link標籤引入的css被同時加載; - @import引入的css將在頁面加載完畢後被加載 - 優先級: - @import中的樣式雖然比link引入的css內容晚加載,可是,優先級卻低於當前link引入的css內的其他樣式 - link支持使用DOM動態添加和改變,而@import用DOM操做不了
> 因爲不一樣的瀏覽器,好比Internet Explorer 6,Internet Explorer 7,Mozilla Firefox等,對CSS的解析認識不同,所以會致使生成的頁面效果不同,得不到咱們所須要的頁面效果。 這個時候咱們就須要針對不一樣的瀏覽器去寫不一樣的CSS,讓它可以同時兼容不一樣的瀏覽器,能在不一樣的瀏覽器中也能獲得咱們想要的頁面效果。 這個針對不一樣的瀏覽器寫不一樣的CSS code的過程,就叫CSS hack,也叫寫CSS hack。
- transition 能夠在必定的時間內實現元素的狀態過渡爲最終狀態,用於模擬以一種過渡動畫效果,可是功能有限,只能用於製做簡單的動畫效果而動畫屬性 - animation 能夠製做相似Flash動畫,經過關鍵幀控制動畫的每一步,控制更爲精確,從而能夠製做更爲複雜的動畫。
> 外邊距合併指的是,當兩個垂直外邊距相遇時,它們將造成一個外邊距。 合併後的外邊距的高度等於兩個發生合併的外邊距的高度中的較大者。
- 移除空格 - 使用margin負值 - 使用font-size:0 - letter-spacing - word-spacing
間隙是怎麼來的: 間隙是由換行或者回車致使的; 只要把標籤寫成一行或者 標籤直接沒有空格,就不會出現間隙; 怎麼去除? 方法1: 元素間的間隙出現的緣由 是元素標籤之間的空格, 把空格去掉間隙天然就會消失。 <div class="itlike"> <span>撩課itlike</span><span>撩課itlike</span> </div> 方法2: 利用HTML註釋標籤 <div class="demo"> <span>撩課itlike</span> <!-- --> <span>撩課itlike</span> </div> 方法3: 取消標籤閉合 <div class="demo"> <span>撩課itlike <span>撩課itlike <span>撩課itlike <span>撩課itlike </div> 方法4: 在父容器上使用font-size:0;能夠消除間隙 <div class="demo"> <span>撩課itlike</span> <span>撩課itlike</span> <span>撩課itlike</span> <span>撩課itlike</span> </div> .demo {font-size: 0;}
.shrink{ -webkit-transform:scale(0.8); -o-transform:scale(1); display:inline-block; }
-webkit-font-smoothing: antialiased;
> Node 底層採用線程池的原理管理異步 IO,因此咱們一般所的 單線程是指 Node 中 JavaScript 的執行是單線程的,但 Node 自己是多線程的。Node.js 中異步 IO 是經過事件循環的方式實現的,異步 IO 事件主要來源於網絡請求和文件 IO。可是正由於如此,Node.js 處理不少計算密集型的任務,就比較吃力,固然有多進程方式能夠解決這個問題。
> cluster 模式,多實例、自動共享端口連接、自動實現負載均衡。fork 模式實現的多進程,單實例、多進程,能夠經過手動分發 socket 對象給不一樣子進程進行定製化處理、實現負載均衡
> 原生的 cluster 和 fork 模式都有 API 封裝好的進行通訊。若是是 execfile 這樣形式調起第三方插件形式,想要與第三方插件進行通訊,能夠本身封裝一個相似 promisyfy 形式進行通訊,維護這塊,子進程能夠監聽到異常,一旦發現異常,馬上通知主進程,殺死這個異常的子進程,而後從新開啓一個子進程~
> 洋蔥圈的實現,有點相似 Promise 中的 then 實現,每次經過 use 方法定義中間件函數時候,就會把這個函數存入一個隊列中,全局維護一個 ctx 對象,每次調用 next(),就會調用隊列的下一個任務函數。 use (fn) { // this.fn = fn 改爲: this.middlewares.push(fn) // 每次use,把當前回調函數存進數組 } compose(middlewares, ctx){ // 簡化版的compose,接收中間件數組、ctx對象做爲參數 function dispatch(index){ // 利用遞歸函數將各中間件串聯起來依次調用 if(index === middlewares.length) return // 最後一次next不能執行,否則會報錯 let middleware = middlewares[index] // 取當前應該被調用的函數 middleware(ctx, () => dispatch(index + 1)) // 調用並傳入ctx和下一個將被調用的函數,用戶next()時執行該函數 } dispatch(0) }
> 咱們公司以前用的 kafka,消息隊列的核心概念,異步,提供者,消費者。例如 IM 應用,天天都會有高峯期,可是咱們不可能爲了高峯期配置那麼多服務器,那樣就是浪費,因此使用消息隊列,在多長時間內流量達到多少,就控制消費頻率,例如客戶端是流的提供者,有一箇中間件消費隊列,咱們的服務器是消費者,每次消費一個任務就回復一個 ACK 給消費隊列,消費頻率由咱們控制,這樣任務不會丟失,服務器也不會掛。 還有一個異步問題,一個用戶下單購買一件商品,可能要更新庫存,已購數量,支付,下單等任務。不可能同步進行,這時候須要異步並行,事務方式處理。這樣既不耽誤時間,也能確保全部的任務成功纔算成功,否則沒有支付成功,可是已購數量增加了就有問題。
- sudo - apache/nginx代理 - 用操做系統的firewall iptables進行端口重定向
用fork嘛,上面講過了.原理是子程序用process.on, process.send,父程序裏用child.on,child.send進行交互. 代碼演示 1) fork-parent.js var cp = require('child_process'); var child = cp.fork('./fork-child.js'); child.on('message', function(msg){ console.log('老爸從兒子接受到數據:', msg); }); child.send('我是你爸爸,送關懷來了!'); 2) fork-child.js process.on('message', function(msg){ console.log("兒子從老爸接收到的數據:", msg); process.send("我不要關懷,我要銀民幣!"); });
那就用spawn吧. var cp = require('child_process'); var child = cp.spawn('echo', ['你好', "鉤子"]); // 執行命令 child.stdout.pipe(process.stdout); // child.stdout是輸入流,process.stdout是輸出流 // 這句的意思是將子進程的輸出做爲當前程序的輸入流,而後重定向到當前程序的標準輸出,即控制檯
> node是異步非阻塞的,這對高併發很是有效.但是咱們還有其它一些經常使用需求,好比和操做系統shell命令交互,調用可執行文件,建立子進程進行阻塞式訪問或高CPU計算等,child-process就是爲知足這些需求而生的.child-process顧名思義,就是把node阻塞的工做交給子進程去作.
> exec能夠用操做系統原生的方式執行各類命令,如管道 cat ab.txt | grep hello; execFile是執行一個文件; spawn是流式和操做系統進行交互; fork是兩個node程序(javascript)之間時行交互.
> 主要實現如下幾個步驟便可: 1) openssl生成公鑰私鑰 2) 服務器或客戶端使用https替代http 3) 服務器或客戶端加載公鑰私鑰證書
> 整體來講有四種: 1) POSIX式低層讀寫 2) 流式讀寫 3) 同步文件讀寫 4) 異步文件讀寫
> 兩者主要用來監聽文件變更.fs.watch利用操做系統原生機制來監聽,可能不適用網絡文件系統; fs.watchFile則是按期檢查文件狀態變動,適用於網絡文件系統,可是相比fs.watch有些慢,由於不是實時機制.
> fs模塊主要由下面幾部分組成: 1) POSIX文件Wrapper,對應於操做系統的原生文件操做 2) 文件流 fs.createReadStream和fs.createWriteStream 3) 同步文件讀寫,fs.readFileSync和fs.writeFileSync 4) 異步文件讀寫, fs.readFile和fs.writeFile
> Readable爲可被讀流,在做爲輸入數據源時使用;Writable爲可被寫流,在做爲輸出源時使用;Duplex爲可讀寫流,它做爲輸出源接受被寫入,同時又做爲輸入源被後面的流讀出.Transform機制和Duplex同樣,都是雙向流,區別時Transfrom只須要實現一個函數_transfrom(chunk, encoding, callback);而Duplex須要分別實現_read(size)函數和_write(chunk, encoding, callback)函數.
> Nginx是一個web服務器和方向代理服務器,用於HTTP、HTTPS、SMTP、POP3和IMAP協議。
- 反向代理/L7負載均衡器 - 嵌入式Perl解釋器 - 動態二進制升級 - 可用於從新編寫URL,具備很是好的PCRE支持
> Nginx使用反應器模式。主事件循環等待操做系統發出準備事件的信號,這樣數據就能夠從套接字讀取,在該實例中讀取到緩衝區並進行處理。單個線程能夠提供數萬個併發鏈接。
- 正向代理: 正向代理也是你們最常接觸的到的代理模式,那究竟什麼是正向代理呢?咱們都知道Google在國內是沒法正常訪問的,可是某些時候咱們因爲技術問題須要去訪問Google時,咱們會先找到一個能夠訪問Google的代理服務器,咱們將請求發送到代理服務器,代理服務器去訪問Google,而後將訪問到的數據返回給咱們,這樣的過程就是正向代理。正向代理最大的特色是客戶端須要明確知道要訪問的服務器地址,Google服務器只清楚請求來自哪一個代理服務器,而不清楚來自哪一個具體的客戶端,正向代理能夠隱藏真實客戶端的具體信息。 客戶端必須設置正向代理服務器,並且須要知道正向代理服務器的IP地址以及代理程序的端口。一句話來歸納就是正向代理代理的是客戶端,是一個位於客戶端和Google服務器之間的服務器,爲了從Google服務器取得數據,客戶端向代理服務器發送一個請求並指定目標(Google服務器),而後代理向原始服務器轉交請求並將得到的數據返回給客戶端。總結正向代理的幾個做用: - 訪問國外沒法訪問的網站作緩存,加速訪問資源 - 對客戶端訪問受權,上網進行認證代理 - 能夠記錄用戶訪問記錄(上網行爲管理),對外隱藏用戶信息 - 反向代理: 多個客戶端給服務器發送的請求,Nginx服務器接收到請求之後,按照必定的規則轉發到不一樣的服務器進行業務邏輯處理,也就是咱們剛纔講到的負載均衡的策略。此時請求來源於哪一個客戶端是肯定的,可是請求由哪臺服務器處理的並不明確,Nginx扮演的就是一個反向代理角色。能夠這樣來理解,反向代理對外都是透明的,訪問者並不知道本身訪問的是一個代理。反向代理代理的是服務端,主要用於服務器集羣分佈式部署的狀況下,反向代理隱藏了服務器的信息。總結下反向代理的兩個做用: - 保證內網的安全,一般將反向代理做爲公網訪問地址,Web服務器是內網 - 負載均衡,經過反向代理服務器來優化網站的負載 - 區別: - 在正向代理中,隱藏了請求來源的客戶端信息; - 在反向代理中,隱藏了請求具體處理的服務端信息;
> Nginx服務器的最佳用法是在網絡上部署動態HTTP內容,使用SCGI、WSGI應用程序服務器、用於腳本的FastCGI處理程序。它還能夠做爲負載均衡器。
> 這個問題出來可能懂一點Nginx的朋友們都是浮現出5個字:異步非阻塞。實際上Nginx就是異步非阻塞,使用了epoll模型並對底層代碼進行大幅度優化。以前其實有講過Nginx是採用1個master進程,多個worker進程的模式,每次接收到一個請求,master會將請求按照必定策略分發給一個worker進程去進行處理請求。worker進程數通常設置爲和CPU核心數一致,異步非阻塞模式就會使得worker線程在等待請求callback的空閒時間能夠接收處理新的請求,當接收到舊請求的callback時再回去繼續處理該請求,這樣就完成了少數幾個worker進程卻實現了高併發的問題。
> 衆所周知,沒建立一個新的線程,都須要爲其分配cpu和內存。固然,建立進程也是同樣,可是因爲線程過多會致使內存消耗過多。因此Nginx採用單線程異步處理用戶請求,這樣不須要不斷地爲新的線程分配cpu和內存,減輕服務器內存消耗,因此使得Nginx性能方面更爲高效。
> Nginx啓動後,首先進行配置文件的解析,解析成功會獲得虛擬服務器的ip和端口號,在主進程master進程中建立socket,對addrreuse選項進行設置,並將socket綁定到對應的ip地址和端口並進行監聽。而後建立子進程worker進程,當客戶端和Nginx進行三次握手,則能夠建立成功與Nginx的鏈接。當有新的請求進入時,空閒的worker進程會競爭,當某一個worker進程競爭成功,則會獲得這個已經成功創建鏈接的socket,而後建立ngx_connection_t結構體,接下來設置讀寫事件處理函數並添加讀寫事件用來與客戶端進行數據交換。當請求結束Nginx或者客戶端主動關閉鏈接,此時一個請求處理完畢。
> 在平常開發中,前端請求靜態文件好比圖片資源是不須要通過後端服務器的,可是調用API這些類型的就須要後端進行處理請求,因此爲了提升對資源文件的響應速度,咱們應該使用動靜分離的策略去作架構。咱們能夠將靜態文件放到Nginx中,將動態資源的請求轉發到後端服務器去進行進一步的處理。
輪詢方式:默認狀況下Nginx使用輪詢的方式實現負載均衡,每一個新的請求按照時間順序逐一分配到不一樣的後端服務器去進行處理,若是後端服務器宕機,則Nginx的健康檢查功能會將這個後端服務器剔除。可是輪詢方式是顯而易見的:可靠性低並且負載分配不平衡,因此輪詢方式更適用於圖片服務器或者靜態資源服務器。 - weight:能夠對不一樣的後端服務器設置不一樣的權重比例,這樣能夠改變不一樣後端服務器處理請求的比例。能夠給性能更優的後端服務器配置更高的權重。 - ip_hash:這種方式會根據請求的ip地址的hash結果分配後端服務器來處理請求,這樣每一個用戶發起的請求固定只會由同一個後端服務器處理,這樣能夠解決session問題。 - fail:這種方式有點相似於輪詢方式,主要是根據後端服務器的響應時間來分配請求,響應時間短的後端服務器優先分配請求。 - url_hash:這種方式是按照請求url的hash結果來將不一樣請求分配到不一樣服務器,使用這種方式每一個url的請求都會由同一個後端服務器進行處理,後端服務器爲緩存時效率會更高。
> 上面其實提過了解決方案,負載均衡方式使用ip_hash方式,若是用戶已經訪問過某個後端器,則再次訪問時會將這個請求的ip地址進行哈希算法轉換,自動定位到該服務器。固然也能夠經過redis緩存用戶session,同樣能夠處理session不一樣步的問題。
- 調整worker_processes指定Nginx須要建立的worker進程數量,剛纔有提到worker進程數通常設置爲和CPU核心數一致。 - 調整worker_connections設置Nginx最多能夠同時服務的客戶端數。結合worker_processes配置能夠得到每秒能夠服務的最大客戶端數。 - 啓動gzip壓縮,能夠對文件大小進行壓縮,減小了客戶端http的傳輸帶寬,能夠大幅度提升頁面的加載速度。 - 啓用緩存,若是請求靜態資源,啓用緩存是能夠大幅度提高性能的。關於啓用緩存能夠觀看Nginx緩存這篇文章:Nginx緩存原理及機制
- MVC:MVC模式能夠這樣理解,將html當作view;js當作controller,處理用戶與應用的交互,響應對view的操做(對事件的監聽),調用Model對數據進行操做,完成model與view的同步(根據model的改變,經過選擇器對view進行操做);將js的ajax當作Model,從服務器獲取數據,MVC是單向的。 - MVVM:它實現了View和Model的自動同步,也就是當Model的屬性改變時,咱們不用再本身手動操做Dom元素,來改變View的顯示,而是改變屬性後該屬性對應View層顯示會自動改變,MVVM是雙向的。 Model–View–ViewModel (MVVM) 是一個軟件架構設計模式,由微軟 WPF 和 Silverlight 的架構師 Ken Cooper 和 Ted Peters 開發,是一種簡化用戶界面的事件驅動編程方式。由 John Gossman(一樣也是 WPF 和 Silverlight 的架構師)於2005年在他的博客上發表 MVVM 源自於經典的 Model–View–Controller(MVC)模式 ,MVVM 的出現促進了前端開發與後端業務邏輯的分離,極大地提升了前端開發效率,MVVM 的核心是 ViewModel 層,它就像是一箇中轉站(value converter),負責轉換 Model 中的數據對象來讓數據變得更容易管理和使用,該層向上與視圖層進行雙向數據綁定,向下與 Model 層經過接口請求進行數據交互,起呈上啓下做用。 (1)View 層 View 是視圖層,也就是用戶界面。前端主要由 HTML 和 CSS 來構建 。 (2)Model 層 Model 是指數據模型,泛指後端進行的各類業務邏輯處理和數據操控,對於前端來講就是後端提供的 api 接口。 (3)ViewModel 層 ViewModel 是由前端開發人員組織生成和維護的視圖數據層。在這一層,前端開發者對從後端獲取的 Model 數據進行轉換處理,作二次封裝,以生成符合 View 層使用預期的視圖數據模型。須要注意的是 ViewModel 所封裝出來的數據模型包括視圖的狀態和行爲兩部分,而 Model 層的數據模型是隻包含狀態的,好比頁面的這一塊展現什麼,而頁面加載進來時發生什麼,點擊這一塊發生什麼,這一塊滾動時發生什麼這些都屬於視圖行爲(交互),視圖狀態和行爲都封裝在了 ViewModel 裏。這樣的封裝使得 ViewModel 能夠完整地去描述 View 層。 MVVM 框架實現了雙向綁定,這樣 ViewModel 的內容會實時展示在 View 層,前端開發者不再必低效又麻煩地經過操縱 DOM 去更新視圖,MVVM 框架已經把最髒最累的一塊作好了,咱們開發者只須要處理和維護 ViewModel,更新數據視圖就會自動獲得相應更新。這樣 View 層展示的不是 Model 層的數據,而是 ViewModel 的數據,由 ViewModel 負責與 Model 層交互,這就徹底解耦了 View 層和 Model 層,這個解耦是相當重要的,它是先後端分離方案實施的重要一環。 咱們如下經過一個 Vue 實例來講明 MVVM 的具體實現,有 Vue 開發經驗的同窗應該一目瞭然: (1)View 層 <div id="app"> <p>{{message}}</p> <button v-on:click="showMessage()">Click me</button> </div> (2)ViewModel 層 var app = new Vue({ el: '#app', data: { // 用於描述視圖狀態 message: 'Hello Vue!', }, methods: { // 用於描述視圖行爲 showMessage(){ let vm = this; alert(vm.message); } }, created(){ let vm = this; // Ajax 獲取 Model 層的數據 ajax({ url: '/your/server/data/api', success(res){ vm.message = res; } }); } }) (3) Model 層 { "url": "/your/server/data/api", "res": { "success": true, "name": "IoveC", "domain": "www.cnblogs.com" } }
> Vue.js經過編譯將模版轉換成渲染函數(render),執行渲染函數就能夠獲得一個虛擬DOM 簡單點講,在Vue的實現上,Vue講模版編譯成虛擬DOM渲染函數。結合Vue自帶的響應系統,在狀態改變時,Vue可以智能地計算出從新渲染組件的最小代價並應用到DOM操做上。 優勢: 保證性能下限: 框架的虛擬 DOM 須要適配任何上層 API 可能產生的操做,它的一些 DOM 操做的實現必須是普適的,因此它的性能並非最優的;可是比起粗暴的 DOM 操做性能要好不少,所以框架的虛擬 DOM 至少能夠保證在你不須要手動優化的狀況下,依然能夠提供還不錯的性能,即保證性能的下限; 無需手動操做 DOM: 咱們再也不須要手動去操做 DOM,只須要寫好 View-Model 的代碼邏輯,框架會根據虛擬 DOM 和 數據雙向綁定,幫咱們以可預期的方式更新視圖,極大提升咱們的開發效率; 跨平臺: 虛擬 DOM 本質上是 JavaScript 對象,而 DOM 與平臺強相關,相比之下虛擬 DOM 能夠進行更方便地跨平臺操做,例如服務器渲染、weex 開發等等。 缺點: 沒法進行極致優化: 雖然虛擬 DOM + 合理的優化,足以應對絕大部分應用的性能需求,但在一些性能要求極高的應用中虛擬 DOM 沒法進行鍼對性的極致優化。
> 實現mvvm的雙向綁定,是採用數據劫持結合發佈者-訂閱者模式的方式,經過Object.defineProperty()來劫持各個屬性的setter,getter,在數據變更時發佈消息給訂閱者,觸發相應的監聽回調。 輸入框內容變化時,Data 中的數據同步變化。即 View => Data 的變化。 Data 中的數據變化時,文本節點的內容同步變化。即 Data => View 的變化。 其中,View 變化更新 Data ,能夠經過事件監聽的方式來實現,因此 Vue 的數據雙向綁定的工做主要是如何根據 Data 變化更新 View。 Vue 主要經過如下 4 個步驟來實現數據雙向綁定的: 實現一個監聽器 Observer:對數據對象進行遍歷,包括子屬性對象的屬性,利用 Object.defineProperty() 對屬性都加上 setter 和 getter。這樣的話,給這個對象的某個值賦值,就會觸發 setter,那麼就能監聽到了數據變化。 實現一個解析器 Compile:解析 Vue 模板指令,將模板中的變量都替換成數據,而後初始化渲染頁面視圖,並將每一個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變更,收到通知,調用更新函數進行數據更新。 實現一個訂閱者 Watcher:Watcher 訂閱者是 Observer 和 Compile 之間通訊的橋樑 ,主要的任務是訂閱 Observer 中的屬性值變化的消息,當收到屬性值變化的消息時,觸發解析器 Compile 中對應的更新函數。 實現一個訂閱器 Dep:訂閱器採用 發佈-訂閱 設計模式,用來收集訂閱者 Watcher,對監聽器 Observer 和 訂閱者 Watcher 進行統一管理。
- 父組件向子組件傳值 --Props傳遞數據 在父組件中使用兒子組件 <template> <div> 父組件:{{money}} <Son1 :money="money"><Son1> </div> </template> <script> import Son1 from ''./Son1"; export default{ components:{ Son1 }, data(){ return { money: 100}; } }; </script> 子組件接受數據 props:{ value:{ type:Number, default:1 } } 若是是數組 props:{ value:{ type:Array, default: ()=>[] } } - 子組件通訊父組件 $emit使用 <template> <div> 父組件:{{money}} <Son1 :money="money" @input="change"><Son1> </div> </template> <script> import Son1 from ''./Son1"; export default{ methods:{ change(data){ this.money = data } }, components:{ Son1 }, data(){ return { money: 100}; } }; </script> 子組件觸發綁定本身身上的方法 <template> <div> 子組件1:{{money}} <button @click="$emit('input',200)">修改父組件的值<Son1> </div> </template> <script> export default{ props:{ money:{ type:Number } } }; </script> - $parent、$children(多層級傳遞) <Grandson1 :value="value"></Grandson1> <template> <div> 孫子1:{{value}} <---調用父組件的input事件--> <button @click="$parent.$emit('input',200)">更改<Son1> </div> </template> <script> export default{ props:{ value:{ type:Number } } }; </script> - $attrs、 $listeners: $attrs批量向下傳入屬性: <Son2 name="小明" age="18"></Son2> <--能夠在son2組件中使用$attrs,能夠將屬性繼續向下傳遞--> <div> 兒子2:{{ $attrs.name }} <Grandson2 v-bind="$attrs"></Grandson2> </div> <tempalte> <div>孫子:{{$attrs}}</div> </template> $listeners批量向下傳入方法: <Son2 name="小明" age="18" @click=「()=>{this.money =500}」></Son2> <--能夠在son2組件中使用$attrs,能夠將屬性繼續向下傳遞--> <Grandson2 v-bind="$attrs" v-on="$listeners"></Grandson2> <button @click="$listeners.click()">更改<Son1> - Provide&Inject Provide 在父級中注入數據 provide(){ return {parentMsg:'父親'}; } Inject 在任意子組件中能夠注入父級數據 inject:['parentMsg']//會將數據掛載在當前實例上 - ref使用 <Grandson2 name="花花" ref="grand2"></Grandson2> mounted(){ console.log(this.$refs.grand2.name); } - EventBus:用於跨組件通知 Vue.prototype.$bus = new Vue(); Son2組件和Grandson1互相通訊 mounted() { //父親組件註冊 this.$bus.$on('my',data=>{ console.log(data) }) } mounted(){ //侄子組件調用 this.$nextTick(()=>{ this.$bus.$emit('my',"我是小紅」); }) }
好比有父組件 Parent 和子組件 Child,若是父組件監聽到子組件掛載 mounted 就作一些邏輯處理,能夠經過如下寫法實現: // Parent.vue <Child @mounted="doSomething"/> // Child.vue mounted() { this.$emit("mounted"); } 以上須要手動經過 $emit 觸發父組件的事件,更簡單的方式能夠在父組件引用子組件時經過 @hook 來監聽便可,以下所示: // Parent.vue <Child @hook:mounted="doSomething" ></Child> doSomething() { console.log('父組件監聽到 mounted 鉤子函數 ...'); }, // Child.vue mounted(){ console.log('子組件觸發 mounted 鉤子函數 ...'); }, // 以上輸出順序爲: // 子組件觸發 mounted 鉤子函數 ... // 父組件監聽到 mounted 鉤子函數 ... 固然 @hook 方法不只僅是能夠監聽 mounted,其它的生命週期事件,例如:created,updated 等均可以監聽。
> computed--適用於從新計算比較費時不用重複數據計算的環境。全部 getter 和 setter 的 this 上下文自動地綁定爲 Vue 實例。若是一個數據依賴於其餘數據,那麼把這個數據設計爲computed watch--像是一個 data 的數據監聽回調,當依賴的 data 的數據變化,執行回調,在方法中會傳入 newVal 和 oldVal。能夠提供輸入值無效,提供中間值 特場景。Vue 實例將會在實例化時調用 $watch(),遍歷 watch 對象的每個屬性。若是你須要在某個數據變化時作一些事情,使用watch。 method-- 跟前面的都不同,咱們一般在這裏面寫入方法,只要調用就會從新執行一次
vue-router 有 3 種路由模式:hash、history、abstract,對應的源碼以下所示: switch (mode) { case 'history': this.history = new HTML5History(this, options.base) break case 'hash': this.history = new HashHistory(this, options.base, this.fallback) break case 'abstract': this.history = new AbstractHistory(this, options.base) break default: if (process.env.NODE_ENV !== 'production') { assert(false, `invalid mode: ${mode}`) } } - hash模式:在瀏覽器中符號「#」,#以及#後面的字符稱之爲hash,用window.location.hash讀取;特色:hash雖然在URL中,但不被包括在HTTP請求中;用來指導瀏覽器動做,對服務端安全無用,hash不會重加載頁面。 早期的前端路由的實現就是基於 location.hash 來實現的。其實現原理很簡單,location.hash 的值就是 URL 中 # 後面的內容。好比下面這個網站,它的 location.hash 的值爲 '#search': hash 路由模式的實現主要是基於下面幾個特性: URL 中 hash 值只是客戶端的一種狀態,也就是說當向服務器端發出請求時,hash 部分不會被髮送; hash 值的改變,都會在瀏覽器的訪問歷史中增長一個記錄。所以咱們能經過瀏覽器的回退、前進按鈕控制hash 的切換; 能夠經過 a 標籤,並設置 href 屬性,當用戶點擊這個標籤後,URL 的 hash 值會發生改變;或者使用 JavaScript 來對 loaction.hash 進行賦值,改變 URL 的 hash 值; 咱們能夠使用 hashchange 事件來監聽 hash 值的變化,從而對頁面進行跳轉(渲染)。 - history模式:history採用HTML5的新特性;且提供了兩個新方法:pushState(),replaceState()能夠對瀏覽器歷史記錄棧進行修改,以及popState事件的監聽到狀態變動。 HTML5 提供了 History API 來實現 URL 的變化。其中作最主要的 API 有如下兩個:history.pushState() 和 history.repalceState()。這兩個 API 能夠在不進行刷新的狀況下,操做瀏覽器的歷史紀錄。惟一不一樣的是,前者是新增一個歷史記錄,後者是直接替換當前的歷史記錄,以下所示: window.history.pushState(null, null, path); window.history.replaceState(null, null, path); history 路由模式的實現主要基於存在下面幾個特性: pushState 和 repalceState 兩個 API 來操做實現 URL 的變化 ; 咱們能夠使用 popstate 事件來監聽 url 的變化,從而對頁面進行跳轉(渲染); history.pushState() 或 history.replaceState() 不會觸發 popstate 事件,這時咱們須要手動觸發頁面跳轉(渲染)。 - abstract : 支持全部 JavaScript 運行環境,如 Node.js 服務器端。若是發現沒有瀏覽器的 API,路由會自動強制進入這個模式.
> 在Vue生命週期的created()鉤子函數進行的DOM操做必定要放在Vue.nextTick()的回調函數中。緣由是什麼呢,緣由是在created()鉤子函數執行的時候DOM 其實並未進行任何渲染,而此時進行DOM操做無異於徒勞,因此此處必定要將DOM操做的js代碼放進Vue.nextTick()的回調函數中 優雅降級:首選 promise.then;而後是 setImmediate;而後是一個瀏覽器目前支持很差的 API ;最後是 setTimeout。dom 真正更新渲染好的時間,不能真正肯定,不管是框架仍是原生,都存在這個問題。因此用 nextTick 並不能保證拿到最新的 dom。
> 在咱們使用vue進行開發的過程當中,可能會遇到一種狀況:當生成vue實例後,當再次給數據賦值時,有時候並不會自動更新到視圖上去,所以能夠使用$set。 initTableData() { this.tableData.forEach(element => { this.$set(element, 'edit', false) }) } 受現代 JavaScript 的限制 ,Vue 沒法檢測到對象屬性的添加或刪除。因爲 Vue 會在初始化實例時對屬性執行 getter/setter 轉化,因此屬性必須在 data 對象上存在才能讓 Vue 將它轉換爲響應式的。可是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value) 來實現爲對象添加響應式屬性,那框架自己是如何實現的呢? 咱們查看對應的 Vue 源碼:vue/src/core/instance/index.js export function set (target: Array<any> | Object, key: any, val: any): any { // target 爲數組 if (Array.isArray(target) && isValidArrayIndex(key)) { // 修改數組的長度, 避免索引>數組長度致使splcie()執行有誤 target.length = Math.max(target.length, key) // 利用數組的splice變異方法觸發響應式 target.splice(key, 1, val) return val } // key 已經存在,直接修改屬性值 if (key in target && !(key in Object.prototype)) { target[key] = val return val } const ob = (target: any).__ob__ // target 自己就不是響應式數據, 直接賦值 if (!ob) { target[key] = val return val } // 對屬性進行響應式處理 defineReactive(ob.value, key, val) ob.dep.notify() return val } 咱們閱讀以上源碼可知,vm.$set 的實現原理是: 若是目標是數組,直接使用數組的 splice 方法觸發相應式; 若是目標是對象,會先判讀屬性是否存在、對象是不是響應式,最終若是要對屬性進行響應式處理,則是經過調用 defineReactive 方法進行響應式處理( defineReactive 方法就是 Vue 在初始化對象時,給對象屬性採用 Object.defineProperty 動態添加 getter 和 setter 的功能所調用的方法)
> 若是被問到 Vue 怎麼實現數據雙向綁定,你們確定都會回答 經過 Object.defineProperty() 對數據進行劫持,可是 Object.defineProperty() 只能對屬性進行數據劫持,不能對整個對象進行劫持,同理沒法對數組進行劫持,可是咱們在使用 Vue 框架中都知道,Vue 能檢測到對象和數組(部分方法的操做)的變化,那它是怎麼實現的呢?咱們查看相關代碼以下: /** * Observe a list of Array items. */ observeArray (items: Array<any>) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) // observe 功能爲監測數據的變化 } } /** * 對屬性進行遞歸遍歷 */ let childOb = !shallow && observe(val) // observe 功能爲監測數據的變化 經過以上 Vue 源碼部分查看,咱們就能知道 Vue 框架是經過遍歷數組 和遞歸遍歷對象,從而達到利用 Object.defineProperty() 也能對對象和數組(部分方法的操做)進行監聽。
Vue 實例從建立到銷燬的過程,就是生命週期。從開始建立、初始化數據、編譯模板、掛載Dom→渲染、更新→渲染、銷燬等一系列過程,稱之爲 Vue 的生命週期。 生命週期中有多個事件鉤子,以下: - beforeCreate(建立前) 在數據觀測和初始化事件還未開始 - created(建立後) 完成數據觀測,屬性和方法的運算,初始化事件,$el屬性尚未顯示出來 - beforeMount(載入前) 在掛載開始以前被調用,相關的render函數首次被調用。實例已完成如下的配置:編譯模板,把data裏面的數據和模板生成html。注意此時尚未掛載html到頁面上。 - mounted(載入後) 在el 被新建立的 vm.$el 替換,並掛載到實例上去以後調用。實例已完成如下的配置:用上面編譯好的html內容替換el屬性指向的DOM對象。完成模板中的html渲染到html頁面中。此過程當中進行ajax交互。 - beforeUpdate(更新前) 在數據更新以前調用,發生在虛擬DOM從新渲染和打補丁以前。能夠在該鉤子中進一步地更改狀態,不會觸發附加的重渲染過程。 - updated(更新後) 在因爲數據更改致使的虛擬DOM從新渲染和打補丁以後調用。調用時,組件DOM已經更新,因此能夠執行依賴於DOM的操做。然而在大多數狀況下,應該避免在此期間更改狀態,由於這可能會致使更新無限循環。該鉤子在服務器端渲染期間不被調用。 - beforeDestroy(銷燬前) 在實例銷燬以前調用。實例仍然徹底可用。 - destroyed(銷燬後) 在實例銷燬以後調用。調用後,全部的事件監聽器會被移除,全部的子實例也會被銷燬。該鉤子在服務器端渲染期間不被調用。
Vue 的父組件和子組件生命週期鉤子函數執行順序能夠歸類爲如下 4 部分: - 加載渲染過程: 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted - 子組件更新過程: 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated - 父組件更新過程: 父 beforeUpdate -> 父 updated - 銷燬過程: 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
> v-if是經過控制dom節點的存在與否來控制元素的顯隱;v-show是經過設置DOM元素的display樣式,block爲顯示,none爲隱藏; 基於以上區別,所以,若是須要很是頻繁地切換,則使用 v-show 較好;若是在運行時條件不多改變,則使用 v-if 較好。
咱們在 vue 項目中主要使用 v-model 指令在表單 input、textarea、select 等元素上建立雙向數據綁定,咱們知道 v-model 本質上不過是語法糖,v-model 在內部爲不一樣的輸入元素使用不一樣的屬性並拋出不一樣的事件: text 和 textarea 元素使用 value 屬性和 input 事件; checkbox 和 radio 使用 checked 屬性和 change 事件; select 字段將 value 做爲 prop 並將 change 做爲事件。 以 input 表單元素爲例: <input v-model='something'> 至關於 <input v-bind:value="something" v-on:input="something = $event.target.value"> 若是在自定義組件中,v-model 默認會利用名爲 value 的 prop 和名爲 input 的事件,以下所示: 父組件: <ModelChild v-model="message"></ModelChild> 子組件: <div>{{value}}</div> props:{ value: String }, methods: { test1(){ this.$emit('input', '小紅') }, },
- $router是VueRouter的一個對象,經過Vue.use(VueRouter)和VueRouter構造函數獲得一個router的實例對象,這個對象中是一個全局的對象,他包含了全部的路由包含了許多關鍵的對象和屬性。 - $route對象表示當前的路由信息,包含了當前 URL 解析獲得的信息 **1.$route.path** 字符串,對應當前路由的路徑,老是解析爲絕對路徑,如 "/foo/bar"。 **2.$route.params** 一個 key/value 對象,包含了 動態片斷 和 全匹配片斷, 若是沒有路由參數,就是一個空對象。 **3.$route.query** 一個 key/value 對象,表示 URL 查詢參數。例如,對於路徑 /foo?user=1,則有 $route.query.user == 1, 若是沒有查詢參數,則是個空對象。 **4.$route.hash** 當前路由的 hash 值 (不帶 #) ,若是沒有 hash 值,則爲空字符串。錨點 **5.$route.fullPath** 完成解析後的 URL,包含查詢參數和 hash 的完整路徑。 **6.$route.matched** 數組,包含當前匹配的路徑中所包含的全部片斷所對應的配置參數對象。 **7.$route.name 當前路徑名字** **8.$route.meta 路由元信息
> 若是data是一個函數的話,這樣每複用一次組件,就會返回一份新的data,相似於給每一個組件實例建立一個私有的數據空間,讓各個組件實例維護各自的數據。而單純的寫成對象形式,就使得全部組件實例共用了一份data,就會形成一個變了全都會變的結果。 // data data() { return { message: "子組件", childName:this.name } } // new Vue new Vue({ el: '#app', router, template: '<App/>', components: {App} }) 由於組件是用來複用的,且 JS 裏對象是引用關係,若是組件中 data 是一個對象,那麼這樣做用域沒有隔離,子組件中的 data 屬性值會相互影響,若是組件中 data 選項是一個函數,那麼每一個實例能夠維護一份被返回對象的獨立的拷貝,組件實例之間的 data 屬性值不會互相影響;而 new Vue 的實例,是不會被複用的,所以不存在引用對象的問題。
- 定義全局的自定義變量 Vue.directive('color',{ inserted(el){ // 各單位注意,這裏的el獲取的是標籤元素,說白了就是能夠直接操做DOM console.log(el) el.style.color = "red" } }) <div >前端僞大叔</div> <div v-color>前端僞大叔</div> - 組件內指令-只有本身組件能夠使用 // template <div >前端僞大叔</div> <div v-color>前端僞大叔</div> // script directives:{ color:{ inserted(el){ el.style.color = 'cyan' } } }
- 全局定義指令:在vue對象的directive方法裏面有兩個參數,一個是指令名稱,另外一個是函數 - 組件內定義指令:directives - 鉤子函數:bind(綁定事件觸發)、inserted(節點插入時候觸發)、update(組件內相關更新) - 鉤子函數參數:el、binding
> 經過設置了keep-alive,能夠簡單理解爲從頁面1跳轉到頁面2後,而後後退到頁面1,只會加載緩存中以前已經渲染好的頁面1,而不會再次從新加載頁面1,及不會再觸發頁面一種的created等相似的鉤子函數,除非本身從新刷新該頁面1。
Class 能夠經過對象語法和數組語法進行動態綁定: - 對象語法: <div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div> data: { isActive: true, hasError: false } - 數組語法: <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> data: { activeClass: 'active', errorClass: 'text-danger' } Style 也能夠經過對象語法和數組語法進行動態綁定: - 對象語法: <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> data: { activeColor: 'red', fontSize: 30 } - 數組語法: <div v-bind:style="[styleColor, styleSize]"></div> data: { styleColor: { color: 'red' }, styleSize:{ fontSize:'23px' } }
> key 是爲 Vue 中 vnode 的惟一標記,經過這個 key,咱們的 diff 操做能夠更準確、更快速。Vue 的 diff 過程能夠歸納爲:oldCh 和 newCh 各有兩個頭尾的變量 oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex,它們會新節點和舊節點會進行兩兩對比,即一共有4種比較方式:newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 、newStartIndex 和 oldEndIndex 、newEndIndex 和 oldStartIndex,若是以上 4 種比較都沒匹配,若是設置了key,就會用 key 再進行比較,在比較的過程當中,遍歷會往中間靠,一旦 StartIdx > EndIdx 代表 oldCh 和 newCh 至少有一個已經遍歷完了,就會結束比較。 因此 Vue 中 key 的做用是:key 是爲 Vue 中 vnode 的惟一標記,經過這個 key,咱們的 diff 操做能夠更準確、更快速 更準確:由於帶 key 就不是就地複用了,在 sameNode 函數 a.key === b.key 對比中能夠避免就地複用的狀況。因此會更加準確。 更快速:利用 key 的惟一性生成 map 對象來獲取對應節點,比遍歷方式更快,源碼以下: function createKeyToOldIdx (children, beginIdx, endIdx) { let i, key const map = {} for (i = beginIdx; i <= endIdx; ++i) { key = children[i].key if (isDef(key)) map[key] = i } return map } 強制替換元素,從而能夠觸發組件的生命週期鉤子或者觸發過渡。由於當key改變時,Vue認爲一個新的元素產生了,從而會新插入一個元素來替換掉原有的元素。 <transition> <span :key="text">{{text}}</span> </transition>、 --這裏若是text發生改變,整個<span>元素會發生更新,由於當text改變時,這個元素的key屬性就發生了改變,在渲染更新時,Vue會認爲這裏新產生了一個元素,而老的元素因爲key不存在了,因此會被刪除,從而觸發了過渡。 同理,key屬性被用在組件上時,當key改變時會引發新組件的建立和原有組件的刪除,此時組件的生命週期鉤子就會被觸發。
- 全局守衛: - router.beforeEach 全局前置守衛 進入路由以前 - router.beforeResolve 全局解析守衛(2.5.0+) 在beforeRouteEnter調用以後調用 - router.afterEach 全局後置鉤子 進入路由以後 - 路由組件內的守衛: - beforeRouteEnter 進入路由前 - beforeRouteUpdate (2.2) 路由複用同一個組件時 - beforeRouteLeave 離開當前路由時
- .stop //組織單擊事件冒泡 - .prevent //提交事件再也不從新加載頁面 - .capture //添加事件偵聽器時使用事件捕獲模式 - .self //只當事件在該元素自己時觸發回調(在其子元素上不觸發) - .once //只觸發一次事件
- state => 基本數據 - getters => 從基本數據派生的數據 - mutations => 提交更改數據的方法,同步! - actions => 像一個裝飾器,包裹mutations,使之能夠異步。 - modules => 模塊化Vuex
> Vue.js 是構建客戶端應用程序的框架。默認狀況下,能夠在瀏覽器中輸出 Vue 組件,進行生成 DOM 和操做 DOM。然而,也能夠將同一個組件渲染爲服務端的 HTML 字符串,將它們直接發送到瀏覽器,最後將這些靜態標記"激活"爲客戶端上徹底可交互的應用程序。 即:SSR大體的意思就是vue在客戶端將標籤渲染成的整個 html 片斷的工做在服務端完成,服務端造成的html 片斷直接返回給客戶端這個過程就叫作服務端渲染。 服務端渲染 SSR 的優缺點以下: (1)服務端渲染的優勢: 更好的 SEO:由於 SPA 頁面的內容是經過 Ajax 獲取,而搜索引擎爬取工具並不會等待 Ajax 異步完成後再抓取頁面內容,因此在 SPA 中是抓取不到頁面經過 Ajax 獲取到的內容;而 SSR 是直接由服務端返回已經渲染好的頁面(數據已經包含在頁面中),因此搜索引擎爬取工具能夠抓取渲染好的頁面; 更快的內容到達時間(首屏加載更快):SPA 會等待全部 Vue 編譯後的 js 文件都下載完成後,纔開始進行頁面的渲染,文件下載等須要必定的時間等,因此首屏渲染須要必定的時間;SSR 直接由服務端渲染好頁面直接返回顯示,無需等待下載 js 文件及再去渲染等,因此 SSR 有更快的內容到達時間; (2) 服務端渲染的缺點: 更多的開發條件限制:例如服務端渲染只支持 beforCreate 和 created 兩個鉤子函數,這會致使一些外部擴展庫須要特殊處理,才能在服務端渲染應用程序中運行;而且與能夠部署在任何靜態文件服務器上的徹底靜態單頁面應用程序 SPA 不一樣,服務端渲染應用程序,須要處於 Node.js server 運行環境; 更多的服務器負載:在 Node.js 中渲染完整的應用程序,顯然會比僅僅提供靜態文件的 server 更加大量佔用CPU 資源 (CPU-intensive - CPU 密集),所以若是你預料在高流量環境 ( high traffic ) 下使用,請準備相應的服務器負載,並明智地採用緩存策略。
SPA( single-page application )僅在 Web 頁面初始化時加載相應的 HTML、JavaScript 和 CSS。一旦頁面加載完成,SPA 不會由於用戶的操做而進行頁面的從新加載或跳轉;取而代之的是利用路由機制實現 HTML 內容的變換,UI 與用戶的交互,避免頁面的從新加載。 - 優勢: - 用戶體驗好、快,內容的改變不須要從新加載整個頁面,避免了沒必要要的跳轉和重複渲染; 基於上面一點,SPA 相對對服務器壓力小; - 先後端職責分離,架構清晰,前端進行交互邏輯,後端負責數據處理; - 缺點: - 初次加載耗時多:爲實現單頁 Web 應用功能及顯示效果,須要在加載頁面的時候將 JavaScript、CSS 統一加載,部分頁面按需加載; - 前進後退路由管理:因爲單頁應用在一個頁面中顯示全部的內容,因此不能使用瀏覽器的前進後退功能,全部的頁面切換須要本身創建堆棧管理; - SEO 難度較大:因爲全部的內容都在一個頁面中動態替換顯示,因此在 SEO 上其有着自然的弱勢。
- Proxy 的優點以下: - Proxy 能夠直接監聽對象而非屬性; - Proxy 能夠直接監聽數組的變化; - Proxy 有多達 13 種攔截方法,不限於 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具有的; - Proxy 返回的是一個新對象,咱們能夠只操做新的對象達到目的,而 Object.defineProperty 只能遍歷對象屬性直接修改; - Proxy 做爲新標準將受到瀏覽器廠商重點持續的性能優化,也就是傳說中的新標準的性能紅利; - Object.defineProperty 的優點以下: - 兼容性好,支持 IE9,而 Proxy 的存在瀏覽器兼容性問題,並且沒法用 polyfill 磨平,所以 Vue 的做者才聲明須要等到下個大版本( 3.0 )才能用 Proxy 重寫。
受現代 JavaScript 的限制 ,Vue 沒法檢測到對象屬性的添加或刪除。因爲 Vue 會在初始化實例時對屬性執行 getter/setter 轉化,因此屬性必須在 data 對象上存在才能讓 Vue 將它轉換爲響應式的。可是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value) 來實現爲對象添加響應式屬性,那框架自己是如何實現的呢? 咱們查看對應的 Vue 源碼:vue/src/core/instance/index.js export function set (target: Array<any> | Object, key: any, val: any): any { // target 爲數組 if (Array.isArray(target) && isValidArrayIndex(key)) { // 修改數組的長度, 避免索引>數組長度致使splcie()執行有誤 target.length = Math.max(target.length, key) // 利用數組的splice變異方法觸發響應式 target.splice(key, 1, val) return val } // key 已經存在,直接修改屬性值 if (key in target && !(key in Object.prototype)) { target[key] = val return val } const ob = (target: any).__ob__ // target 自己就不是響應式數據, 直接賦值 if (!ob) { target[key] = val return val } // 對屬性進行響應式處理 defineReactive(ob.value, key, val) ob.dep.notify() return val } 咱們閱讀以上源碼可知,vm.$set 的實現原理是: 若是目標是數組,直接使用數組的 splice 方法觸發相應式; 若是目標是對象,會先判讀屬性是否存在、對象是不是響應式,最終若是要對屬性進行響應式處理,則是經過調用 defineReactive 方法進行響應式處理( defineReactive 方法就是 Vue 在初始化對象時,給對象屬性採用 Object.defineProperty 動態添加 getter 和 setter 的功能所調用的方法)
- 代碼層面的優化: - v-if 和 v-show 區分使用場景 - computed 和 watch 區分使用場景 - v-for 遍歷必須爲 item 添加 key,且避免同時使用 v-if - 長列表性能優化 - 事件的銷燬 - 圖片資源懶加載 - 路由懶加載 - 第三方插件的按需引入 - 優化無限列表性能 - 服務端渲染 SSR or 預渲染 - Webpack 層面的優化: - Webpack 對圖片進行壓縮 - 減小 ES6 轉爲 ES5 的冗餘代碼 - 提取公共代碼 - 模板預編譯 - 提取組件的 CSS - 優化 SourceMap - 構建結果輸出分析 - Vue 項目的編譯優化 - 基礎的 Web 技術的優化: - 開啓 gzip 壓縮 - 瀏覽器緩存 - CDN的使用 - 使用Chrome Performance 查找性能瓶頸
Vue 3.0 正走在發佈的路上,Vue 3.0 的目標是讓 Vue 核心變得更小、更快、更強大,所以 Vue 3.0 增長如下這些新特性: (1)監測機制的改變 3.0 將帶來基於代理 Proxy 的 observer 實現,提供全語言覆蓋的反應性跟蹤。這消除了 Vue 2 當中基於 Object.defineProperty 的實現所存在的不少限制: 只能監測屬性,不能監測對象 檢測屬性的添加和刪除; 檢測數組索引和長度的變動; 支持 Map、Set、WeakMap 和 WeakSet。 新的 observer 還提供瞭如下特性: 用於建立 observable 的公開 API。這爲中小規模場景提供了簡單輕量級的跨組件狀態管理解決方案。 默認採用惰性觀察。在 2.x 中,無論反應式數據有多大,都會在啓動時被觀察到。若是你的數據集很大,這可能會在應用啓動時帶來明顯的開銷。在 3.x 中,只觀察用於渲染應用程序最初可見部分的數據。 更精確的變動通知。在 2.x 中,經過 Vue.set 強制添加新屬性將致使依賴於該對象的 watcher 收到變動通知。在 3.x 中,只有依賴於特定屬性的 watcher 纔會收到通知。 不可變的 observable:咱們能夠建立值的「不可變」版本(即便是嵌套屬性),除非系統在內部暫時將其「解禁」。這個機制可用於凍結 prop 傳遞或 Vuex 狀態樹之外的變化。 更好的調試功能:咱們能夠使用新的 renderTracked 和 renderTriggered 鉤子精確地跟蹤組件在何時以及爲何從新渲染。 (2)模板 模板方面沒有大的變動,只改了做用域插槽,2.x 的機制致使做用域插槽變了,父組件會從新渲染,而 3.0 把做用域插槽改爲了函數的方式,這樣只會影響子組件的從新渲染,提高了渲染的性能。 同時,對於 render 函數的方面,vue3.0 也會進行一系列更改來方便習慣直接使用 api 來生成 vdom 。 (3)對象式的組件聲明方式 vue2.x 中的組件是經過聲明的方式傳入一系列 option,和 TypeScript 的結合須要經過一些裝飾器的方式來作,雖然能實現功能,可是比較麻煩。3.0 修改了組件的聲明方式,改爲了類式的寫法,這樣使得和 TypeScript 的結合變得很容易。 此外,vue 的源碼也改用了 TypeScript 來寫。其實當代碼的功能複雜以後,必須有一個靜態類型系統來作一些輔助管理。如今 vue3.0 也全面改用 TypeScript 來重寫了,更是使得對外暴露的 api 更容易結合 TypeScript。靜態類型系統對於複雜代碼的維護確實頗有必要。 (4)其它方面的更改 vue3.0 的改變是全面的,上面只涉及到主要的 3 個方面,還有一些其餘的更改: 支持自定義渲染器,從而使得 weex 能夠經過自定義渲染器的方式來擴展,而不是直接 fork 源碼來改的方式。 支持 Fragment(多個根節點)和 Protal(在 dom 其餘部分渲染組建內容)組件,針對一些特殊的場景作了處理。 基於 treeshaking 優化,提供了更多的內置功能。
- 配置打包工具,將組件分別打包到不一樣的js代碼塊中 build/webpack.base.conf.js output:{ path: config.build.assetsRoot, filename:’[name].js’, //新增 chunkFilename:」[name].js」, publicPath: process.env.NODE_ENV===」production」 ?config.build.assetsPublicPath :config.dev.assetsPublicPath } - 當路由請求到該組件時,才動態加載組件的內容 const Index=()=>import(‘@/views/Index.vue’) // 當用戶在vue中請求當前組件對應的路由地址時,由vue-router自動調用加載函數,動態請求Index.vue組件對象 - 服務端渲染SSR
> 虛擬 DOM (VDOM)是真實 DOM 在內存中的表示。UI 的表示形式保存在內存中,並與實際的 DOM 同步。這是一個發生在渲染函數被調用和元素在屏幕上顯示之間的步驟,整個過程被稱爲調和。
> 類組件能夠使用其餘特性,如狀態 state 和生命週期鉤子;當組件只是接收 props 渲染到頁面時,就是無狀態組件,就屬於函數組件,也被稱爲啞組件或展現組件。函數組件和類組件固然是有區別的,並且函數組件的性能比類組件的性能要高,由於類組件使用的時候要實例化,而函數組件直接執行函數取返回結果便可。爲了提升性能,儘可能使用函數組件。 區別: - 是否有this:函數組件沒有,類組件有 - 是否有生命週期:函數組件沒有,類組件有 - 是否有狀態state:函數組件沒有,類組件有
> 它被視爲普通函數,但 render() 函數必須返回某些值,不管值是否爲空。調用組件文件時默認會調用 render() 方法,由於組件須要顯示 HTML 標記,或者咱們能夠說 JSX 語法。每一個 React 組件必須有一個 render() 函數,它返回單個 React 元素,該元素表明原生 DOM 組件。若是須要渲染多個 HTML 元素,則必須將它們分組在一個封閉的標籤內,如 <form>、<group>和 <div> 等。此函數必須保持純淨,就是說它在每次調用時必須返回相同的結果。
> Refs 提供了一種訪問在render方法中建立的 DOM 節點或者 React 元素的方法。在典型的數據流中,props 是父子組件交互的惟一方式,想要修改子組件,須要使用新的pros從新渲染它。凡事有例外,某些狀況下我們須要在典型數據流外,強制修改子代,這個時候能夠使用 Refs。 我們能夠在組件添加一個 ref 屬性來使用,該屬性的值是一個回調函數,接收做爲其第一個參數的底層 DOM 元素或組件的掛載實例。 class UnControlledForm extends Component{ handleSubmit=()=>{ console.log('Input Value:',this.input.value) } render(){ return ( <form onSubmit={this.handleSubmit}> <input type='text' ref={(input)=>this.input=input} /> <button type='submit'>Submit</button> </form> ) } } 請注意,input 元素有一個ref屬性,它的值是一個函數。該函數接收輸入的實際 DOM 元素,而後將其放在實例上,這樣就能夠在 handleSubmit 函數內部訪問它。 常常被誤解的只有在類組件中才能使用 refs,可是refs也能夠經過利用 JS 中的閉包與函數組件一塊兒使用。 function CustomForm({handleSubmit}){ let inputElement return ( <form onSubmit={()=>handleSubmit(inputElement.value)}> <input type='text' ref={(input)=>inputElement=input} /> <button type='submit'>Submit</button> </form> ) }
> 爲了解決跨瀏覽器的兼容性問題,SyntheticEvent 實例將被傳遞給你的事件處理函數,SyntheticEvent是 React 跨瀏覽器的瀏覽器原生事件包裝器,它還擁有和瀏覽器原生事件相同的接口,包括 stopPropagation() 和 preventDefault()。 比較有趣的是,React 實際上並不將事件附加到子節點自己。React 使用單個事件偵聽器偵聽頂層的全部事件。這對性能有好處,也意味着 React 在更新 DOM 時不須要跟蹤事件監聽器。
> props和state是普通的 JS 對象。雖然它們都包含影響渲染輸出的信息,可是它們在組件方面的功能是不一樣的。即 - state 是組件本身管理數據,控制本身的狀態,可變; - props 是外部傳入的數據參數,不可變; - 沒有state的叫作無狀態組件,有state的叫作有狀態組件; - 多用 props,少用 state,也就是多寫無狀態組件。
- 有狀態組件: - 在內存中存儲組件狀態更改的信息 - 有權更改狀態 - 包含過去、如今和可能的將來狀態更改的信息 - 無安裝組件愛你通知它們關於狀態更改的需求,而後它們將props傳遞給前者 - 無狀態組件: - 計算組件的內部狀態 - 無權更改狀態 - 沒有包含關於狀態更改的信息 - 它們從有狀態組件接收props,將其視爲回調函數
> Refs 是使用 React.createRef() 建立的,並經過 ref 屬性附加到 React 元素。在構造組件時,一般將 Refs 分配給實例屬性,以即可以在整個組件中引用它們。 class MyComponent extends React.Component{ constructor(props){ super(props); this.myRef=React.createRef(); } render(){ return <div ref={this.myRef} /> } } 或者這樣用: class UserForm extends Component{ handleSubmit=()=>{ console.log('Input Value is:',this.input.value) } render(){ return ( <form onSubmit={this.handleSubmit}> <input type='text' ref={(input)=>this.input=input} /> <button type='submit'>Submit</button> </form> ) } }
> 高階組件(HOC)是接受一個組件並返回一個新組件的函數。基本上,這是一個模式,是從 React 的組合特性中衍生出來的,稱其爲純組件,由於它們能夠接受任何動態提供的子組件,但不會修改或複製輸入組件中的任何行爲。 const EnhancedComponent = higherOrderComponent(WrappedComponent); HOC 能夠用於如下許多用例: - 代碼重用、邏輯和引導抽象 - 渲染劫持 - state 抽象和操做 - props 處理
super
並將 props
做爲參數傳入的做用是啥?> 在調用 super() 方法以前,子類構造函數沒法使用this引用,ES6 子類也是如此。將 props 參數傳遞給 super() 調用的主要緣由是在子構造函數中可以經過this.props來獲取傳入的 props。 傳遞props: class MyComponent extends React.Component{ constructor(props){ super(props); console.log(this.props) } } 沒傳遞props: class MyComponent extends React.Component{ constructor(props){ super(); console.log(this.props); // undefined // 可是 Props 參數仍然可用 console.log(props); // Prints {name:'sudheer',age:30} } render(){ // 構造函數外部不受影響 console.log(this.props) // {name:'sudheer',age:30} } } 上面示例揭示了一點。props 的行爲只有在構造函數中是不一樣的,在構造函數以外也是同樣的。
> 在 HTML 中,表單元素如 <input>、<textarea>和<select>一般維護本身的狀態,並根據用戶輸入進行更新。當用戶提交表單時,來自上述元素的值將隨表單一塊兒發送。 而 React 的工做方式則不一樣。包含表單的組件將跟蹤其狀態中的輸入值,並在每次回調函數(例如onChange)觸發時從新渲染組件,由於狀態被更新。以這種方式由 React 控制其值的輸入表單元素稱爲受控組件。
const element = ( <h1 className="greeting"> Hello, world! </h1> ) 上述代碼如何使用 React.createElement 來實現: const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' );
> 當 Facebook 第一次發佈 React 時,他們還引入了一種新的 JS 方言 JSX,將原始 HTML 模板嵌入到 JS 代碼中。JSX 代碼自己不能被瀏覽器讀取,必須使用Babel和webpack等工具將其轉換爲傳統的JS。不少開發人員就能無心識使用 JSX,由於它已經與 React 結合在一直了。 class MyComponent extends React.Component{ render(){ let props=this.props; return ( <div className='my-component'> <a href={props.url}>{props.name}</a> </div> ) } }
state
呢 ?> 若是試圖直接更新 state ,則不會從新渲染組件。 // 錯誤 This.state.message = 'Hello world'; 須要使用setState()方法來更新 state。它調度對組件state對象的更新。當state改變時,組件經過從新渲染來響應: // 正確作法 This.setState({message: ‘Hello World’});
在組件生命週期中有四個不一樣的階段: - Initialization:在這個階段,組件準備設置初始化狀態和默認屬性。 - Mounting:react 組件已經準備好掛載到瀏覽器 DOM 中。這個階段包括componentWillMount和componentDidMount生命週期方法。 - Updating:在這個階段,組件以兩種方式更新,發送新的 props 和 state 狀態。此階段包括shouldComponentUpdate、componentWillUpdate和componentDidUpdate生命週期方法。 - Unmounting:在這個階段,組件已經再也不被須要了,它從瀏覽器 DOM 中卸載下來。這個階段包含 componentWillUnmount 生命週期方法。 除以上四個經常使用生命週期外,還有一個錯誤處理的階段: Error Handling:在這個階段,不論在渲染的過程當中,仍是在生命週期方法中或是在任何子組件的構造函數中發生錯誤,該組件都會被調用。這個階段包含了 componentDidCatch 生命週期方法。
- componentWillMount:在渲染以前執行,用於根組件中的 App 級配置。 - componentDidMount:在第一次渲染以後執行,能夠在這裏作AJAX請求,DOM 的操做或狀態更新以及設置事件監聽器。 - componentWillReceiveProps:在初始化render的時候不會執行,它會在組件接受到新的狀態(Props)時被觸發,通常用於父組件狀態更新時子組件的從新渲染 - shouldComponentUpdate:肯定是否更新組件。默認狀況下,它返回true。若是肯定在 state 或 props 更新後組件不須要在從新渲染,則能夠返回false,這是一個提升性能的方法。 - componentWillUpdate:在shouldComponentUpdate返回 true 肯定要更新組件以前件以前執行。 - componentDidUpdate:它主要用於更新DOM以響應props或state更改。 - componentWillUnmount:它用於取消任何的網絡請求,或刪除與組件關聯的全部事件監聽器。
> ... 在React(使用JSX)代碼中作什麼?它叫什麼? <Modal {...this.props} title='Modal heading' animation={false}/> 這個叫擴展操做符號或者展開操做符,例如,若是this.props包含a:1和b:2,則 <Modal {...this.props} title='Modal heading' animation={false}> 等價於下面內容: <Modal a={this.props.a} b={this.props.b} title='Modal heading' animation={false}> 擴展符號不只適用於該用例,並且對於建立具備現有對象的大多數(或所有)屬性的新對象很是方便,在更新state 我們就常常這麼作: this.setState(prevState => { return {foo: {...prevState.foo, a: "updated"}}; });
> 首先,Hooks 一般支持提取和重用跨多個組件通用的有狀態邏輯,而無需承擔高階組件或渲染 props 的負擔。Hooks 能夠輕鬆地操做函數組件的狀態,而不須要將它們轉換爲類組件。 Hooks 在類中不起做用,經過使用它們,我們能夠徹底避免使用生命週期方法,例如 componentDidMount、componentDidUpdate、componentWillUnmount。相反,使用像useEffect這樣的內置鉤子。
> Hooks是 React 16.8 中的新添加內容。它們容許在不編寫類的狀況下使用state和其餘 React 特性。使用 Hooks,能夠從組件中提取有狀態邏輯,這樣就能夠獨立地測試和重用它。Hooks 容許我們在不改變組件層次結構的狀況下重用有狀態邏輯,這樣在許多組件之間或與社區共享 Hooks 變得很容易。
useState()
是什麼?const [count,setCounter]=useState(0); const [moreStuff,setMoreStuff]=useState(...); const setCount=()=>{ setCounter(count+1); setMoreStuff(...); } useState 是一個內置的 React Hook。useState(0) 返回一個元組,其中第一個參數count是計數器的當前狀態,setCounter 提供更新計數器狀態的方法。 我們能夠在任何地方使用setCounter方法更新計數狀態-在這種狀況下,我們在setCount函數內部使用它能夠作更多的事情,使用 Hooks,可以使我們的代碼保持更多功能,還能夠避免過多使用基於類的組件。
> React 的StrictMode是一種輔助組件,能夠幫助我們編寫更好的 react 組件,能夠使用<StrictMode />包裝一組組件,而且能夠幫我們如下檢查: - 驗證內部組件是否遵循某些推薦作法,若是沒有,會在控制檯給出警告。 - 驗證是否使用的已經廢棄的方法,若是有,會在控制檯給出警告。 - 經過識別潛在的風險預防一些反作用。
> 在 JS 中,this 值會根據當前上下文變化。在 React 類組件方法中,開發人員一般但願 this 引用組件的當前實例,所以有必要將這些方法綁定到實例。一般這是在構造函數中完成的: class SubmitButton extends React.Component{ constructor(props){ super(props); this.state={ isFormSubmitted:false }; this.handleSubmit=this.handleSubmit.bind(this); } handleSubmit(){ this.setState({ isFormSubmitted:true }) } render(){ return ( <button onClick={this.handleSubmit}>Submit</button> ) } }
> 在構建 React 應用程序時,在多層嵌套組件來使用另外一個嵌套組件提供的數據。最簡單的方法是將一個 prop 從每一個組件一層層的傳遞下去,從源組件傳遞到深層嵌套組件,這叫作prop drilling。 prop drilling的主要缺點是本來不須要數據的組件變得沒必要要地複雜,而且難以維護。 爲了不prop drilling,一種經常使用的方法是使用React Context。經過定義提供數據的Provider組件,並容許嵌套的組件經過Consumer組件或useContext Hook 使用上下文數據。
> 傳統的 MVC 模式在分離數據(Model)、UI(View和邏輯(Controller)方面工做得很好,可是 MVC 架構常常遇到兩個主要問題: - 數據流不夠清晰:跨視圖發生的級聯更新經常會致使混亂的事件網絡,難於調試。 - 缺少數據完整性:模型數據能夠在任何地方發生突變,從而在整個UI中產生不可預測的結果。 使用 Flux 模式的複雜用戶界面再也不遭受級聯更新,任何給定的React 組件都可以根據 store 提供的數據重建其狀態。Flux 模式還經過限制對共享數據的直接訪問來增強數據完整性。
- 受控組件是 React 控制中的組件,而且是表單數據真實的惟一來源。 - 非受控組件是由 DOM 處理表單數據的地方,而不是在 React 組件中。 儘管非受控組件一般更易於實現,由於只需使用refs便可從 DOM 中獲取值,但一般建議優先選擇受控制的組件,而不是非受控制的組件。 這樣作的主要緣由是受控組件支持即時字段驗證,容許有條件地禁用/啓用按鈕,強制輸入格式。
> Context 經過組件樹提供了一個傳遞數據的方法,從而避免了在每個層級手動的傳遞 props 屬性。
> Fiber 是 React 16 中新的協調引擎或從新實現核心算法。它的主要目標是支持虛擬DOM的增量渲染。React Fiber 的目標是提升其在動畫、佈局、手勢、暫停、停止或重用等方面的適用性,併爲不一樣類型的更新分配優先級,以及新的併發原語。 React Fiber 的目標是加強其在動畫、佈局和手勢等領域的適用性。它的主要特性是增量渲染:可以將渲染工做分割成塊,並將其分散到多個幀中。
> 當應用程序在開發模式下運行時,React 將自動檢查我們在組件上設置的全部 props,以確保它們具備正確的數據類型。對於不正確的類型,開發模式下會在控制檯中生成警告消息,而在生產模式中因爲性能影響而禁用它。強制的 props 用 isRequired定義的。 下面是一組預約義的 prop 類型 - React.PropTypes.string - React.PropTypes.number - React.PropTypes.func - React.PropTypes.node - React.PropTypes.bool 例如,我們爲用戶組件定義了以下的propTypes import PropTypes from 'prop-types'; class User extends React.Component{ render(){ return ( <h1>Welcome,{this.props.name}</h1> <h2>Age,{this.props.age}</h2> ) } } User.propTypes={ name:PropTypes.string.isRequired, age:PropTypes.number.isRequired }
> 構造函數和getInitialState之間的區別就是ES6和ES5自己的區別。在使用ES6類時,應該在構造函數中初始化state,並在使用React.createClass時定義getInitialState方法。 class MyComponent extends React.Component { constructor(props) { super(props); this.state = { /* initial state */ }; } } 等價於 var MyComponent = React.createClass({ getInitialState() { return { /* initial state */ }; }, });
> 對於某些屬性,React 很是聰明,若是傳遞給它的值是虛值,能夠省略該屬性。例如: var InputComponent=React.createClass({ render:function(){ var required=true; var disabled=false; return ( <input type='text' disabled={disabled} required={required} /> ) } }) 渲染結果: <input type="text" required> 另外一種可能的方法是: var condition = true; var component = ( <div value="foo" { ...( condition && { disabled: true } ) } /> );
render props
和高階組件嗎?> 一般,render props和高階組件僅渲染一個子組件。React團隊認爲,Hooks 是服務此用例的更簡單方法。 這兩種模式仍然有一席之地(例如,一個虛擬的 scroller 組件可能有一個 renderItem prop,或者一個可視化的容器組件可能有它本身的 DOM 結構)。但在大多數狀況下,Hooks 就足夠了,能夠幫助減小樹中的嵌套。
> React 中最多見的問題之一是組件沒必要要地從新渲染。React 提供了兩個方法,在這些狀況下很是有用: - React.memo():這能夠防止沒必要要地從新渲染函數組件 - PureComponent:這能夠防止沒必要要地從新渲染類組件 這兩種方法都依賴於對傳遞給組件的props的淺比較,若是 props 沒有改變,那麼組件將不會從新渲染。雖然這兩種工具都很是有用,可是淺比較會帶來額外的性能損失,所以若是使用不當,這兩種方法都會對性能產生負面影響。 經過使用 React Profiler,能夠在使用這些方法先後對性能進行測量,從而確保經過進行給定的更改來實際改進性能。
> 純函數是不依賴而且不會在其做用域以外修改變量狀態的函數。本質上,純函數始終在給定相同參數的狀況下返回相同結果。
setState
時,React render
是如何工做的?> 我們能夠將"render"分爲兩個步驟: - 虛擬 DOM 渲染:當render方法被調用時,它返回一個新的組件的虛擬 DOM 結構。當調用setState()時,render會被再次調用,由於默認狀況下shouldComponentUpdate老是返回true,因此默認狀況下 React 是沒有優化的。 - 原生 DOM 渲染:React 只會在虛擬DOM中修改真實DOM節點,並且修改的次數很是少——這是很棒的React特性,它優化了真實DOM的變化,使React變得更快。
有幾種經常使用方法能夠避免在 React 中綁定方法: - 將事件處理程序定義爲內聯箭頭函數 class SubmitButton extends React.Component{ constructor(props){ super(props); this.state={ isFormSubmitted:false }; } render(){ return ( <button onClick={()=>{ this.setState({isFormSubmitted:true}) }}>Submit</button> ) } } - 使用箭頭函數來定義方法 class SubmitButton extends React.Component{ state={ isFormSubmitted:false } handleSubmit=()=>{ this.setState({ isFormSubmitted:true }); } render(){ return ( <button onClick={this.handleSubmit}>Submit</button> ) } } - 使用帶有 Hooks 的函數組件 const SubmitButton=()=>{ const [isFormSubmitted,setIsFormSubmitted]=useState(false); return ( <button onClick={()=>{ setIsFormSubmitted(true); }}>Submit</button> ) }
> 單一可信源(SSOT)是構造信息模型和相關數據模式的實踐,其中每一個數據元素都只能在一個地方掌握(或編輯) Redux 使用「存儲」將應用程序的整個狀態存儲在一個位置。所以,組件的全部狀態都存儲在存儲中,而且存儲自己會接收更新。單一狀態樹使咱們能更容易地跟蹤歷史更改,更方便地調試或檢查應用程序。
- 動做——這是一個描述發生了什麼的對象。 - Reducer——肯定狀態如何變化的地方。 - 存儲——整個應用程序的狀態 / 對象樹保存在存儲中。 - 視圖——僅顯示存儲提供的數據。
> React 中的動做必須具備 type 屬性,該屬性指示正在執行的 ACTION 的類型。必須將它們定義爲字符串常量,你也能夠爲其添加更多屬性。在 Redux 中使用稱爲「動做建立者」的函數來建立動做。如下是動做和動做建立者的示例: function addTodo(text) { return { type: ADD_TODO, text } }
> Reducer 是用於指示 ACTION 反應中應用程序狀態變化的簡單功能。它接收先前的狀態和動做,而後返回新的狀態。它根據動做類型肯定須要哪一種更新,而後返回新值。若是沒有要完成的工做,它將按原樣返回先前狀態。
> 存儲是一個 JavaScript 對象,能夠保存應用程序的狀態,並提供一些輔助方法來訪問狀態、調度動做並記錄偵聽器。應用程序的整個狀態 / 對象樹存儲在單個存儲中。所以 Redux 很是容易理解且可預測。咱們能夠將中間件轉移到存儲,以管理數據處理任務,並維護更改存儲狀態的各類活動的日誌。經過 Reducer,全部活動都返回新的狀態。
- Flux - 存儲包括狀態和更改邏輯 - 有多個存儲 - 全部存儲不互通,是平行的 - 有單個調度器 - React組件訂閱到存儲 - 狀態是可變的 - Redux - 存儲和更改邏輯是分離的 - 只有一個存儲 - 帶有分層Reducer的單個存儲 - 沒有調度器的概念 - 容器組件是有聯繫的 - 狀態是不可變的
Redux 的優勢以下: - 結果的可預測性——因爲老是有單一可信源,好比存儲,所以當前狀態與動做及應用程序的其餘部分同步時不會出現混亂。 - 可維護性——代碼易於維護,具備可預測的結果和嚴格的結構。 - 服務端渲染——你只需將在服務器上建立的存儲傳遞給客戶端便可。這對於初始渲染很是有用,並優化了應用程序性能,提供了更好的用戶體驗。 - 開發人員工具——從動做到狀態更改,開發人員能夠利用這些工具實時跟蹤應用程序中發生的全部事情。 - 社區和生態系統——Redux 背後擁有巨大的社區,用起來更加便利。大批優秀的開發者爲庫的發展作出了貢獻,並開發了不少應用程序。 - 易於測試——Redux 的代碼主要是較小的、純淨的和孤立的函數。這使代碼可測試且獨立。 - 組織——Redux 精確地規定了代碼的組織方式,這使得團隊合做時代碼更加一致,更容易理解。
> React Router 是創建在 React 之上的功能強大的路由庫。它使 URL 與網頁上顯示的數據保持同步。它保持標準化的結構和行爲,可用於開發單頁 Web 應用程序。React Router 有一個簡單的 API。React Router 提供了一種方法,只會顯示你的應用中路由匹配你的定義的那些組件。
> 路由器用於定義多個路由,而且當用戶鍵入特定的 URL 時,若是該 URL 與路由器內部定義的任何「路由」的路徑匹配,則該用戶將被重定向到該路由。所以咱們須要在應用程序中添加一個路由器庫,以容許建立多個路由,每一個路由都爲咱們指向一個獨特的視圖。 從 React Router 包導入的組件有兩個屬性,一個是將用戶引導到指定路徑的 path,另外一個是用於定義所述路徑中內容的 component。
- 就像 React 基於組件的理念同樣,在 React Router v4 中 API 是「徹底組件化的」。路由器能夠可視化爲單個根組件(<BrowserRouter>),其中包含特定的子路由(<route>)。 - 無需手動設置歷史值:在 React Router v4 中,咱們要作的就是將路由包裝在<BrowserRouter>組件中。 - 包是拆分的:三個包分別用於 Web、Native 和 Core。這使咱們的應用更加緊湊。它們的編碼樣式相似,因此很容易來回切換。
- 傳統路由: - 參與的頁面:每一個視圖對應一個新頁面 - URL更改:向服務器發送一個HTTP請求並接收對應的HTML頁面 - 體驗:用戶實際上是在每一個視圖的不一樣頁面間切換 - React路由: - 參與的頁面:只涉及單個HTML頁面 - URL更改:只有歷史屬性被更改 - 體驗:用戶覺得本身正在不一樣的頁面間切換
> 微信小程序採用 JavaScript、WXML、WXSS 三種技術進行開發,本質就是一個單頁面應用,全部的頁面渲染和事件處理,都在一個頁面內進行,但又能夠經過微信客戶端調用原生的各類接口 微信的架構,是數據驅動的架構模式,它的 UI 和數據是分離的,全部的頁面更新,都須要經過對數據的更改來實現 小程序分爲兩個部分 webview 和 appService 。其中 webview 主要用來展示 UI ,appService 有來處理業務邏輯、數據及接口調用。它們在兩個進程中運行,經過系統層 JSBridge 實現通訊,實現 UI 的渲染、事件的處理
小程序直接 this.data 的屬性是不能夠同步到視圖的,必須調用 this.setData({ // 這裏設置 })
WXSS 和 CSS 相似,不過在 CSS 的基礎上作了一些補充和修改 尺寸單位 rpx rpx 是響應式像素,能夠根據屏幕寬度進行自適應。規定屏幕寬爲 750rpx。如在 iPhone6 上,屏幕寬度爲 375px,共有 750 個物理像素,則 750rpx = 375px = 750 物理像素 使用 @import 標識符來導入外聯樣式。@import 後跟須要導入的外聯樣式表的相對路徑,用;表示語句結束 /** index.wxss **/ @import './base.wxss'; .container{ color: red; }
- 都是用來描述頁面的結構; - 都由標籤、屬性等構成; - 標籤名字不同,且小程序標籤更少,單一標籤更多; - 多了一些 wx:if 這樣的屬性以及 {{ }} 這樣的表達式 - WXML僅能在微信小程序開發者工具中預覽,而HTML能夠在瀏覽器內預覽 - 組件封裝不一樣, WXML對組件進行了從新封裝, - 小程序運行在JS Core內,沒有DOM樹和window對象,小程序中沒法使用window對象和document對象。
- 使用全局變量實現數據傳遞 在 app.js 文件中定義全局變量 globalData, 將須要存儲的信息存放在裏面 // app.js App({ // 全局變量 globalData: { userInfo: null } }) 使用的時候,直接使用 getApp() 拿到存儲的信息 - 使用 wx.navigateTo 與 wx.redirectTo 的時候,能夠將部分數據放在 url 裏面,並在新頁面 onLoad 的時候初始化 //pageA.js wx.navigateTo({ url: '../pageD/pageD?name=raymond&gender=male', }) wx.redirectTo({ url: '../pageD/pageD?name=raymond&gender=male', }) // pageB.js ... Page({ onLoad: function(option){ console.log(option.name + 'is' + option.gender) this.setData({ option: option }) } }) 須要注意的問題: wx.navigateTo 和 wx.redirectTo 不容許跳轉到 tab 所包含的頁面;onLoad 只執行一次 - 使用本地緩存 Storage 相關
- onLoad 頁面加載時觸發。一個頁面只會調用一次,能夠在 onLoad 的參數中獲取打開當前頁面路徑中的參數 - onShow() 頁面顯示/切入前臺時觸發 - onReady() 頁面初次渲染完成時觸發。一個頁面只會調用一次,表明頁面已經準備穩當,能夠和視圖層進行交互 - onHide() 頁面隱藏/切入後臺時觸發。 如 navigateTo 或底部 tab 切換到其餘頁面,小程序切入後臺等 - onUnload() 頁面卸載時觸發。如 redirectTo 或 navigateBack 到其餘頁面時
- 提升頁面加載速度 - 用戶行爲預測 - 減小默認 data 的大小 - 組件化方案
wx.navigateTo()
, wx.redirectTo()
, wx.switchTab()
, wx.navigateBack()
, wx.reLaunch()
的區別- wx.navigateTo():保留當前頁面,跳轉到應用內的某個頁面。可是不能跳到 tabbar 頁面 - wx.redirectTo():關閉當前頁面,跳轉到應用內的某個頁面。可是不容許跳轉到 tabbar 頁面 - wx.switchTab():跳轉到 abBar 頁面,並關閉其餘全部非 tabBar 頁面 - wx.navigateBack()關閉當前頁面,返回上一頁面或多級頁面。可經過 getCurrentPages() 獲取當前的頁面棧,決定須要返回幾層 - wx.reLaunch():關閉全部頁面,打開到應用內的某個頁面
- 相同點:首先他們都是做爲點擊事件函數,就是點擊時觸發。在這個做用上他們是同樣的,能夠不作區分 - 不一樣點:他們的不一樣點主要是bindtap是不會阻止冒泡事件的,catchtap是阻值冒泡的
- 第一條是運行環境的不一樣 傳統的HTML5的運行環境是瀏覽器,包括webview,而微信小程序的運行環境並不是完整的瀏覽器,是微信開發團隊基於瀏覽器內核徹底重構的一個內置解析器,針對小程序專門作了優化,配合本身定義的開發語言標準,提高了小程序的性能。 - 第二條是開發成本的不一樣 只在微信中運行,因此不用再去顧慮瀏覽器兼容性,不用擔憂生產環境中出現不可預料的奇妙BUG - 第三條是獲取系統級權限的不一樣 系統級權限均可以和微信小程序無縫銜接 - 第四條即是應用在生產環境的運行流暢度 長久以來,當HTML5應用面對複雜的業務邏輯或者豐富的頁面交互時,它的體驗老是不盡人意,須要不斷的對項目優化來提高用戶體驗。可是因爲微信小程序運行環境獨立
- 首先在外部網頁的HTML要引入最新版的jweixin-1.3.2.js: <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script> - 而後 wx.miniProgram.navigateTo({ //若是跳轉到tabBar中的頁面,必須換成switchTo url: '/pages/login/login'+'$params' })
- 必需要在小程序後臺使用管理員添加業務域名; - h5頁面跳轉至小程序的腳本必須是1.3.1以上; - 微信分享只能夠都是小程序的主名稱了,若是要自定義分享的內容,需小程序版本在1.7.1以上; - h5的支付不能夠是微信公衆號的appid,必須是小程序的appid,並且用戶的openid也必須是用戶和小程序的。
> React-native 的坑仍是比較多,可是目前也算擁有成熟的生態了,開發簡單的 APP 能夠使用它。可是複雜的應用仍是原生比較好,Electron 目前很是受歡迎,它基本上能夠完成桌面應用的大部分需求,重型應用開發也是徹底沒問題的,能夠配合大量 C# C++ 插件等。
- hybrid是客戶端與前端的混合開發 - hybrid存在的核心意義在乎快速迭代,無需審覈 - hybrid實現流程,以及webview和file協議
- 服務端的版本和zip包維護 - 更新zip包以前,先對比版本號 - zip下載解壓和覆蓋
- 優勢: 體驗好,可快速迭代 - 缺點: 開發成本高,運維成本高 - 適合的場景: hybrid適合產品型,H5適合運營型
- 通信的基本形式: 前端調用能力,傳遞參數,監聽回調 - 對schema協議的理解和使用: - 定義了前端與客戶端的約定; - 能夠經過ifream使用 - 調用schema代碼的封裝 放在客戶端內置上線的好處: 更快,更安全
> Flutter是谷歌的移動UI框架,能夠快速在iOS和Android上構建高質量的原生用戶界面。 Flutter能夠與現有的代碼一塊兒工做。在全世界,Flutter正在被愈來愈多的開發者和組織使用,而且Flutter是徹底免費、開源的。
- Widget是用戶界面的一部分,而且是不可變的。 - Element是在樹中特定位置Widget的實例。 - RenderObject是渲染樹中的一個對象,它的層次結構是渲染庫的核心。 Widget會被inflate(填充)到Element,並由Element管理底層渲染樹。Widget並不會直接管理狀態及渲染,而是經過State這個對象來管理狀態。Flutter建立Element的可見樹,相對於Widget來講,是可變的,一般界面開發中,咱們不用直接操做Element,而是由框架層實現內部邏輯。就如一個UI視圖樹中,可能包含有多個TextWidget(Widget被使用屢次),可是放在內部視圖樹的視角,這些TextWidget都是填充到一個個獨立的Element中。Element會持有renderObject和widget的實例。記住,Widget 只是一個配置,RenderObject 負責管理佈局、繪製等操做。 在第一次建立 Widget 的時候,會對應建立一個 Element, 而後將該元素插入樹中。若是以後 Widget 發生了變化,則將其與舊的 Widget 進行比較,而且相應地更新 Element。重要的是,Element 不會被重建,只是更新而已。
繼承(關鍵字 extends)、混入 mixins (關鍵字 with)、接口實現(關鍵字 implements)。這三者能夠同時存在,先後順序是extends -> mixins -> implements。 Flutter中的繼承是單繼承,子類重寫超類的方法要用@Override,子類調用超類的方法要用super。 在Flutter中,Mixins是一種在多個類層次結構中複用類代碼的方法。mixins的對象是類,mixins毫不是繼承,也不是接口,而是一種全新的特性,能夠mixins多個類,mixins的使用須要知足必定條件。
- Widget: 在Flutter中,幾乎全部東西都是Widget。將一個Widget想象爲一個可視化的組件(或與應用可視化方面交互的組件),當你須要構建與佈局直接或間接相關的任何內容時,你正在使用Widget。 - Widget樹: Widget以樹結構進行組織。包含其餘Widget的widget被稱爲父Widget(或widget容器)。包含在父widget中的widget被稱爲子Widget。 - Context: 僅僅是已建立的全部Widget樹結構中的某個Widget的位置引用。簡而言之,將context做爲widget樹的一部分,其中context所對應的widget被添加到此樹中。一個context只從屬於一個widget,它和widget同樣是連接在一塊兒的,而且會造成一個context樹。 - State: 定義了StatefulWidget實例的行爲,它包含了用於」交互/干預「Widget信息的行爲和佈局。應用於State的任何更改都會強制重建Widget。 這些狀態的引入,主要是爲了解決多個部件之間的交互和部件自身狀態的維護。
const path = require('path') module.exports = { entry: { // main是默認入口,也能夠是多入口 main: './src/main.js' }, // 出口 output: { filename: './build.js', // 指定js路徑 path: path.join(__dirname, '..', '', 'dist') // 最好是絕對路徑 // 表明上一級的dist }, module: { // 同樣的功能rules: webpack2.xx新加的 loaders: [ // require('./a.css||./a.js') { test: /\.css$/, loader: 'style-loader!css=loader', //多個loader用!分割 //順序是反過來的 2!1 多個loader }, { test: /\.(jpg|svg)$/, loaderL 'url-loader?limit=4096&name=[name].[ext]', // limit=4096&name=[name].[ext]' 多個參數之間用&符號分割 //[name].[ext]內置提供的 options: { limit: 4096, name: '[name].[ext]' } } ] }, plugins: [ // 插件的執行順序是依次執行的,和loader是反過來的 new htmlWebpackPlugin({ template: './src/index.html', }) // 將src下的template屬性描述的文件根據當前配置的output.path,將文件移動到該目錄。 // 在插件的執行過程當中,它自己能夠去拿當前所設置的webpack選項,便於對webpack選項的複用, ] }
- 重複的HTTP請求數量應儘可能減小 - 壓縮Javascript、CSS代碼 - 在文件頭部放置css樣式的定義 - 在文件末尾放Javascript腳本 - css、javascript改由外部調用,避免重複調用 - 儘量減小DOM元素 - 避免使用CSS Expressions - 添加文件過時或緩存頭 - 使用CDN(Content Delivery Network)網絡加速 - 服務器啓用gzip壓縮功能 - Ajax採用緩存調用 - 若是能夠,Ajax調用盡可能採用GET方法調用(其餘方法會發送兩次請求,一次option,一次爲正常請求) - 縮減iframe的使用,如無必要,儘可能不要使用 - 合理使用Flush(後端) - 避免採用30一、302轉向 - 優化圖片文件 - 採用分頁或翻頁後展現 - 使用多域名負載網頁內的多個文件、圖片
- 減小http請求,合理設置http緩存 - 使用瀏覽器緩存 - 啓用壓縮 - css sprites - lazyload images - css放最上部,js放最下面 - 異步請求callback - 減小cookie傳輸 - JavaScript代碼優化: - dom:html colleciton、重繪 - 慎用with - 避免使用eval和Function - 減小做用域鏈查找 - 數據訪問 - 字符串拼接 - css選擇符優化 - cdn加速 - 反向代理
> babel是一個轉譯器,感受相對於編譯器compiler,叫轉譯器transpiler更準確,由於它只是把同種語言的高版本規則翻譯成低版本規則,而不像編譯器那樣,輸出的是另外一種更低級的語言代碼。 可是和編譯器相似,babel的轉譯過程也分爲三個階段:parsing、transforming、generating,以ES6代碼轉譯爲ES5代碼爲例,babel轉譯的具體過程以下: ES6代碼輸入 ==》 babylon進行解析 ==》 獲得AST ==》 plugin用babel-traverse對AST樹進行遍歷轉譯 ==》 獲得新的AST樹 ==》 用babel-generator經過AST樹生成ES5代碼
- 下載 webpack-dev-server 插件 - 配置 webpack.config.js 文件 // webpack.config.js var WebpackDevServer = require("webpack-dev-server"); module.exports = { ... devServer: { ... port: '8088', //設置端口號 // 代理設置 proxy: { '/api': { target: 'http://localhost:80/index.php', // 目標代理 pathRewrite: {'^/api' : ''}, // 重寫路徑 secure: false, // 是否接受運行在 HTTPS 上 } } } }
> 三者都是前端構建工具 grunt 和 gulp 是基於任務和流的。找到一個(或一類)文件,對其作一系列鏈式操做,更新流上的數據, 整條鏈式操做構成了一個任務,多個任務就構成了整個web的構建流程 webpack 是基於入口的。webpack 會自動地遞歸解析入口所須要加載的全部資源文件,而後用不一樣的 Loader 來處理不一樣的文件,用 Plugin 來擴展 webpack 功能 webpack 與前者最大的不一樣就是支持代碼分割,模塊化(AMD,CommonJ,ES2015),全局分析
- css-loader:加載 CSS,支持模塊化、壓縮、文件導入等特性 - style-loader:把 CSS 代碼注入到 JavaScript 中,經過 DOM 操做去加載 CSS - slint-loader:經過 SLint 檢查 JavaScript 代碼 - babel-loader:把 ES6 轉換成 ES5 - file-loader:把文件輸出到一個文件夾中,在代碼中經過相對 URL 去引用輸出的文件 - url-loader:和 file-loader 相似,可是能在文件很小的狀況下以 base64 的方式把文件內容注入到代碼中去
- define-plugin:定義環境變量 - commons-chunk-plugin:提取公共代碼
- loader 加載器 Webpack 將一切文件視爲模塊,可是 webpack 原生是隻能解析 js 文件. Loader 的做用是讓 webpack 擁有了加載和解析非 JavaScript 文件的能力 在 module.rules 中配置,也就是說他做爲模塊的解析規則而存在,類型爲數組 - Plugin 插件 擴展 webpack 的功能,讓 webpack 具備更多的靈活性 在 plugins 中單獨配置。類型爲數組,每一項是一個 plugin 的實例,參數都經過構造函數傳入
- 初始化參數:從配置文件和 Shell 語句中讀取與合併參數,得出最終的參數 - 開始編譯:用上一步獲得的參數初始化 Compiler 對象,加載全部配置的插件,執行對象的 run 方法開始執行編譯 - 肯定入口:根據配置中的 entry 找出全部的入口文件 - 編譯模塊:從入口文件出發,調用全部配置的 Loader 對模塊進行翻譯,再找出該模塊依賴的模塊,再遞歸本步驟直到全部入口依賴的文件都通過了本步驟的處理 - 完成模塊編譯:在通過第4步使用 Loader 翻譯完全部模塊後,獲得了每一個模塊被翻譯後的最終內容以及它們之間的依賴關係 - 輸出資源:根據入口和模塊之間的依賴關係,組裝成一個個包含多個模塊的 Chunk,再把每一個 Chunk 轉換成一個單獨的文件加入到輸出列表,這步是能夠修改輸出內容的最後機會 - 輸出完成:在肯定好輸出內容後,根據配置肯定輸出的路徑和文件名,把文件內容寫入到文件系統 在以上過程當中,Webpack 會在特定的時間點廣播出特定的事件,插件在監聽到感興趣的事件後會執行特定的邏輯,而且插件能夠調用 Webpack 提供的 API 改變 Webpack 的運行結果
編寫 Loader 時要遵循單一原則,每一個 Loader 只作一種"轉義"工做。 每一個 Loader 的拿到的是源文件內容(source),能夠經過返回值的方式將處理後的內容輸出,也能夠調用 this.callback() 方法,將內容返回給 webpack 。 還能夠經過 this.async() 生成一個 callback 函數,再用這個 `callback`` 將處理後的內容輸出出去 相對於 Loader 而言,Plugin 的編寫就靈活了許多。 webpack 在運行的生命週期中會廣播出許多事件,Plugin 能夠監聽這些事件,在合適的時機經過 Webpack 提供的 API 改變輸出結果
- 第一步,在 webpack 的 watch 模式下,文件系統中某一個文件發生修改,webpack 監聽到文件變化,根據配置文件對模塊從新編譯打包,並將打包後的代碼經過簡單的 JavaScript 對象保存在內存中。 - 第二步是 webpack-dev-server 和 webpack 之間的接口交互,而在這一步,主要是 dev-server 的中間件 webpack-dev-middleware 和 webpack 之間的交互,webpack-dev-middleware 調用 webpack 暴露的 API對代碼變化進行監控,而且告訴 webpack,將代碼打包到內存中。 - 第三步是 webpack-dev-server 對文件變化的一個監控,這一步不一樣於第一步,並非監控代碼變化從新打包。當咱們在配置文件中配置了devServer.watchContentBase 爲 true 的時候,Server 會監聽這些配置文件夾中靜態文件的變化,變化後會通知瀏覽器端對應用進行 live reload。注意,這兒是瀏覽器刷新,和 HMR 是兩個概念。 - 第四步也是 webpack-dev-server 代碼的工做,該步驟主要是經過 sockjs(webpack-dev-server 的依賴)在瀏覽器端和服務端之間創建一個 websocket 長鏈接,將 webpack 編譯打包的各個階段的狀態信息告知瀏覽器端,同時也包括第三步中 Server 監聽靜態文件變化的信息。瀏覽器端根據這些 socket 消息進行不一樣的操做。固然服務端傳遞的最主要信息仍是新模塊的 hash 值,後面的步驟根據這一 hash 值來進行模塊熱替換。 - webpack-dev-server/client 端並不可以請求更新的代碼,也不會執行熱更模塊操做,而把這些工做又交回給了 webpack,webpack/hot/dev-server 的工做就是根據 webpack-dev-server/client 傳給它的信息以及 dev-server 的配置決定是刷新瀏覽器呢仍是進行模塊熱更新。固然若是僅僅是刷新瀏覽器,也就沒有後面那些步驟了。 - HotModuleReplacement.runtime 是客戶端 HMR 的中樞,它接收到上一步傳遞給他的新模塊的 hash 值,它經過 JsonpMainTemplate.runtime 向 server 端發送 Ajax 請求,服務端返回一個 json,該 json 包含了全部要更新的模塊的 hash 值,獲取到更新列表後,該模塊再次經過 jsonp 請求,獲取到最新的模塊代碼。這就是上圖中 七、八、9 步驟。 - 而第 10 步是決定 HMR 成功與否的關鍵步驟,在該步驟中,HotModulePlugin 將會對新舊模塊進行對比,決定是否更新模塊,在決定更新模塊後,檢查模塊之間的依賴關係,更新模塊的同時更新模塊間的依賴引用。 - 最後一步,當 HMR 失敗後,回退到 live reload 操做,也就是進行瀏覽器刷新來獲取最新打包代碼。
- 壓縮代碼。刪除多餘的代碼、註釋、簡化代碼的寫法等等方式 - 利用 CDN 加速。在構建過程當中,將引用的靜態資源路徑修改成 CDN 上對應的路徑 - 刪除死代碼 Tree Shaking)。將代碼中永遠不會走到的片斷刪除掉 - 優化圖片,對於小圖能夠使用 base64 的方式寫入文件中 - 按照路由拆分代碼,實現按需加載,提取公共代碼 - 給打包出來的文件名添加哈希,實現瀏覽器緩存文件
- 多入口狀況下,使用CommonsChunkPlugin來提取公共代碼 - 經過externals配置來提取經常使用庫 - 利用DllPlugin和DllReferencePlugin預編譯資源模塊 經過DllPlugin來對那些咱們引用可是絕對不會修改的npm包來進行預編譯,再經過DllReferencePlugin將預編譯的模塊加載進來。 - 使用Happypack 實現多線程加速編譯 - 使用webpack-uglify-parallel來提高uglifyPlugin的壓縮速度。 原理上webpack-uglify-parallel採用了多核並行壓縮來提高壓縮速度 - 使用Tree-shaking和Scope Hoisting來剔除多餘代碼
- 單頁應用能夠理解爲 webpack 的標準模式,直接在 entry 中指定單頁應用的入口便可 - 多頁應用的話,能夠使用 webpack 的 AutoWebPlugin 來完成簡單自動化的構建,可是前提是項目的目錄結構必須遵照他預設的規範
> bundle 是由 webpack 打包出來的文件,chunk 是指 webpack 在進行模塊的依賴分析的時候,代碼分割出來的代碼塊。module是開發中的單個模塊
- Git: - Git是一個分佈式的版本控制工具 - 它屬於第3代版本控制工具 - 客戶端能夠在其本地系統上克隆整個存儲庫 - 即便離線也能夠提交 - Push/pull 操做更快 - 工程能夠用 commit 自動共享 - SVN: - SVN 是集中版本控制工具 - 它屬於第2代版本控制工具 - 版本歷史記錄存儲在服務器端存儲庫中 - 只容許在線提交 - Push/pull 操做較慢 - 沒有任何東西自動共享
- Git 是分佈式版本控制系統(DVCS)。它能夠跟蹤文件的更改,並容許你恢復到任何特定版本的更改。 - 與 SVN 等其餘版本控制系統(VCS)相比,其分佈式架構具備許多優點,一個主要優勢是它不依賴於中央服務器來存儲項目文件的全部版本。 - 每一個開發人員均可以「克隆」我在圖中用「Local repository」標註的存儲庫的副本,而且在他的硬盤驅動器上具備項目的完整歷史記錄,所以當服務器中斷時,你須要的全部恢復數據都在你隊友的本地 Git 存儲庫中。 - 還有一箇中央雲存儲庫,開發人員能夠向其提交更改,並與其餘團隊成員進行共享,如圖所示,全部協做者都在提交更改「遠程存儲庫」。
> Git 中的 「裸」 存儲庫只包含版本控制信息而沒有工做文件(沒有工做樹),而且它不包含特殊的 .git 子目錄。相反,它直接在主目錄自己包含 .git 子目錄中的全部內容,其中工做目錄包括: 一個 .git 子目錄,其中包含你的倉庫全部相關的 Git 修訂歷史記錄。 工做樹,或簽出的項目文件的副本。
> Git使用 C 語言編寫。 GIT 很快,C 語言經過減小運行時的開銷來作到這一點。
- 新增文件的命令:git add file或者git add . - 提交文件的命令:git commit –m或者git commit –a - 查看工做區情況:git status –s - 拉取合併遠程分支的操做:git fetch/git merge或者git pull - 查看提交記錄命令:git reflog
> 開發過程當中,咱們都有本身的特性分支,因此衝突發生的並很少,但也碰到過。諸如公共類的公共方法,我和別人同時修改同一個文件,他提交後我再提交就會報衝突的錯誤。 發生衝突,在IDE裏面通常都是對比本地文件和遠程分支的文件,而後把遠程分支上文件的內容手工修改到本地文件,而後再提交衝突的文件使其保證與遠程分支的文件一致,這樣纔會消除衝突,而後再提交本身修改的部分。特別要注意下,修改本地衝突文件使其與遠程倉庫的文件保持一致後,須要提交後才能消除衝突,不然沒法繼續提交。必要時可與同事交流,消除衝突。 發生衝突,也能夠使用命令。 - 經過git stash命令,把工做區的修改提交到棧區,目的是保存工做區的修改; - 經過git pull命令,拉取遠程分支上的代碼併合併到本地分支,目的是消除衝突; - 經過git stash pop命令,把保存在棧區的修改部分合併到最新的工做空間中;
> 若是想撤銷提交到索引區的文件,能夠經過git reset HEAD file;若是想撤銷提交到本地倉庫的文件,能夠經過git reset –soft HEAD^n恢復當前分支的版本庫至上一次提交的狀態,索引區和工做空間不變動;能夠經過git reset –mixed HEAD^n恢復當前分支的版本庫和索引區至上一次提交的狀態,工做區不變動;能夠經過git reset –hard HEAD^n恢復當前分支的版本庫、索引區和工做空間至上一次提交的狀態。
若是修改最近一次提交的歷史記錄,就能夠用git commit –amend命令;vim編輯的方式; 若是修改以前提交的歷史記錄,就須要按照下面的步驟: 第一步:首先查看前三次的提交歷史記錄: 第二步:執行命令git rebase –i HEAD~3,會把前3次的提交記錄按照倒敘列出來;這裏把第一行的‘pick’修改成‘edit’,而後esc + :wq退出vim編輯器; 第三步:根據提示,執行git commit –amend命令,進入vim編輯器並修改提交信息。 第四步:而後執行git rebase –continue命令
命令git stash是把工做區修改的內容存儲在棧區。 如下幾種狀況會使用到它: - 解決衝突文件時,會先執行git stash,而後解決衝突; - 遇到緊急開發任務但目前任務不能提交時,會先執行git stash,而後進行緊急任務的開發,而後經過git stash pop取出棧區的內容繼續開發; - 切換分支時,當前工做空間內容不能提交時,會先執行git stash再進行分支切換;
> git stash drop 命令用於刪除隱藏的項目。默認狀況下,它將刪除最後添加的存儲項,若是提供參數的話,它還能夠刪除特定項。 下面舉個例子。 若是要從隱藏項目列表中刪除特定的存儲項目,能夠使用如下命令: git stash list:它將顯示隱藏項目列表,如: stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c264051 Revert 「added file_size」 stash@{2}: WIP on master: 21d80a5 added number to log 若是要刪除名爲 stash@{0} 的項目,請使用命令 git stash drop stash@{0}。
查看分支的提交歷史記錄: - 命令git log –number:表示查看當前分支前number個詳細的提交歷史記錄; - 命令git log –number –pretty=oneline:在上個命令的基礎上進行簡化,只顯示sha-1碼和提交信息; - 命令git reflog –number: 表示查看全部分支前number個簡化的提交歷史記錄; - 命令git reflog –number –pretty=oneline:顯示簡化的信息歷史信息; 若是要查看某文件的提交歷史記錄,直接在上面命令後面加上文件名便可。 注意:若是沒有number則顯示所有提交次數
> 要獲取特定提交中已更改的列表文件,請使用如下命令: git diff-tree -r {hash} 給定提交哈希,這將列出在該提交中更改或添加的全部文件。 -r 標誌使命令列出單個文件,而不是僅將它們摺疊到根目錄名稱中。 你還能夠包括下面提到的內容,雖然它是可選的,但有助於給面試官留下深入印象。 輸出還將包含一些額外信息,能夠經過包含兩個標誌把它們輕鬆的屏蔽掉: git diff-tree –no-commit-id –name-only -r {hash} 這裏 -no-commit-id 將禁止提交哈希值出如今輸出中,而 -name-only 只會打印文件名而不是它們的路徑。
git 使用你的用戶名將提交與身份相關聯。 git config 命令可用來更改你的 git 配置,包括你的用戶名。 下面用一個例子來解釋。 假設你要提供用戶名和電子郵件 ID 用來將提交與身份相關聯,以便你能夠知道是誰進行了特定提交。爲此,我將使用: git config –global user.name "Your Name": 此命令將添加用戶名。 git config –global user.email "Your E-mail Address": 此命令將添加電子郵件ID。
- git branch –merged 它列出了已合併到當前分支的分支。 - git branch –no-merged 它列出了還沒有合併的分支。
Commit 對象包含如下組件,你應該提到如下這三點: - 一組文件,表示給定時間點的項目狀態 - 引用父提交對象 - SHAI 名稱,一個40個字符的字符串,提交對象的惟一標識。
將N個提交壓縮到單個提交中有兩種方式: - 若是要從頭開始編寫新的提交消息,請使用如下命令: git reset –soft HEAD~N && git commit - 若是你想在新的提交消息中串聯現有的提交消息,那麼須要提取這些消息並將它們傳給 git commit,能夠這樣: git reset –soft HEAD~N && git commit –edit -m"$(git log –format=%B –reverse .HEAD@{N})"
Git bisect 用於查找使用二進制搜索引入錯誤的提交。 Git bisect的命令是 git bisect <subcommand> <options> 既然你已經提到過上面的命令,那就解釋一下這個命令會作什麼。 此命令用了二進制搜索算法來查找項目歷史記錄中的哪一個提交引入了錯誤。你能夠經過告訴它已知包含該錯誤的「錯誤」提交以及在引入錯誤以前已知的「良好」提交來使用它。而後 git bisect 在這兩個端點之間選擇一個提交,並詢問你所選的提交是「好」仍是「壞」。它繼續縮小範圍,直到找到引入更改的確切提交。
- 功能分支(Feature branching) 要素分支模型將特定要素的全部更改保留在分支內。當經過自動化測試對功能進行全面測試和驗證時,該分支將合併到主服務器中。 - 任務分支(Task branching) 在此模型中,每一個任務都在其本身的分支上實現,任務鍵包含在分支名稱中。很容易看出哪一個代碼實現了哪一個任務,只需在分支名稱中查找任務鍵。 - 發佈分支(Release branching) 一旦開發分支得到了足夠的發佈功能,你就能夠克隆該分支來造成發佈分支。建立該分支將會啓動下一個發佈週期,因此在此以後不能再添加任何新功能,只有錯誤修復,文檔生成和其餘面向發佈的任務應該包含在此分支中。一旦準備好發佈,該版本將合併到主服務器並標記版本號。此外,它還應該再將自發布以來已經取得的進展合併回開發分支。
> SubGit 是將 SVN 到 Git遷移的工具。它建立了一個可寫的本地或遠程 Subversion 存儲庫的 Git 鏡像,而且只要你願意,能夠隨意使用 Subversion 和 Git。 這樣作有不少優勢,好比你能夠從 Subversion 快速一次性導入到 Git 或者在 Atlassian Bitbucket Server 中使用SubGit。咱們能夠用 SubGit 建立現有 Subversion 存儲庫的雙向 Git-SVN 鏡像。你能夠在方便時 push 到 Git 或提交 Subversion。同步由 SubGit 完成。
> 簡單來講:git fetch branch是把名爲branch的遠程分支拉取到本地;而git pull branch是在fetch的基礎上,把branch分支與當前分支進行merge;所以pull = fetch + merge。
> 簡單的說,git merge和git rebase都是合併分支的命令。 git merge branch會把branch分支的差別內容pull到本地,而後與本地分支的內容一併造成一個committer對象提交到主分支上,合併後的分支與主分支一致; git rebase branch會把branch分支優先合併到主分支,而後把本地分支的commit放到主分支後面,合併後的分支就好像從合併後主分支又拉了一個分支同樣,本地分支自己不會保留提交歷史。
> HEAD文件包含當前分支的引用(指針); 工做樹是把當前分支檢出到工做空間後造成的目錄樹,通常的開發工做都會基於工做樹進行; 索引index文件是對工做樹進行代碼修改後,經過add命令更新索引文件;GIT系統經過索引index文件生成tree對象;
GitFlow能夠用來管理分支。GitFlow工做流中經常使用的分支有下面幾類: - master分支:最爲穩定功能比較完整的隨時可發佈的代碼,即代碼開發完成,通過測試,沒有明顯的bug,才能合併到 master 中。請注意永遠不要在 master 分支上直接開發和提交代碼,以確保 master 上的代碼一直可用; - develop分支;用做平時開發的主分支,並一直存在,永遠是功能最新最全的分支,包含全部要發佈 到下一個 release 的代碼,主要用於合併其餘分支,好比 feature 分支; 若是修改代碼,新建 feature 分支修改完再合併到 develop 分支。全部的 feature、release 分支都是從 develop 分支上拉的。 - feature分支;這個分支主要是用來開發新的功能,一旦開發完成,經過測試沒問題(這個測試,測試新功能沒問題),咱們合併回develop 分支進入下一個 release - release分支;用於發佈準備的專門分支。當開發進行到必定程度,或者說快到了既定的發佈日,能夠發佈時,創建一個 release 分支並指定版本號(能夠在 finish 的時候添加)。開發人員能夠對 release 分支上的代碼進行集中測試和修改bug。(這個測試,測試新功能與已有的功能是否有衝突,兼容性)所有完成通過測試沒有問題後,將 release 分支上的代碼合併到 master 分支和 develop 分支 - hotfix分支;用於修復線上代碼的bug。**從 master 分支上拉。**完成 hotfix 後,打上 tag 咱們合併回 master 和 develop 分支。 GitFlow主要工做流程 - 1.初始化項目爲gitflow , 默認建立master分支 , 而後從master拉取第一個develop分支 - 2.從develop拉取feature分支進行編碼開發(多個開發人員拉取多個feature同時進行並行開發 , 互不影響) - 3.feature分支完成後 , 合併到develop(不推送 , feature功能完成還未提測 , 推送後會影響其餘功能分支的開發);合併feature到develop , 能夠選擇刪除當前feature , 也能夠不刪除。但當前feature就不可更改了,必須從release分支繼續編碼修改 4.從develop拉取release分支進行提測 , 提測過程當中在release分支上修改BUG 5.release分支上線後 , 合併release分支到develop/master並推送;合併以後,可選刪除當前release分支,若不刪除,則當前release不可修改。線上有問題也必須從master拉取hotfix分支進行修改; 6.上線以後若發現線上BUG , 從master拉取hotfix進行BUG修改; 7.hotfix經過測試上線後,合併hotfix分支到develop/master並推送;合併以後,可選刪除當前hotfix ,若不刪除,則當前hotfix不可修改,若補丁未修復,須要從master拉取新的hotfix繼續修改; 8.當進行一個feature時 , 若develop分支有變更 , 如其餘開發人員完成功能並上線 , 則須要將完成的功能合併到本身分支上,即合併develop到當前feature分支; 9.當進行一個release分支時 , 若develop分支有變更 , 如其餘開發人員完成功能並上線 , 則須要將完成的功能合併到本身分支上,即合併develop到當前release分支 (!!! 由於當前release分支經過測試後會發佈到線上 , 若是不合並最新的develop分支 , 就會發生丟代碼的狀況); GitFlow的好處 爲不一樣的分支分配一個明確的角色,並定義分支之間如何交互以及什麼時間交互;能夠幫助大型項目理清分支之間的關係,簡化分支的複雜度。
命令git cherry-pick能夠把branch A的commit複製到branch B上。 在branch B上進行命令操做: - 複製單個提交:git cherry-pick commitId - 複製多個提交:git cherry-pick commitId1…commitId3 注意:複製多個提交的命令不包含commitId1.
> GIT是分佈式版本控制系統,其餘相似於SVN是集中式版本控制系統。 分佈式區別於集中式在於:每一個節點的地位都是平等,擁有本身的版本庫,在沒有網絡的狀況下,對工做空間內代碼的修改能夠提交到本地倉庫,此時的本地倉庫至關於集中式的遠程倉庫,能夠基於本地倉庫進行提交、撤銷等常規操做,從而方便平常開發。
- 首先利用命令touch .gitignore新建文件 $ touch .gitignore - 而後往文件中添加須要忽略哪些文件夾下的什麼類型的文件 $ vim .gitignore $ cat .gitignore /target/class .settings .imp *.ini
- 首先確保本地倉庫與遠程之間是連同的。若是提交失敗,則須要進行下面的命令進行連通: git remote add origin XXXX - 若是是第一次推送,則進行下面命令: git push -u origin master // -u 是指定origin爲默認主分支 - 以後的提交,只須要下面的命令: git push origin master
> GZIP,SSR 同構、PWA 應用、預渲染、localStorage 緩存 js 文件等。 下面就是細分拆解答案,無限的連帶問題,這裏很是耗時,這些內容大都網上能搜到,我這裏就不詳細說 其中有問到 PWA 的原理,個人回答是: Service Worker 有一套本身的聲明週期,當安裝而且處於激活狀態時候,網站在 https 或者 localhost 的協議時候,能夠攔截過濾發出的請求,會先把請求克隆一份(請求是流,消費就沒有了),而後判斷請求的資源是否在 Service Worker 緩存中,若是存在那麼能夠直接從 Service Worker 緩存中取出,若是不存在,那麼就真正的發出這個請求。
1)Jenkins 自動化構建 2)本身搭建 Node.js 服務器,實現 Jenkins 3)Docker 配合 Travis CI 實現自動化構建 Jenkins 自動化構建: 配置,自動同步某個分支代碼,打包構建。 本身搭建 Node.js 服務器,實現 Jenkins: 本身搭建 Node.js 的服務器,在 GitLab 上指定 webhook 地址,分支代碼更新觸發事件,服務器接受到 post 請求,裏面附帶分支的信息,執行本身的 shell 腳本命令,指定文件夾,構建打包。 服務器上使用 Docker-compose 指定鏡像,每次代碼推送到 gitHub,經過本身編寫的 yml 和 dockerfile 文件構建打包,服務器自動拉取最新鏡像而且發佈到正式環境。
> 前端的架構,首先明確項目的兼容性,面向瀏覽器編程,是否作成 PC、移動端的響應式佈局。根據項目規模、後期可能迭代的需求制定技術方案,若是比較重型的應用應該選用原生開發,儘可能少使用第三方庫。 客戶端架構:是否跨平臺,明確兼容系統,例如是否兼容 XP ,若是兼容 XP 就選擇 nw.js,再而後根據項目複雜度招聘相應技術梯度人員,安排系統學習相關內容,招聘人員或者購買定製開發相關原生插件內容。 雖說只是談談,可是感受面試的職位越高級、輪數越日後,越考驗你的架構能力,前面考察基礎,後面考察你的技術廣度以及邏輯思惟,可否在複雜的應用中保持清醒頭腦,定位性能這類型的細節能力。不少人基礎面試面得很好,可是拿不到 offer,緣由就是沒有這種架構能力,只能本身寫代碼,不能帶領你們學習、寫代碼。這也是我在面試時偶然聽到某個大公司 HR 之間的對話,原話是:他面試還能夠,看起來是很老實(某個以前的面試者),可是他對以前項目總體流程並非那麼清楚,連本身作的項目,先後端流程都不清楚,感受不合適。
> 將 Vue 和 React 一塊兒開發,其實一點都不難,只要本身能造出 Redux 這樣的輪子,熟悉兩個框架原理,就能一塊兒開發,難的是將這些在一個合適的場景中使用。以前看到網上有微前端的實踐,可是並非那麼完美,固然,相似 Electron 這樣的應用,混合開發很正常,微前端並非只單單多個框架混合開發,更可能是多個框架引入後解決了什麼問題、帶來的問題怎麼解決?畢竟 5G 還沒徹底普及,數據傳輸仍是不那麼快。過大的包容易帶來客戶端的過長白屏時間(本身給本身挖坑)
Jenkins是一個開源軟件項目,是基於Java開發的一種持續集成工具,用於監控持續重複的工做,旨在提供一個開放易用的軟件平臺,使軟件的持續集成變成可能。 Jenkins功能包括: - 持續的軟件版本發佈/測試項目。 - 監控外部調用執行的工做
我會經過將 jobs 目錄從舊服務器複製到新服務器的方式來完成這個事情。有不少種方法能夠作到這一點: - 只需複製相應的 job 目錄,便可將 job 從一個 Jenkins 服務器移動到另外一個。 - 經過使用其它名稱克隆 job 目錄來製做現有 job 的副本。 - 經過重命名目錄來重命名現有 job。請注意,若是你更改了 job 名稱,則須要更改嘗試調用該重命名 job 的全部 job 。
> 按期備份 JENKINS_HOME 目錄。這包含全部構建 job 配置,從屬節點配置和構建歷史記錄。要建立 Jenkins 的備份,只需複製此目錄便可,你還能夠複製 job 目錄或重命名目錄。
關於這個答案的解決方法是首先提一下如何建立 job:轉到 Jenkins 首頁,選擇「New Job」,而後選擇「Build a free-style software project」。而後你能夠設置這個自由式 job 的元素: - 可選的 SCM,例如源代碼所在的 CVS 或 Subversion。 - 用於控制 Jenkins 什麼時候執行構建的觸發器。 - 某種構建腳本,用於執行實際工做的構建(ant,maven,shell 腳本,批處理文件等)。 - 從構建中收集信息的可選步驟,例如歸檔製品、記錄 javadoc 和測試結果。 - 配置構建結果通知其餘人/系統的步驟,例如發送電子郵件、即時消息、更新問題跟蹤器等。
- Maven 2 project - Amazon EC2 - HTML publisher - Copy artifact - Join - Green Balls
- 確保 global security 配置項已經打開。 - 確保用適當的插件將 Jenkins 與企業員工目錄進行集成。 - 確保啓用項目矩陣的權限訪問設置。 - 經過自定義版本控制的腳原本自動化 Jenkins 中設置權限/特權的過程。 - 限制對 Jenkins 數據/文件夾的物理訪問。 - 按期對其進行安全審覈。
- Docker是一個容器化平臺,它將應用程序及其全部依賴項以容器的形式打包在一塊兒,以確保應用程序在任何環境(不管是開發環境、測試環境仍是生產環境)中無縫運行。 - Docker容器,將一個軟件包在一個完整的文件系統中,其中包含運行所需的一切:代碼、運行時、系統工具、系統庫等任何能夠安裝在服務器上的東西。 - 它都將始終運行相同的程序,不管軟件的環境如何。
> Docker鏡像是Docker容器的源代碼。換句話說,Docker鏡像用於建立容器。使用build命令建立鏡像,而且在使用run啓動時它們將生成容器。鏡像存儲在Docker註冊表中,registry.hub.docker.com由於它們可能變得很是大,鏡像被設計爲由其餘鏡像層組成,容許在經過網絡傳輸鏡像時發送最少許的數據。
> Docker容器包括應用程序及其全部依賴項,但與其餘容器共享內核,在主機操做系統的用戶空間中做爲獨立進程運行。Docker容器不依賴於任何特定的基礎架構:它們能夠在任何計算機,任何基礎架構和任何雲中運行。
> Docker hub是一個基於雲的註冊表服務,容許您連接到代碼存儲庫,構建映像並測試它們,存儲手動推送的鏡像以及指向Docker雲的連接,以便您能夠將鏡像部署到主機。它爲整個開發流程中的容器發現,分發和變動管理,用戶和團隊協做以及工做流自動化提供了集中資源。
- FROM:咱們使用FROM爲後續指令設置基本鏡像。在每一個有效的Dockerfile中,FROM是第一條指令。 - LABEL:咱們使用LABEL根據項目,模塊,許可等組織咱們的鏡像。咱們也能夠使用LABEL來幫助實現自動化。在LABEL中,咱們指定一個鍵值對,之後可用於以編程方式處理Dockerfile。 - RUN:咱們使用RUN命令在當前圖像之上的新圖層中執行任何指令。使用每一個RUN命令,咱們在圖像上添加一些內容,並在Dockerfile的後續步驟中使用它。 - CMD:咱們使用CMD命令提供執行容器的默認值。在Dockerfile中,若是咱們包含多個CMD命令,則只使用最後一條指令。
> 最好爲Docker Container建立無狀態應用程序。咱們能夠從應用程序中建立一個容器,並從應用程序中取出可配置的狀態參數。如今咱們能夠在生產環境和具備不一樣參數的QA環境中運行相同的容器。這有助於在不一樣場景中重用相同的鏡像。另外,無狀態應用程序比有狀態應用程序更容易使用Docker容器進行擴展。
- 一切都從Dockerfile開始。Dockerfile是鏡像的源代碼。 - 建立Dockerfile後,您能夠構建它以建立容器的鏡像。圖像只是「源代碼」的「編譯版本」,即Dockerfile。 - 得到容器的鏡像後,應使用註冊表從新分發容器。註冊表就像一個git存儲庫 - 你能夠推送和拉取鏡像。 - 接下來,您能夠使用該圖像來運行容器。在許多方面,正在運行的容器與虛擬機(但沒有虛擬機管理程序)很是類似。
在其構想的形式中,虛擬化被認爲是邏輯上劃分大型機以容許多個應用程序同時運行的方法。可是,當公司和開源社區可以以某種方式提供處理特權指令的方法,並容許在單個基於x86的系統上同時運行多個操做系統時,狀況發生了巨大變化。 實際效果是虛擬化容許您在同一硬件上運行兩個徹底不一樣的操做系統。每一個客戶操做系統都經歷了引導,加載內核等全部過程。您能夠擁有很是嚴格的安全性,例如,客戶操做系統沒法徹底訪問主機操做系統或其餘客戶,從而徹底混亂。 能夠基於虛擬化方法如何模仿客戶操做系統的硬件並模擬客戶操做環境來對虛擬化方法進行分類。主要有三種類型的虛擬化: - 仿真 - 半虛擬化 - 基於容器的虛擬化
> 管理程序處理建立用戶虛擬機運行的虛擬環境。它監督用戶系統,並確保在必要時爲客戶分配資源。虛擬機管理程序位於物理機和虛擬機之間,併爲虛擬機提供虛擬化服務。爲了實現它,它攔截虛擬機上的客戶操做系統操做,並模擬主機操做系統上的操做。 虛擬化技術的快速發展(主要是在雲端),經過容許在一個物理服務器上建立多個虛擬服務器,藉助於管理程序(如Xen、VMware Player、KVM等),以及在商品處理器(如Intel VT AN)中集成硬件支持,進一步推進了虛擬化的使用。例如Intel VT和AMD-V。
> Docker Swarm是Docker的羣集管理工具。它將Docker主機池轉變爲一個虛擬Docker主機。Docker Swarm提供標準的Docker API,任何已經與Docker守護進程通訊的工具均可以使用Swarm擴展到多個主機。
> Docker提供docker stats和docker events等工具來監控生產中的Docker。咱們能夠使用這些命令獲取重要統計數據的報告。 Docker stats:當咱們使用容器ID調用docker stats時,咱們得到容器的CPU,內存使用狀況等。它相似於Linux中的top命令。 Docker events:Docker events是一個命令,用於查看Docker守護程序中正在進行的任務。 一些常見的Docker事件是:attach,commit,die,detach,rename,destroy等。咱們還能夠使用各類選項來限制或過濾咱們感興趣的事件。
- 原子性:要麼所有完成,要麼不完成,若發生錯誤會進行回滾操做; - 一致性:開始到結束後,數據庫完整性約束沒收到破壞;(實體完整性,參照完整性,用戶定義的完整性) - 隔離性:事務與事務之間相隔離,串行化執行; - 持久性:事務完成對數據的影響是永久的;
- 1NF:每一列都是不可分割的基本數據項,同一列無二值;無重複的域; - 2NF:實例依賴於主鍵部分; - 3NF:屬性不依賴於其餘非主屬性; 首先要明確的是:知足着第三範式,那麼就必定知足第二範式、知足着第二範式就必定知足第一範式 第一範式:字段是最小的的單元不可再分 學生信息組成學生信息表,有年齡、性別、學號等信息組成。這些字段都不可再分,因此它是知足第一範式的 第二範式:知足第一範式,表中的字段必須徹底依賴於所有主鍵而非部分主鍵。 其餘字段組成的這行記錄和主鍵表示的是同一個東西,而主鍵是惟一的,它們只須要依賴於主鍵,也就成了惟一的 學號爲1的同窗,姓名是damao,年齡是100歲。姓名和年齡字段都依賴着學號主鍵。 第三範式:知足第二範式,非主鍵外的全部字段必須互不依賴 就是數據只在一個地方存儲,不重複出如今多張表中,能夠認爲就是消除傳遞依賴 好比,咱們大學分了不少系(中文系、英語系、計算機系……),這個系別管理表信息有如下字段組成:系編號,系主任,系簡介,系架構。那咱們能不能在學生信息表添加系編號,系主任,系簡介,系架構字段呢?不行的,由於這樣就冗餘了,非主鍵外的字段造成了依賴關係(依賴到學生信息表了)!正確的作法是:學生表就只能增長一個系編號字段。
- 分表 :真正的分表,每張表對應三個文件;提升MYSQL的併發能力; - 分區 :表中的數據分紅多個區塊;突破磁盤的讀寫能力;
- 樂觀鎖:假定不會發生併發衝突,只在提交時檢查,如有其餘數據更新了數據,則回滾;使用數據版本標示數據(時間戳,版本號) - 悲觀鎖:假定會發生併發衝突,屏蔽一切破壞數據庫一致性的操做,主要用於數據爭用激烈的環境,以及鎖成本低於回滾成本時;排他鎖;
- SQL語句及索引的優化 - 數據庫表結構的優化 - 系統配置的優化 - 硬件的優化
- 選取最適用的字段屬性,儘量減小定義字段寬度,儘可能把字段設置NOTNULL,例如'省份'、'性別'最好適用ENUM - 使用鏈接(JOIN)來代替子查詢 - 適用聯合(UNION)來代替手動建立的臨時表 - 事務處理 - 鎖定表、優化事務處理 - 適用外鍵,優化鎖定表 - 創建索引 - 優化查詢語句
> 索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着對數據表裏全部記錄的引用指針。 普通索引(由關鍵字KEY或INDEX定義的索引)的惟一任務是加快對數據的訪問速度。 普通索引容許被索引的數據列包含重複的值。若是能肯定某個數據列將只包含彼此各不相同的值,在爲這個數據列建立索引的時候就應該用關鍵字UNIQUE把它定義爲一個惟一索引。也就是說,惟一索引能夠保證數據記錄的惟一性。 主鍵,是一種特殊的惟一索引,在一張表中只能定義一個主鍵索引,主鍵用於惟一標識一條記錄,使用關鍵字 PRIMARY KEY 來建立。 索引能夠覆蓋多個數據列,如像INDEX(columnA, columnB)索引,這就是聯合索引。 索引能夠極大的提升數據的查詢速度,可是會下降插入、刪除、更新表的速度,由於在執行這些寫操做時,還要操做索引文件。
> NoSQL是非關係型數據庫,NoSQL = Not Only SQL。 關係型數據庫採用的結構化的數據,NoSQL採用的是鍵值對的方式存儲數據。 在處理非結構化/半結構化的大數據時;在水平方向上進行擴展時;隨時應對動態增長的數據項時能夠優先考慮使用NoSQL數據庫。 在考慮數據庫的成熟度;支持;分析和商業智能;管理及專業性等問題時,應優先考慮關係型數據庫。
> GridFS是一種將大型文件存儲在MongoDB中的文件規範。使用GridFS能夠將大文件分隔成多個小文檔存放,這樣咱們可以有效的保存大文檔,並且解決了BSON對象有限制的問題。
> MongoDB沒有使用傳統的鎖或者複雜的帶回滾的事務,由於它設計的宗旨是輕量,快速以及可預計的高性能。能夠把它類比成MySQL MylSAM的自動提交模式。經過精簡對事務的支持,性能獲得了提高,特別是在一個可能會穿過多個服務器的系統裏。
> MongoDB 分片是基於區域(range)的。因此一個集合(collection)中的全部的對象都被存放到一個塊(chunk)中。只有當存在多餘一個塊的時候,纔會有多個分片獲取數據的選項。如今,每一個默認塊的大小是 64Mb,因此你須要至少 64 Mb 空間才能夠實施一個遷移。
> 更新操做會當即發生在舊的分片(shard)上,而後更改纔會在全部權轉移(ownership transfers)前複製到新的分片上。
> Redis 將數據存儲在內存中,key-value 形式存儲,因此獲取也快。支持的 key 格式相對於 memorycache 更多,並且支持 RDB 快照形式、AOF。 > RDB 持久化是指在指定的時間間隔內將內存中的數據集快照寫入磁盤,實際操做過程是 fork 一個子進程,先將數據集寫入臨時文件,寫入成功後,再替換以前的文件,用二進制壓縮存儲。RDB 是 Redis 默認的持久化方式,會在對應的目錄下生產一個 dump.rdb 文件,重啓會經過加載 dump.rdb 文件恢復數據。 優勢: 1)只有一個文件 dump.rdb,方便持久化; 2)容災性好,一個文件能夠保存到安全的磁盤; 3)性能最大化,fork 子進程來完成寫操做,讓主進程繼續處理命令,因此是 IO 最大化(使用單獨子進程來進行持久化,主進程不會進行任何 IO 操做,保證了 redis 的高性能) ; 4)若是數據集偏大,RDB 的啓動效率會比 AOF 更高。 缺點: 1)數據安全性低。(RDB 是間隔一段時間進行持久化,若是持久化之間 redis 發生故障,會發生數據丟失。因此這種方式更適合數據要求不是特別嚴格的時候) 2)因爲 RDB 是經過 fork 子進程來協助完成數據持久化工做的,所以,若是當數據集較大時,可能會致使整個服務器中止服務幾百毫秒,甚至是 1 秒鐘。 > AOF 持久化是以日誌的形式記錄服務器所處理的每個寫、刪除操做,查詢操做不會記錄,以文本的方式記錄,文件中能夠看到詳細的操做記錄。她的出現是爲了彌補 RDB 的不足(數據的不一致性),因此它採用日誌的形式來記錄每一個寫操做,並追加到文件中。Redis 重啓的會根據日誌文件的內容將寫指令從前到後執行一次以完成數據的恢復工做。 優勢: 1)數據安全性更高,AOF 持久化能夠配置 appendfsync 屬性,其中 always,每進行一次命令操做就記錄到 AOF 文件中一次。 2)經過 append 模式寫文件,即便中途服務器宕機,能夠經過 redis-check-aof 工具解決數據一致性問題。 3)AOF 機制的 rewrite 模式。(AOF 文件沒被 rewrite 以前(文件過大時會對命令進行合併重寫),能夠刪除其中的某些命令(好比誤操做的 flushall)) 缺點 1)AOF 文件比 RDB 文件大,且恢復速度慢;數據集大的時候,比 RDB 啓動效率低。 2)根據同步策略的不一樣,AOF 在運行效率上每每會慢於 RDB。
- 高性能: 假如用戶第一次訪問數據庫中的某些數據。這個過程會比較慢,由於是從硬盤上讀取的。將該用戶訪問的數據存在數緩存中,這樣下一次再訪問這些數據的時候就能夠直接從緩存中獲取了。操做緩存就是直接操做內存,因此速度至關快。若是數據庫中的對應數據改變的以後,同步改變緩存中相應的數據便可! - 高併發: 直接操做緩存可以承受的請求是遠遠大於直接訪問數據庫的,因此咱們能夠考慮把數據庫中的部分數據轉移到緩存中去,這樣用戶的一部分請求會直接到緩存這裏而不用通過數據庫。
> 緩存分爲本地緩存和分佈式緩存。以 Java 爲例,使用自帶的 map 或者 guava 實現的是本地緩存,最主要的特色是輕量以及快速,生命週期隨着 jvm 的銷燬而結束,而且在多實例的狀況下,每一個實例都須要各自保存一份緩存,緩存不具備一致性。 使用 redis 或 memcached 之類的稱爲分佈式緩存,在多實例的狀況下,各實例共用一份緩存數據,緩存具備一致性。缺點是須要保持 redis 或 memcached服務的高可用,整個程序架構上較爲複雜。
- redis支持更豐富的數據類型(支持更復雜的應用場景):Redis不只僅支持簡單的k/v類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。memcache支持簡單的數據類型,String。 - Redis支持數據的持久化,能夠將內存中的數據保持在磁盤中,重啓的時候能夠再次加載進行使用,而Memecache把數據所有存在內存之中。 - 集羣模式:memcached沒有原生的集羣模式,須要依靠客戶端來實現往集羣中分片寫入數據;可是 redis 目前是原生支持 cluster 模式的. - Memcached是多線程,非阻塞IO複用的網絡模型;Redis使用單線程的多路 IO 複用模型。
- String:經常使用命令: set,get,decr,incr,mget 等。 String數據結構是簡單的key-value類型,value其實不只能夠是String,也能夠是數字。 常規key-value緩存應用; 常規計數:微博數,粉絲數等。 - Hash:經常使用命令: hget,hset,hgetall 等。 Hash 是一個 string 類型的 field 和 value 的映射表,hash 特別適合用於存儲對象,後續操做的時候,你能夠直接僅僅修改這個對象中的某個字段的值。 好比咱們能夠Hash數據結構來存儲用戶信息,商品信息等等 - List:經常使用命令: lpush,rpush,lpop,rpop,lrange等 list 就是鏈表,Redis list 的應用場景很是多,也是Redis最重要的數據結構之一,好比微博的關注列表,粉絲列表,消息列表等功能均可以用Redis的 list 結構來實現。 Redis list 的實現爲一個雙向鏈表,便可以支持反向查找和遍歷,更方便操做,不過帶來了部分額外的內存開銷。 另外能夠經過 lrange 命令,就是從某個元素開始讀取多少個元素,能夠基於 list 實現分頁查詢,這個很棒的一個功能,基於 redis 實現簡單的高性能分頁,能夠作相似微博那種下拉不斷分頁的東西(一頁一頁的往下走),性能高。 - Set:經常使用命令: sadd,spop,smembers,sunion 等 set 對外提供的功能與list相似是一個列表的功能,特殊之處在於 set 是能夠自動排重的。 當你須要存儲一個列表數據,又不但願出現重複數據時,set是一個很好的選擇,而且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的。能夠基於 set 輕易實現交集、並集、差集的操做。 - Sorted Set:經常使用命令: zadd,zrange,zrem,zcard等 和set相比,sorted set增長了一個權重參數score,使得集合中的元素可以按score進行有序排列。 舉例: 在直播系統中,實時排行信息包含直播間在線用戶列表,各類禮物排行榜,彈幕消息(能夠理解爲按消息維度的消息排行榜)等信息,適合使用 Redis 中的 SortedSet 結構進行存儲。
Redis中有個設置時間過時的功能,即對存儲在 redis 數據庫中的值能夠設置一個過時時間。做爲一個緩存數據庫,這是很是實用的。如咱們通常項目中的 token 或者一些登陸信息,尤爲是短信驗證碼都是有時間限制的,按照傳統的數據庫處理方式,通常都是本身判斷過時,這樣無疑會嚴重影響項目性能。 咱們 set key 的時候,均可以給一個 expire time,就是過時時間,經過過時時間咱們能夠指定這個 key 能夠存活的時間。 若是假設你設置了一批 key 只能存活1個小時,那麼接下來1小時後,redis是怎麼對這批key進行刪除的? 按期刪除+惰性刪除。 經過名字大概就能猜出這兩個刪除方式的意思了。 按期刪除:redis默認是每隔 100ms 就隨機抽取一些設置了過時時間的key,檢查其是否過時,若是過時就刪除。注意這裏是隨機抽取的。爲何要隨機呢?你想想假如 redis 存了幾十萬個 key ,每隔100ms就遍歷全部的設置過時時間的 key 的話,就會給 CPU 帶來很大的負載! 惰性刪除 :按期刪除可能會致使不少過時 key 到了時間並無被刪除掉。因此就有了惰性刪除。假如你的過時 key,靠按期刪除沒有被刪除掉,還停留在內存裏,除非你的系統去查一下那個 key,纔會被redis給刪除掉。這就是所謂的惰性刪除,也是夠懶的哈! 可是僅僅經過設置過時時間仍是有問題的。咱們想一下:若是按期刪除漏掉了不少過時 key,而後你也沒及時去查,也就沒走惰性刪除,此時會怎麼樣?若是大量過時key堆積在內存裏,致使redis內存塊耗盡了。怎麼解決這個問題呢? redis 內存淘汰機制。。
redis 提供 6種數據淘汰策略: - volatile-lru:從已設置過時時間的數據集(server.db[i].expires)中挑選最近最少使用的數據淘汰 - volatile-ttl:從已設置過時時間的數據集(server.db[i].expires)中挑選將要過時的數據淘汰 - volatile-random:從已設置過時時間的數據集(server.db[i].expires)中任意選擇數據淘汰 - allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key(這個是最經常使用的). - allkeys-random:從數據集(server.db[i].dict)中任意選擇數據淘汰 - no-eviction:禁止驅逐數據,也就是說當內存不足以容納新寫入數據時,新寫入操做會報錯。這個應該沒人使用吧!
> Redis 經過 MULTI、EXEC、WATCH 等命令來實現事務(transaction)功能。事務提供了一種將多個命令請求打包,而後一次性、按順序地執行多個命令的機制,而且在事務執行期間,服務器不會中斷事務而改去執行其餘客戶端的命令請求,它會將事務中的全部命令都執行完畢,而後纔去處理其餘客戶端的命令請求。 在傳統的關係式數據庫中,經常用 ACID 性質來檢驗事務功能的可靠性和安全性。在 Redis 中,事務老是具備原子性(Atomicity)、一致性(Consistency)和隔離性(Isolation),而且當 Redis 運行在某種特定的持久化模式下時,事務也具備持久性(Durability)。
- 緩存穿透:是指查詢一個數據庫必定不存在的數據。正常的使用緩存流程大體是,數據查詢先進行緩存查詢,若是 key 不存在或者 key 已通過期,再對數據庫進行查詢,並把查詢到的對象,放進緩存。若是數據庫查詢對象爲空,則不放進緩存。 - 緩存擊穿:是指一個 key 很是熱點,在不停的扛着大併發,大併發集中對這一個點進行訪問,當這個 key 在失效的瞬間,持續的大併發就穿破緩存,直接請求數據庫,就像在一個屏障上鑿開了一個洞。
- 緩存雪崩 簡介:緩存同一時間大面積的失效,因此,後面的請求都會落到數據庫上,形成數據庫短期內承受大量請求而崩掉。 解決辦法: - 事前:儘可能保證整個 redis 集羣的高可用性,發現機器宕機儘快補上。選擇合適的內存淘汰策略。 - 事中:本地ehcache緩存 + hystrix限流&降級,避免MySQL崩掉 - 過後:利用 redis 持久化機制保存的數據儘快恢復緩存 - 緩存穿透 簡介:通常是黑客故意去請求緩存中不存在的數據,致使全部的請求都落到數據庫上,形成數據庫短期內承受大量請求而崩掉。 解決辦法: 有不少種方法能夠有效地解決緩存穿透問題,最多見的則是採用布隆過濾器,將全部可能存在的數據哈希到一個足夠大的bitmap中,一個必定不存在的數據會被 這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。另外也有一個更爲簡單粗暴的方法(咱們採用的就是這種),若是一個查詢返回的數據爲空(無論是數 據不存在,仍是系統故障),咱們仍然把這個空結果進行緩存,但它的過時時間會很短,最長不超過五分鐘。
> 所謂 Redis 的併發競爭 Key 的問題也就是多個系統同時對一個 key 進行操做,可是最後執行的順序和咱們指望的順序不一樣,這樣也就致使告終果的不一樣! 推薦一種方案:分佈式鎖(zookeeper 和 redis 均可以實現分佈式鎖)。(若是不存在 Redis 的併發競爭 Key 問題,不要使用分佈式鎖,這樣會影響性能) 基於zookeeper臨時有序節點能夠實現的分佈式鎖。大體思想爲:每一個客戶端對某個方法加鎖時,在zookeeper上的與該方法對應的指定節點的目錄下,生成一個惟一的瞬時有序節點。 判斷是否獲取鎖的方式很簡單,只須要判斷有序節點中序號最小的一個。 當釋放鎖的時候,只需將這個瞬時節點刪除便可。同時,其能夠避免服務宕機致使的鎖沒法釋放,而產生的死鎖問題。完成業務流程後,刪除對應的子節點釋放鎖。 在實踐中,固然是從以可靠性爲主。因此首推Zookeeper。
你只要用緩存,就可能會涉及到緩存與數據庫雙存儲雙寫,你只要是雙寫,就必定會有數據一致性的問題,那麼你如何解決一致性問題? 通常來講,就是若是你的系統不是嚴格要求緩存+數據庫必須一致性的話,緩存能夠稍微的跟數據庫偶爾有不一致的狀況,最好不要作這個方案,讀請求和寫請求串行化,串到一個內存隊列裏去,這樣就能夠保證必定不會出現不一致的狀況 串行化以後,就會致使系統的吞吐量會大幅度的下降,用比正常狀況下多幾倍的機器去支撐線上的一個請求。 還有一種方式就是可能會暫時產生不一致的狀況,可是發生的概率特別小,就是先更新數據庫,而後再刪除緩存。 這種狀況不存在併發問題麼? 不是的。假設這會有兩個請求,一個請求A作查詢操做,一個請求B作更新操做,那麼會有以下情形產生 (1)緩存恰好失效 (2)請求A查詢數據庫,得一箇舊值 (3)請求B將新值寫入數據庫 (4)請求B刪除緩存 (5)請求A將查到的舊值寫入緩存 ok,若是發生上述狀況,確實是會發生髒數據。 然而,發生這種狀況的機率又有多少呢? 發生上述狀況有一個先天性條件,就是步驟(3)的寫數據庫操做比步驟(2)的讀數據庫操做耗時更短,纔有可能使得步驟(4)先於步驟(5)。但是,你們想一想,數據庫的讀操做的速度遠快於寫操做的(否則作讀寫分離幹嗎,作讀寫分離的意義就是由於讀操做比較快,耗資源少),所以步驟(3)耗時比步驟(2)更短,這一情形很難出現。 如何解決上述併發問題? 首先,給緩存設有效時間是一種方案。其次,採用異步延時刪除策略,保證讀請求完成之後,再進行刪除操做。