瀏覽器與網路#
瀏覽器內核#
渲染引擎#
用來解釋網頁語法並渲染到網頁上。負責顯示請求的內容。如果請求的內容是 HTML,它就負責解析 HTML 和 CSS 內容,並將解析後的內容顯示在螢幕上。
常見的渲染引擎可以分這四種:Trident、Gecko、Blink、Webkit。
js 引擎#
用於解析和執行 JavaScript 代碼。chrome 的 javascript 解釋器是 V8。
地址欄輸入 url,到頁面呈現,的過程#
- 將 url 解析成 ip 地址,先查找快取,沒有則進行 dns 解析,向域名伺服器處獲取 ip
- 建立 tcp 連接
- ssl 有一個驗證的過程
- 發送 http 請求
- 收到伺服器響應的資源文檔
- 構建 dom 樹和 cssom 樹,佈局,再渲染
- 畫面呈現
http 與 https#
http#
http:超文本傳輸協議,位於應用層,基於 TCP/IP 協議,使用無狀態連接。
請求字段#
- User-agent,瀏覽器標識
- Authorization,認證信息,可以放 token
- Origin,後端可以判斷,access-control-allow-origin
- cookie
- Cache-control:裡面有 max-age,判斷是否命中強快取
- If-Modified-Since,304,協商快取
- If-None-Match,304
響應字段#
- set-cookie,
Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
- ETag,對於某個資源的某個特定版本的一個標識符,通常是一個 消息散列
ETag: "737060cd8c284d8af7ad3082f209582d"
- Expires,指定一個日期 / 時間,超過該時間則認為此回應已經過期,
Expires: Thu, 01 Dec 1994 16:00:00 GMT
- Last-Modified,所請求的對象的最後修改日期,
Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
狀態碼#
1:消息,臨時響應,代表請求已被接受,需要繼續處理
2:成功,代表請求已成功被伺服器接收、理解、並接受。
3:重定向,後續的請求地址(重定向目標)在本次響應的 Location 域中指明。
被請求的資源已永久移動到新位置,並且將來任何對此資源的引用都應該使用本響應返回的若干個 URI 之一。
要求客戶端執行臨時重定向(原始描述短語為 “Moved Temporarily”)
對應當前請求的響應可以在另一個 URI 上被找到
- 304 Not Modified
未修改。所請求的資源未修改,伺服器返回此狀態碼時,不會返回任何資源。
表示資源在由請求頭中的If-Modified-Since
或If-None-Match
參數指定的這一版本之後,未曾被修改。在這種情況下,由於客戶端仍然具有以前下載的副本,因此不需要重新傳輸資源。
注:與協商快取有關
- 305 Use Proxy
被請求的資源必須通過指定的代理才能被訪問。Location 域中將給出指定的代理所在的 URI 信息,接收者需要重複發送一個單獨的請求,通過這個代理才能訪問相應資源。
4:客戶端錯誤
- 400 Bad Request
由於明顯的客戶端錯誤(例如,格式錯誤的請求語法,太大的大小,無效的請求消息或欺騙性路由請求),伺服器不能或不會處理該請求。
- 401 Unauthorized
未認證,類似於 403 Forbidden
- 404 Not Found
- 405 Method Not Allowed
- 418 I’m a teapot
5:伺服器錯誤
- 500 Internal Server Error
通用錯誤消息,伺服器遇到了未曾預料的狀況,導致了它無法完成對請求的處理。沒有給出具體錯誤信息。
- 502 Bad Gateway
作為網關或者代理工作的伺服器嘗試執行請求時,從上游伺服器接收到無效的響應。
- 503 Service Unavailable
由於臨時的伺服器維護或者過載,伺服器當前無法處理請求。這個狀況是暫時的,並且將在一段時間以後恢復。
- 504 Gateway Timeout
作為網關或者代理工作的伺服器嘗試執行請求時,未能及時從上游伺服器(URI 標識出的伺服器,例如HTTP、FTP、LDAP)或者輔助伺服器(例如DNS)收到響應。
https#
在 http 的基礎上加了 ssl 協議,是 http 的安全版本。
http2.0 的改進#
- 基於 https,安全性更有保證
- 使用二進制格式,比以前基於文本的傳輸更具普適性
- 多路復用,是以前長連接的增強
Get、Post、Put、Delete、Options、Trace、Connect#
請求類型#
- get:請求資源,可以使用快取
- Post:提交數據,POST 產生兩個 TCP 數據包,瀏覽器先發送 header,伺服器響應 100 continue,瀏覽器再發送 data,伺服器響應 200 ok(返回數據),不能使用快取
- Put:更新資源
- Delete:刪除資源
- Options:返回某資源所支持的 HTTP 請求方法
- Trace:回顯伺服器收到的請求,主要用於測試或診斷
- Connect:HTTP/1.1 協議中預留給能夠將連接改為管道方式的代理伺服器
幂等#
參考:mdn
一個 HTTP 方法是幂等的,指的是同樣的請求被執行一次與連續執行多次的效果是一樣的,伺服器的狀態也是一樣的。換句話說就是,幂等方法不應該具有副作用(統計用途除外)。在正確實現的條件下, GET
, HEAD
, PUT
和 DELETE
等方法都是幂等的,而 POST
方法不是。所有的 safe 方法也都是幂等的。
幂等性只與後端伺服器的實際狀態有關,而每一次請求接收到的狀態碼不一定相同。例如,第一次調用 DELETE
方法有可能返回 200
,但是後續的請求可能會返回 404
。 DELETE
的言外之意是,開發者不應該使用 DELETE
法實現具有刪除最後條目功能的 RESTful API。
Restful 規範#
用 url 來指代資源,用請求類型來指代動作。
Ajax、Fetch 與 axios#
Ajax#
使用 XMLHttpRequest
實現
特點:
- 會遇到 XSS、CSRF 攻擊
- 本身是針對 MVC 的編程,不符合現在前端 MVVM 的浪潮。
- 基於原生的 XHR 開發,XHR 本身的架構不清晰,已經有了 fetch 的替代方案。
Fetch#
Fetch 是 XHR 的替代,ES6 出現的,使用了 ES6 中的 promise 對象,由 js 原生提供,沒有使用 XMLHttpRequest 對象
特點:
- 符合關注分離,沒有將輸入、輸出和用事件來跟蹤的狀態混雜在一個對象裡
- 更好更方便的寫法
- 更加底層,提供的 API 豐富(request, response)
- 脫離了 XHR,是 ES 規範裡新的實現方式
- fetch 只對網路請求報錯,對 400,500 都當做成功的請求,需要封裝去處理
- fetch 預設不會帶 cookie,需要添加配置項
- fetch 不支持 abort,不支持超時控制,使用 setTimeout 及 Promise.reject 的實現的超時控制並不能阻止請求過程繼續在後台運行,造成了量的浪費
- fetch 沒有辦法原生監測請求的進度,而 XHR 可以。
fetch(url, {
body: JSON.stringify(data), // must match 'Content-Type' header
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, same-origin, *omit
headers: {
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json'
},
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
})
.then(response => response.json()) // parses response to JSON
}
axios#
axios 是一個基於 Promise 的用於瀏覽器和 nodejs 的 HTTP 客戶端,本質上也是對原生 XHR 的封裝,只不過它是 Promise 的實現版本,符合最新的 ES 規範。
- 支持 Promise API。
- 提供了一些並發請求的接口(重要,方便了很多的操作)。
- 在瀏覽器中創建 XMLHttpRequests。在 node.js 則創建 http 請求。(自動性強)
- 支持攔截請求和響應。
- 自動轉換 JSON 數據。
- 客戶端支持防禦 CSRF、 XSRF。
Fetch 與 Ajax 的不同#
- 當接收到一個代表錯誤的 HTTP 狀態碼時,從
fetch()
返回的 Promise 不會被標記為 reject,而是標記為 resolve (返回值的ok
屬性設置為 false ),僅當網路故障時或請求被阻止時,才會標記為 reject。 fetch()
** 可以接受跨域 cookies;** 你也可以使用fetch()
建立起跨域會話。fetch
不會發送 cookies。除非你使用了_credentials_ 的初始化選項。
fetch 發送 2 次請求的原因#
用 fetch 的 post 請求時,第一次發送了個 Options 請求,詢問伺服器是否支持該類型的請求,如果伺服器支持,則在第二次中發送真正的請求。
強快取與協商快取#
流程圖#
快取#
快取是一種保存資源副本並在下次請求時直接使用該副本的技術。當 web 快取發現請求的資源已經被存儲,它會攔截請求,返回該資源的拷貝,而不會去源伺服器重新下載。
好處:緩解伺服器端壓力,提升性能 (獲取資源的耗時更短了)
告訴瀏覽器在約定的這個時間前,可以直接從快取中獲取資源,而無需跑到伺服器去獲取。
http 快取機制主要在 http 響應頭中設定,響應頭中相關字段為Expires、Cache-Control、Last-Modified、Etag。
強快取(過期時間)#
瀏覽器不會像伺服器發送任何請求,直接從本地快取中讀取文件並返回
Cache-Control(優先級更高):當值設為 max-age=300 時,則代表在這個請求正確返回時間的 5 分鐘內再次加載資源,就會命中強快取。
Expires:過期時間,如果設置了時間,則瀏覽器會在設置的時間內直接讀取快取,不再請求
協商快取(修改時間)#
當資源過期時,向伺服器發送請求,伺服器根據請求頭參數來判斷是否命中協商快取,如果命中,則返回 304 狀態碼並告訴瀏覽器從快取中讀取資源,否則返回 200 狀態碼和新資源。
- 協商快取標識 1,加上時間戳的 hash 值:etag/if-none-match
當資源過期時,瀏覽器發現響應頭裡有 Etag, 則再次像伺服器請求時帶上請求頭 if-none-match (值是 Etag 的值)。伺服器收到請求進行比對,決定返回新頁面 200 或 304
- 標識 2,最新修改時間:Last-modified/if-modify-since
當資源過期時(瀏覽器判斷 Cache-Control 標識的 max-age 過期),發現響應頭具有 Last-Modified 聲明,則再次向伺服器請求時帶上頭 if-modified-since,表示請求時間。伺服器收到請求後發現有 if-modified-since 則與被請求資源的最後修改時間進行對比(Last-Modified), 若最後修改時間較新(大),說明資源又被改過,則返回最新資源,HTTP 200 OK; 若最後修改時間較舊(小),說明資源無新修改,響應 HTTP 304 走快取。
Cache-control#
- max-age:最大有效時間
- no-cache:不使用強快取,需要與伺服器驗證快取是否新鮮
- no-store:不使用快取,包括強快取和協商快取
- public:所有的內容都可以被快取,包括客戶端和代理伺服器,比如 CDN
- provate:所有的內容只有單個用戶即客戶端才可以快取,代理伺服器不能快取。是默認值。
字段#
- name
- value
- size
- domain
- path
- expires/max-age:過期時間
- http:httponly,無法通過 document.cookie 來獲取 cookie
- secure:設置是否只能由 https 來傳遞此 cookie
Cookie 與 Session#
作用:由於 http 的無狀態性(這一次請求和上一次請求是沒有任何關係的,互不認識的,沒有關聯的),為了使某個域名下的所有網頁能夠共享某些數據,session 和 cookie 出現了。
Cookie 是用戶通行證,存儲在客戶端,在發送請求時附帶。session 有如用戶信息檔案表,裡面包含了用戶的認證信息和登錄狀態等信息,保存在伺服器端。
Cookie 和 Session 共同使用#
- 客戶端發一個 http 請求給伺服器
- 伺服器接受請求後,建立一個 session,發回 http 響應給客戶端,其中的 set-cookie 頭部帶有 sessionid
- 客戶端收到響應後,後續會自動在請求頭中加 cookie
- 伺服器接收請求,分解 cookie,驗證成功後將請求返回給客戶端。
用 session 只需在客戶端保存一個 id,大量數據都是保存在伺服器端,對伺服器有壓力。
用 cookie 不用 session,那麼賬戶信息全部保存在客戶端,一旦被劫持,全部信息都會泄露。並且客戶端數據量變大,網路傳輸的數據量也會變大。
Token#
是一種伺服器無狀態的認證方式。Token 類似一個令牌,無狀態,用戶信息都被加密到 token 中,伺服器收到 token 後解密就可知道是哪個用戶。需要開發者手動添加。
token 在客戶端一般存放於 localStorage,cookie,或 sessionStorage 中。在伺服器一般存於數據庫中
token 的認證流程與 cookie 很相似
- 用戶登錄,成功後伺服器返回 Token 給客戶端。
- 客戶端收到數據後保存在客戶端
- 客戶端再次訪問伺服器,將 token 放入 headers 中
- 伺服器端採用 filter 過濾器校驗。校驗成功則返回請求數據,校驗失敗則返回錯誤碼
用 Token,不用 Cookie+Session 的優點#
- 避免 CSRF 攻擊
用戶登錄銀行網頁,同時登錄了危險網頁,攻擊者在網頁放一個表單,該表單提交 src 為http://www.bank.com/api/transfer
,body 為count=1000&to=Tom
。倘若是 session+cookie,表單發起的 POST 請求不受到瀏覽器同源策略限制,可以使用其它域的 Cookie 向其它域發送 POST 請求,形成 CSRF 攻擊。在 post 請求的瞬間,cookie 會被瀏覽器自動添加到請求頭中。但 token 不同,token 是開發者為了防範 csrf 而特別設計的令牌,瀏覽器不會自動添加到 headers 裡,攻擊者也無法訪問用戶的 token,所以提交的表單無法通過伺服器過濾,也就無法形成攻擊。
- Token 是一種伺服器無狀態的認證方式,而 cookie+session 是有狀態的。( 所謂無狀態就是伺服器並不會保存身份認證相關的數據。)
JWT#
頭部 + 負載 + 簽名
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ,加鹽secret組合加密
// 將這三部分用.連接成一個完整的字符串,構成了最終的jwt:
在客戶端應用:一般是在請求頭裡加入Authorization
,並加上Bearer
標註
流程和 token 的一樣
缺點:一旦 JWT 簽發了,在到期之前就會始終有效,除非伺服器部署額外的邏輯。JWT 本身包含了認證信息,一旦泄露,任何人都可以獲得該令牌的所有權限。
cookie 和 token 都存放在 header 中,為什麼不會劫持 token?#
- Cookie
攻擊者通過 xss 拿到用戶的 cookie 然後就可以偽造 cookie 了。
通過 csrf 在同個瀏覽器下面通過瀏覽器會自動帶上 cookie 的特性
在通過 用戶網站 - 攻擊者網站 - 攻擊者請求用戶網站的方式 瀏覽器會自動帶上 cookie
- token
不會被瀏覽器帶上 問題 2 解決
token 是放在 jwt 裡面下發給客戶端的 而且不一定存儲在哪裡 不能通過 document.cookie 直接拿到,通過 jwt+ip 的方式 可以防止 被劫持 即使被劫持 也是無效的 jwt
瀏覽器存儲方式:Cookie、SessionStorage、LocalStorage#
共同點:都是存儲在瀏覽器端
不同點:
-
cookie 可以在瀏覽器和伺服器中來回傳遞的,而餘下兩者是只在本地保存的。
-
可存儲數據的大小不一樣,cookie 數據不能超過 4k,剩下的就比較大了,大概是在 5M 左右。
-
數據有效期不一樣,sessionStorage 當關閉瀏覽器的時候就失效了,localStorage 一直有效,需要手動清
除,而 cookie 只在有效期之內一直有效。
- 作用域不一樣,localStorage、cookie 在所有同源窗口中都是共享的,sessionStorage 是某個窗口私有
的。
跨頁面通信#
同源頁面(不在同一 tab):可以使用 LocalStorage
非同源頁面:iframe、postmessage
H5 之後為 window 新增了 window.postMessage () 方法,第一個參數是要發送的數據,第二個參數是域名。
Doctype#
聲明於文檔最前面,告訴伺服器以什麼方式(例如 h5)來渲染頁面。
運行模式分:嚴格模式(瀏覽器支持的最高版本)、混雜模式(向後兼容)
XSS#
跨站腳本攻擊,指攻擊者在網頁中注入惡意代碼,在用戶瀏覽網頁的時候進行攻擊。
類型:
- 反射型:將攻擊代碼放在 url 地址的請求參數中
- 存儲型:攻擊者輸入一些數據並且存儲到了數據庫中,其他瀏覽者看到的時候進行攻擊。
- DOM 型:攻擊者通過各種手段將惡意腳本注入用戶的頁面中
處理:
- set-cookie:httponly,禁止 javascript 腳本來訪問 cookie,secure - 這個屬性告訴瀏覽器僅在請求為 https 的時候發送 cookie。
- 輸入檢查:對於用戶的任何輸入要進行檢查、過濾和轉義
- 輸出檢查
CSRF#
Cross Site Request Forgery,跨站請求偽造
在講 token 時講了
防禦 CSRF 攻擊主要有三種策略:
- 驗證 HTTP Referer 字段;
- 在請求地址中添加 token 並驗證;
- 在 HTTP 頭中自定義屬性並驗證。
addEventListener#
addEventListener(event, function, useCapture)
其中,event 指定事件名;function 指定要事件觸發時執行的函數;useCapture 指定事件是否在捕獲或冒泡階段執行。
跨域#
可看:參考
- postMessage 跨域:可以跨域操作的 window 屬性之一。
- CORS:伺服器設置 Access-Control-Allow-Origin 即可,前端無須設置,若要帶 cookie 請求,前後端都需要設置。
Access-Control-Allow-Origin *;
Access-Control-Allow-Methods "POST, GET, OPTIONS";
Access-Control-Allow-Headers "Origin, Authorization, Accept";
Access-Control-Allow-Credentials true;
- 代理跨域:啟一個代理伺服器,實現數據的轉發
- JSONP:script 標籤 src 屬性中的鏈接卻可以訪問跨域的 js 腳本,利用這個特性,伺服器不再返回 JSON 格式的數據,而是返回一段調用某個函數的 js 代碼,在 src 中進行了調用,這樣實現了跨域。例如:
script.src = http://another.com/weather.json?callback=gotWeather;
,需要後端配合。
查看網站性能#
檢測頁面加載時間
-
被動檢測:在頁面設置檢測腳本,當用戶訪問網頁時,記錄數據,傳回數據庫分析
-
主動監測:搭建分佈式環境,模擬用戶訪問頁面,主動採集數據並分析
前端優化#
- 降低請求量:合併資源,減少 http 請求數
- 加快請求速度:預解析 DNS、CDN 分發
- 使用快取
- 加快渲染:加載順序(js 放最後),SSR,使用 GPU
螢幕卡頓#
原因:
- 記憶體溢出
- 資源過大
- 資源加載
- canvas 繪製幀率
辦法:
- 在物體離開螢幕可視區域即回收銷毀
- 選用體積較小的資源,例如圖片,可以在近處放清晰度高的,遠處放清晰度低的
- 預加載
- 大部分顯示器刷新頻率為 60 次 /s,因此遊戲的每一幀繪製間隔時間需要小於 1000/60=16.7ms,才能讓用戶覺得不卡頓
js 加載過程阻塞,解決方法#
- 制定 script 標籤的 async 屬性,異步執行
- 設置 defer 屬性,腳本將在頁面完成解析時執行
BOM#
Brower object model,瀏覽器對象模型,它使 JavaScript 有能力與瀏覽器進行 “對話”。window 對象 所有瀏覽器都支持 window 對象。它表示瀏覽器窗口。
頂層對象#
- 瀏覽器:window/self
- web worker:self
- node:global
- ES5:頂層對象的屬性與全局變量等價
- ES6:globalThis
This#
同一段代碼為了能夠在各種環境,都能取到頂層對象,現在一般是使用this
變量,但是有局限性。
-
全局環境中,this 會返回等層對象。但是在 node 的模塊中,返回的是當前模塊,ES6 中返回的是 undifined。
-
函數裡的 this,作為對象方法運行返回的是對象,或者是調用方,單純作為函數運行會指向頂層對象。
-
ES2020 引入 globalThis 對象,在任何環境下都指向全局環境下的 this
CSS 與 html#
DOM#
DOM(Document Object Model—— 文檔對象模型)
Document#
查找#
-
document.getElementById(id)
-
document.getElementByClassName(class)
-
document.getElementByTagName(tag)
-
document.querySelector(selectors)
表示文檔中與指定的一組 CSS 選擇器匹配的第一個元素,一個 HTMLElement
對象。如果沒有匹配到,則返回 null。
var el = document.querySelector("div.user-panel.main input[name='login']");
這裡,一個 class 屬性為”user-panel main” 的 div 元素<div class="user-panel main">
內包含一個 name 屬性為”login” 的 input 元素<input name="login"/>
-
querySelectorAll()
,與指定選擇器匹配的所有元素的列表 -
ParentNode.firstElementChild
,返回第一個子元素或者 null -
ParentNode.lastElementChild
-
node.parentNode
創建#
用給定標籤名 tagName 創建一個新的元素。
node.cloneNode(deep)
返回調用該方法的節點的一個副本。deep,可選參數,是否採用深度克隆,如果為true,
則該節點的所有後代節點也都會被克隆,如果為false,則只克隆該節點本身.
加入#
parentNode.append(node|str)
在 ParentNode
的最後一個子節點之後插入一組 Node
對象或 DOMString
對象。被插入的 DOMString
對象等價為 Text
節點。
將一個節點附加到指定父節點的子節點列表的末尾處。
如果將被插入的節點已經存在於當前文檔的文檔樹中,那麼 appendChild()
只會將它從原先的位置移動到新的位置(不需要事先移除要移動的節點)。這意味著,一個節點不可能同時出現在文檔的不同位置。
如果某個節點已經擁有父節點,在被傳遞給此方法後,它首先會被移除,再被插入到新的位置。
若要保留已在文檔中的節點,可以先使用 Node.cloneNode()
方法來為它創建一個副本,再將副本附加到目標父節點下。請注意,用 cloneNode
製作的副本不會自動保持同步。
【與 Node.appendChild()
的差異】
ParentNode.append()
允許追加DOMString
對象,而Node.appendChild()
只接受Node
對象。ParentNode.append()
沒有返回值,而Node.appendChild()
返回追加的Node
對象。ParentNode.append()
可以追加多個節點和字符串,而Node.appendChild()
只能追加一個節點。
刪除#
parent.removeChild(child)
根據父母節點來刪除節點。刪除後的節點雖然不在文檔樹中了,但其實它還在內存中,可以隨時再次被添加到別的位置。
獲取子節點#
node.children()
,返回 一個 Node 的子elements
,是一個動態更新的HTMLCollection
。
CSS 選擇器#
基本選擇器#
- 通用選擇器:
* {}
- 元素 (類型) 選擇器:
p {}
- 類選擇器:
.class {}
- ID 選擇器:
#id {}
- 屬性選擇器:
[attr=value] {}
分組選擇器#
- , 的選擇器列表:
div, span {}
組合器(關係選符)#
- 後代組合器:
div span {}
- 直接子代組合器:
div>span {}
- 一般兄弟組合器:
A~B {}
- 緊鄰兄弟組合器:
A+B {}
- 列選擇器:
col || td
伪选择器#
- 伪類:
a:visited {}
,匹配所有曾被訪問過的<a>
元素。支持按照狀態信息來選擇元素。 - 伪元素:
p::first-line
,匹配所有<p>
元素的第一行。伪選擇器用於表示無法用 HTML 語義表達的實體。
CSS 優先級#
遞增的:
- 類型選擇器(例如,
h1
)和伪元素(例如,::before
) - 類選擇器 (例如,
.example
),屬性選擇器(例如,[type="radio"]
)和伪類(例如,:hover
) - ID 選擇器(例如,
#example
)。
帶有 !important
標記的樣式屬性的優先級最高;
通配選擇符(universal selector)(*
)關係選擇符(combinators)(+
, >
, ~
, ' '
, ||
)和 否定伪類(negation pseudo-class)(:not()
)對優先級沒有影響。
樣式表的來源不同時,優先級順序為:內聯樣式 > 內部樣式 > 外部樣式 > 瀏覽器用戶自定義樣式 > 瀏覽器默認樣式
盒子模型#
content-padding-border-margin#
- 標準(W3C)盒子模型:width = content
- IE 盒子模型:width = content+padding+border
content 大小自適應
使用 box-sizing 設置#
box-sizing
:規定兩個並排的帶邊框的框,語法為box-sizing:content-box/border-box/inherit
content-box
:寬度和高度分別應用到元素的內容框,在寬度和高度之外繪製元素的內邊距和邊框
border-box
:為元素設定的寬度和高度決定了元素的邊框盒,
inherit
:繼承父元素的 box-sizing
overflow#
CSS 屬性 overflow 定義當一個元素的內容太大而無法適應 塊級格式化上下文 時該做什麼。
- visible:默認值。內容不會被修剪,會呈現在元素框之外
- hidden:內容會被修剪,並且其餘內容不可見
- scroll:內容會被修剪,瀏覽器會顯示滾動條以便查看其餘內容
- auto:由瀏覽器定奪,如果內容被修剪,就會顯示滾動條
為使 overflow
有效果,塊級容器必須有一個指定的高度。除了 visible 外,其它都觸發 BFC
background-size#
用於調整背景圖片的寬和高,可以放大和縮小。(因為默認圖片佈局是根據其尺寸平鋪)
background-size: 300px 150px; // 寬和長,強制轉換
background-size: contain; // 確保兩個尺寸(圖片的寬和高)都小於或等於容器的相應尺寸。
background-size: cover; // 確保兩個尺寸(圖片的寬和高)都大於或等於容器的相應尺寸。
Background-origin#
animation#
從一個 CSS 樣式配置轉換到另一個 CSS 樣式配置
兩個部分:描述動畫的樣式規則,用於制定動畫開始、結束以及中間點樣式的關鍵幀
p {
animation-duration: 3s; // 持續時間
animation-name: slidein; // 動畫名字
background: red;
animation-iteration-count: 2; // 設置運行次數,無窮是默認值,或許直接寫infinite
animation-direction: alternate; // 來回播放,而不是從頭播放
}
@keyframes slidein {
from {
// 代表0%
margin-left: 100%;
width: 300%;
}
50% {
font-size: 300%;
}
to {
// 代表 100%
margin-left: 0%;
width: 100%;
}
}
transform#
box-sizing#
content 內容自適應。
- box-sizing: content-box;
表示標準的 W3C 盒子模型,width = content;
- box-sizing: border-box
表示的是 IE 盒子模型,width = border + padding + content
Border-radius#
- 邊框:
border-radius
,box-shadow
等;
畫一個三角形#
<div id="demo"></div>
#demo{ width:0px; height:0px; border:40px solid transparent; border-bottom:80px
solid red; }
垂直居中#
#box {
width: 300px;
height: 300px;
background: #ddd;
}
#child {
background: orange;
position: absolute;
top: 50%;
transform: translate(0, -50%);
}
// 2
#box {
width: 300px;
background: #ddd;
padding: 100px 0;
}
#child {
width: 200px;
height: 100px;
background: orange;
}
// 3
#box {
width: 300px;
height: 300px;
background: #ddd;
display: flex;
align-items: center; // 垂直居中
justify-content: center;// 水平居中
}
<div id="box">
<div id="child">test vertical align</div>
</div>
三列佈局#
1: parent的div設置display:grid,設置grid-template-columns屬性,固定第一列第二列寬度,第三列auto
2: 給div設置float:left,再給right的div設置overflow:hidden。這樣子兩個盒子浮動,另一個盒子觸發bfc達到自適應
<div class="div1"> 1</div>
<div class="div2">2</div>
<div class= "div3"> 3 </div>
.div1,
.div2 {
float: left;
height: 100px;
width: 100px;
background: blue;
}
.div3 {
overflow: auto;
height: 100px;
width: 100px;
background: red;
}
js 設置輪播#
圖片輪播的原理就是圖片排成一行,然後準備一個只有一張圖片大小的容器,對這個容器設置超出部分隱藏,在控制定時器來讓這些圖片整體左移或右移,這樣呈現出來的效果就是圖片在輪播了。
如果有兩個輪播,可封裝一個輪播組件,供兩處調用。
link 和 @import 的區別#
- link 是 html 標籤,沒有兼容性問題,@import 是 css 提供的,IE5 以上才能識別
- link 樣式的權重高於 @import
- link 和頁面同時加載,@import 在頁面加載結束後才加載
transition 和 animation 的區別#
Animation 和 transition 大部分屬性是相同的,他們都是隨時間改變元素的屬性值,他們的主要區別是 transition 需要觸發一個事件才能改變屬性,而 animation 不需要觸發任何事件的情況下才會隨時間改變屬性值,並且 transition 為 2 幀,從 from …. to,而 animation 可以一幀一幀的。
Flex 佈局#
Flex 是彈性佈局,用來為盒狀模型提供最大的靈活性,傳統佈局方式依賴 position、float 和 display 屬性,對特殊佈局不方便。
- 容器屬性
- flex-direction:決定主軸的方向(即子 item 的排列方法)
- flex-wrap:決定換行規則
- justify-content:對其方式,水平主軸對齊方式
- align-items:對齊方式,豎直軸線方向
- 元素屬性
- align-self:允許單個項目與其他項目不一樣的對齊方式,可以覆蓋 align-items,默認屬性為 auto,表示繼承父元素的 align-items
雙邊距重疊問題(外邊距折疊)#
多個相鄰(兄弟或者父子關係)普通流的塊元素垂直方向 marigin 會重疊
折疊的結果為:
兩個相鄰的外邊距都是正數時,折疊結果是它們兩者之間較大的值。
兩個相鄰的外邊距都是負數時,折疊結果是兩者絕對值的較大值。
兩個外邊距一正一負時,折疊結果是兩者的相加的和。
.div {
display: flex; //display: flex;的元素生成BFC
flex-direction: column;
}
// 分組選擇器
.div1,
.div2 {
height: 100px;
width: 100px;
border: 1px solid #000;
margin: 100px;
/* 顯然,兩個邊框之間的距離不是400,而是200 */
}
BFC#
作用:用於清除浮動,防止 margin 重疊等
塊級格式化上下文,是一個獨立的渲染區域,並且有一定的佈局規則。BFC 是頁面上的一個獨立容器,子元素不會影響到外面。
BFC 區域不會與 float box 重疊,計算 BFC 的高度時,浮動元素也會參與計算
那些元素會生成 BFC:
-
根元素
-
float:不為 none
-
position:fixed/absolute
-
display:inline-block、table-cell、table-caption,flex,inline-flex
-
overflow:不為 visible
元素消失#
visibility=hidden, opacity=0,display
-
opacity=0,元素隱藏,不改變頁面佈局,可觸發綁定事件
-
visibility=hidden,元素隱藏,不改變頁面佈局,不會觸發綁定事件
-
display=none,元素隱藏,改變頁面佈局,可以理解成在頁面中把該元素刪除掉一樣。
position 屬性#
- fixed:相對瀏覽器窗口固定,即使窗口滾動它也不懂,不在文檔流中,fixed 元素可與其餘元素重疊
- relative:相對於自身原本位置偏移,元素在文檔流中仍佔據原來的空間
- absolute:相對於最近的父元素定位,不在文檔流中
- static:默認值,沒有定位,出現在正常的文檔流中,忽略任何 top,buttom,left,right 聲明
- sticky:元素先按照普通文檔流定位,然後相對於該元素在流中的 flow root(BFC)和 containing block(最近的塊級祖先元素)定位。 一個 sticky 元素會 “固定” 在離它最近的擁有 “滾動機制” 的祖先上。
浮動清除#
在非 IE 瀏覽器(如 Firefox)下,當容器的高度為 auto,且容器的內容中有浮動(float 為 left 或 right)的元素,在這種情況下,容器的高度不能自動伸長以適應內容的高度,使得內容溢出到容器外面而影響(甚至破壞)佈局的現象。這個現象叫浮動溢出,為了防止這個現象的出現而進行的 CSS 處理,就叫 CSS 清除浮動。
- 設置 css 的 overflow 為 auto
- 使用空元素,css 的 clear 屬性為 both
block、inline、inline-block 的區別#
- block 塊元素獨佔一行,寬度自動填充為父元素寬度。元素的高度、寬度、行高以及頂和底邊距都可設置。即使設置了寬度,仍然是獨佔一行。例如,
div, p
- inline 元素,多個該元素排列在一行,直到超出,才會換新一行,其寬度隨元素內容而變化。width,height 屬性無效,垂直方向的 padding 和 margin 會失效。例如,
a, span
- inline-block:既具有 block 的寬度高度特性又具有 inline 的同行特性。例如,
img,input
CSS 預處理器#
預先編譯處理CSS
。擴展 CSS
,增加了變量、Mixin、函數等特性,供開發者編寫源代碼,隨後經過專門的編譯工具將源碼轉化為CSS
語法。
特點:
・嵌套;
・變量;
・mixin / 繼承;
・運算;
・模塊化;
類別與發展:
- 2007, sass-scss(兩套語法規則:一個依舊是用縮進作為分隔符來區分代碼塊的;另一套規則和 CSS 一樣採用了大括號({})作為分隔符。後一種語法規則又名
SCSS
,在 Sass3 之後的版本都支持這種語法規則。) - 2009,less(使用
CSS
的語法) - 2010,Stylus(同時支持縮進和 CSS 常規樣式書寫規則)
HTML5#
新增的元素#
語義化:header, footer, nav, aside, article, section
存儲:sessionStorage, localStorage
多媒體:audio, video
繪製:svg, canvas, webgl
通信:websocket
多線程:web worker
操作系統#
進程和線程#
進程#
系統調用資源的最小單位,也就是說是擁有資源且獨立運行的最小單位。
線程#
CPU 調度的最小單位。一個進程可以擁有多個線程,線程是可以獨立運行的最小單位。
進程切換開銷#
- 切換虛擬地址空間
- 切換 CPU 上下文
- 切換內核棧
線程切換開銷#
- 切換 CPU 上下文
- 切換內核棧
進程通信#
- 管道
- 消息隊列
- 信號量
- 信號
- 套接字 socket
- 共享內存
js#
數據類型#
- 6 種原始類型,可以使用 typeof 判斷
- undefined
- Number
- Boolean
- String
- Symbol
- BigInt:BigInt 是通過在整數末尾附加
n
或調用構造函數來創建的。
- null,和其上六種屬於原始值
- Object
- Array
- Map
- WeakMap
- Set
- WeakSet
- Date
基本數據類型與引用類型的區別#
- 基本數據類型
字符串(String)、數值(Number)、布爾值(Boolean)、Null、Undefined
1. 佔用空間固定,保存在棧中
2、保存與複製的是值本身
3、使用 typeof 檢測數據的類型
- 引用類型
對象(Object)、數組(Array)、函數(Function)
1、佔用空間不固定,保存在堆中
2、保存與複製的是指向對象的一個指針
3、使用 instanceof 檢測數據類型
Object#
-
Object.keys (),獲取自身屬性
-
for in 遍歷原型鏈上擴展的屬性
-
Object.create()
方法創建一個新對象,使用現有的對象來提供新創建的對象的__proto__
-
Object.assign()
方法用於將所有可枚舉屬性的值從一個或多個源對象分配到目標對象。它將返回目標對象。
Object.assign(target, ...sources)=>target
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }
Object.defineProperties()
方法直接在一個對象上定義新的屬性或修改現有屬性,並返回該對象。
Object.defineProperties(obj, props)
陣列方法#
修改#
- push、unshift,原地修改
- pop、shift,原地修改
- splice,原地修改,(index, num, val1, val2, …)
排序#
- sort,原地修改
截取子陣列#
- slice,返回新陣列
合併陣列#
- concat,合併兩個或多個陣列。此方法不會更改現有陣列,而是返回一個新陣列。
var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])
Flat#
flat()
方法會按照一個可指定的深度遞歸遍歷陣列,並將所有元素與遍歷到的子陣列中的元素合併為一個新陣列返回。
Join#
join()
方法將一個陣列(或一個類陣列對象)的所有元素連接成一個字符串並返回這個字符串。如果陣列只有一個項目,那麼將返回該項目而不使用分隔符。
遍歷#
-
forEach,遍歷陣列,不返回新陣列,且無法提前跳出
-
map,根據元素做處理,返回各個處理結果的新陣列
-
reduce,返回新陣列
測試#
filter()
方法創建一個新陣列,其包含通過所提供函數實現的測試的所有元素。
var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
- every,測試一個陣列內的所有元素是否都能通過某個指定函數的測試。它返回一個布爾值。若收到一個空陣列,此方法在一切情況下都會返回
true
。
arr.every(callback(element[, index[, array]])[, thisArg])
-
some()
方法測試陣列中是不是至少有 1 個元素通過了被提供的函數測試。它返回的是一個 Boolean 類型的值。 -
find
方法對陣列中的每一項元素執行一次callback
函數,直至有一個 callback 返回true
。當找到了這樣一個元素後,該方法會立即返回這個元素的值,否則返回undefined
。 -
findIndex()
方法返回陣列中滿足提供的測試函數的第一個元素的索引。若沒有找到對應元素則返回 - 1。 -
indexOf()
方法返回在陣列中可以找到一個給定元素的第一個索引,如果不存在,則返回 - 1。 -
includes()
方法用來判斷一個陣列是否包含一個指定的值,根據情況,如果包含則返回 true,否則返回 false。
ES6 新特性#
- 在變量聲明方面有:let, const
- 面向對象編程有 語法糖 class
- 在模塊導入方面是 import export
- 新的數據結構:map、set
- 新的陣列方法:map、reduce
- 異步:promise
- 箭頭函數
- 解構賦值和三點表達式
- 等等……
異步#
Async,promise 和 generator,區別是什麼#
-
Generator 函數是將函數分步驟阻塞 ,只有主動調用 next () 才能進行下一步
-
async
和await
關鍵字讓我們可以用一種更簡潔的方式寫出基於Promise
的異步行為,而無需刻意地鏈式調用promise
。
async 函數可能包含 0 個或者多個await
表達式。await 表達式會暫停整個 async 函數的執行進程並出讓其控制權,只有當其等待的基於 promise 的異步操作被兌現或被拒絕之後才會恢復進程。promise 的解決值會被當作該 await 表達式的返回值。使用async
/ await
關鍵字就可以在異步代碼中使用普通的try
/ catch
代碼塊。
async 函數一定會返回一個 promise 對象。如果一個 async 函數的返回值看起來不是 promise,那麼它將會被隱式地包裝在一個 promise 中。
async
/await
的行為就好像搭配使用了生成器和 promise。
Promise#
三種狀態#
pending、fulfilled、rejected
靜態方法#
Promise.all( iterable)#
@ para:promise 陣列,或者 iterable
@ return:成功則返回,所有 promise 返回值的陣列;失敗,則把第一個觸發失敗的 promise 對象的錯誤信息作為它的失敗錯誤信息。
相比於 allSettled ,all 更適合彼此相互依賴或者在其中任何一個reject
時立即結束
Promise.allSettled(iterable)#
方法返回一個在所有給定的 promise 都已經fulfilled
或rejected
後的 promise,並帶有一個對象陣列,每個對象表示對應的 promise 結果。
當您有多個彼此不依賴的異步任務成功完成時,或者您總是想知道每個promise
的結果時,通常使用它。
Promise.any(iterable)#
接收一個 Promise 對象的集合,當其中的某個 promise 成功,就返回那個成功的 promise 的值。最快成功的那一個。
Promise.race(iterable)#
返回一個 promise,一旦迭代器中的某個promise 解決或拒絕,返回的 promise 就會解決或拒絕。也就是最快處理的那個,無論是成功還是失敗。
Promise.reject(reason)#
返回一個狀態為失敗的 Promise 對象,並將給定的失敗信息傳遞給對應的處理方法
Promise.resolve(value)#
返回一個以給定值解析後的Promise
對象。
如果這個值是一個 promise ,那麼將返回執行後的這個 promise ;
如果這個值是 thenable(即帶有then方法),返回的 promise 會 “跟隨” 這個 thenable 的對象,採用它的最終狀態;否則返回的 promise 將以此值完成。
此函數將類 promise 對象的多層嵌套展平。
實現 promise#
// 定義三種狀態
const PENDING = 'PENDING'; // 進行中
const FULFILLED = 'FULFILLED'; // 已成功
const REJECTED = 'REJECTED'; // 已失敗
class Promise {
constructor(exector) {
// 初始化狀態
this.status = PENDING;
// 將成功、失敗結果放在this上,便於then、catch訪問
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
// 只有進行中狀態才能更改狀態
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
};
const reject = (reason) => {
// 只有進行中狀態才能更改狀態
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
};
// 立即執行exector
// 把內部的resolve和reject傳入executor,用戶可調用resolve和reject
exector(resolve, reject);
}
then(onFulfilled, onRejected) {
// then是微任務,這裡用setTimeout模擬
setTimeout(() => {
if (this.status === FULFILLED) {
// FULFILLED狀態下才執行
onFulfilled(this.value);
} else if (this.status === REJECTED) {
// REJECTED狀態下才執行
onRejected(this.reason);
}
});
}
}
實現 all#
var all = (promises) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('arguments must be an array'));
}
var l = promises.length;
var result = new Array(l);
var num = 0;
return new Promise((resolve) => {
for (let i = 0; i < l; i++) {
Promise.resolve(promises[i]).then(
(res) => {
result[i] = res;
num += 1;
if (num === l) {
return resolve(result);
}
},
(reason) => {
return reject(reason);
}
);
}
});
};
this#
函數裡的 this 是函數的調用者;
嚴格模式下,在全局頂層是 undefined。
箭頭函數#
作用:更簡短的函數並且不綁定this
。
不能用作構造函數,無自己的原型屬性;
無 arguments ,但是可以用三點解析式,用…rest 參數獲取
Symbol#
每個從Symbol()
返回的 symbol 值都是唯一的。 一個 symbol 值能作為對象屬性的標識符;這是該數據類型僅有的目的。
不支持語法:”new Symbol()
“。
Object.getOwnPropertySymbols()
方法讓你在查找一個給定對象的符號屬性時返回一個 symbol 類型的陣列。
Set#
Set:成員值唯一的陣列(就是集合)
// Set 結構的實例有以下屬性。
Set.prototype.constructor:構造函數,默認就是Set函數。
Set.prototype.size:返回Set實例的成員總數。
// Set 實例的方法分為兩大類:操作方法(用於操作數據)和遍歷方法(用於遍歷成員)。下面先介紹四個操作方法。
Set.prototype.add(value):添加某個值,返回 Set 結構本身。
Set.prototype.delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功。
Set.prototype.has(value):返回一個布爾值,表示該值是否為Set的成員。
Set.prototype.clear():清除所有成員,沒有返回值。
WeakSet 結構與 Set 類似,也是不重複的值的集合。
WeakSet 的成員只能是對象,而不能是其他類型的值。
其次,WeakSet 中的對象都是弱引用,即垃圾回收機制不考慮 WeakSet 對該對象的引用。
Map#
只有對同一個對象的引用,Map 結構才將其視為同一鍵。但是對於數值和字符串沒有影響
Map 的鍵實際上是跟內存地址綁定的,只要內存地址不一樣,就視為兩個鍵。這就解決了同名屬性碰撞(clash)問題
注意:如果 Map 的鍵是一個簡單類型的值(數字、字符串、布爾值),則只要兩個值嚴格相等,Map 將其視為一個鍵,比如 0 和 - 0 就是一個鍵,布爾值 true 和字符串 true 則是兩個不同的鍵。
另外,undefined 和 null 也是兩個不同的鍵。雖然 NaN 不嚴格相等於自身,但 Map 將其視為同一鍵。
可以按照數據插入時的順序遍歷所有的元素。
- new Map (),或者以這樣的方式初始化,
new Map([['foo', 3], ['bar', {}], ['baz', undefined]])
- Map.prototype.set(k, v)
- Map.prototype.delete (k),刪除
- Map.prototype.clear (),清空 map
- Map.prototype.get (k),對於 v 是引用類型,可以直接在返回值上原地修改(但是不能直接賦值),假如是基礎類型,還是得用 set 修改。
- Map.prototype.has(k)
- Map.prototype.size,獲取大小
- for (var [key, value] of mapObj),遍歷
- Map.prototype.forEach ((v, k, map)=>{}),按插入順序遍歷,對每個元素做一次操作
- Map.prototype.keys()
- Map.prototype.values()
- Map.prototype.entries()
Proxy#
Proxy 在目標對象之前架設一層 “攔截”,外界對該對象的訪問,都必須先通過這層攔截,在這層攔截中可以對外界的訪問進行過濾和改寫。
// new Proxy()表示生成一個Proxy實例,target參數表示所要攔截的目標對象,handler參數是一個配置對象,用來定制攔截行為,對於每一個被代理的操作,需要提供一個對應的處理函數,該函數將攔截對應的操作
// var proxy = new Proxy(target, handler);
var proxy = new Proxy(
{},
{
get: function (target, propKey) {
// get方法的兩個參數分別是目標對象和所要訪問的屬性。
return 35; // 由於攔截函數總是返回35,所以訪問任何屬性都得到35。
},
}
);
proxy.time; // 35
proxy.name; // 35
proxy.title; // 35
// 如果 handler === {}
可繼承,即 Proxy 實例可以作為其他對象的原型對象
var proxy = new