什麼是語義化?就是用合理、正確的標籤來展現內容,好比h1~h6定義標題。javascript
http://www.daqianduan.com/6549.htmlcss
<link>
標籤放在<head></head>
之間?爲何最好把 JS 的<script>
標籤剛好放在</body>
以前,有例外狀況嗎?把<link>
放在<head>
中html
把<link>
標籤放在<head></head>
之間是規範要求的內容。此外,這種作法可讓頁面逐步呈現,提升了用戶體驗。將樣式表放在文檔底部附近,會使許多瀏覽器(包括 Internet Explorer)不能逐步呈現頁面。一些瀏覽器會阻止渲染,以免在頁面樣式發生變化時,從新繪製頁面中的元素。這種作法能夠防止呈現給用戶空白的頁面或沒有樣式的內容。前端
把<script>
標籤剛好放在</body>
以前vue
腳本在下載和執行期間會阻止 HTML 解析。把<script>
標籤放在底部,保證 HTML 首先完成解析,將頁面儘早呈現給用戶。html5
例外狀況是當你的腳本里包含document.write()
時。可是如今,document.write()
不推薦使用。同時,將<script>
標籤放在底部,意味着瀏覽器不能開始下載腳本,直到整個文檔(document)被解析。也許,對此比較好的作法是,<script>
使用defer
屬性,放在<head>
中。java
漸進式渲染是用於提升網頁性能(尤爲是提升用戶感知的加載速度),以儘快呈現頁面的技術。node
在之前互聯網帶寬較小的時期,這種技術更爲廣泛。現在,移動終端的盛行,而移動網絡每每不穩定,漸進式渲染在現代前端開發中仍然有用武之地。jquery
一些舉例:nginx
DOMContentLoaded
/load
事件加載其餘資源和內容。Viewport :字面意思爲視圖窗口,在移動web開發中使用。表示將設備瀏覽器寬度虛擬成一個特定的值(或計算得出),這樣利於移動web站點跨設備顯示效果基本一致。移動版的 Safari 瀏覽器最新引進了 viewport 這個 meta tag,讓網頁開發者來控制 viewport 的大小和縮放,其餘手機瀏覽器也基本支持。
在移動端瀏覽器當中,存在着兩種視口,一種是可見視口(也就是咱們說的設備大小),另外一種是視窗視口(網頁的寬度是多少)。
舉個例子:若是咱們的屏幕是320像素 * 480像素的大小(iPhone4),假設在瀏覽器中,320像素的屏幕寬度可以展現980像素寬度的內容。那麼320像素的寬度就是可見視口的寬度,而可以顯示的980像素的寬度就是視窗視口的寬度。
爲了顯示更多的內容,大多數的瀏覽器會把本身的視窗視口擴大,簡易的理解,就是讓本來320像素的屏幕寬度可以容下980像素甚至更寬的內容(將網頁等比例縮小)。
當涉及到DOM節點的佈局屬性發生變化時,就會從新計算該屬性,瀏覽器會從新描繪相應的元素,此過程叫Reflow(迴流或重排)。
當影響DOM元素可見性的屬性發生變化 (如 color) 時, 瀏覽器會從新描繪相應的元素, 此過程稱爲Repaint(重繪)。所以重排必然會引發重繪。
https://harttle.land/2015/08/11/reflow-repaint.html
http://www.blueidea.com/tech/web/2011/8365.asp
https://blog.csdn.net/lhjuejiang/article/details/80795081
https://baijiahao.baidu.com/s?id=1593097105869520145&wfr=spider&for=pc
不一樣產品,不一樣標準,不一樣實現方式
2.作到什麼程度
3..如何作
條件註釋、CSS Hack、js 能力檢測作一些修補
優雅降級 (graceful degradation): 一開始就構建完整的功能,而後再針對低版本瀏覽器進行兼容。
https://github.com/jirengu/frontend-interview/issues/35
doctype是一種標準通用標記語言的文檔類型聲明,目的是告訴標準通用標記語言解析器要使用什麼樣的文檔類型定義(DTD)來解析文檔。
<!DOCTYPE>聲明是用來指示web瀏覽器關於頁面使用哪一個HTML版本進行編寫的指令。
<!DOCTYPE>聲明必須是HTML文檔的第一行,位於html標籤以前。
瀏覽器自己分爲兩種模式,一種是標準模式,一種是怪異模式,瀏覽器經過doctype來區分這兩種模式,doctype在html中的做用就是觸發瀏覽器的標準模式,若是html中省略了doctype,瀏覽器就會進入到Quirks模式的怪異狀態,在這種模式下,有些樣式會和標準模式存在差別,而html標準和dom標準值規定了標準模式下的行爲,沒有對怪異模式作出規定,所以不一樣瀏覽器在怪異模式下的處理也是不一樣的,因此必定要在html開頭使用doctype。
一個行內元素只佔據它對應標籤的邊框所包含的空間
通常狀況下,行內元素只能包含數據和其餘行內元素
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
若是遇到加載緩慢的第三方內容如圖標和廣告,這些問題能夠由iframe來解決。
iframe頁面會增長服務器的http請求
label
標籤一般是寫在表單內,它關聯一個控件,使用 label
能夠實現點擊文字選取對應的控件。
<input type="checkbox" id="test"> <label for="test" >test</label>
將不想要自動完成的 form
或 input
設置爲 autocomplete=off
Document Object Model,文檔對象模型
DOM 是爲了操做文檔出現的 API,document 是其的一個對象
DOM和文檔有關,這裏的文檔指的是網頁,也就是html文檔。DOM和瀏覽器無關,他關注的是網頁自己的內容。
Browser Object Model,瀏覽器對象模型
BOM 是爲了操做瀏覽器出現的 API,window 是其的一個對象
window 對象既爲 javascript 訪問瀏覽器提供API,同時在 ECMAScript 中充當 Global 對象
瀏覽器經過優先級規則,判斷元素展現哪些樣式。優先級經過 4 個維度指標肯定,咱們假定以a、b、c、d
命名,分別表明如下含義:
a
表示是否使用內聯樣式(inline style)。若是使用,a
爲 1,不然爲 0。b
表示 ID 選擇器的數量。c
表示類選擇器、屬性選擇器和僞類選擇器數量之和。d
表示標籤(類型)選擇器和僞元素選擇器之和。優先級的結果並不是經過以上四個值生成一個得分,而是每一個值分開比較。a、b、c、d
權重從左到右,依次減少。判斷優先級時,從左到右,一一比較,直到比較出最大值,便可中止。因此,若是b
的值不一樣,那麼c
和d
無論多大,都不會對結果產生影響。好比0,1,0,0
的優先級高於0,0,10,10
。
當出現優先級相等的狀況時,最晚出現的樣式規則會被採納。若是你在樣式表裏寫了相同的規則(不管是在該文件內部仍是其它樣式文件中),那麼最後出現的(在文件底部的)樣式優先級更高,所以會被採納。
在寫樣式時,我會使用較低的優先級,這樣這些樣式能夠輕易地覆蓋掉。尤爲對寫 UI 組件的時候更爲重要,這樣使用者就不須要經過很是複雜的優先級規則或使用!important
的方式,去覆蓋組件的樣式了。
margin
、padding
、font-size
這些樣式所有置成同樣。你將必須從新定義各類元素的樣式。當須要實現很是個性化的網頁設計時,我會選擇重置的方式,由於我要寫不少自定義的樣式以知足設計需求,這時候就再也不須要標準化的默認樣式了。
Float
定位的工做原理。浮動(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),而且父元素會擴張本身,使其可以包圍它的子元素。
z-index
屬性,並說明如何造成層疊上下文(stacking 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
類已經提到。overflow: auto
或overflow: hidden
方法:上文已經提到。在大型項目中,我會使用 Clearfix 方法,在須要的地方使用.clearfix
。設置overflow: hidden
的方法可能使其子元素顯示不完整,當子元素的高度大於父元素時。
雪碧圖是把多張圖片整合到一張上的圖片。它被運用在衆多使用了不少小圖標的網站上(Gmail 在使用)。實現方法:
background-image
、background-position
和background-size
屬性。好處:
:hover
僞類中的圖片,不會出現閃爍。autoprefixer
自動生成 CSS 屬性前綴。autoprefixer
自動生成 CSS 屬性前綴。這些方法與可訪問性(a11y)有關。
visibility: hidden
:元素仍然在頁面流中,並佔用空間。width: 0; height: 0
:使元素不佔用屏幕上的任何空間,致使不顯示它。position: absolute; left: -99999px
: 將它置於屏幕以外。text-indent: -9999px
:這隻適用於block
元素中的文本。即便 WAI-ARIA 是理想的解決方案,我也會採用絕對定位方法,由於它具備最少的注意事項,適用於大多數元素,並且使用起來很是簡單。
screen
,你還能說出一個 @media 屬性的例子嗎?首先,瀏覽器從最右邊的選擇器,即關鍵選擇器(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)。在寫樣式時,避免觸發從新佈局的可能。
優勢:
缺點:
喜歡:
Dislikes:
node-sass
使用 Sass,它用 C ++ 編寫的 LibSass 綁定。在 Node 版本切換時,我必須常常從新編譯。@
做爲前綴,容易與 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 樣式繪製三角形是不太可能的。CSS 盒模型描述了以文檔樹中的元素而生成的矩形框,並根據排版模式進行佈局。每一個盒子都有一個內容區域(例如文本,圖像等)以及周圍可選的padding
、border
和margin
區域。
CSS 盒模型負責計算:
盒模型有如下規則:
width
、height
、padding
、border
和margin
決定。height
,則塊級元素的高度等於其包含子元素的內容高度加上padding
(除非有浮動元素,請參閱下文)。width
,則非浮動塊級元素的寬度等於其父元素的寬度減去父元素的padding
。height
是由內容的height
來計算的。width
是由內容的width
來計算的。padding
和border
不是元素width
和height
的組成部分。* { box-sizing: border-box; }
會產生怎樣的效果?box-sizing: content-box
,元素的寬高只會決定內容(content)的大小。box-sizing: border-box
改變計算元素width
和height
的方式,border
和padding
的大小也將計算在內。height
= 內容(content)的高度 + 垂直方向的padding
+ 垂直方向border
的寬度width
= 內容(content)的寬度 + 水平方向的padding
+ 水平方向border
的寬度display
的屬性值都有哪些?none
, block
, inline
, inline-block
, table
, table-row
, table-cell
, list-item
.inline
和inline-block
有什麼區別?我把block
也加入其中,爲了得到更好的比較。
block |
inline-block |
inline |
|
---|---|---|---|
大小 | 填充其父容器的寬度。 | 取決於內容。 | 取決於內容。 |
定位 | 重新的一行開始,而且不容許旁邊有 HTML 元素(除非是float ) |
與其餘內容一塊兒流動,並容許旁邊有其餘元素。 | 與其餘內容一塊兒流動,並容許旁邊有其餘元素。 |
可否設置width 和height |
能 | 能 | 不能。 設置會被忽略。 |
能夠使用vertical-align 對齊 |
不能夠 | 能夠 | 能夠 |
邊距(margin)和填充(padding) | 各個方向都存在 | 各個方向都存在 | 只有水平方向存在。垂直方向會被忽略。 儘管border 和padding 在content 周圍,但垂直方向上的空間取決於'line-height' |
浮動(float) | - | - | 就像一個block 元素,能夠設置垂直邊距和填充。 |
relative
、fixed
、absolute
和static
四種定位有什麼區別?通過定位的元素,其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
相同。Flex 主要用於一維佈局,而 Grid 則用於二維佈局。
flex容器中存在兩條軸, 橫軸和縱軸, 容器中的每一個單元稱爲flex item。
在容器上能夠設置6個屬性:
注意:當設置 flex 佈局以後,子元素的 float、clear、vertical-align 的屬性將會失效。
有六種屬性可運用在 item 項目上:
CSS網格佈局用於將頁面分割成數個主要區域,或者用來定義組件內部元素間大小、位置和圖層之間的關係。
像表格同樣,網格佈局讓咱們可以按行或列來對齊元素。 可是,使用CSS網格可能仍是比CSS表格更容易佈局。 例如,網格容器的子元素能夠本身定位,以便它們像CSS定位的元素同樣,真正的有重疊和層次。
響應式設計和自適應設計都以提升不一樣設備間的用戶體驗爲目標,根據視窗大小、分辨率、使用環境和控制方式等參數進行優化調整。
響應式設計的適應性原則:網站應該憑藉一份代碼,在各類設備上都有良好的顯示和使用效果。響應式網站經過使用媒體查詢,自適應柵格和響應式圖片,基於多種因素進行變化,創造出優良的用戶體驗。就像一個球經過膨脹和收縮,來適應不一樣大小的籃圈。
自適應設計更像是漸進式加強的現代解釋。與響應式設計單一地去適配不一樣,自適應設計經過檢測設備和其餘特徵,從早已定義好的一系列視窗大小和其餘特性中,選出最恰當的功能和佈局。與使用一個球去穿過各類的籃筐不一樣,自適應設計容許使用多個球,而後根據不一樣的籃筐大小,去選擇最合適的一個。
我傾向於使用更高分辨率的圖形(顯示尺寸的兩倍)來處理視網膜顯示。更好的方法是使用媒體查詢,像@media only screen and (min-device-pixel-ratio: 2) { ... }
,而後改變background-image
。
對於圖標類的圖形,我會盡量使用 svg 和圖標字體,由於它們在任何分辨率下,都能被渲染得十分清晰。
還有一種方法是,在檢查了window.devicePixelRatio
的值後,利用 JavaScript 將<img>
的src
屬性修改,用更高分辨率的版本進行替換。
translate()
而不用絕對定位?何時,狀況相反。translate()
是transform
的一個值。改變transform
或opacity
不會觸發瀏覽器從新佈局(reflow)或重繪(repaint),只會觸發複合(compositions)。而改變絕對定位會觸發從新佈局,進而觸發重繪和複合。transform
使瀏覽器爲元素建立一個 GPU 圖層,但改變絕對定位會使用到 CPU。 所以translate()
更高效,能夠縮短平滑動畫的繪製時間。
當使用translate()
時,元素仍然佔據其原始空間(有點像position:relative
),這與改變絕對定位不一樣。
行內元素:和其餘元素都在一行上,高度、行高及外邊距和內邊距都不可改變,文字圖片的寬度不可改變,只能容納文本或者其餘行內元素;其中img是行元素
塊級元素:老是在新行上開始,高度、行高及外邊距和內邊距均可控制,能夠容納內斂元素和其餘元素;行元素轉換爲塊級元素方式:display:block;
能夠使用flex佈局 複製下面的HTML和CSS代碼 用瀏覽器打開能夠看到效果
<div class="wrap"> <div class="div1"></div> <div class="div2"></div> </div> .wrap { display: flex; justify-content: space-between; } .div1 { min-width: 200px; } .div2 { width: 100%; background: #e6e6e6; } html, body, div { height: 100%; margin: 0; }
// 父容器 display: flex; justify-content: center; align-items: center;
// 父容器 position: relative; // 子容器 position:absolute; margin:auto; top:0; bottom:0; left:0; right:0;
// 父容器 position: relative; // 子容器 position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
<div class="box"> <div class="content"> <div class="inner"></div> </div> </div> html, body { height: 100%; width: 100%; margin: 0; } .box { display: table; height: 100%; width: 100%; } .content { display: table-cell; vertical-align: middle; text-align: center; } .inner { background-color: #000; display: inline-block; width: 200px; height: 200px; }
是否隱藏 | 是否在文檔中佔用空間 | 是否會觸發事件 | |
---|---|---|---|
display: none | 是 | 否 | 否 |
visibile: hidden | 是 | 是 | 否 |
opacity: 0 | 是 | 是 | 是 |
利用column-count和break-inside這兩個CSS3屬性便可,複製以下代碼便可察看效果
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> body { margin: 0; } .waterfall-container { /*分幾列*/ column-count: 2; width: 100%; /* 列間距 */ column-gap: 10px; } .waterfall-item { break-inside: avoid; width: 100%; height: 100px; margin-bottom: 10px; background: #ddd; column-gap: 0; text-align: center; color: #fff; font-size: 40px; } </style> </head> <body> <div class="waterfall-container"> <div class="waterfall-item" style="height: 100px">1</div> <div class="waterfall-item" style="height: 300px">2</div> <div class="waterfall-item" style="height: 400px">3</div> <div class="waterfall-item" style="height: 100px">4</div> <div class="waterfall-item" style="height: 300px">5</div> <div class="waterfall-item" style="height: 600px">6</div> <div class="waterfall-item" style="height: 400px">7</div> <div class="waterfall-item" style="height: 300px">8</div> <div class="waterfall-item" style="height: 700px">9</div> <div class="waterfall-item" style="height: 100px">10</div> </div> </body> </html>
overflow: hidden; text-overflow:ellipsis; white-space: nowrap;
display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; // 最多顯示幾行 overflow: hidden;
.info-tab { position: relative; } .info-tab::after { content: ''; border: 4px solid transparent; border-top-color: #2c8ac2; position: absolute; top: 0; }
須要用到css
的object-fit
屬性
div { width: 200px; height: 200px; } img { object-fit: cover; width: 100%; height: 100%; }
iframe是用來在網頁中插入第三方頁面,早期的頁面使用iframe主要是用於導航欄這種不少頁面都相同的部分,這樣在切換頁面的時候避免重複下載。
優勢
缺點
因爲不一樣的瀏覽器,好比Internet Explorer 6,Internet Explorer 7,Mozilla Firefox等,對CSS的解析認識不同,所以會致使生成的頁面效果不同,得不到咱們所須要的頁面效果。
這個時候咱們就須要針對不一樣的瀏覽器去寫不一樣的CSS,讓它可以同時兼容不一樣的瀏覽器,能在不一樣的瀏覽器中也能獲得咱們想要的頁面效果。
這個針對不一樣的瀏覽器寫不一樣的CSS code的過程,就叫CSS hack,也叫寫CSS hack。
具體請看:
http://www.cnblogs.com/Renyi-Fan/p/9006084.html
外邊距合併指的是,當兩個垂直外邊距相遇時,它們將造成一個外邊距。
合併後的外邊距的高度等於兩個發生合併的外邊距的高度中的較大者。
更詳細的介紹請看:去除inline-block元素間間距的N種方法
同源策略可防止 JavaScript 發起跨域請求。源被定義爲 URI、主機名和端口號的組合。此策略可防止頁面上的惡意腳本經過該頁面的文檔對象模型,訪問另外一個網頁上的敏感數據。
https://zhuanlan.zhihu.com/p/41479807
跨域資源共享 CORS 阮一峯
這是我認爲寫得比較通俗易懂的一篇文章 直接轉載過來
https://blog.csdn.net/hansexploration/article/details/80314948
PC 時代爲了突破瀏覽器的域名併發限制。有了域名發散。
瀏覽器有併發限制,是爲了防止DDOS攻擊。
域名收斂:就是將靜態資源放在一個域名下。減小DNS解析的開銷。
域名發散:是將靜態資源放在多個子域名下,就能夠多線程下載,提升並行度,使客戶端加載靜態資源更加迅速。
域名發散是pc端爲了利用瀏覽器的多線程並行下載能力。而域名收斂多用與移動端,提升性能,由於dns解析是是從後向前迭代解析,若是域名過多性能會降低,增長DNS的解析開銷。
<button onclick="func()">按鈕</button>
btn.onclick = function(){}
btn.addEventListener('click',function(){})
事件委託利用了事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。全部用到按鈕的事件(多數鼠標事件和鍵盤事件)都適合採用事件委託技術,
使用事件委託能夠節省內存。
<ul> <li>蘋果</li> <li>香蕉</li> <li>鳳梨</li> </ul> // good document.querySelector('ul').onclick = (event) => { let target = event.target if (target.nodeName === 'LI') { console.log(target.innerHTML) } } // bad document.querySelectorAll('li').forEach((e) => { e.onclick = function() { console.log(this.innerHTML) } })
事件循環是一個單線程循環,用於監視調用堆棧並檢查是否有工做即將在任務隊列中完成。若是調用堆棧爲空而且任務隊列中有回調函數,則將回調函數出隊並推送到調用堆棧中執行。
<input onclick="sayHi()"/> btn.onclick = function() {} btn.onclick = null
// 綁定 btn.addEventListener('click', sayHi) // 解綁 btn.removeEventListener('click', sayHi)
UI事件,當用戶與頁面上的元素交互時觸發,如:load、scroll 焦點事件,當元素得到或失去焦點時觸發,如:blur、focus 鼠標事件,當用戶經過鼠標在頁面執行操做時觸發如:dbclick、mouseup 滾輪事件,當使用鼠標滾輪或相似設備時觸發,如:mousewheel 文本事件,當在文檔中輸入文本時觸發,如:textInput 鍵盤事件,當用戶經過鍵盤在頁面上執行操做時觸發,如:keydown、keypress 合成事件,當爲IME(輸入法編輯器)輸入字符時觸發,如:compositionstart 變更事件,當底層DOM結構發生變化時觸發,如:DOMsubtreeModified
https://www.jianshu.com/p/3acdf5f71d5b
let obj = {} obj.__proto__ === Object.prototype // true function Test(){} test.__proto__ == Test.prototype // true
全部的函數都同時擁有__proto__和protytpe屬性
函數的__proto__指向本身的函數實現 函數的protytpe是一個對象 因此函數的prototype也有__proto__屬性 指向Object.prototype
function func() {} func.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__指向null
Object.prototype.__proto__ // null
全部的JS對象都有一個prototype屬性,指向它的原型對象。當試圖訪問一個對象的屬性時,若是沒有在該對象上找到,它還會搜尋該對象的原型,以及該對象的原型的原型,依次層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾。
JS高程第3版 第6章 繼承
寄生組合式繼承
function SuperType(name) { this.name = name this.colors = ['red'] } SuperType.prototype.sayName = function() { console.log(this.name) } // 繼承實例屬性 function SubType(name, age) { SuperType.call(this, name) this.age = age } function inheritPrototype(subType, superType) { let prototype = Object.create(superType.prototype) prototype.constructor = subType subType.prototype = prototype } // 繼承原型方法 inheritPrototype(SubType, SuperType) // 定義本身的原型方法 SubType.prototype.sayAge = function() { console.log(this.age) }
閉包是指有權訪問另外一個函數做用域中的變量的函數。
function sayHi(name) { return () => { console.log(`Hi! ${name}`) } } const test = sayHi('xiaoming') test() // Hi! xiaoming
雖然sayHi函數已經執行完畢,可是其活動對象也不會被銷燬,由於test函數仍然引用着sayHi函數中的變量name,這就是閉包。
但也由於閉包引用着另外一個函數的變量,致使另外一個函數已經不使用了也沒法銷燬,因此閉包使用過多,會佔用較多的內存,這也是一個反作用。
這個題目是考察閉包的使用
function sayHi() { console.log('hi') } function threeTimes(fn) { let times = 0 return () => { if (times++ < 3) { fn() } } } const newFn = threeTimes(sayHi) newFn() newFn() newFn() newFn() newFn() // 後面兩次執行都無任何反應
經過閉包變量 times
來控制函數的執行
function add(a, b) { if (b === undefined) { return function(x) { return a + x } } return a + b }
Ajax(asynchronous JavaScript and XML)是使用客戶端上的許多 Web 技術,建立異步 Web 應用的一種 Web 開發技術。藉助 Ajax,Web 應用能夠異步(在後臺)向服務器發送數據和從服務器檢索數據,而不會干擾現有頁面的顯示和行爲。經過將數據交換層與表示層分離,Ajax 容許網頁和擴展 Web 應用程序動態更改內容,而無需從新加載整個頁面。實際上,如今一般將 JSON 替換爲 XML,由於 JavaScript 對 JSON 有原生支持優點。
XMLHttpRequest API 常常用於異步通訊。此外還有最近流行的fetch API。
let xmlhttp if (window.XMLHttpRequest) { // IE7+, Firefox, Chrome, Opera, Safari 瀏覽器執行代碼 xmlhttp = new XMLHttpRequest() } else { // IE6, IE5 瀏覽器執行代碼 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP") } xmlhttp.onreadystatechange = () => { if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { document.getElementById("myDiv").innerHTML = xmlhttp.responseText } } xmlhttp.open("GET", "/ajax/test.txt", true) xmlhttp.send()
優勢
缺點
https://github.com/yangshun/front-end-interview-handbook/blob/master/questions/javascript-questions.md
var會使變量提高,這意味着變量能夠在聲明以前使用。let和const不會使變量提高,提早使用會報錯。
變量提高(hoisting)是用於解釋代碼中變量聲明行爲的術語。使用var關鍵字聲明或初始化的變量,會將聲明語句「提高」到當前做用域的頂部。 可是,只有聲明纔會觸發提高,賦值語句(若是有的話)將保持原樣。
用var聲明的變量的做用域是它當前的執行上下文,它能夠是嵌套的函數,也能夠是聲明在任何函數外的變量。let和const是塊級做用域,意味着它們只能在最近的一組花括號(function、if-else 代碼塊或 for 循環中)中訪問。
function foo() { // 全部變量在函數中均可訪問 var bar = 'bar'; let baz = 'baz'; const qux = 'qux'; console.log(bar); // bar console.log(baz); // baz console.log(qux); // qux } console.log(bar); // ReferenceError: bar is not defined console.log(baz); // ReferenceError: baz is not defined console.log(qux); // ReferenceError: qux is not defined
if (true) { var bar = 'bar'; let baz = 'baz'; const qux = 'qux'; } // 用 var 聲明的變量在函數做用域上均可訪問 console.log(bar); // bar // let 和 const 定義的變量在它們被定義的語句塊以外不可訪問 console.log(baz); // ReferenceError: baz is not defined console.log(qux); // ReferenceError: qux is not defined
var會使變量提高,這意味着變量能夠在聲明以前使用。let和const不會使變量提高,提早使用會報錯。
console.log(foo); // undefined var foo = 'foo'; console.log(baz); // ReferenceError: can't access lexical declaration 'baz' before initialization let baz = 'baz'; console.log(bar); // ReferenceError: can't access lexical declaration 'bar' before initialization const bar = 'bar';
用var重複聲明不會報錯,但let和const會。
var foo = 'foo'; var foo = 'bar'; console.log(foo); // "bar" let baz = 'baz'; let baz = 'qux'; // Uncaught SyntaxError: Identifier 'baz' has already been declared
let和const的區別在於:let容許屢次賦值,而const只容許一次。
// 這樣不會報錯。 let foo = 'foo'; foo = 'bar'; // 這樣會報錯。 const baz = 'baz'; baz = 'qux';
https://github.com/yangshun/front-end-interview-handbook/blob/master/Translations/Chinese/questions/javascript-questions.md#%E4%BD%BF%E7%94%A8letvar%E5%92%8Cconst%E5%88%9B%E5%BB%BA%E5%8F%98%E9%87%8F%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB
在 JS
中,除了基本數據類型,還存在對象、數組這種引用類型。
基本數據類型,拷貝是直接拷貝變量的值,而引用類型拷貝的實際上是變量的地址。
let o1 = {a: 1} let o2 = o1
在這種狀況下,若是改變 o1
或 o2
其中一個值的話,另外一個也會變,由於它們都指向同一個地址。
o2.a = 3 console.log(o1.a) // 3
而淺拷貝和深拷貝就是在這個基礎之上作的區分,若是在拷貝這個對象的時候,只對基本數據類型進行了拷貝,而對引用數據類型只是進行了引用的傳遞,而沒有從新建立一個新的對象,則認爲是淺拷貝。反之,在對引用數據類型進行拷貝的時候,建立了一個新的對象,而且複製其內的成員變量,則認爲是深拷貝。
let o1 = {a:{ b:1 } } let o2 = JSON.parse(JSON.stringify(o1))
另外一種方法
function deepCopy(s) { const d = {} for (let k in s) { if (typeof s[k] == 'object') { d[k] = deepCopy(s[k]) } else { d[k] = s[k] } } return d }
ES5
function unique(arry) { const temp = [] arry.forEach(e => { if (temp.indexOf(e) == -1) { temp.push(e) } }) return temp }
ES6
function unique (arr) { return Array.from(new Set(arr)) }
原始值 "I am a string" 並非一個對象,它只是一個字面量,而且是一個不可變的值。
若是要在這個字面量上執行一些操做,好比獲取長度、訪問其中某個字符等,那須要將其
轉換爲 String 對象。
幸虧,在必要時語言會自動把字符串字面量轉換成一個 String 對象,也就是說你並不須要
顯式建立一個對象。
Array.isArray([]) // true Array.isArray({}) // false typeof [] // "object" typeof {} // "object" Object.prototype == [].__proto__ // false Object.prototype == {}.__proto__ // true Array.prototype == [].__proto__ // true Array.prototype == {}.__proto__ // false
有時 JavaScript 會自動爲代碼行補上缺失的分號,即自動分號插入(Automatic SemicolonInsertion,ASI)。
由於若是缺失了必要的 ; ,代碼將沒法運行,語言的容錯性也會下降。ASI 能讓咱們忽略那些沒必要要的 ; 。
請注意,ASI 只在換行符處起做用,而不會在代碼行的中間插入分號。
若是 JavaScript 解析器發現代碼行可能由於缺失分號而致使錯誤,那麼它就會自動補上分
號。而且,只有在代碼行末尾與換行符之間除了空格和註釋以外沒有別的內容時,它纔會
這樣作。
https://www.css88.com/archives/7340
特性 | cookie | localStorage | sessionStorage |
---|---|---|---|
由誰初始化 | 客戶端或服務器,服務器能夠使用Set-Cookie 請求頭。 |
客戶端 | 客戶端 |
數據的生命週期 | 通常由服務器生成,可設置失效時間,若是在瀏覽器生成,默認是關閉瀏覽器以後失效 | 永久保存,可清除 | 僅在當前會話有效,關閉頁面後清除 |
存放數據大小 | 4KB | 5MB | 5MB |
與服務器通訊 | 每次都會攜帶在HTTP頭中,若是使用cookie保存過多數據會帶來性能問題 | 僅在客戶端保存 | 僅在客戶端保存 |
用途 | 通常由服務器生成,用於標識用戶身份 | 用於瀏覽器緩存數據 | 用於瀏覽器緩存數據 |
訪問權限 | 任意窗口 | 任意窗口 | 當前頁面窗口 |
自執行函數:一、聲明一個匿名函數二、立刻調用這個匿名函數。
做用:建立一個獨立的做用域。
好處:防止變量彌散到全局,以避免各類js庫衝突。隔離做用域避免污染,或者截斷做用域鏈,避免閉包形成引用變量沒法釋放。利用當即執行特性,返回須要的業務函數或對象,避免每次經過條件判斷來處理
場景:通常用於框架、插件等場景
有以下幾個方式:
https://zhuanlan.zhihu.com/p/41479807
斷點續傳最核心的內容就是把文件「切片」而後再一片一片的傳給服務器,可是這看似簡單的上傳過程卻有着無數的坑。
首先是文件的識別,一個文件被分紅了若干份以後如何告訴服務器你切了多少塊,以及最終服務器應該如何把你上傳上去的文件進行合併,這都是要考慮的。
所以在文件開始上傳以前,咱們和服務器要有一個「握手」的過程,告訴服務器文件信息,而後和服務器約定切片的大小,當和服務器達成共識以後就能夠開始後續的文件傳輸了。
前臺要把每一塊的文件傳給後臺,成功以後前端和後端都要標識一下,以便後續的斷點。
當文件傳輸中斷以後用戶再次選擇文件就能夠經過標識來判斷文件是否已經上傳了一部分,若是是的話,那麼咱們能夠接着上次的進度繼續傳文件,以達到續傳的功能。
有了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); // ......
在把文件切成片以後,接下來要作的事情就是把這些碎片傳到服務器上。
若是中間掉線了,下次再傳的時候就得先從服務器獲取上一次上傳文件的位置,而後以這個位置開始上傳接下來的文件內容。
https://www.cnblogs.com/zhwl/p/3580776.html
function Test(){} const test = new Test()
const obj = {}
obj.constructor = Test obj.__proto__ = Test.prototype
Test.call(obj)
call和apply實際上是同樣的,區別就在於傳參時參數是一個一個傳或者是以一個數組的方式來傳。
call和apply都是在調用時生效,改變調用者的this指向。
let name = 'Jack' const obj = {name: 'Tom'} function sayHi() {console.log('Hi! ' + this.name)} sayHi() // Hi! Jack sayHi.call(obj) // Hi! Tom
bind也是改變this指向,不過不是在調用時生效,而是返回一個新函數。
const newFunc = sayHi.bind(obj) newFunc() // Hi! Tom
JavaScript
中的this
。JS 中的this
是一個相對複雜的概念,不是簡單幾句能解釋清楚的。粗略地講,函數的調用方式決定了this
的值。我閱讀了網上不少關於this
的文章,Arnav Aggrawal 寫的比較清楚。this
取值符合如下規則:
new
關鍵字,函數內的this
是一個全新的對象。apply
、call
或bind
方法用於調用、建立一個函數,函數內的 this 就是做爲參數傳入這些方法的對象。this
是調用該函數的對象。好比當obj.method()
被調用時,函數內的 this 將綁定到obj
對象。this
的值指向全局對象(global object)。瀏覽器環境下this
的值指向window
對象,可是在嚴格模式下('use strict'
),this
的值爲undefined
。this
的值。this
被設置爲它被建立時的上下文。想得到更深刻的解釋,請查看他在 Medium 上的文章。
https://github.com/yangshun/front-end-interview-handbook/blob/master/Translations/Chinese/questions/javascript-questions.md#%E8%AF%B7%E7%AE%80%E8%BF%B0javascript%E4%B8%AD%E7%9A%84this
若是要判斷一個運行中函數的 this 綁定,就須要找到這個函數的直接調用位置。找到以後就能夠順序應用下面這四條規則來判斷 this 的綁定對象。
必定要注意,有些調用可能在無心中使用默認綁定規則。若是想「更安全」地忽略 this 綁定,你能夠使用一個 DMZ 對象,好比 ø = Object.create(null) ,以保護全局對象。
ES6 中的箭頭函數並不會使用四條標準的綁定規則,而是根據當前的詞法做用域來決定this ,具體來講,箭頭函數會繼承外層函數調用的 this 綁定(不管 this 綁定到什麼)。這其實和 ES6 以前代碼中的 self = this 機制同樣
參考:《你不知道的JavaScript》
==
是抽象相等運算符,而===
是嚴格相等運算符。==
運算符是在進行必要的類型轉換後,再比較。===
運算符不會進行類型轉換,因此若是兩個值不是相同的類型,會直接返回false
。使用==
時,可能發生一些特別的事情,例如:
1 == '1'; // true 1 == [1]; // true 1 == true; // true 0 == ''; // true 0 == '0'; // true 0 == false; // true
若是你對==
和===
的概念不是特別瞭解,建議大多數狀況下使用===
this
對象,就是定義時所在的對象,而不是使用時所在的對象,用call
apply
bind
也不能改變this
指向new
命令,不然會拋出一個錯誤。arguments
對象,該對象在函數體內不存在。若是要用,能夠用 rest
參數代替。yield
命令,所以箭頭函數不能用做 Generator
函數。prototype
Performance 接口能夠獲取到當前頁面中與性能相關的信息。
該類型的對象能夠經過調用只讀屬性 Window.performance 來得到。
白屏時間:
performance.timing.responseStart - performance.timing.navigationStart
首屏時間
window.onload = () => { new Date() - performance.timing.responseStart }
https://developer.mozilla.org/zh-CN/docs/Web/API/Performance
https://github.com/skyline75489/what-happens-when-zh_CN/blob/master/README.rst?utm_medium=social&utm_source=wechat_session&from=timeline&isappinstalled=0
https://www.jianshu.com/p/5d82bba9e1a1
防抖(debounce)
在函數須要頻繁觸發時,只有當有足夠空閒的時間時,才執行一次。就好像在百度搜索時,每次輸入以後都有聯想詞彈出,這個控制聯想詞的方法就不多是輸入框內容一改變就觸發的,他必定是當你結束輸入一段時間以後纔會觸發。
節流(thorttle)
預約一個函數只有在大於等於執行週期時才執行,週期內調用不執行。就好像你在淘寶搶購某一件限量熱賣商品時,你不斷點刷新點購買,但是總有一段時間你點上是沒有效果,這裏就用到了節流,就是怕點的太快致使系統出現bug。
區別
在發生持續觸發事件時,防抖設置事件延遲並在空閒時間去觸發事件,而節流則是隔必定的時間觸發一次。
具體請看:
https://blog.csdn.net/jacoox/article/details/80719456
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。
function format(str) { let s = '' let count = 0 for (let i = str.length - 1; i >= 0; i--) { s = str[i] + s count++ if (count % 3 == 0 && i != 0) { s = ',' + s } } return s }
function format(str) { return str.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') }
str.replace(/\s/g, '')
str.replace(/^\s+|\s+$/g, '') // 原生方法 str.trim()
REST 指的是一組架構約束條件和原則。知足這些約束條件和原則的應用程序或設計就是 RESTful。
https://blog.csdn.net/jnshu_it/article/details/80203696
Accept 請求頭用來告知客戶端能夠處理的內容類型,這種內容類型用MIME類型來表示。
服務器使用 Content-Type 應答頭通知客戶端它的選擇。
Accept: text/html Accept: image/* Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
1.Accept屬於請求頭, Content-Type屬於實體頭。
Http報頭分爲通用報頭,請求報頭,響應報頭和實體報頭。
請求方的http報頭結構:通用報頭|請求報頭|實體報頭
響應方的http報頭結構:通用報頭|響應報頭|實體報頭
2.Accept表明發送端(客戶端)但願接受的數據類型。
好比:Accept:text/xml;
表明客戶端但願接受的數據類型是xml類型
Content-Type表明發送端(客戶端|服務器)發送的實體數據的數據類型。
好比:Content-Type:text/html;
表明發送端發送的數據格式是html。
兩者合起來,
Accept:text/xml;
Content-Type:text/html
即表明但願接受的數據類型是xml格式,本次請求發送的數據的數據格式是html。
狀態碼 | 類別 | 描述 |
---|---|---|
1xx | Informational(信息狀態碼) | 接受請求正在處理 |
2xx | Success(成功狀態碼) | 請求正常處理完畢 |
3xx | Redirection(重定向狀態碼) | 須要附加操做已完成請求 |
4xx | Client Error(客戶端錯誤狀態碼) | 服務器沒法處理請求 |
5xx | Server Error(服務器錯誤狀態碼) | 服務器處理請求出錯 |
https://segmentfault.com/a/1190000010690320
https://zhuanlan.zhihu.com/p/33778904
無狀態協議對於事務處理沒有記憶能力。缺乏狀態意味着若是後續處理須要前面的信息也就是說,
當客戶端一次HTTP請求完成之後,客戶端再發送一次HTTP請求,HTTP並不知道當前客戶端是一個」老用戶「。
能夠使用Cookie來解決無狀態的問題,Cookie就至關於一個通行證,第一次訪問的時候給客戶端發送一個Cookie,
當客戶端再次來的時候,拿着Cookie(通行證),那麼服務器就知道這個是」老用戶「。
https://zhuanlan.zhihu.com/p/33778904
HTTP通訊機制是在一次完整的HTTP通訊過程當中,Web瀏覽器與Web服務器之間將完成下列7個步驟:
在HTTP工做開始以前,Web瀏覽器首先要經過網絡與Web服務器創建鏈接,該鏈接是經過TCP來完成的,該協議與IP協議共同構建 Internet,即著名的TCP/IP協議族,所以Internet又被稱做是TCP/IP網絡。HTTP是比TCP更高層次的應用層協議,根據規則, 只有低層協議創建以後才能,才能進行更層協議的鏈接,所以,首先要創建TCP鏈接,通常TCP鏈接的端口號是80。
一旦創建了TCP鏈接,Web瀏覽器就會向Web服務器發送請求命令。例如:GET /sample/hello.jsp HTTP/1.1。
瀏覽器發送其請求命令以後,還要以頭信息的形式向Web服務器發送一些別的信息,以後瀏覽器發送了一空白行來通知服務器,它已經結束了該頭信息的發送。
客戶機向服務器發出請求後,服務器會客戶機回送應答, HTTP/1.1 200 OK ,應答的第一部分是協議的版本號和應答狀態碼。
正如客戶端會隨同請求發送關於自身的信息同樣,服務器也會隨同應答向用戶發送關於它本身的數據及被請求的文檔。
Web服務器向瀏覽器發送頭信息後,它會發送一個空白行來表示頭信息的發送到此爲結束,接着,它就以Content-Type應答頭信息所描述的格式發送用戶所請求的實際數據。
通常狀況下,一旦Web服務器向瀏覽器發送了請求數據,它就要關閉TCP鏈接,而後若是瀏覽器或者服務器在其頭信息加入了這行代碼:
Connection:keep-alive
TCP鏈接在發送後將仍然保持打開狀態,因而,瀏覽器能夠繼續經過相同的鏈接發送請求。保持鏈接節省了爲每一個請求創建新鏈接所需的時間,還節約了網絡帶寬。
創建TCP鏈接->發送請求行->發送請求頭->(到達服務器)發送狀態行->發送響應頭->發送響應數據->斷TCP鏈接
https://juejin.im/post/5a8102e0f265da4e710f5910
MVVM最先由微軟提出來,它借鑑了桌面應用程序的MVC思想,在前端頁面中,把Model用純JavaScript對象表示,View負責顯示,二者作到了最大限度的分離
把Model和View關聯起來的就是ViewModel。
ViewModel負責把Model的數據同步到View顯示出來,還負責把View的修改同步回Model
View 和 Model 之間的同步工做徹底是自動的,無需人爲干涉(由viewModel完成,在這裏指VUE)
所以開發者只需關注業務邏輯,不須要手動操做DOM, 不須要關注數據狀態的同步問題,複雜的數據狀態維護徹底由 MVVM 來統一管理
須要用JavaScript編寫一個通用的ViewModel,這樣,就能夠複用整個MVVM模型了
一個MVVM框架和jQuery操做DOM相比有什麼區別?
咱們先看用jQuery實現的修改兩個DOM節點的例子:
<!-- HTML --> <p>Hello, <span id="name">Bart</span>!</p> <p>You are <span id="age">12</span>.</p> Hello, Bart! You are 12.
用jQuery修改name和age節點的內容:
var name = 'Homer'; var age = 51; $('#name').text(name); $('#age').text(age);
若是咱們使用MVVM框架來實現一樣的功能,咱們首先並不關心DOM的結構,而是關心數據如何存儲。最簡單的數據存儲方式是使用JavaScript對象:
var person = { name: 'Bart', age: 12 }
咱們把變量person看做Model,把HTML某些DOM節點看做View,並假定它們之間被關聯起來了。
要把顯示的name從Bart改成Homer,把顯示的age從12改成51,咱們並不操做DOM,而是直接修改JavaScript對象:
person.name = 'Homer'; person.age = 51;
執行上面的代碼,咱們驚訝地發現,改變JavaScript對象的狀態,會致使DOM結構做出對應的變化!這讓咱們的關注點從如何操做DOM變成了如何更新JavaScript對象的狀態,而操做JavaScript對象比DOM簡單多了!
這就是MVVM的設計思想:關注Model的變化,讓MVVM框架去自動更新DOM的狀態,從而把開發者從操做DOM的繁瑣步驟中解脫出來!
下圖能夠很好的解釋view viewModel model之間的關係
mvvm的優勢便是vue的優勢,在這裏再總結一下:
數據和視頻之間的同步工做徹底是自動的,無需人爲干涉,因此開發者只需關注業務邏輯,不須要手動操做DOM, 不須要關注數據狀態的同步問題,
複雜的數據狀態維護徹底由 MVVM 來統一管理,節省了不少精力。
建立一個Vue實例,是一個漫長的過程,要經歷初始化,數據合併,模板解析,數據渲染等等一系列過程。
因此,爲了能實如今這個過程裏面插入本身想要提早作的事情,就有了生命週期鉤子函數。
一輛公交車,從出發點A站到終點站B,中間有不少站點,公交車每到一個站點,就得停下來, 等待客人上車,而後再駛往下一個站點,一直到終點站爲止。 A和B之間的站點,就像是這個路程的生命週期。每個站點都是一個不一樣的生命週期(站點名不一樣), 只要到了站點,就得執行該站點對應的生命週期函數, 只不過每一個站點的生命週期函數都是同樣的(等待客人上車)。
Vue中的生命週期也是同樣,對應了Vue實例從建立到結束之間的每個過程。
例如,Vue的beforeCreate
週期,指的就是Vue在實例初始化以後,數據觀測 (data observer) 和 event/watcher 事件配置以前被調用。
至於Vue具體的生命週期函數有哪些,請看官網API文檔
具體例子請看官方文檔
Vue的數據雙向綁定都是依據Object.defineProperty()這一方法來作的
Object.defineProperty到底有什麼做用呢?
MDN
Object.defineProperty(obj, prop, descriptor) obj 要在其上定義屬性的對象。 prop 要定義或修改的屬性的名稱。 descriptor 將被定義或修改的屬性描述符。
簡單來講 這個方法能夠定義一個對象某個屬性的描述符
咱們須要用到的就是描述符當中的getter和setter
const obj = {a:1} obj.a // 1 obj.a = 2
像上面代碼中的兩個操做 讀取和賦值 就是在訪問obj.a的getter和setter
當咱們輸入obj.a時 就是在訪問obj對象a屬性的getter 當輸入obj.a = 2 時就是在訪問obj對象a屬性的setter
Object.defineProperty(obj, 'a', { get : function(){ return val }, set : function(newValue){ val = newValue }, enumerable : true, configurable : true })
getter和setter都是一個函數 咱們還能夠這樣作 例如
get: function() { // 每次訪問obj.a時都會執行這段代碼 console.log('hello, 你在讀取a的值') return val } set: function(newValue) { val = newValue // 每次給obj.a賦值時都會執行這段代碼 console.log('你設置了a的值') }
Vue的雙向數據綁定就是根據上面的原理來實現的
只要在讀取值時收集觀察者 在賦值時觸發觀察者更新函數 就能夠實現數據變動 從而實現DOM從新渲染
說到這可能還不是很明白 不要急 慢慢來 先看一下這段代碼 複製放到HTML文件裏本身運行一下
而後打開網頁 在控制檯裏輸入data.user.name看看 會有驚喜
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>動態數據綁定(一)</title> </head> <body> <script> var data = { user: { name: 'xiaoming', age: 18, occupation: 'frontend' }, address: { city: 'shaoguan' } }; function Observer(data) { this.data = data; this.walk(data); } Observer.prototype = { walk: function(obj) { var value, key; for (key in obj) { if (obj.hasOwnProperty(key)) { value = obj[key]; if (typeof value === 'object') { new Observer(value); } this.convert(key, value); } } }, convert: function(key, value) { Object.defineProperty(this.data, key, { get : function(){ console.log("你訪問了" + key); return value; }, set : function(newValue){ value = newValue; console.log('你設置了' + key + '=' + value); } }); } } var example = new Observer(data); </script> </body> </html>
說簡單點,vue-router的原理就是經過對URL地址變化的監聽,繼而對不一樣的組件進行渲染。
每當URL地址改變時,就對相應的組件進行渲染。原理是很簡單,實現方式可能有點複雜,主要有hash模式和history模式。
若是想了解得詳細點,建議百度或者閱讀源碼。
vuex的原理其實很是簡單,它爲何能實現全部的組件共享同一份數據?
由於vuex生成了一個store實例,而且把這個實例掛在了全部的組件上,全部的組件引用的都是同一個store實例。
store實例上有數據,有方法,方法改變的都是store實例上的數據。因爲其餘組件引用的是一樣的實例,因此一個組件改變了store上的數據,
致使另外一個組件上的數據也會改變,就像是一個對象的引用。
若是對vuex的實現有興趣,能夠看看我本身造的一個vue輪子對應的vuex插件。它實現了除vuex模塊外的全部功能。
v-if
是「真正」的條件渲染,由於它會確保在切換過程當中條件塊內的事件監聽器和子組件適當地被銷燬和重建。
v-if
也是惰性的:若是在初始渲染時條件爲假,則什麼也不作——直到條件第一次變爲真時,纔會開始渲染條件塊。
相比之下,v-show
就簡單得多——無論初始條件是什麼,元素老是會被渲染,而且只是簡單地基於 CSS 進行切換。
通常來講,v-if
有更高的切換開銷,而 v-show
有更高的初始渲染開銷。所以,若是須要很是頻繁地切換,則使用 v-show
較好;若是在運行時條件不多改變,則使用v-if
較好。
https://cn.vuejs.org/v2/guide/conditional.html#v-if-vs-v-show
利用 vue-router
的 beforeEach
事件,能夠在跳轉頁面前判斷用戶的權限(利用 cookie 或 token),是否可以進入此頁面,若是不能則提示錯誤或重定向到其餘頁面,在後臺管理系統中這種場景常常能遇到。
在 Vue
中,每次切換組件時,都會從新渲染。若是有多個組件切換,又想讓它們保持原來的狀態,避免從新渲染,這個時候就能夠使用 keep-alive
。
keep-alive
能夠使被包含的組件保留狀態,或避免從新渲染。
先來看一下計算屬性的定義:
當其依賴的屬性的值發生變化的時,計算屬性會從新計算。反之則使用緩存中的屬性值。
計算屬性和vue中的其它數據同樣,都是響應式的,只不過它必須依賴某一個數據實現,而且只有它依賴的數據的值改變了,它纔會更新。
$route
是路由信息對象,包括path
,params
,hash
,query
,fullPath
,matched
,name
等路由信息參數。
而 $router
是路由實例對象,包括了路由的跳轉方法,鉤子函數等
watch
主要做用是監聽某個數據值的變化。和計算屬性相比除了沒有緩存,做用是同樣的。
藉助 watch
還能夠作一些特別的事情,例如監聽頁面路由,當頁面跳轉時,咱們能夠作相應的權限控制,拒絕沒有權限的用戶訪問頁面。