天天看點

Web前端知識體系精簡

Web前端技術由 html、css 和 javascript 三大部分構成,是一個龐大而複雜的技術體系,其複雜程度不低于任何一門後端語言。而我們在學習它的時候往往是先從某一個點切入,然後不斷地接觸和學習新的知識點,是以對于初學者很難理清楚整個體系的脈絡結構。本文将對Web前端知識體系進行簡單的梳理,對應的每個知識點點到為止,不作詳細介紹。目的是幫助大家審查自己的知識結構是否完善,如有遺漏或不正确的地方,希望共勉。

Web前端知識體系精簡

JAVASCRIPT 篇

0、基礎文法

Javascript 基礎文法包括:變量聲明、資料類型、函數、控制語句、内置對象等。

在ES5 中,變量聲明有兩種方式,分别是  var 和 function ,var 用于聲明普通的變量,接收任意類型,function用于聲明函數。另外,ES6 新增了 let、const、import 和 class 等四個指令,分别用以聲明 普通變量、靜态變量、子產品 和 類 。

JS資料類型共有六種,分别是 String、Number、Boolean、Null、Undefined 和 Object 等, 另外,ES6 新增了 Symbol 類型。其中,Object 是引用類型,其他的都是原始類型(Primitive Type)。

原始類型也稱為基本類型或簡單類型,因為其占據空間固定,是簡單的資料段,為了便于提升變量查詢速度,将其存儲在棧(stack)中(按值通路)。為了便于操作這類資料,ECMAScript 提供了 3 個基本包裝類型:Boolean、Number 和 String 。基本包裝類型是一種特殊的引用類型,每當讀取一個基本類型值的時候,JS内部就會建立一個對應的包裝對象,進而可以調用一些方法來操作這些資料。

引用類型由于其值的大小會改變,是以不能将其存放在棧中,否則會降低變量查詢速度,是以其存儲在堆(heap)中,存儲在變量處的值是一個指針,指向存儲對象的記憶體處(按址通路),對于引用類型的值,可以為其添加屬性和方法,也可以改變和删除其屬性和方法;但基本類型不可以添加屬性和方法。關于更多介紹請參考:詳解 ECMAScript 資料類型

JavaScript 可以通過 typeof 來判斷原始資料類型,但不能判斷引用類型,要知道引用類型的具體類型,需要通過 Object 原型上的 toString 方法來判斷,關于資料類型判斷可以參考:判斷JS資料類型的4種方法

在 JavaScript 中,函數有三種角色,即普通函數、構造函數 和 對象方法。同一個函數,調用方式不同,函數的作用不一樣,所扮演的角色也不一樣。直接調用時就是普通函數,通過new建立對象時就是構造函數,通過對象調用時就是方法。

JavaScript 常用的内置對象有 Date、Array、JSON、RegExp 等等,Date 和 Array 使用場景最多,JSON主要用于對象的序列化和反序列化,還有一個作用就是實作對象的深拷貝。RegExp 即正規表達式,是處理字元串的利器。 關于更多介紹請參考:數組常用操作方法總結 , 正規表達式基礎知識

1、函數原型鍊

JS是一種基于對象的語言,但在 ES6 之前是不支援繼承的,為了具備繼承的能力,JavaScript 在函數對象上建立了原型對象 prototype,并以函數對象為主線,從上至下,在JS内部建構了一條原型鍊。原型鍊把一個個獨立的對象聯系在一起,Object 則是所有對象的祖宗, 任何對象所建立的原型鍊最終都指向了 Object,并以 Object 終結。

簡單來說,就是建立了變量查找機制,當通路一個對象的屬性時,先查找對象本身是否存在,如果不存在就去該對象所在的原型連上去找,直到 Object 對象為止,如果都沒有找到該屬性才會傳回 undefined。是以,我們可以通過原型鍊來實作繼承機制。關于函數原型鍊請參考:認識原型對象和原型鍊

2、函數作用域

函數作用域就是變量在聲明它們的函數體以及這個函數體嵌套的任意函數體内都是有定義的。通俗來講就是,在一個函數裡,有些變量可以通路,有些不可以通路。那些能通路的變量所形成的範圍,就是這個函數的作用域。

在 JavaScript 中,沒有塊級作用域,隻有函數作用域,也就是說 if、while、for 語句不會形成獨立的作用域。但有一個特殊情況,即 with 語句和 catch 語句會形成臨時作用域,語句執行結束後,該作用域就會被釋放。關于函數作用域請參考:函數作用域和作用域鍊

3、this 指針

this 指針存在于函數中,用以辨別函數運作時所處的上下文。函數的類型不同,this 指向規則也不一樣:對于普通函數,this 始終指向全局對象window;對于構造函數,this則指向新建立的對象;對于方法,this指向調用該方法的對象。另外,Function 對象也提供了call、apply 和 bind 等方法來改變函數的 this 指向,其中,call 和 apply 主動執行函數,bind 一般在事件回調中使用,而 call 和 apply 的差別隻是參數的傳遞方式不同。關于更多介紹請參考:深入了解 call,apply和 bind

如果往深的去了解,無論什麼函數,this 是否被改變, 本質上,this 均指向觸發函數運作時的那個對象。而在函數運作時,this 的值是不能被改變的。

4、new 操作符

函數的建立有三種方式,即 顯式聲明、匿名定義 和 new Function() 。前面提到,JS 中的函數即可以是函數,也可以是方法,還可以是構造函數。

當使用 new 來建立對象時,該函數就是構造函數,JS 将新對象的原型鍊指向了構造函數的原型對象,于是就在新對象和函數對象之間建立了一條原型鍊,通過新對象可以通路到函數對象原型 prototype 中的方法和屬性。關于構造函數和 new 操作符請參考: 深入了解 new 操作符

5、閉包

閉包不是一個孤立的概念,需要從函數作用域的角度來了解。

每個函數都有自己的作用域,如果在一個函數裡定義了另一個函數,那麼對應的就有兩個作用域,這兩個作用域就會形成一個鍊條,俗稱作用域鍊。本質上講,作用域鍊是一個自上而下的連結清單, 連結清單的最頂端是内部函數作用域,連結清單的最底端是全局作用域。内部函數有權通路整個作用域鍊上的變量。正常情況下,每當一個函數執行完畢,對應的作用域就會從該連結清單上移除,然後銷毀。

但如果函數 A 把函數 B 作為傳回值傳回時,情況又不一樣。

首先,函數 A 傳回的是函數 B 的引用,也就是說,B 可能會在其他地方被調用。上面提到,函數 B 的定義是位于函數 A 内部,是以 A 和 B 會形成一條作用域鍊,函數 B 有可能會讀取 A 中的變量 。為了保證函數 B 能夠在其他地方正确執行,函數 B 所在的這條作用域鍊就不能被破壞。是以,即使函數 A 執行傳回後,A 的作用域也不能釋放,需要一直儲存在記憶體中,以確定函數 B 能夠正常讀取裡面的變量。函數 B 具有永久通路 A 作用域的特權,确切說,函數 B 就是閉包 。

總而言之,閉包就是一個有權通路另一個函數作用域的函數。

6、單線程與事件循環

JavaScript 是單線程語言。在浏覽器中,當JS代碼被加載時,浏覽器會為其配置設定一個主線程來執行任務,主線程會在棧中建立一個全局執行環境 (全局作用域)。每當有一個函數進入執行流時,就會形成一個對應的執行環境(函數作用域),并将該執行環境壓入棧中。每當一個函數執行完畢以後,對應的執行環境就會從棧中彈出,然後被銷毀。這就是執行環境棧,執行環境棧的作用就是保證所有的函數能按照正确的順序被執行。

但在浏覽器中,有一些任務是非常耗時的,比如 ajax請求、定時器、事件等。為了保證主線程上的任務不被阻塞,JavaScript 内部維護了一個任務隊列, 當這些耗時任務結束時(Ajax 請求傳回、定時器逾時、事件被觸發),就将對應的回調函數插入隊列中進行等待。這些任務的執行時機并不确定,隻有當所有同步任務執行完畢後,執行環境棧被清空(棧底的全局執行環境會一直存在,直到程序退出)以後,然後再從任務隊列中依次讀取回調函數,并将其壓入執行環境棧中。于是,主線程開始執行新的同步任務,執行完畢後再從棧中彈出,棧被清空。

主線程從任務隊列中讀取任務是不斷循環的,每當棧被清空後,主線程就會從任務隊列中讀取新的任務并執行,如果沒有新的任務,就會一直等待,直到有新的任務。JavaScript 的這種執行機制就叫做任務循環。因為每個任務都由一個事件所觸發,是以也叫 “事件循環”。

7、Ajax 和 跨域技術    

Ajax 是浏覽器專門用來和伺服器進行互動的異步通訊技術,其核心對象是 XMLHttpRequest,通過該對象可以建立一個 Ajax 請求。Ajax 請求是一個耗時的異步操作,當請求發出以後,Ajax 提供了兩個狀态位來描述請求在不同階段的狀态,這兩個狀态位分别是 readyState 和 status ,readyState 通過 5個狀态碼來描述一個請求的 5 個階段:

  • 0 - 請求未發送,初始化階段
  • 1 - 請求發送中,伺服器還未收到請求
  • 2 - 請求發送成功,伺服器已收到請求
  • 3 - 伺服器處理完成,開始響應請求,傳輸資料
  • 4 - 用戶端收到請求,并完成了資料下載下傳,生成了響應對象

status 用于描述服務端對請求處理的情況,200 表示正确響應了請求,404 表示伺服器找不到資源,500 代表伺服器内部異常等等。

Ajax 對象還可以設定一個 timeout 值,代表逾時時間。切記:timeout 隻會影響 readyState,而不會影響 status,因為逾時隻會中斷資料傳輸,但不會影響伺服器的處理結果。 如果 timeout 設定的不合理,就會導緻響應碼 status 是 200,但 response裡卻沒有資料,這種情況就是伺服器正确響應了請求,但資料的下載下傳被逾時中斷了。

為了保證使用者資訊的安全,浏覽器引入了同源政策,對腳本請求做了限制,不允許 Ajax 跨域請求伺服器 ,隻允許請求和目前位址同域的伺服器資源。但不限制 HTML 标簽發送跨域請求,比如 script、img、a 标簽等,是以可以利用标簽跨域能力來實作跨域請求,這就是 JSONP 能夠跨域的原理。

JSONP 雖然可以解決跨域問題,但隻能發送 GET 請求,并且沒有有效的錯誤捕獲機制 。為了解決這個問題,W3C 在 XMLHttpRequest Level2 中提出了 CORS 規範,即 “跨域資源共享”。它不是一個新的 API,而是一個标準規範 。當浏覽器發現該請求需要跨域時,就會自動在頭資訊中添加一個 Origin 字段,用以說明本次請求來自哪個源。伺服器根據這個值,決定是否同意這次請求。 關于 CORS 的詳細介紹請參考:跨域資源共享 CORS 詳解 

随着移動端的快速發展,Web 技術的應用場景正在變得越來越複雜,“關注點分離” 原則在系統設計層面就顯得越來越重要,而 XMLHttpRequest 是 Ajax 最古老的一個接口,因而不太符合現代化的系統設計理念。是以,浏覽器提供了一個新的 Ajax 接口,即 Fetch,Fetch 是基于 ES6 的 Promise 思想設計的,更符合關注點分離原則。關于 Fetch 的更多介紹請參考:傳統 Ajax 已死,Fetch 永生

8、子產品化

曆史上,JavaScript 規範一直沒有子產品(Module)體系,即無法将一個大程式拆分成互相依賴的小檔案,再用簡單的方法拼裝起來。在 ES6 之前,為了實作 JS 子產品化程式設計,社群制定了一些子產品加載方案,最主要有 CMD 和 AMD 兩種,分别以 commonjs 和 requirejs 為代表。ES6 在語言标準的層面上,實作了子產品化程式設計,其設計思想是,盡量靜态化,使得編譯時就能确定子產品的依賴關系,即編譯時加載,而 CMD 和 AMD 是在運作時确定依賴關系,即運作時加載。關于 ES6 子產品化請參考:ES6子產品化

9、Node.js

Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運作環境,它的運作不依賴于浏覽器作為宿主環境,而是和服務端程式一樣可以獨立的運作,這使得 JavaScript 程式設計第一次從用戶端被帶到了服務端,Node.js 在服務端的優勢是,它采用單線程和異步 I/O 模型,實作了一個高并發、高性能的運作時環境。相比傳統的多線程模型,Node.js 實作簡單,并且可以減少資源開銷。關于 Node.js單線程模型請參考:Node.js 事件循環機制

10、ES6

ES6 是 ECMAScript 6.0 的簡寫,即 JavaScript 語言的下一代标準,已經在 2015年6月正式釋出了,它的目标是讓JS能夠友善的開發企業級大型應用程式,是以,ES6的一些規範正在逐漸向Java、C# 等後端語言标準靠近。在 ES6 規範中,比較重大的變化有以下幾個方面:

  • 新增 let、const 指令 來聲明變量,和var 相比,let 聲明的變量不存在變量提升問題,但沒有改變JS弱類型的特點,依然可以接受任意類型變量的聲明;const 聲明的變量不允許在後續邏輯中改變,提高了JS文法的嚴謹性。
  • 新增解構指派、rest 文法、箭頭函數等,這些都是為了讓代碼看起來更簡潔,而包裝的文法糖。
  • 新增子產品化機制,這是 JavaScript 走向規範比較重要的一步,讓前端更友善的實作工程化。
  • 新增類和繼承的概念,配合子產品化,JavaScript 也可以實作高複用、高擴充的系統架構。
  • 新增模闆字元串功能,高效簡潔,結束拼接字元串的時代。
  • 新增 Promise 機制,解決異步回調多層嵌套的問題。

CSS 篇

1、CSS選擇器

CSS 選擇器即通過某種規則來比對相應的标簽,并為其設定 CSS 樣式,常用的有類選擇器、标簽選擇器、ID選擇器、後代選擇器、群組選擇器、僞類選擇器(before/after)、兄弟選擇器(+~)、屬性選擇器等等。

2、CSS Reset

HTML 标簽在不設定任何樣式的情況下,也會有一個預設的 CSS 樣式,而不同核心浏覽器對于這個預設值的設定則不盡相同,這樣可能會導緻同一套代碼在不同浏覽器上的顯示效果不一緻,而出現相容性問題。是以,在初始化時,需要對常用标簽的樣式進行初始化,使其預設樣式統一,這就是 CSS Reset ,即 CSS 樣式重置,比如:

*{ margin:0; padding:0; }       

就是最簡單 CSS Reset。 關于 CSS 重置請參考:Neat.css

3、盒子布局

盒子模型是CSS比較重要的一個概念,也是CSS 布局的基石。 常見的盒子模型有塊級盒子(block)和行内盒子(inline-block),與盒子相關的幾個屬性有:margin、border、padding和content 等,這些屬性的作用是設定盒子與盒子之間的關系以及盒子與内容之間的關系,而 box-sizing 屬性會影響盒子大小的計算方式。

需要注意的是:

隻有普通文檔流中塊級盒子的垂直外邊距才會發生合并,而具有 BFC 特性盒子的外邊距不會合并。

4、浮動布局

設定元素的 float 屬性值為 left 或 right,就能使該元素脫離普通文檔流,向左或向右浮動。一般在做宮格布局時會用到,如果子元素全部設定為浮動,則父元素是塌陷的,這時就需要清除浮動,清除浮動的方法也很多,常用的方法是在元素末尾加空元素設定 clear: both,更進階一點的就給父容器設定 before/after 來模拟一個空元素,還可以直接設定 overflow 屬性為 auto/hidden 來清除浮動。除浮動可以實作宮格布局,行内盒子(inline-block) 和 table 也可以實作同樣的效果。 

5、定位布局

設定元素的 position 屬性值為 relative/absolute/fixed,就可以使該元素脫離文檔流,并以某種參照坐标進行偏移。其中,releave 是相對定位,它以自己原來的位置進行偏移,偏移後,原來的空間不會被其他元素占用;absolute 是絕對定位,它以離自己最近的定位父容器作為參照進行偏移;為了對某個元素進行定位,常用的方式就是設定父容器的 poistion:relative,因為相對定位元素在不設定 top 和 left 值時,不會對元素位置産生影響;fixed 即固定定位,它則以浏覽器視窗為參照物,PC網頁底部懸停的banner一般都可以通過fixed定位來實作,但fixed屬性在移動端有相容性問題,是以不推薦使用,可替代的方案是:絕對定位+内部滾動。

6、彈性布局

彈性布局即 Flex 布局,定義了 flex 的容器一個可伸縮容器,首先容器本身會根據容器中的元素動态設定自身大小;然後當Flex容器被應用一個大小時(width和height),将會自動調整容器中的元素适應新大小。Flex容器也可以設定伸縮比例和固定寬度,還可以設定容器中元素的排列方向(橫向和縱向)和是否支援元素的自動換行。有了這個神器,做頁面布局的可以友善很多了。注意,設為Flex 布局以後,子元素的 float、clear、inline-block 和 vertical-align 屬性将失效。關于 flexbox 請參考:圖解CSS3 Flexbox屬性

7、CSS3 動畫

CSS3 中規範引入了兩種動畫,分别是 transition 和 animation,transition 可以讓元素的 CSS 屬性值的變化在一段時間内平滑的過渡,形成動畫效果,為了使元素的變換更加豐富多彩,CSS3 還引入了 transfrom 屬性,它可以通過對元素進行 平移(translate)、旋轉(rotate)、放大縮小(scale)、傾斜(skew) 等操作,來實作 2D 和 3D 變換效果。transiton 還有一個結束事件 transitionEnd,該事件是在 CSS 完成過渡後觸發,但如果過渡在完成之前被移除,則不會觸發 transitionEnd 。

animation 需要設定一個 @keyframes,來定義元素以哪種形式進行變換, 然後再通過動畫函數讓這種變換平滑的進行,進而達到動畫效果,動畫可以被設定為永久循環示範。設定 animation-play-state:paused 可以暫停動畫,設定 animation-fill-mode:forwards 可以讓動畫完成後定格在最後一幀。

另外,還可以通過JS 監聽 animation 的“開始”、“結束” 和 “重複播放” 狀态,分别對應三個事件,即 animationStart、animationEnd、animationIteration 。需要注意的是:

當播放次數設定為1時,不會觸發 animationIteration 。

和 transition相比,animation 設定動畫效果更靈活更豐富,二者還有一個差別是:transition 隻能通過主動改變元素的 css 值才能觸發動畫效果,而 animation 一旦被應用,就開始執行動畫。

另外,HTML5 還新增了一個動畫API,即 requestAnimationFrame,它通過JS來調用,并按照螢幕的繪制頻率來改變元素的CSS屬性,進而達到動畫效果,關于這個API的介紹請參考:requestAnimationFrame 知多少?

8、BFC

BFC 是頁面上的一個隔離的獨立容器,容器裡面的子元素不會影響到外面元素。比如:内部滾動就是一個 BFC,當一個父容器的 overflow-y 設定為 auto 時,并且子容器的長度大于父容器時,就會出現内部滾動,無論内部的元素怎麼滾動,都不會影響父容器以外的布局,這個父容器的渲染區域就叫 BFC。滿足下列條件之一就可觸發 BFC:

  • 根元素,即 HTML 元素
  • float 的值不為 none
  • overflow 的值不為 visible
  • display 的值為 inline-block、table-cell、table-caption
  • position 的值為 absolute 或 fixed

9、Sprite,Iconfont,font-face

對于大型站點,為了減少 http 請求的次數,一般會将常用的小圖示排到一個大圖中,頁面加載時隻需請求一次網絡, 然後在 css 中通過設定 background-position 來控制顯示所需要的小圖示,這就是 Sprite 圖。

Iconfont,即字型圖示,就是将常用的圖示轉化為字型資源存在檔案中,通過在 CSS 中引用該字型檔案,然後可以直接用控制字型的css屬性來設定圖示的樣式,字型圖示的好處是節省網絡請求、其大小不受螢幕分辨率的影響,并且可以任意修改圖示的顔色。

font-face 是 CSS3 中的一個子產品,通過 font-face 可以定義一種全新的字型,然後就可以通過 css 屬性 font-family 來使用這個字型了,即使作業系統沒有安裝這種字型,網頁上也會正常顯示出來。

10、CSS HACK

早期,不同核心浏覽器對CSS屬性的解析存在着差異,導緻顯示效果不一緻,比如 margin 屬性在 ie6 中顯示的距離會比其他浏覽器中顯示的距離寬 2 倍,也就是說 margin-left:20px; 在ie6中距左側元素的實際顯示距離是 40px,而在非 ie6 的浏覽器上顯示正常。是以,如果要想讓所有浏覽器中都顯示是 20px 的寬度,就需要在CSS樣式中加入一些特殊的符号,讓不同的浏覽器識别不同的符号,以達到應用不同的CSS樣式的目的,這種方式就是 “css hack”, 對于 ie6 中的 margin 應用 hack 就會變成這樣:

.el {
  margin-left: 20px;  // 其他
  _margin-left: 10px; // ie6
}      

相容各大浏覽器的 css hack 如下:

.element {
    color: #000;      // W3c标準
    [;color:#f00;];   // Webkit(Chrome/Safari) 
    color: #666\9;    // IE8
    *color: #999;     // IE7
    _color: #333;     // IE6  
}
:root .element {color: #0f0\9;}  // IE9
// Opera
@media all and (-webkit-min-device-pixel-ratio:10000), 
not all and (-webkit-min-device-pixel-ratio:0) {
    .element {color:#336699;}
}
// Firefox
@-moz-document url-prefix() {
    .element {color: #f1f1f1}
}        

HTML 篇

1、BOM 

BOM 是 Browser Object Model 的縮寫,即浏覽器對象模型,當一個浏覽器頁面初始化時,會在記憶體建立一個全局的對象,用以描述目前視窗的屬性和狀态,這個全局對象被稱為浏覽器對象模型,即BOM。BOM的核心對象就是 window,window 對象也是BOM的頂級對象,其包含了浏覽器的六個核心子產品:

  • document - 即文檔對象,渲染引擎在解析HTML代碼時,會為每一個元素生成對應的DOM對象,由于元素之間有層級關系,是以整個HTML代碼解析完以後,會生成一個由不同節點組成的樹形結構,俗稱DOM樹,document 用于描述DOM樹的狀态和屬性,并提供了很多操作DOM的API。
  • frames - HTML 子架構,即在浏覽器裡嵌入另一個視窗,父架構和子架構擁有獨立的作用域和上下文。
  • history - 以棧(FIFO)的形式儲存着頁面被通路的曆史記錄,頁面前進即入棧,頁面傳回即出棧。
  • location - 提供了目前視窗中加載的文檔相關資訊以及一些導航功能。
  • navigator - 用來描述浏覽器本身,包括浏覽器的名稱、版本、語言、系統平台、使用者特性字元串等資訊。
  • screen - 提供了浏覽器顯示螢幕的相關屬性,比如顯示螢幕的寬度和高度,可用寬度和高度。

2、DOM 系統

DOM 是 Document Object Model 的縮寫,即 文檔對象模型,是所有浏覽器公共遵守的标準,DOM 将HTML和XML文檔映射成一個由不同節點組成的樹型結構,俗稱DOM樹。其核心對象是document,用于描述DOM樹的狀态和屬性,并提供對應的DOM操作API。随着曆史的發展,DOM 被劃分為1級、2級、3級,共3個級别:

  • 1級DOM - 在1998年10月份成為W3C的提議,由 DOM 核心與 DOM HTML 兩個子產品組成。DOM核心能映射以XML為基礎的文檔結構,允許擷取和操作文檔的任意部分。DOM HTML通過添加HTML專用的對象與函數對DOM核心進行了擴充。
  • 2級DOM - 鑒于1級DOM僅以映射文檔結構為目标,DOM 2級面向更為寬廣。通過對原有DOM的擴充,2級DOM通過對象接口增加了對滑鼠和使用者界面事件(DHTML長期支援滑鼠與使用者界面事件)、範圍、周遊(重複執行DOM文檔)和層疊樣式表(CSS)的支援。同時也對DOM 1的核心進行了擴充,進而可支援XML命名空間。
  • 3級DOM - 通過引入統一方式載入和儲存文檔和文檔驗證方法對DOM進行進一步擴充,DOM3包含一個名為“DOM載入與儲存”的新子產品,DOM核心擴充後可支援XML1.0的所有内容,包括XML Infoset、 XPath、和XML Base。

浏覽器對不同級别DOM的支援情況如下所示:

Web前端知識體系精簡

從圖中可以看出,移動端常用的 webkit 核心浏覽器目前隻支援 DOM2,而不支援 DOM3 。

3、事件系統

事件是使用者與頁面互動的基礎,到目前為止,DOM事件從PC端的 滑鼠事件(mouse) 發展到了 移動端的 觸摸事件(touch) 和 手勢事件(guesture),touch事件描述了手指在螢幕操作的每一個細節,guesture 則是描述多手指操作時更為複雜的情況,總結如下:

  • 第一根手指放下,觸發 touchstart,除此之外什麼都不會發生
  • 手指滑動時,觸發touchmove
  • 第二根手指放下,觸發 gesturestart 
  • 觸發第二根手指的 touchstart 
  • 立即觸發 gesturechange 
  • 任意手指移動,持續觸發 gesturechange
  • 第二根手指彈起時,觸發 gestureend,以後将不會再觸發 gesturechange 
  • 觸發第二根手指的 touchend 
  • 觸發touchstart (多根手指在螢幕上,提起一根,會重新整理一次全局touch)  
  • 彈起第一根手指,觸發 touchend 

更多關于手勢事件的介紹請參考:gesture事件處理複雜手勢

DOM2.0 模型将事件處理流程分為三個階段,即 事件捕獲階段、事件處理階段、事件冒泡階段,如圖所示:

Web前端知識體系精簡
  • 事件捕獲:當使用者觸發點選事件後,頂層對象 document 就會發出一個事件流,從最外層的 DOM 節點向目标元素節點傳遞,最終到達目标元素。
  • 事件處理:當到達目标元素之後,執行目标元素綁定的處理函數。如果沒有綁定監聽函數,則不做任何處理。
  • 事件冒泡:事件流從目标元素開始,向最外層DOM節點傳遞,途中如果有節點綁定了事件處理函數,這些函數就會被執行。

利用事件冒泡原理可以實作 “事件委托”。

所謂事件委托,就是在父元素上添加事件監聽器,用以監聽和處理子元素的事件,避免重複為子元素綁定相同的事件。當目标元素的事件被觸發以後,這個事件就從目标元素開始,向最外層元素傳遞,最終冒泡到父元素上,父元素再通過 event.target 擷取到這個目标元素,這樣做的好處是,父元素隻需綁定一個事件監聽,就可以對所有子元素的事件進行處理了,進而減少了不必要的事件綁定,對頁面性能有一定的提升。

4、HTML 渲染流程

渲染引擎一開始會從網絡層擷取請求文檔的内容,内容的大小一般限制在 8000 個塊以内。

然後進行如下所示的基本流程: 

Web前端知識體系精簡
  • HTML Parser 解析 HTML 文檔,并将各标記逐個轉化為 DOM 節點,生成 “DOM樹”。
  • CSS Parser 解析外部 CSS 檔案以及樣式元素中的樣式資料,生成 “CSSOM樹”。
  • “DOM樹” 和 “CSSOM樹” 通過 “附着” 将建立另一個樹結構:“渲染樹”。
  • 渲染樹包含多個帶有視覺屬性(如顔色和尺寸)的矩形,這些矩形的排列順序就是它們将在螢幕上顯示的順序。
  • 渲染樹建構完畢之後,進入“布局” 處理階段,也就是為每個節點配置設定一個應出現在螢幕上的确切坐标。
  • 下一個階段是 “繪制”,渲染引擎會周遊渲染樹,由使用者界面後端層将每個節點繪制出來。
這是一個漸進的過程。為達到更好的使用者體驗,呈現引擎會力求盡快将内容顯示在螢幕上。它不必等到整個 HTML 文檔解析完畢之後,就會開始建構呈現樹和設定布局。在不斷接收和處理來自網絡的其餘内容的同時,呈現引擎會将部分内容解析并顯示出來。

5、重繪與回流

當渲染樹中的一部分(或全部)因為元素的規模尺寸、布局、隐藏等改變而需要重新建構,就稱為 “回流”。

當渲染樹中的一些元素需要更新屬性,而這些屬性隻是影響元素的外觀,風格,而不會影響布局,就稱為 “重繪”。

回流必将引起重繪,而重繪不一定會引起回流。

引起重繪和回流的操作如下:

  • 添加、删除元素(回流+重繪)
  • 隐藏元素,display: none(回流+重繪),visibility:hidden(隻重繪,不回流)
  • 移動元素,比如改變 top、left 的值,或者移動元素到另外一個父元素中。(重繪+回流)
  • 對 style 的操作(對不同的屬性操作,影響不一樣)
  • 還有一種是使用者的操作,比如改變浏覽器大小,改變浏覽器的字型大小等(回流+重繪)

注意問題:

transform 操作不會引起重繪和回流,是一種高效率的渲染。這是因為transform屬于合成屬性,對合成屬性進行transition/animation 動畫時将會建立一個合成層,這使得動畫元素在一個獨立的層中進行渲染,當元素的内容沒有發生改變,就沒必要進行重繪,浏覽器會通過重新複合來建立動畫幀。

6、本地存儲

本地存儲最原始的方式就是 cookie,cookie 是存放在本地浏覽器的一段文本,資料以鍵值對的形式儲存,可以設定過期時間。 但是 cookie 不适合大量資料的存儲,因為每請求一次頁面,cookie 都會發送給伺服器,這使得 cookie 速度很慢而且效率也不高。是以cookie的大小被限制為4k左右(不同浏覽器可能不同,分HOST),如下所示:

  • Firefox 和 Safari 允許 cookie 多達 4097 個位元組,包括名(name)、值(value) 和 等号。
  • Opera 允許 cookie 多達 4096 個位元組,包括:名(name)、值(value) 和 等号。
  • Internet Explorer 允許 cookie 多達4095個位元組,包括:名(name)、值(value) 和 等号。

在所有浏覽器中,任何 cookie 大小超過限制都被忽略,且永遠不會被設定。

html5 提供了兩種在用戶端存儲資料的新方法:localStorage 和 sessionStorage, 它們都是以 key/value 的形式來存儲資料,前者是永久存儲,後者的存儲期限僅限于浏覽器會話(session),即當浏覽器視窗關閉後,sessionStorage中的資料被清除。

localStorage 的存儲空間大約5M左右(不同浏覽器可能不同,分 HOST),這個相當于一個5M大小的前端資料庫,相比于cookie,可以節約帶寬,但localStorage在浏覽器隐私模式下是不可讀取的,當存儲資料超過了localStorage 的存儲空間後會抛出異常。

此外,H5還提供了 websql 和 indexedDB,允許前端以關系型資料庫的方式來存儲本地資料,相對來說,這個功能目前應用的場景比較少,此處不作介紹。

7、浏覽器緩存機制

浏覽器緩存機制是指通過 HTTP 協定頭裡的 Cache-Control (或 Expires) 和 Last-Modified (或 Etag) 等字段來控制檔案緩存的機制。

Cache-Control 用于控制檔案在本地緩存有效時長。最常見的,比如伺服器回包:Cache-Control:max-age=600 表示檔案在本地應該緩存,且有效時長是600秒 (從送出請求算起)。在接下來600秒内,如果有請求這個資源,浏覽器不會發出 HTTP 請求,而是直接使用本地緩存的檔案。

Last-Modified 是辨別檔案在伺服器上的最新更新時間。下次請求時,如果檔案緩存過期,浏覽器通過 If-Modified-Since 字段帶上這個時間,發送給伺服器,由伺服器比較時間戳來判斷檔案是否有修改。如果沒有修改,伺服器傳回304告訴浏覽器繼續使用緩存;如果有修改,則傳回200,同時傳回最新的檔案。

Cache-Control 通常與 Last-Modified 一起使用。一個用于控制緩存有效時間,一個在緩存失效後,向服務查詢是否有更新。

Cache-Control 還有一個同功能的字段:Expires。Expires 的值一個絕對的時間點,如:Expires: Thu, 10 Nov 2015 08:45:11 GMT,表示在這個時間點之前,緩存都是有效的。

Expires 是 HTTP1.0 标準中的字段,Cache-Control 是 HTTP1.1 标準中新加的字段,功能一樣,都是控制緩存的有效時間。當這兩個字段同時出現時,Cache-Control 是高優化級的。

Etag 也是和 Last-Modified 一樣,對檔案進行辨別的字段。不同的是,Etag 的取值是一個對檔案進行辨別的特征字串。在向伺服器查詢檔案是否有更新時,浏覽器通過 If-None-Match 字段把特征字串發送給伺服器,由伺服器和檔案最新特征字串進行比對,來判斷檔案是否有更新。沒有更新回包304,有更新回包200。Etag 和 Last-Modified 可根據需求使用一個或兩個同時使用。兩個同時使用時,隻要滿足基中一個條件,就認為檔案沒有更新。

浏覽緩存的基本架構如下圖所示:

Web前端知識體系精簡

關于更多浏覽器緩存介紹請參考:H5 緩存機制淺析 移動端 Web 加載性能優化

8、History

使用者通路網頁的曆史記錄通常會被儲存在一個類似于棧的對象中,即 history 對象,點選傳回就出棧,跳下一頁就入棧。 它提供了以下方法來操作頁面的前進和後退:

  • window.history.back( )  傳回到上一個頁面
  • window.history.forward( )  進入到下一個頁面
  • window.history.go( [delta] )  跳轉到指定頁面

HTML5 對History Api 進行了增強,新增了兩個Api 和一個事件,分别是 pushState、replaceState 和 onpopstate:

  • pushState 是往 history 對象裡添加一個新的曆史記錄。
  • replaceState 是替換 history 對象中的目前曆史記錄。
  • onpopstate 當點選浏覽器後退按鈕或JS調用 history.back 都會觸發該事件。

onpopstate 和 onhashchange 的差別:

onhashchange 本來是用來監聽hash變化的,但可以被利用來做用戶端前進和後退事件的監聽,而 onpopstate 是專門用來監聽浏覽器前進後退的,不僅可以支援 hash,非 hash 的同源 url 也支援。

9、HTML5 離線緩存

HTML5離線緩存又叫Application Cache,是從浏覽器的緩存中分出來的一塊緩存區,如果要在這個緩存中儲存資料,可以使用一個描述檔案(manifest file),列出要下載下傳和緩存的資源。

manifest 檔案是簡單的文本檔案,它告知浏覽器被緩存的内容(以及不緩存的内容)。manifest 檔案可分為三個部分:

  • CACHE MANIFEST - 在此标題下列出的檔案将在首次下載下傳後進行緩存
  • NETWORK - 在此标題下列出的檔案需要與伺服器的連接配接,且不會被緩存
  • FALLBACK - 在此标題下列出的檔案規定當頁面無法通路時的回退頁面(比如 404 頁面)

離線緩存為應用帶來三個優勢:

  • 離線浏覽 - 使用者可在應用離線時使用它們
  • 速度 - 已緩存資源加載得更快
  • 減少伺服器負載 - 浏覽器将隻從伺服器下載下傳更新過或更改過的資源。

10、Web語義化與SEO

Web語義化是指使用語義恰當的标簽,使頁面有良好的結構,頁面元素有含義,能夠讓人和搜尋引擎都容易了解。

SEO是指在了解搜尋引擎自然排名機制的基礎之上,對網站進行内部及外部的調整優化,改進網站在搜尋引擎中關鍵詞的自然排名,獲得更多的展現量,吸引更多目标客戶點選通路網站,進而達到網際網路營銷及品牌建設的目标。

搜尋引擎通過爬蟲技術擷取的頁面就是由一堆 html 标簽組成的代碼,人可以通過可視化的方式來判斷頁面上哪些内容是重點,而機器做不到。 但搜尋引擎會根據标簽的含義來判斷内容的權重,是以,在合适的位置使用恰當的标簽,使整個頁面的語義明确,結構清晰,搜尋引擎才能正确識别頁面中的重要内容,并予以較高的權值。比如h1~h6這幾個标簽在SEO中的權值非常高,用它們作頁面的标題就是一個簡單的SEO優化。

原創釋出  @一像素  2017.06  

更新:2017.07.25

更新:2018.06.06

更新:2018.07.17 

繼續閱讀