JS學習筆記06-網頁擴展

本小節主要記錄了網頁中一些其餘的知識點:javascript

  • 模型
  • 網頁對象
  • 存儲對象
  • 通訊對象

1、模型

1.1 瀏覽器模型

瀏覽器的核心是兩部分:渲染引擎和 JavaScript引擎。html

1.1.1 渲染引擎

渲染引擎的主要做用是,將網頁代碼渲染爲用戶視覺能夠感知的平面文檔。渲染引擎處理網頁,一般分紅四個階段:java

  1. 解析代碼:HTML代碼解析爲 DOM,CSS代碼解析爲 CSSOM(CSS Object Model)。
  2. 對象合成:將 DOM和 CSSOM合成一棵渲染樹。
  3. 佈局:計算出渲染樹的佈局(layout)。
  4. 繪製:將渲染樹繪製到屏幕。

以上四步並不是嚴格按順序執行,每每第一步還沒完成,第二步和第三步就已經開始了。因此會看到這種狀況:網頁的HTML代碼還沒下載完,但瀏覽器已經顯示出內容。web

來看一下很是重要的重流和重繪。渲染樹轉換爲網頁佈局,稱爲「佈局流」(flow);佈局顯示到頁面的這個過程,稱爲「繪製」(paint)。它們都具備阻塞效應,而且會耗費不少時間和計算資源。json

頁面生成之後,腳本操做和樣式表操做都會觸發「重流」(reflow)和「重繪」(repaint)。用戶的互動也會觸發重流和重繪,如改變窗口大小、頁面滾動、設置鼠標懸停a:hover效果、在輸入框中輸入文本等。api

重流和重繪並不必定一塊兒發生,重流必然致使重繪,重繪不必定須要重流。好比改變元素顏色,只會致使重繪,而不會致使重流;改變元素的佈局,則會致使重繪和重流。大多數狀況下,瀏覽器會智能判斷,將重流和重繪只限制到相關的子樹上面,最小化所耗費的代價,而不會全局從新生成網頁。跨域

做爲開發者,應該儘可能設法下降重繪的次數和成本。好比,儘可能不要變更高層的 DOM 元素,而以底層 DOM 元素的變更代替;再好比重繪table佈局和flex佈局,開銷都會比較大。瀏覽器

1.1.2 JavaScript引擎

JavaScript引擎的主要做用是,讀取網頁中的JavaScript代碼,對其處理後運行。早期瀏覽器內部對 JavaScript 的處理過程以下:緩存

  1. 對代碼進行詞法分析,將代碼分解成詞元。
  2. 對詞元進行語法分析,將代碼整理成語法樹。
  3. 使用翻譯器,將代碼轉爲字節碼。
  4. 使用解釋器,將字節碼轉爲機器碼。

逐行所有解釋將字節碼轉爲機器碼,是很低效的。爲了提升運行速度,現代瀏覽器改成採用「即時編譯」(Just In Time compiler,縮寫 JIT),即字節碼只在運行時編譯,用到哪一行就編譯哪一行,而且把編譯結果緩存(inline cache)。一般一個程序被常常用到的只是其中一小部分代碼,有了緩存的編譯結果,整個程序的運行速度就會顯著提高。服務器

字節碼不能直接運行,而是運行在一個虛擬機之上,通常也把虛擬機稱爲JavaScript引擎。並不是全部的 JavaScript虛擬機運行時都有字節碼,有的JavaScript虛擬機基於源碼,即只要有可能,就經過 JIT編譯器直接把源碼編譯成機器碼運行,省略字節碼步驟。這樣作的目的,是爲了儘量地優化代碼、提升性能。

1.2 script 元素

瀏覽器加載 JavaScript 腳本,主要經過<script>元素完成。正常的網頁加載流程是這樣的:

  1. 渲染引擎一邊下載HTML網頁,一邊開始解析。
  2. 解析過程當中發現<script>元素後暫停解析,把網頁的控制權轉交給JavaScript引擎。
  3. JavaScript引擎下載腳本後執行代碼。
  4. 執行完畢後,把網頁控制權交還渲染引擎,恢復往下解析 HTML 網頁。

瀏覽器會並行下載腳本,可是執行仍是按照定義的順序來執行。

較好的作法是將<script>標籤都放在頁面底部而不是頭部的緣由以下:

  • 若是外部腳本加載時間很長(一直沒法完成下載),那麼瀏覽器就會一直等待腳本下載完成,形成網頁長時間失去響應,瀏覽器就會呈現「假死」狀態,這被稱爲「阻塞效應」。
  • 由於在 DOM 結構生成以前就調用 DOM 節點JS會報錯,若是腳本都在網頁尾部加載就不存在這個問題。

對於來自同一個域名的資源,好比腳本文件、樣式表文件、圖片文件等,瀏覽器通常有限制,同時最多下載6~20個資源,即最多同時打開的 TCP 鏈接有限制,這是爲了防止對服務器形成太大壓力。若是是來自不一樣域名的資源,就沒有這個限制。因此,一般把靜態文件放在不一樣的域名之下,以加快下載速度。

<script>標籤中還常見以下屬性:

  • defer屬性的做用是延遲腳本的執行,等到 DOM 加載生成後再執行腳本。該屬性的運行流程以下:

    1. 渲染引擎一邊下載HTML網頁,一邊開始解析。
    2. 解析過程當中,發現帶有defer屬性的<script>標籤,繼續往下解析 HTML 網頁,同時開啓另外一個進程並行下載<script>元素加載的外部腳本。
    3. 渲染引擎完成解析 HTML 網頁,此時再交由JavaScript引擎執行已經下載完成的腳本。

    defer屬性能夠保證腳本下載的同時,瀏覽器繼續渲染。須要注意的是,一旦採用這個屬性,依舊保證腳本的執行順序。

  • async屬性的做用是使用另外一個進程下載腳本,下載時不會阻塞渲染。該屬性的運行流程以下:

    1. 渲染引擎一邊下載HTML網頁,一邊開始解析。
    2. 解析過程當中,發現帶有async屬性的<script>標籤,繼續往下解析 HTML 網頁,同時開啓另外一個進程並行下載<script>標籤中的外部腳本。
    3. 腳本下載完成,渲染引擎暫停解析 HTML 網頁,交由JavaScript引擎執行。
    4. 腳本執行完畢,JavaScript引擎交由渲染引擎恢復解析 HTML 網頁。

    async屬性能夠保證腳本下載的同時,瀏覽器繼續渲染。須要注意的是,一旦採用這個屬性,就沒法保證腳本的執行順序。

通常來講,若是腳本之間有依賴關係,就使用defer屬性;若是腳本之間沒有依賴關係,就使用async屬性。若是同時使用和deferasync屬性,瀏覽器行爲由async屬性決定。

2、網頁對象

2.1 window

window對象指當前的瀏覽器窗口。它也是當前頁面的頂層對象,即最高一層的對象,全部其餘對象都是它的下屬。

對象屬性

網頁對象屬性

  • window.document:指向document對象
  • window.location:指向Location對象
  • window.localStorage:指向本地儲存的localStorage數據
  • window.sessionStorage:指向本地儲存的sessionStorage數據

工具對象屬性:

  • window.locationbar:地址欄對象,對象的visible屬性是一個布爾值,表示該組件是否可見。
  • window.menubar:菜單欄對象,對象的visible屬性是一個布爾值,表示該組件是否可見。
  • window.toolbar:工具欄對象,對象的visible屬性是一個布爾值,表示該組件是否可見。
  • window.statusbar:狀態欄對象,對象的visible屬性是一個布爾值,表示該組件是否可見。

對象事件

window.onload屬性事件發生在文檔在瀏覽器窗口加載完畢時

2.2 Location

Location對象是瀏覽器提供的原生對象,提供URL相關的信息和操做方法。經過window.locationdocument.location屬性,能夠拿到這個對象。

對象屬性

  • Location.href:整個 URL。若是對它寫入新的 URL 地址,瀏覽器會馬上跳轉到這個新地址
  • Location.origin:URL 的協議、主機名和端口。
  • Location.protocol:當前URL的協議,包括冒號
  • Location.hostname:當前URL的主機(不包括端口)。而Location.host包括端口
  • Location.port:當前URL的端口號
  • Location.pathname:URL 的路徑部分,從根路徑/開始
  • Location.search:查詢字符串部分,從問號?開始。

輔助方法

  • encodeURI()方法用於轉碼整個URL。它的參數是一個字符串,表明整個 URL。
  • decodeURI()方法用於整個URL的解碼。它接受一個參數,就是轉碼後的URL。

3、存儲對象

Cookie是服務器保存在瀏覽器的一小段文本信息,瀏覽器每次向服務器發出請求,就會自動附上這段信息。不一樣瀏覽器對Cookie數量和大小的限制是不同的。通常來講單個域名設置的 Cookie 不該超過30個,每一個Cookie的大小不能超過4KB。超過限制之後Cookie將被忽略,不會被設置。

Cookie 有一些主要用途:

  • 對話(session)管理:保存登陸、購物車等須要記錄的信息。
  • 個性化信息:保存用戶的偏好,好比網頁的字體大小、背景色等等。

cookie的屬性

cookie除了使用K-V形式設置Cookie的名字Cookie的值外,它還有如下屬性:

  • Expires屬性指定一個具體的到期時間,到了指定時間之後,瀏覽器就再也不保留這個Cookie。它的值是UTC格式,可使用Date.prototype.toUTCString()進行格式轉換。若是不設置該屬性或設爲null,Cookie只在當前會話有效,瀏覽器窗口一旦關閉該Cookie就會被刪除。
  • Max-Age屬性指定從如今開始Cookie存在的秒數,好比60 * 60 * 24 * 365(即一年)。過了這個時間之後,瀏覽器就再也不保留這個 Cookie。若是同時指定了ExpiresMax-Age,那麼Max-Age的值將優先生效。
  • Domain屬性指定瀏覽器發出HTTP請求時,哪些域名要附帶這個Cookie。若是沒有指定該屬性,瀏覽器會默認將其設爲當前域名,這時子域名將不會附帶這個Cookie。若是指定了該屬性,那麼子域名也會附帶這個 Cookie。
  • Path屬性指定瀏覽器發出HTTP請求時,哪些路徑要附帶這個Cookie。只要瀏覽器發現,Path屬性是HTTP請求路徑的開頭一部分,就會在頭信息裏面帶上這個Cookie。好比PATH屬性是/,那麼請求/docs路徑也會包含它。
  • Secure屬性指定瀏覽器只有在加密協議 HTTPS 下,才能將這個 Cookie 發送到服務器。
  • HttpOnly屬性指定該Cookie沒法經過JS腳本拿到,主要讓document.cookieXMLHttpRequestRequest API拿不到該屬性。這樣就防止該Cookie被腳本讀到。只有瀏覽器發出HTTP請求時,纔會帶上該Cookie。

cookie的交互

cookie的生成

在瀏覽器保存Cookie,就要在HTTP迴應的頭信息裏面,放置一個或多個Set-Cookie字段。一個Set-Cookie字段可同時包括該cookie的多個屬性,沒有次序的要求。這個過程是服務端手動處理的。

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

cookie的發送

向瀏覽器發送Cookie,就要在HTTP迴應的頭信息裏面,放置一個或多個Cookie字段。一個Cookie字段可同時包含多個 Cookie,使用分號分隔。這個過程是瀏覽器自動處理的。

Cookie: name=value; name2=value2; name3=value3

3.2 Storage

Storage接口用於腳本在瀏覽器保存數據,保存的數據都以「鍵值對」的形式存在。sessionStorage保存的數據用於瀏覽器的一次會話,當會話結束數據被清空;localStorage保存的數據長期存在,下一次訪問該網站的時候,網頁能夠直接讀取之前保存的數據。除了保存期限的長短不一樣,這兩個對象的其餘方面都一致。目前每一個域名的存儲上限視瀏覽器而定,Chrome是2.5MB,Firefox和Opera是5MB。

對象屬性

  • StorageEvent.storageArea:返回鍵值對所在的整個對象。也就是說能夠從這個屬性上拿到當前域名儲存的全部鍵值對。
  • StorageEvent.url:表示原始觸發storage事件的那個網頁的網址。
  • StorageEvent.key:表示發生變更的鍵名。若是storage事件是由clear()方法引發,該屬性返回null
  • StorageEvent.newValue:表示新的鍵值。若是storage事件是由clear()方法引發,該屬性返回null
  • StorageEvent.oldValue:表示舊的鍵值。若是該鍵值對是新增的,該屬性返回null

對象方法

  • Storage.key()接受一個整數做爲參數(從0開始),返回該位置對應的鍵值。
  • Storage.getItem()方法用於讀取數據。它只有一個參數:鍵名。若是鍵名不存在,該方法返回null
  • Storage.setItem()方法用於存入數據。它接受兩個參數:第一個是鍵名;第二個是保存的數據。若是鍵名已經存在,該方法會更新已有的鍵值。
  • Storage.removeItem()方法用於清除某個鍵名對應的鍵值。它接受鍵名做爲參數,若是鍵名不存在,該方法不會作任何事情。
  • Storage.clear()方法用於清除全部保存的數據。

4、通訊對象

4.1 跨域通訊

所謂「同源」指的是「三個相同」:

  • 協議相同
  • 域名相同
  • 端口相同

常見的跨源通行方式有以下:

WebSocket

WebSocket是一種通訊協議,使用ws://(非加密)和wss://(加密)做爲協議前綴。該協議不實行同源政策,只要服務器支持,就能夠經過它進行跨源通訊。具體使用狀況後續再補充。

JSONP

JSONP是服務器與客戶端跨源通訊的經常使用方法。最大特色就是簡單易用,老式瀏覽器所有支持,服務端改造很是小。去電就是JSONP只能發GET請求。

  • 第一步,網頁添加一個<script>元素,向服務器請求一個腳本,這不受同源政策限制,能夠跨域請求。

    <script src="http://api.foo.com?callback=bar"></script>

    注意請求的腳本網址有一個callback參數(?callback=bar),用來告訴服務器客戶端的回調函數名稱。

  • 第二步,服務器收到請求後,拼接一個字符串,將JSON數據放在函數名裏面,做爲字符串返回(bar({...}))。

  • 第三步,客戶端會將服務器返回的字符串,做爲代碼解析。由於瀏覽器認爲這是script標籤請求的腳本內容。這時客戶端只要定義了bar()函數,就能在該函數體內,拿到服務器返回的JSON數據。做爲參數的JSON數據被視爲 JavaScript對象而不是字符串,所以避免了使用JSON.parse的步驟。

function addScriptTag(src) {
  var script = document.createElement('script');
  script.setAttribute('type', 'text/javascript');
  script.src = src;
  document.body.appendChild(script);
}

window.onload = function () {
  addScriptTag('http://example.com/ip?callback=foo');
}

function foo(data) {
  console.log('Your public IP address is: ' + data.ip);
};

CORS

CORS是跨域資源分享(Cross-Origin Resource Sharing)的縮寫。它是W3C標準,屬於跨源AJAX請求的根本解決方法。它容許瀏覽器向跨域的服務器發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。相比JSONP只能發GET請求,CORS容許任何類型的請求。

CORS通訊與普通的AJAX通訊沒有差異,代碼徹底同樣。瀏覽器一旦發現AJAX請求跨域,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求。所以實現 CORS 通訊的關鍵是服務器。只要服務器實現了CORS接口就可跨域通訊。

CORS 請求分紅兩類:簡單請求和非簡單請求。只要同時知足如下兩大條件就屬於簡單請求,凡是不一樣時知足上面兩個條件就屬於非簡單請求。非簡單請求是那種對服務器提出特殊要求的請求,好比請求方法是PUTDELETE,或者Content-Type字段的類型是application/json

  • 請求方法是如下三種方法之一:

    • HEAD
    • GET
    • POST
  • HTTP的頭信息不超出如下幾種字段:

    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:只限於三個值application/x-www-form-urlencodedmultipart/form-datatext/plain

簡單請求

簡單請求發送:

對於簡單請求,瀏覽器直接發出CORS請求。具體來講就是在頭信息之中,增長一個Origin字段。

GET /cors HTTP/1.1
Host: api.alice.com
Origin: http://api.bob.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

簡單請求響應:

服務器收到查詢請求之後,檢查了Origin字段之後,確認容許跨源請求就能夠作出迴應,返回的響應會多出幾個頭信息字段。若是服務器否認了查詢請求,會返回一個正常的HTTP迴應,可是沒有任何CORS相關的頭信息字段,或者明確表示請求不符合條件。瀏覽器發現這個迴應的頭信息沒有包含Access-Control-Allow-Origin字段就知道出錯了,從而拋出一個錯誤。該錯誤被XMLHttpRequestonerror回調函數捕獲。注意這種錯誤沒法經過狀態碼識別,由於HTTP迴應的狀態碼有多是200。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的頭信息之中,有三個與CORS請求相關的字段,都以Access-Control-開頭:

  • Access-Control-Allow-Origin:該字段是必須的。它的值要麼是請求時Origin字段的值,要麼是一個*來表示接受任意域名的請求。注意若是服務器要求瀏覽器發送Cookie,該字段就不能設爲星號,必須指定明確的、與請求網頁一致的域名。
  • Access-Control-Allow-Credentials:該字段可選。表示是否容許發送Cookie。默認狀況下,Cookie不包括在CORS請求之中。設爲true即表示服務器明確許可,瀏覽器能夠把Cookie包含在請求中,一塊兒發給服務器。設爲true同時要求開發者必須在AJAX請求中打開withCredentials屬性,不然即便服務器要求發送 Cookie,瀏覽器也不會發送。
  • Access-Control-Expose-Headers:該字段可選。CORS請求時XMLHttpRequest對象的getResponseHeader()方法只能拿到服務器返回頭的6個基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。若是想拿到其餘字段,就必須在Access-Control-Expose-Headers裏面指定。上面的例子指定getResponseHeader('FooBar')能夠返回FooBar字段的值。

非簡單請求

非簡單請求發送:

非簡單請求的CORS請求,會在正式通訊以前增長一次HTTP查詢請求。瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可使用哪些HTTP方法和頭信息字段。只有獲得確定答覆後瀏覽器纔會發出正式的XMLHttpRequest請求,不然就報錯。

這是爲了防止這些新增的請求,對傳統的沒有CORS支持的服務器造成壓力,給服務器一個提早拒絕的機會,這樣能夠防止服務器收到大量DELETEPUT請求,這些傳統的表單不可能跨域發出的請求。

4.2 XMLHttpRequest

XMLHttpRequest對象是AJAX的主要接口,用於瀏覽器與服務器之間的通訊。儘管名字裏面有XMLHttp,它實際上可使用多種協議(好比fileftp),發送任何格式的數據(包括字符串和二進制)。下面是XMLHttpRequest對象簡單用法的完整例子:

var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function(){
  // 通訊成功時,狀態值爲4
  if (xhr.readyState === 4){
    if (xhr.status === 200){
      console.log(xhr.responseText);
    } else {
      console.error(xhr.statusText);
    }
  }
};

xhr.onerror = function (e) {
  console.error(xhr.statusText);
};

xhr.open('GET', '/endpoint', true);
xhr.send(null);

對象屬性

  • XMLHttpRequest.withCredentials屬性是一個布爾值,表示跨域請求時用戶信息(好比Cookie和認證的HTTP頭信息)是否會包含在請求之中,默認爲false,即向example.com發出跨域請求時,不會發送example.com設置在本機上的Cookie(若是有的話)。若是須要跨域AJAX請求發送Cookie,須要withCredentials屬性設爲true。注意同源的請求不須要設置這個屬性。

  • XMLHttpRequest.upload屬性返回一個對象,表示上傳文件的相關事件。主要就是監聽這個對象的各類事件。

  • XMLHttpRequest.timeout屬性返回一個整數,表示多少毫秒後,若是請求仍然沒有獲得結果就會自動終止。若是該屬性等於0,就表示沒有時間限制。

  • XMLHttpRequest.readyState返回一個整數,表示實例對象的當前狀態。它可能返回如下值:

    • 0,表示XMLHttpRequest實例已經生成,可是實例的open()方法尚未被調用。
    • 1,表示open()方法已經調用,可是實例的send()方法尚未調用,仍然可使用實例的setRequestHeader()方法,設定HTTP請求的頭信息。
    • 2,表示實例的send()方法已經調用,而且服務器返回的頭信息和狀態碼已經收到。
    • 3,表示正在接收服務器傳來的數據體(body 部分)。這時若是實例的responseType屬性等於text或者空字符串,responseText屬性就會包含已經收到的部分信息。
    • 4,表示服務器返回的數據已經徹底接收,或者本次接收已經失敗。
  • XMLHttpRequest.status屬性返回一個整數,表示服務器迴應的HTTP狀態碼。

  • XMLHttpRequest.responseType屬性是一個字符串,表示服務器返回數據的類型。

  • XMLHttpRequest.response屬性表示服務器返回的數據體(即HTTP迴應的 body 部分),它多是任何數據類型(如字符串、對象、二進制對象等)。

  • XMLHttpRequest.responseURL屬性是字符串,表示發送數據的服務器的網址。

  • XMLHttpRequest.responseText屬性返回從服務器接收到的字符串。只有HTTP請求完成接收之後,該屬性纔會包含完整的數據。

對象事件

  • XMLHttpRequest.ontimeout:timeout事件的監聽函數
  • XMLHttpRequest.onloadstart:loadstart事件(請求發出)的監聽函數
  • XMLHttpRequest.onprogress:progress事件(正在發送和加載數據)的監聽函數
  • XMLHttpRequest.onloadend:loadend 事件(請求完成,無論成功或失敗)的監聽函數
  • XMLHttpRequest.onabort:abort 事件(請求停止,如用戶調用了abort()方法)的監聽函數
  • XMLHttpRequest.onerror:error 事件(請求失敗)的監聽函數
  • XMLHttpRequest.onload:load事件(請求成功完成)的監聽函數
  • XMLHttpRequest.onreadystatechange:readystatechange事件(實例的readyState屬性變化)的監聽函數

對象方法

  • XMLHttpRequest.open()方法用於指定HTTP請求的參數,或者說初始化XMLHttpRequest實例對象。注意若是對使用過open()方法的AJAX請求,再次使用這個方法,等同於調用abort(),即終止請求。
  • XMLHttpRequest.overrideMimeType()方法用來指定MIME類型,覆蓋服務器返回的MIME類型,從而讓瀏覽器進行不同的處理。好比服務器返回的數據類型是text/xml,因爲種種緣由瀏覽器解析不成功報錯,這時就拿不到數據。爲了拿到原始數據能夠把MIME類型改爲text/plain,這樣瀏覽器就不會去自動解析,從而可拿到原始文本。注意該方法必須在send()方法以前調用。
  • XMLHttpRequest.setRequestHeader()方法用於設置瀏覽器發送的HTTP請求的頭信息。若是該方法屢次調用且設定同一個字段,則每一次調用的值會被合併成一個單一的值發送。注意該方法必須在send()方法以前調用。
  • XMLHttpRequest.getResponseHeader()方法返回HTTP頭信息指定字段的值,若是尚未收到服務器迴應或者指定字段不存在,則返回null
  • XMLHttpRequest.send()方法用於實際發出HTTP請求。
  • XMLHttpRequest.abort()方法用來終止已經發出的HTTP請求。調用這個方法之後,readyState屬性變爲4,status屬性變爲0。

至於其餘的還有File對象、Blob對象、ArrayBuffer對象等,到時候隨用隨查吧。

相關文章
相關標籤/搜索