項目地址
HTML問題
HTML5語義化
什么是語義化?就是用合理、正確的標簽來展示內容,比如h1~h6定義標題。
好處
- 易於用戶閱讀,樣式丟失的時候能讓頁面呈現清晰的結構。
- 有利於SEO,搜索引擎根據標簽來確定上下文和各個關鍵字的權重。
- 方便其他設備解析,如盲人閱讀器根據語義渲染網頁
- 有利於開發和維護,語義化更具可讀性,代碼更好維護,與CSS3關系更和諧。
http://www.daqianduan.com/6549.html
為什么最好把 CSS 的<link>
標簽放在<head></head>
之間?為什么最好把 JS 的<script>
標簽恰好放在</body>
之前,有例外情況嗎?
把<link>
放在<head>
中
把<link>
標簽放在<head></head>
之間是規范要求的內容。此外,這種做法可以讓頁面逐步呈現,提高了用戶體驗。將樣式表放在文檔底部附近,會使許多瀏覽器(包括 Internet Explorer)不能逐步呈現頁面。一些瀏覽器會阻止渲染,以避免在頁面樣式發生變化時,重新繪制頁面中的元素。這種做法可以防止呈現給用戶空白的頁面或沒有樣式的內容。
把<script>
標簽恰好放在</body>
之前
腳本在下載和執行期間會阻止 HTML 解析。把<script>
標簽放在底部,保證 HTML 首先完成解析,將頁面盡早呈現給用戶。
例外情況是當你的腳本里包含document.write()
時。但是現在,document.write()
不推薦使用。同時,將<script>
標簽放在底部,意味着瀏覽器不能開始下載腳本,直到整個文檔(document)被解析。也許,對此比較好的做法是,<script>
使用defer
屬性,放在<head>
中。
什么是漸進式渲染(progressive rendering)?
漸進式渲染是用於提高網頁性能(尤其是提高用戶感知的加載速度),以盡快呈現頁面的技術。
在以前互聯網帶寬較小的時期,這種技術更為普遍。如今,移動終端的盛行,而移動網絡往往不穩定,漸進式渲染在現代前端開發中仍然有用武之地。
一些舉例:
- 圖片懶加載——頁面上的圖片不會一次性全部加載。當用戶滾動頁面到圖片部分時,JavaScript 將加載並顯示圖像。
- 確定顯示內容的優先級(分層次渲染)——為了盡快將頁面呈現給用戶,頁面只包含基本的最少量的 CSS、腳本和內容,然后可以使用延遲加載腳本或監聽
DOMContentLoaded
/load
事件加載其他資源和內容。 - 異步加載 HTML 片段——當頁面通過后台渲染時,把 HTML 拆分,通過異步請求,分塊發送給瀏覽器。更多相關細節可以在這里找到。
viewport
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代表允許這些屬性可以同時使用,也可以單獨使用或混合使用,多個屬性同時使用時用逗號隔開就行了。
Reflow和Repaint
Reflow
當涉及到DOM節點的布局屬性發生變化時,就會重新計算該屬性,瀏覽器會重新描繪相應的元素,此過程叫Reflow(回流或重排)。
Repaint
當影響DOM元素可見性的屬性發生變化 (如 color) 時, 瀏覽器會重新描繪相應的元素, 此過程稱為Repaint(重繪)。因此重排必然會引起重繪。
引起Repaint和Reflow的一些操作
- 調整窗口大小
- 字體大小
- 樣式表變動
- 元素內容變化,尤其是輸入控件
- CSS偽類激活,在用戶交互過程中發生
- DOM操作,DOM元素增刪、修改
- width, clientWidth, scrollTop等布局寬高的計算
Repaint和Reflow是不可避免的,只能說對性能的影響減到最小,給出下面幾條建議:
- 避免逐條更改樣式。建議集中修改樣式,例如操作className。
- 避免頻繁操作DOM。創建一個documentFragment或div,在它上面應用所有DOM操作,最后添加到文檔里。設置display:none的元素上操作,最后顯示出來。
- 避免頻繁讀取元素幾何屬性(例如scrollTop)。絕對定位具有復雜動畫的元素。
- 絕對定位使它脫離文檔流,避免引起父元素及后續元素大量的回流
https://harttle.land/2015/08/11/reflow-repaint.html
http://www.blueidea.com/tech/web/2011/8365.asp
img中的alt和元素的title屬性作用
- img的alt屬性
如果無法顯示圖像,瀏覽器將顯示alt指定的內容 - 元素title屬性
在鼠標移到元素上時顯示title的內容
href和src區別
- href
href標識超文本引用,用在link和a等元素上,href是引用和頁面關聯,是在當前元素和引用資源之間建立聯系
若在文檔中添加href ,瀏覽器會識別該文檔為 CSS 文件,就會並行下載資源並且不會停止對當前文檔的處理。這也是為什么建議使用 link 方式加載 CSS,而不是使用 @import 方式。 - src
src表示引用資源,替換當前元素,用在img,script,iframe上,src是頁面內容不可缺少的一部分。
當瀏覽器解析到src ,會暫停其他資源的下載和處理(圖片不會暫停其他資源下載和處理),直到將該資源加載、編譯、執行完畢,圖片和框架等也如此,類似於將所指向資源應用到當前內容。這也是為什么建議把 js 腳本放在底部而不是頭部的原因。
https://blog.csdn.net/lhjuejiang/article/details/80795081
瀏覽器的渲染過程
- 解析HTML生成DOM樹。
- 解析CSS生成CSSOM規則樹。
- 將DOM樹與CSSOM規則樹合並在一起生成渲染樹。
- 遍歷渲染樹開始布局,計算每個節點的位置大小信息。
- 將渲染樹每個節點繪制到屏幕。
https://baijiahao.baidu.com/s?id=1593097105869520145&wfr=spider&for=pc
為何會出現瀏覽器兼容問題
- 同一產品,版本越老 bug 越多
- 同一產品,版本越新,功能越多
- 不同產品,不同標准,不同實現方式
處理兼容問題的思路
- 要不要做
- 產品的角度(產品的受眾、受眾的瀏覽器比例、效果優先還是基本功能優先)
- 成本的角度 (有無必要做某件事)
2.做到什么程度
- 讓哪些瀏覽器支持哪些效果
3..如何做
-
根據兼容需求選擇技術框架/庫(jquery)
-
根據兼容需求選擇兼容工具(html5shiv.js、respond.js、css reset、normalize.css、Modernizr)
-
條件注釋、CSS Hack、js 能力檢測做一些修補
-
漸進增強(progressive enhancement): 針對低版本瀏覽器進行構建頁面,保證最基本的功能,然后再針對高級瀏覽器進行效果、交互等改進和追加功能達到更好的用戶體驗
-
優雅降級 (graceful degradation): 一開始就構建完整的功能,然后再針對低版本瀏覽器進行兼容。
https://github.com/jirengu/frontend-interview/issues/35
doctype有什么用
doctype是一種標准通用標記語言的文檔類型聲明,目的是告訴標准通用標記語言解析器要使用什么樣的文檔類型定義(DTD)來解析文檔。
聲明是用來指示web瀏覽器關於頁面使用哪個HTML版本進行編寫的指令。 聲明必須是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能夠原封不動的把嵌入的網頁展現出來。
- 如果有多個網頁引用iframe,那么你只需要修改iframe的內容,就可以實現調用的每一個頁面內容的更改,方便快捷。
- 網頁如果為了統一風格,頭部和版本都是一樣的,就可以寫成一個頁面,用iframe來嵌套,可以增加代碼的可重用。
- 如果遇到加載緩慢的第三方內容如圖標和廣告,這些問題可以由iframe來解決。
缺點:
- 搜索引擎的爬蟲程序無法解讀這種頁面
- 框架結構中出現各種滾動條
- 使用框架結構時,保證設置正確的導航鏈接。
- iframe頁面會增加服務器的http請求
label標簽有什么作用
label
標簽通常是寫在表單內,它關聯一個控件,使用 label
可以實現點擊文字選取對應的控件。
<input type="checkbox" id="test">
<label for="test" >test</label>
HTML5的form如何關閉自動完成功能
將不想要自動完成的 form
或 input
設置為 autocomplete=off
DOM和BOM有什么區別
- DOM
Document Object Model,文檔對象模型
DOM 是為了操作文檔出現的 API,document 是其的一個對象
DOM和文檔有關,這里的文檔指的是網頁,也就是html文檔。DOM和瀏覽器無關,他關注的是網頁本身的內容。
- BOM
Browser Object Model,瀏覽器對象模型
BOM 是為了操作瀏覽器出現的 API,window 是其的一個對象
window 對象既為 javascript 訪問瀏覽器提供API,同時在 ECMAScript 中充當 Global 對象
CSS問題
CSS 選擇器的優先級是如何計算的?
瀏覽器通過優先級規則,判斷元素展示哪些樣式。優先級通過 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
的方式,去覆蓋組件的樣式了。
參考
- https://www.smashingmagazine.com/2007/07/css-specificity-things-you-should-know/
- https://www.sitepoint.com/web-foundations/specificity/
重置(resetting)CSS 和 標准化(normalizing)CSS 的區別是什么?你會選擇哪種方式,為什么?
- 重置(Resetting): 重置意味着除去所有的瀏覽器默認樣式。對於頁面所有的元素,像
margin
、padding
、font-size
這些樣式全部置成一樣。你將必須重新定義各種元素的樣式。 - 標准化(Normalizing): 標准化沒有去掉所有的默認樣式,而是保留了有用的一部分,同時還糾正了一些常見錯誤。
當需要實現非常個性化的網頁設計時,我會選擇重置的方式,因為我要寫很多自定義的樣式以滿足設計需求,這時候就不再需要標准化的默認樣式了。
參考
請闡述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
。
參考
- https://css-tricks.com/almanac/properties/z/z-index/
- https://philipwalton.com/articles/what-no-one-told-you-about-z-index/
- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
請闡述塊格式化上下文(Block Formatting Context)及其工作原理。
塊格式上下文(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)。
參考
- https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context
- https://www.sitepoint.com/understanding-block-formatting-contexts-in-css/
有哪些清除浮動的技術,都適用哪些情況?
- 空
div
方法:<div style="clear:both;"></div>
。 - Clearfix 方法:上文使用
.clearfix
類已經提到。 overflow: auto
或overflow: hidden
方法:上文已經提到。
在大型項目中,我會使用 Clearfix 方法,在需要的地方使用.clearfix
。設置overflow: hidden
的方法可能使其子元素顯示不完整,當子元素的高度大於父元素時。
請解釋什么是雪碧圖(css sprites),以及如何實現?
雪碧圖是把多張圖片整合到一張上的圖片。它被運用在眾多使用了很多小圖標的網站上(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 是理想的解決方案,我也會采用絕對定位方法,因為它具有最少的注意事項,適用於大多數元素,而且使用起來非常簡單。
參考
- https://www.w3.org/TR/wai-aria-1.1/
- https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA
- http://a11yproject.com/
除了screen
,你還能說出一個 @media 屬性的例子嗎?
- all
適用於所有設備。 - print
為了加載合適的文檔到當前使用的可視窗口. 需要提前咨詢 paged media(媒體屏幕尺寸), 以滿足個別設備網頁尺寸不匹配等問題。 - screen
主要適用於彩色的電腦屏幕 - speech
解析speech這個合成器. 注意: CSS2已經有一個相似的媒體類型叫aural.
https://developer.mozilla.org/zh-CN/docs/Web/CSS/@media
編寫高效的 CSS 應該注意什么?
首先,瀏覽器從最右邊的選擇器,即關鍵選擇器(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 預處理的優缺點分別是什么?
優點:
- 提高 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 請求加載這些文件。
缺點:
- 需要預處理工具。
- 重新編譯的時間可能會很慢。
對於你使用過的 CSS 預處理,說說喜歡和不喜歡的地方?
喜歡:
- 絕大部分優點上題以及提過。
- Less 用 JavaScript 實現,與 NodeJS 高度結合。
Dislikes:
- 我通過
node-sass
使用 Sass,它用 C ++ 編寫的 LibSass 綁定。在 Node 版本切換時,我必須經常重新編譯。 - Less 中,變量名稱以
@
作為前綴,容易與 CSS 關鍵字混淆,如@media
、@import
和@font-face
。
如何實現一個使用非標准字體的網頁設計?
使用@font-face
並為不同的font-weight
定義font-family
。
解釋瀏覽器如何確定哪些元素與 CSS 選擇器匹配。
這部分與上面關於編寫高效的 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
相同。
參考
你使用過哪些現有的 CSS 框架?你是如何改進它們的?
- Bootstrap: 更新周期緩慢。Bootstrap 4 已經處於 alpha 版本將近兩年了。添加了在頁面中廣泛使用的微調按鈕組件。
- Semantic UI:源代碼結構使得自定義主題很難理解。非常規主題系統的使用體驗很差。外部庫的路徑需要硬編碼(hard code)配置。變量重新賦值沒有 Bootstrap 設計得好。
- Bulma: 需要很多非語義的類和標記,顯得很多余。不向后兼容,以至於升級版本后,會破壞應用的正常運行。
你了解 CSS Flex 和 Grid 嗎?
Flex 主要用於一維布局,而 Grid 則用於二維布局。
Flex
flex容器中存在兩條軸, 橫軸和縱軸, 容器中的每個單元稱為flex item。
在容器上可以設置6個屬性:
- flex-direction
- flex-wrap
- flex-flow
- justify-content
- align-items
- align-content
注意:當設置 flex 布局之后,子元素的 float、clear、vertical-align 的屬性將會失效。
Flex 項目屬性
有六種屬性可運用在 item 項目上:
- order
- flex-basis
- flex-grow
- flex-shrink
- flex
- align-self
Grid
CSS網格布局用於將頁面分割成數個主要區域,或者用來定義組件內部元素間大小、位置和圖層之間的關系。
像表格一樣,網格布局讓我們能夠按行或列來對齊元素。 但是,使用CSS網格可能還是比CSS表格更容易布局。 例如,網格容器的子元素可以自己定位,以便它們像CSS定位的元素一樣,真正的有重疊和層次。
響應式設計與自適應設計有何不同?
響應式設計和自適應設計都以提高不同設備間的用戶體驗為目標,根據視窗大小、分辨率、使用環境和控制方式等參數進行優化調整。
響應式設計的適應性原則:網站應該憑借一份代碼,在各種設備上都有良好的顯示和使用效果。響應式網站通過使用媒體查詢,自適應柵格和響應式圖片,基於多種因素進行變化,創造出優良的用戶體驗。就像一個球通過膨脹和收縮,來適應不同大小的籃圈。
自適應設計更像是漸進式增強的現代解釋。與響應式設計單一地去適配不同,自適應設計通過檢測設備和其他特征,從早已定義好的一系列視窗大小和其他特性中,選出最恰當的功能和布局。與使用一個球去穿過各種的籃筐不同,自適應設計允許使用多個球,然后根據不同的籃筐大小,去選擇最合適的一個。
參考
- https://developer.mozilla.org/en-US/docs/Archive/Apps/Design/UI_layout_basics/Responsive_design_versus_adaptive_design
- http://mediumwell.com/responsive-adaptive-mobile/
- https://css-tricks.com/the-difference-between-responsive-and-adaptive-design/
你有沒有使用過視網膜分辨率的圖形?當中使用什么技術?
我傾向於使用更高分辨率的圖形(顯示尺寸的兩倍)來處理視網膜顯示。更好的方法是使用媒體查詢,像@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
),這與改變絕對定位不同。
參考
其他答案
- https://neal.codes/blog/front-end-interview-css-questions
- https://quizlet.com/28293152/front-end-interview-questions-css-flash-cards/
- http://peterdoes.it/2015/12/03/a-personal-exercise-front-end-job-interview-questions-and-my-answers-all/
行內元素、塊級元素區別
行內元素:和其他元素都在一行上,高度、行高及外邊距和內邊距都不可改變,文字圖片的寬度不可改變,只能容納文本或者其他行內元素;其中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;
}
水平垂直居中的方式
flex
// 父容器
display: flex;
justify-content: center;
align-items: center;
position
// 父容器
position: relative;
// 子容器
position:absolute;
margin:auto;
top:0;
bottom:0;
left:0;
right:0;
position+transform
// 父容器
position: relative;
// 子容器
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
table-cell
<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的區別
是否隱藏 | 是否在文檔中占用空間 | 是否會觸發事件 | |
---|---|---|---|
display: none | 是 | 否 | 否 |
visibile: hidden | 是 | 是 | 否 |
opacity: 0 | 是 | 是 | 是 |
CSS中link和@import的區別
- link屬於HTML標簽,而@import是CSS提供的
- 頁面被加載的時,link會同時被加載,而@import引用的CSS會等到頁面被加載完再加載
- import只在IE5以上才能識別,而link是HTML標簽,無兼容問題
- link方式的樣式的權重 高於@import的權重
如何用css實現瀑布流布局
利用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;
}
已知父級盒子的寬高,子級img寬高未知,想讓img鋪滿父級盒子且圖片不能變形
需要用到css
的object-fit
屬性
div {
width: 200px;
height: 200px;
}
img {
object-fit: cover;
width: 100%;
height: 100%;
}
iframe的作用
iframe是用來在網頁中插入第三方頁面,早期的頁面使用iframe主要是用於導航欄這種很多頁面都相同的部分,這樣在切換頁面的時候避免重復下載。
優點
- 便於修改,模擬分離,像一些信息管理系統會用到。
- 但現在基本不推薦使用。除非特殊需要,一般不推薦使用。
缺點
- iframe的創建比一般的DOM元素慢了1-2個數量級
- iframe標簽會阻塞頁面的的加載,如果頁面的onload事件不能及時觸發,會讓用戶覺得網頁加載很慢,用戶體驗不好,在Safari和Chrome中可以通過js動態設置iframe的src屬性來避免阻塞。
- iframe對於SEO不友好,替換方案一般就是動態語言的Incude機制和ajax動態填充內容等。
css hack是什么
由於不同的瀏覽器,比如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
過渡與動畫的區別是什么
- transition
可以在一定的時間內實現元素的狀態過渡為最終狀態,用於模擬以一種過渡動畫效果,但是功能有限,只能用於制作簡單的動畫效果而動畫屬性 - animation
可以制作類似Flash動畫,通過關鍵幀控制動畫的每一步,控制更為精確,從而可以制作更為復雜的動畫。
什么是外邊距合並
外邊距合並指的是,當兩個垂直外邊距相遇時,它們將形成一個外邊距。
合並后的外邊距的高度等於兩個發生合並的外邊距的高度中的較大者。
去除inline-block元素間間距的方法
- 移除空格
- 使用margin負值
- 使用font-size:0
- letter-spacing
- word-spacing
更詳細的介紹請看:去除inline-block元素間間距的N種方法
# JavaScript問題 ## 同源策略 同源策略可防止 JavaScript 發起跨域請求。源被定義為 URI、主機名和端口號的組合。此策略可防止頁面上的惡意腳本通過該頁面的文檔對象模型,訪問另一個網頁上的敏感數據。
跨域
- 原因
瀏覽器的同源策略導致了跨域 - 作用
用於隔離潛在惡意文件的重要安全機制 - 解決
- jsonp ,允許 script 加載第三方資源
- 反向代理(nginx 服務內部配置 Access-Control-Allow-Origin *)
- cors 前后端協作設置請求頭部,Access-Control-Allow-Origin 等頭部信息
- iframe 嵌套通訊,postmessage
https://zhuanlan.zhihu.com/p/41479807
跨域資源共享 CORS 阮一峰
JSONP
這是我認為寫得比較通俗易懂的一篇文章 直接轉載過來
https://blog.csdn.net/hansexploration/article/details/80314948
域名收斂
PC 時代為了突破瀏覽器的域名並發限制。有了域名發散。
瀏覽器有並發限制,是為了防止DDOS攻擊。
域名收斂:就是將靜態資源放在一個域名下。減少DNS解析的開銷。
域名發散:是將靜態資源放在多個子域名下,就可以多線程下載,提高並行度,使客戶端加載靜態資源更加迅速。
域名發散是pc端為了利用瀏覽器的多線程並行下載能力。而域名收斂多用與移動端,提高性能,因為dns解析是是從后向前迭代解析,如果域名過多性能會下降,增加DNS的解析開銷。
事件綁定的方式
- 嵌入dom
<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)
}
})
事件循環
事件循環是一個單線程循環,用於監視調用堆棧並檢查是否有工作即將在任務隊列中完成。如果調用堆棧為空並且任務隊列中有回調函數,則將回調函數出隊並推送到調用堆棧中執行。
事件模型
- DOM0
直接綁定
<input onclick="sayHi()"/>
btn.onclick = function() {}
btn.onclick = null
- DOM2
DOM2級事件可以冒泡和捕獲
通過addEventListener綁定
通過removeEventListener解綁
// 綁定
btn.addEventListener('click', sayHi)
// 解綁
btn.removeEventListener('click', sayHi)
- DOM3
DOM3具有更多事件類型
DOM3級事件在DOM2級事件的基礎上添加了更多的事件類型,全部類型如下:
UI事件,當用戶與頁面上的元素交互時觸發,如:load、scroll
焦點事件,當元素獲得或失去焦點時觸發,如:blur、focus
鼠標事件,當用戶通過鼠標在頁面執行操作時觸發如:dbclick、mouseup
滾輪事件,當使用鼠標滾輪或類似設備時觸發,如:mousewheel
文本事件,當在文檔中輸入文本時觸發,如:textInput
鍵盤事件,當用戶通過鍵盤在頁面上執行操作時觸發,如:keydown、keypress
合成事件,當為IME(輸入法編輯器)輸入字符時觸發,如:compositionstart
變動事件,當底層DOM結構發生變化時觸發,如:DOMsubtreeModified
https://www.jianshu.com/p/3acdf5f71d5b
如何自定義事件
- 原生提供了3個方法實現自定義事件
- createEvent,設置事件類型,是 html 事件還是 鼠標事件
- initEvent 初始化事件,事件名稱,是否允許冒泡,是否阻止自定義事件
- dispatchEvent 觸發事件
target和currentTarget區別
- event.target
返回觸發事件的元素 - event.currentTarget
返回綁定事件的元素
prototype和__proto__的關系是什么
所有的對象都擁有__proto__屬性,它指向對象構造函數的prototype屬性
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,這就是閉包。
但也因為閉包引用着另一個函數的變量,導致另一個函數已經不使用了也無法銷毀,所以閉包使用過多,會占用較多的內存,這也是一個副作用。
有一個函數,參數是一個函數,返回值也是一個函數,返回的函數功能和入參的函數相似,但這個函數只能執行3次,再次執行無效,如何實現
這個題目是考察閉包的使用
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
來控制函數的執行
實現add函數,讓add(a)(b)和add(a,b)兩種調用結果相同
function add(a, b) {
if (b === undefined) {
return function(x) {
return a + x
}
}
return a + b
}
Ajax
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()
使用Ajax的優缺點分別是什么
優點
- 交互性更好。來自服務器的新內容可以動態更改,無需重新加載整個頁面。
- 減少與服務器的連接,因為腳本和樣式只需要被請求一次。
- 狀態可以維護在一個頁面上。JavaScript 變量和 DOM 狀態將得到保持,因為主容器頁面未被重新加載。
- 基本上包括大部分 SPA 的優點。
缺點
- 動態網頁很難收藏。
- 如果 JavaScript 已在瀏覽器中被禁用,則不起作用。
- 有些網絡爬蟲不執行 JavaScript,也不會看到 JavaScript 加載的內容。
- 基本上包括大部分 SPA 的缺點。
Ajax和Fetch區別
- ajax是使用XMLHttpRequest對象發起的,但是用起來很麻煩,所以ES6新規范就有了fetch,fetch發一個請求不用像ajax那樣寫一大堆代碼。
- 使用fetch無法取消一個請求,這是因為fetch基於Promise,而Promise無法做到這一點。
- 在默認情況下,fetch不會接受或者發送cookies
- fetch沒有辦法原生監測請求的進度,而XMLHttpRequest可以
- fetch只對網絡請求報錯,對400,500都當做成功的請求,需要封裝去處理
- fetch由於是ES6規范,兼容性上比不上XMLHttpRequest
變量提升
var會使變量提升,這意味着變量可以在聲明之前使用。let和const不會使變量提升,提前使用會報錯。
變量提升(hoisting)是用於解釋代碼中變量聲明行為的術語。使用var關鍵字聲明或初始化的變量,會將聲明語句“提升”到當前作用域的頂部。 但是,只有聲明才會觸發提升,賦值語句(如果有的話)將保持原樣。
使用let、var和const創建變量有什么區別
用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';
對象淺拷貝和深拷貝有什么區別
在 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))
}
數據類型
- Undefined
- Null
- Boolean
- Number
- String
- Object
- symbol(ES6新增)
內置函數(原生函數)
- String
- Number
- Boolean
- Object
- Function
- Array
- Date
- RegExp
- Error
- Symbol
原始值 "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區別
特性 | cookie | localStorage | sessionStorage |
---|---|---|---|
由誰初始化 | 客戶端或服務器,服務器可以使用Set-Cookie 請求頭。 |
客戶端 | 客戶端 |
數據的生命周期 | 一般由服務器生成,可設置失效時間,如果在瀏覽器生成,默認是關閉瀏覽器之后失效 | 永久保存,可清除 | 僅在當前會話有效,關閉頁面后清除 |
存放數據大小 | 4KB | 5MB | 5MB |
與服務器通信 | 每次都會攜帶在HTTP頭中,如果使用cookie保存過多數據會帶來性能問題 | 僅在客戶端保存 | 僅在客戶端保存 |
用途 | 一般由服務器生成,用於標識用戶身份 | 用於瀏覽器緩存數據 | 用於瀏覽器緩存數據 |
訪問權限 | 任意窗口 | 任意窗口 | 當前頁面窗口 |
自執行函數?用於什么場景?好處?
自執行函數:1、聲明一個匿名函數2、馬上調用這個匿名函數。
作用:創建一個獨立的作用域。
好處:防止變量彌散到全局,以免各種js庫沖突。隔離作用域避免污染,或者截斷作用域鏈,避免閉包造成引用變量無法釋放。利用立即執行特性,返回需要的業務函數或對象,避免每次通過條件判斷來處理
場景:一般用於框架、插件等場景
多個頁面之間如何進行通信
有如下幾個方式:
- cookie
- web worker
- localeStorage和sessionStorage
css動畫和js動畫的差異
- 代碼復雜度,js 動畫代碼相對復雜一些
- 動畫運行時,對動畫的控制程度上,js 能夠讓動畫,暫停,取消,終止,css動畫不能添加事件
- 動畫性能看,js 動畫多了一個js 解析的過程,性能不如 css 動畫好
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
new一個對象經歷了什么
function Test(){}
const test = new Test()
- 創建一個新對象:
const obj = {}
- 設置新對象的constructor屬性為構造函數的名稱,設置新對象的__proto__屬性指向構造函數的prototype對象
obj.constructor = Test
obj.__proto__ = Test.prototype
- 使用新對象調用函數,函數中的this被指向新實例對象
Test.call(obj)
- 將初始化完畢的新對象地址,保存到等號左邊的變量中
bind、call、apply的區別
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
。 - 如果符合上述多個規則,則較高的規則(1 號最高,4 號最低)將決定
this
的值。 - 如果該函數是 ES2015 中的箭頭函數,將忽略上面的所有規則,
this
被設置為它被創建時的上下文。
想獲得更深入的解釋,請查看他在 Medium 上的文章。
如何確定this指向
如果要判斷一個運行中函數的 this 綁定,就需要找到這個函數的直接調用位置。找到之后就可以順序應用下面這四條規則來判斷 this 的綁定對象。
- 由 new 調用?綁定到新創建的對象。
- 由 call 或者 apply (或者 bind )調用?綁定到指定的對象。
- 由上下文對象調用?綁定到那個上下文對象。
- 默認:在嚴格模式下綁定到 undefined ,否則綁定到全局對象。
一定要注意,有些調用可能在無意中使用默認綁定規則。如果想“更安全”地忽略 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
當你在瀏覽器輸入一個地址后發生了什么
頁面大量圖片,如何優化加載,優化用戶體驗
- 圖片懶加載。在頁面的未可視區域添加一個滾動事件,判斷圖片位置與瀏覽器頂端的距離與頁面的距離,如果前者小於后者,優先加載。
- 如果為幻燈片、相冊等,可以使用圖片預加載技術,將當前展示圖片的前一張和后一張優先下載。
- 如果圖片為css圖片,可以使用CSSsprite,SVGsprite等技術。
- 如果圖片過大,可以使用特殊編碼的圖片,加載時會先加載一張壓縮的特別厲害的縮略圖,以提高用戶體驗。
- 如果圖片展示區域小於圖片的真實大小,應在服務器端根據業務需要先進行圖片壓縮,圖片壓縮后大小與展示一致。
https://www.jianshu.com/p/5d82bba9e1a1
js網絡請求性能優化之防抖與節流
-
防抖(debounce)
在函數需要頻繁觸發時,只有當有足夠空閑的時間時,才執行一次。就好像在百度搜索時,每次輸入之后都有聯想詞彈出,這個控制聯想詞的方法就不可能是輸入框內容一改變就觸發的,他一定是當你結束輸入一段時間之后才會觸發。 -
節流(thorttle)
預定一個函數只有在大於等於執行周期時才執行,周期內調用不執行。就好像你在淘寶搶購某一件限量熱賣商品時,你不斷點刷新點購買,可是總有一段時間你點上是沒有效果,這里就用到了節流,就是怕點的太快導致系統出現bug。 -
區別
在發生持續觸發事件時,防抖設置事件延遲並在空閑時間去觸發事件,而節流則是隔一定的時間觸發一次。
具體請看:
https://blog.csdn.net/jacoox/article/details/80719456
如何做到修改url參數頁面不刷新
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,')
}
請用js去除字符串空格
去除所有空格
str.replace(/\s/g, '')
去除兩邊空格
str.replace(/^\s+|\s+$/g, '')
// 原生方法
str.trim()
# HTTP問題 ## RESTful 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請求都不會破壞數據
https://blog.csdn.net/jnshu_it/article/details/80203696
GET和POST的區別
- 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和Content-Type
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(服務器錯誤狀態碼) | 服務器處理請求出錯 |
HTTP緩存
https://segmentfault.com/a/1190000010690320
如何處理不讓別人盜用你的圖片,訪問你的服務器資源
- http header, 對refer做判斷看來源是不是自己的網站,如果不是就拒絕
- 通過session校驗,如果不通過特定服務生成cookie和session就不能請求得到資源
Http與Https的區別
- HTTP 的URL 以http:// 開頭,而HTTPS 的URL 以https:// 開頭
- HTTP 是不安全的,而 HTTPS 是安全的
- HTTP 標准端口是80 ,而 HTTPS 的標准端口是443
- 在OSI 網絡模型中,HTTP工作於應用層,而HTTPS 的安全傳輸機制工作在傳輸層
- HTTP 無法加密,而HTTPS 對傳輸的數據進行加密
- HTTP無需證書,而HTTPS 需要CA機構wosign的頒發的SSL證書
https://zhuanlan.zhihu.com/p/33778904
什么是Http協議無狀態協議?怎么解決Http協議無狀態協議?
無狀態協議對於事務處理沒有記憶能力。缺少狀態意味着如果后續處理需要前面的信息也就是說,
當客戶端一次HTTP請求完成以后,客戶端再發送一次HTTP請求,HTTP並不知道當前客戶端是一個”老用戶“。
可以使用Cookie來解決無狀態的問題,Cookie就相當於一個通行證,第一次訪問的時候給客戶端發送一個Cookie,
當客戶端再次來的時候,拿着Cookie(通行證),那么服務器就知道這個是”老用戶“。
https://zhuanlan.zhihu.com/p/33778904
常用的HTTP方法有哪些
- GET:用於請求訪問已經被URL(統一資源標識符)識別的資源,可以通過URL傳參給服務器。
- POST:用於傳輸信息給服務器,主要功能與Get方法類似,但一般推薦POST方式。
- PUT:傳輸文件,報文主體包含文件內容,保存到對應URL位置。
- HEAD:獲取報文首部,與GET方法類似,只是不返回報文主體,一般用於驗證URL是否有效。
- DELET:刪除文件,與PUT方法相反,刪除對應URL位置的文件。OPTIONS:查詢相應URL支持的HTTP方法。
一次完整的HTTP請求所經歷的7個步驟
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連接
https://juejin.im/post/5a8102e0f265da4e710f5910
# Vue問題 ## 什么是mvvm MVVM最早由微軟提出來,它借鑒了桌面應用程序的MVC思想,在前端頁面中,把Model用純JavaScript對象表示,View負責顯示,兩者做到了最大限度的分離 把Model和View關聯起來的就是ViewModel。
ViewModel負責把Model的數據同步到View顯示出來,還負責把View的修改同步回Model
View 和 Model 之間的同步工作完全是自動的,無需人為干涉(由viewModel完成,在這里指VUE)
因此開發者只需關注業務邏輯,不需要手動操作DOM, 不需要關注數據狀態的同步問題,復雜的數據狀態維護完全由 MVVM 來統一管理
ViewModel如何編寫?
需要用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之間的關系
Vue的優點是什么
mvvm的優點即是vue的優點,在這里再總結一下:
數據和視頻之間的同步工作完全是自動的,無需人為干涉,所以開發者只需關注業務邏輯,不需要手動操作DOM, 不需要關注數據狀態的同步問題,
復雜的數據狀態維護完全由 MVVM 來統一管理,節省了很多精力。
對於生命周期的理解
創建一個Vue實例,是一個漫長的過程,要經歷初始化,數據合並,模板解析,數據渲染等等一系列過程。
所以,為了能實現在這個過程里面插入自己想要提前做的事情,就有了生命周期鈎子函數。
舉個例子:
一輛公交車,從出發點A站到終點站B,中間有很多站點,公交車每到一個站點,就得停下來,
等待客人上車,然后再駛往下一個站點,一直到終點站為止。
A和B之間的站點,就像是這個路程的生命周期。每一個站點都是一個不同的生命周期(站點名不同),
只要到了站點,就得執行該站點對應的生命周期函數,
只不過每個站點的生命周期函數都是一樣的(等待客人上車)。
Vue中的生命周期也是一樣,對應了Vue實例從創建到結束之間的每一個過程。
例如,Vue的beforeCreate
周期,指的就是Vue在實例初始化之后,數據觀測 (data observer) 和 event/watcher 事件配置之前被調用。
至於Vue具體的生命周期函數有哪些,請看官網API文檔
組件傳值
- 父組件通過prop向子組件傳值
- 子組件通過事件向父組件傳值
- 子組件與子組件之間不能直接傳值,需要通過父組件來做間接傳值,在這種情況下推薦使用vuex
具體例子請看官方文檔
vue數據綁定原理
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原理
說簡單點,vue-router的原理就是通過對URL地址變化的監聽,繼而對不同的組件進行渲染。
每當URL地址改變時,就對相應的組件進行渲染。原理是很簡單,實現方式可能有點復雜,主要有hash模式和history模式。
如果想了解得詳細點,建議百度或者閱讀源碼。
vuex原理
vuex的原理其實非常簡單,它為什么能實現所有的組件共享同一份數據?
因為vuex生成了一個store實例,並且把這個實例掛在了所有的組件上,所有的組件引用的都是同一個store實例。
store實例上有數據,有方法,方法改變的都是store實例上的數據。由於其他組件引用的是同樣的實例,所以一個組件改變了store上的數據,
導致另一個組件上的數據也會改變,就像是一個對象的引用。
如果對vuex的實現有興趣,可以看看我自己造的一個vue輪子對應的vuex插件。它實現了除vuex模塊外的所有功能。
v-if和v-show的區別
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怎么實現頁面的權限控制
利用 vue-router
的 beforeEach
事件,可以在跳轉頁面前判斷用戶的權限(利用 cookie 或 token),是否能夠進入此頁面,如果不能則提示錯誤或重定向到其他頁面,在后台管理系統中這種場景經常能遇到。
keep-alive有什么作用
在 Vue
中,每次切換組件時,都會重新渲染。如果有多個組件切換,又想讓它們保持原來的狀態,避免重新渲染,這個時候就可以使用 keep-alive
。
keep-alive
可以使被包含的組件保留狀態,或避免重新渲染。
計算屬性有什么作用
先來看一下計算屬性的定義:
當其依賴的屬性的值發生變化的時,計算屬性會重新計算。反之則使用緩存中的屬性值。
計算屬性和vue中的其它數據一樣,都是響應式的,只不過它必須依賴某一個數據實現,並且只有它依賴的數據的值改變了,它才會更新。
$route和$router的區別
$route
是路由信息對象,包括path
,params
,hash
,query
,fullPath
,matched
,name
等路由信息參數。
而 $router
是路由實例對象,包括了路由的跳轉方法,鈎子函數等
watch的作用是什么
watch
主要作用是監聽某個數據值的變化。和計算屬性相比除了沒有緩存,作用是一樣的。
借助 watch
還可以做一些特別的事情,例如監聽頁面路由,當頁面跳轉時,我們可以做相應的權限控制,拒絕沒有權限的用戶訪問頁面。