一、CSS相關
#1.1 左邊定寬,右邊自適應方案:float + margin,float + calc
/* 方案1 */
.left {
width: 120px;
float: left;
}
.right {
margin-left: 120px;
}
/* 方案2 */
.left {
width: 120px;
float: left;
}
.right {
width: calc(100% - 120px);
float: left;
}
#1.2 左右兩邊定寬,中間自適應:float,float + calc, 聖盃佈局(設置BFC,margin負值法),flex
.wrap {
width: 100%;
height: 200px;
}
.wrap > div {
height: 100%;
}
/* 方案1 */
.left {
width: 120px;
float: left;
}
.right {
float: right;
width: 120px;
}
.center {
margin: 0 120px;
}
/* 方案2 */
.left {
width: 120px;
float: left;
}
.right {
float: right;
width: 120px;
}
.center {
width: calc(100% - 240px);
margin-left: 120px;
}
/* 方案3 */
.wrap {
display: flex;
}
.left {
width: 120px;
}
.right {
width: 120px;
}
.center {
flex: 1;
}
#1.3 左右居中
- 行內元素:
text-align: center
- 定寬塊狀元素: 左右
margin
值爲auto
- 不定寬塊狀元素:
table
佈局,position + transform
/* 方案1 */
.wrap {
text-align: center
}
.center {
display: inline;
/* or */
/* display: inline-block; */
}
/* 方案2 */
.center {
width: 100px;
margin: 0 auto;
}
/* 方案2 */
.wrap {
position: relative;
}
.center {
position: absulote;
left: 50%;
transform: translateX(-50%);
}
#1.4 上下垂直居中
- 定高:
margin
,position + margin
(負值) - 不定高:
position
+transform
,flex
,IFC + vertical-align:middle
/* 定高方案1 */
.center {
height: 100px;
margin: 50px 0;
}
/* 定高方案2 */
.center {
height: 100px;
position: absolute;
top: 50%;
margin-top: -25px;
}
/* 不定高方案1 */
.center {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
/* 不定高方案2 */
.wrap {
display: flex;
align-items: center;
}
.center {
width: 100%;
}
/* 不定高方案3 */
/* 設置 inline-block 則會在外層產生 IFC,高度設爲 100% 撐開 wrap 的高度 */
.wrap::before {
content: '';
height: 100%;
display: inline-block;
vertical-align: middle;
}
.wrap {
text-align: center;
}
.center {
display: inline-block;
vertical-align: middle;
}
#1.5 盒模型:content(元素內容) + padding(內邊距) + border(邊框) + margin(外邊距)
延伸:
box-sizing
content-box
:默認值,總寬度 =margin
+border
+padding
+width
border-box
:盒子寬度包含padding
和border
,總寬度 = margin + width
inherit
:從父元素繼承box-sizing
屬性
#1.6 BFC、IFC、GFC、FFC:FC(Formatting Contexts),格式化上下文
BFC
:塊級格式化上下文,容器裏面的子元素不會在佈局上影響到外面的元素,反之也是如此(按照這個理念來想,只要脫離文檔流,肯定就能產生BFC
)。產生BFC
方式如下
float
的值不爲none
。overflow
的值不爲visible
。position
的值不爲relative
和static
。display
的值爲table-cell
,table-caption
,inline-block
中的任何一個
用處?常見的多欄佈局,結合塊級別元素浮動,裏面的元素則是在一個相對隔離的環境裏運行
IFC
:內聯格式化上下文,IFC
的line
box
(線框)高度由其包含行內元素中最高的實際高度計算而來(不受到豎直方向的padding/margin
影響)。
IFC
中的line box
一般左右都貼緊整個IFC
,但是會因爲float
元素而擾亂。float
元素會位於 IFC 與line box
之間,使得line box
寬度縮短。 同個ifc
下的多個line box
高度會不同。IFC
中時不可能有塊級元素的,當插入塊級元素時(如p
中插入div
)會產生兩個匿名塊與div
分隔開,即產生兩個IFC
,每個IFC
對外表現爲塊級元素,與div
垂直排列。
用處?
- 水平居中:當一個塊要在環境中水平居中時,設置其爲
inline-block
則會在外層產生IFC
,通過text-align
則可以使其水平居中。 - 垂直居中:創建一個
IFC
,用其中一個元素撐開父元素的高度,然後設置其vertical-align
:middle
,其他行內元素則可以在此父元素下垂直居中
- GFC:網格佈局格式化上下文(
display: grid
)- FFC:自適應格式化上下文(
display: flex
)
#二、JS 基礎(ES5)
#2.1 原型
這裏可以談很多,只要圍繞
[[ prototype ]]
談,都沒啥問題
#2.2 閉包
牽扯作用域,可以兩者聯繫起來一起談
#2.3 作用域
詞法作用域,動態作用域
#2.4 this
不同情況的調用,
this
指向分別如何。順帶可以提一下es6
中箭頭函數沒有this
,arguments
,super
等,這些只依賴包含箭頭函數最接近的函數
#2.5 call,apply,bind 三者用法和區別
參數、綁定規則(顯示綁定和強綁定),運行效率(最終都會轉換成一個一個的參數去運行)、運行情況(
call
,apply
立即執行,bind
是return
出一個this
“固定”的函數,這也是爲什麼bind
是強綁定的一個原因)
注:“固定”這個詞的含義,它指的固定是指只要傳進去了
context
,則bind
中return
出來的函數this
便一直指向context
,除非context
是個變量
#2.6 變量聲明提升
js
代碼在運行前都會進行AST
解析,函數申明默認會提到當前作用域最前面,變量申明也會進行提升。但賦值不會得到提升。關於AST
解析,這裏也可以說是形成詞法作用域的主要原因
#三、JS 基礎(ES6)
#3.1 let,const
let
產生塊級作用域(通常配合for
循環或者{}
進行使用產生塊級作用域),const
申明的變量是常量(內存地址不變)
#3.2 Promise
這裏你談
promise
的時候,除了將他解決的痛點以及常用的API
之外,最好進行拓展把eventloop
帶進來好好講一下,microtask
(微任務)、macrotask
(任務) 的執行順序,如果看過promise
源碼,最好可以談一談 原生Promise
是如何實現的。Promise
的關鍵點在於callback
的兩個參數,一個是resovle
,一個是reject
。還有就是Promise
的鏈式調用(Promise.then()
,每一個then
都是一個責任人)
#3.3 Generator
遍歷器對象生成函數,最大的特點是可以交出函數的執行權
function
關鍵字與函數名之間有一個星號;- 函數體內部使用
yield
表達式,定義不同的內部狀態; next
指針移向下一個狀態
這裏你可以說說
Generator
的異步編程,以及它的語法糖async
和awiat
,傳統的異步編程。ES6
之前,異步編程大致如下
- 回調函數
- 事件監聽
- 發佈/訂閱
傳統異步編程方案之一:協程,多個線程互相協作,完成異步任務。
#3.4 async、await
Generator
函數的語法糖。有更好的語義、更好的適用性、返回值是Promise
。
async => *
await => yield
// 基本用法
async function timeout (ms) {
await new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
async function asyncConsole (value, ms) {
await timeout(ms)
console.log(value)
}
asyncConsole('hello async and await', 1000)
注:最好把2,3,4 連到一起講
#3.5 AMD,CMD,CommonJs,ES6 Module:解決原始無模塊化的痛點
- AMD:
requirejs
在推廣過程中對模塊定義的規範化產出,提前執行,推崇依賴前置 - CMD:
seajs
在推廣過程中對模塊定義的規範化產出,延遲執行,推崇依賴就近 - CommonJs:模塊輸出的是一個值的
copy
,運行時加載,加載的是一個對象(module.exports
屬性),該對象只有在腳本運行完纔會生成 - ES6 Module:模塊輸出的是一個值的引用,編譯時輸出接口,
ES6
模塊不是對象,它對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。
#四、框架相關
#4.1 數據雙向綁定原理:常見數據綁定的方案
Object.defineProperty(vue)
:劫持數據的getter
和setter
- 髒值檢測(
angularjs
):通過特定事件進行輪循 發佈/訂閱模式:通過消息發佈並將消息進行訂閱
#4.2 VDOM:三個 part
- 虛擬節點類,將真實
DOM
節點用js
對象的形式進行展示,並提供render
方法,將虛擬節點渲染成真實DOM
- 節點
diff
比較:對虛擬節點進行js
層面的計算,並將不同的操作都記錄到patch
對象 re-render
:解析patch
對象,進行re-render
補充1:VDOM 的必要性?
- 創建真實DOM的代價高:真實的
DOM
節點node
實現的屬性很多,而vnode
僅僅實現一些必要的屬性,相比起來,創建一個vnode
的成本比較低。 - 觸發多次瀏覽器重繪及迴流:使用
vnode
,相當於加了一個緩衝,讓一次數據變動所帶來的所有node
變化,先在vnode
中進行修改,然後diff
之後對所有產生差異的節點集中一次對DOM tree
進行修改,以減少瀏覽器的重繪及迴流。
補充2:vue 爲什麼採用 vdom?
引入
Virtual DOM
在性能方面的考量僅僅是一方面。
- 性能受場景的影響是非常大的,不同的場景可能造成不同實現方案之間成倍的性能差距,所以依賴細粒度綁定及
Virtual DOM
哪個的性能更好還真不是一個容易下定論的問題。 Vue
之所以引入了Virtual DOM
,更重要的原因是爲了解耦HTML
依賴,這帶來兩個非常重要的好處是:
- 不再依賴
HTML
解析器進行模版解析,可以進行更多的AOT
工作提高運行時效率:通過模版AOT
編譯,Vue
的運行時體積可以進一步壓縮,運行時效率可以進一步提升;- 可以渲染到
DOM
以外的平臺,實現SSR
、同構渲染這些高級特性,Weex
等框架應用的就是這一特性。
綜上,
Virtual DOM
在性能上的收益並不是最主要的,更重要的是它使得Vue
具備了現代框架應有的高級特性。
#4.3 vue 和 react 區別
- 相同點:都支持
ssr
,都有vdom
,組件化開發,實現webComponents
規範,數據驅動等 - 不同點:
vue
是雙向數據流(當然爲了實現單數據流方便管理組件狀態,vuex
便出現了),react
是單向數據流。vue
的vdom
是追蹤每個組件的依賴關係,不會渲染整個組件樹,react
每當應該狀態被改變時,全部子組件都會re-render
#4.4 爲什麼用 vue
簡潔、輕快、舒服
#五、網絡基礎類
#5.1 跨域
很多種方法,但萬變不離其宗,都是爲了搞定同源策略。重用的有
jsonp
、iframe
、cors
、img
、HTML5 postMessage
等等。其中用到html
標籤進行跨域的原理就是html
不受同源策略影響。但只是接受Get
的請求方式,這個得清楚。
延伸1:img iframe script 來發送跨域請求有什麼優缺點?
1. iframe
- 優點:跨域完畢之後
DOM
操作和互相之間的JavaScript
調用都是沒有問題的 - 缺點:1.若結果要以
URL
參數傳遞,這就意味着在結果數據量很大的時候需要分割傳遞,巨煩。2.還有一個是iframe
本身帶來的,母頁面和iframe
本身的交互本身就有安全性限制。
2. script
- 優點:可以直接返回
json
格式的數據,方便處理 - 缺點:只接受
GET
請求方式
3. 圖片ping
- 優點:可以訪問任何
url
,一般用來進行點擊追蹤,做頁面分析常用的方法 - 缺點:不能訪問響應文本,只能監聽是否響應
延伸2:配合 webpack 進行反向代理?
webpack
在 devServer
選項裏面提供了一個 proxy
的參數供開發人員進行反向代理
'/api': {
target: 'http://www.example.com', // your target host
changeOrigin: true, // needed for virtual hosted sites
pathRewrite: {
'^/api': '' // rewrite path
}
},
然後再配合
http-proxy-middleware
插件對api
請求地址進行代理
const express = require('express');
const proxy = require('http-proxy-middleware');
// proxy api requests
const exampleProxy = proxy(options); // 這裏的 options 就是 webpack 裏面的 proxy 選項對應的每個選項
// mount `exampleProxy` in web server
const app = express();
app.use('/api', exampleProxy);
app.listen(3000);
然後再用
nginx
把允許跨域的源地址添加到報頭裏面即可
說到
nginx
,可以再談談CORS
配置,大致如下
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'DNT, X-Mx-ReqToken, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type';
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 200;
}
}
#5.2 http 無狀態無連接
http
協議對於事務處理沒有記憶能力- 對同一個
url
請求沒有上下文關係 - 每次的請求都是獨立的,它的執行情況和結果與前面的請求和之後的請求是無直接關係的,它不會受前面的請求應答情況直接影響,也不會直接影響後面的請求應答情況
- 服務器中沒有保存客戶端的狀態,客戶端必須每次帶上自己的狀態去請求服務器
- 人生若只如初見,請求過的資源下一次會繼續進行請求
http協議無狀態中的 狀態 到底指的是什麼?!
- 【狀態】的含義就是:客戶端和服務器在某次會話中產生的數據
- 那麼對應的【無狀態】就意味着:這些數據不會被保留
- 通過增加
cookie
和session
機制,現在的網絡請求其實是有狀態的 - 在沒有狀態的
http
協議下,服務器也一定會保留你每次網絡請求對數據的修改,但這跟保留每次訪問的數據是不一樣的,保留的只是會話產生的結果,而沒有保留會話
#5.3 http-cache:就是 http 緩存
1. 首先得明確 http 緩存的好處
- 減少了冗餘的數據傳輸,減少網費
- 減少服務器端的壓力
Web
緩存能夠減少延遲與網絡阻塞,進而減少顯示某個資源所用的時間- 加快客戶端加載網頁的速度
2. 常見 http 緩存的類型
- 私有緩存(一般爲本地瀏覽器緩存)
- 代理緩存
3. 然後談談本地緩存
本地緩存是指瀏覽器請求資源時命中了瀏覽器本地的緩存資源,瀏覽器並不會發送真正的請求給服務器了。它的執行過程是
- 第一次瀏覽器發送請求給服務器時,此時瀏覽器還沒有本地緩存副本,服務器返回資源給瀏覽器,響應碼是
200 OK
,瀏覽器收到資源後,把資源和對應的響應頭一起緩存下來 - 第二次瀏覽器準備發送請求給服務器時候,瀏覽器會先檢查上一次服務端返回的響應頭信息中的
Cache-Control
,它的值是一個相對值,單位爲秒,表示資源在客戶端緩存的最大有效期,過期時間爲第一次請求的時間減去Cache-Control
的值,過期時間跟當前的請求時間比較,如果本地緩存資源沒過期,那麼命中緩存,不再請求服務器 - 如果沒有命中,瀏覽器就會把請求發送給服務器,進入緩存協商階段。
與本地緩存相關的頭有:
Cache-Control
、Expires
,Cache-Control
有多個可選值代表不同的意義,而Expires
就是一個日期格式的絕對值。
3.1 Cache-Control
Cache-Control
是HTPP
緩存策略中最重要的頭,它是HTTP/1.1
中出現的,它由如下幾個值
no-cache
:不使用本地緩存。需要使用緩存協商,先與服務器確認返回的響應是否被更改,如果之前的響應中存在ETag
,那麼請求的時候會與服務端驗證,如果資源未被更改,則可以避免重新下載no-store
:直接禁止遊覽器緩存數據,每次用戶請求該資源,都會向服務器發送一個請求,每次都會下載完整的資源public
:可以被所有的用戶緩存,包括終端用戶和CDN
等中間代理服務器。private
:只能被終端用戶的瀏覽器緩存,不允許CDN
等中繼緩存服務器對其緩存。max-age
:從當前請求開始,允許獲取的響應被重用的最長時間(秒)。
# 例如:
Cache-Control: public, max-age=1000
# 表示資源可以被所有用戶以及代理服務器緩存,最長時間爲1000秒。
3.2 Expires
Expires
是HTTP/1.0
出現的頭信息,同樣是用於決定本地緩存策略的頭,它是一個絕對時間,時間格式是如Mon, 10 Jun 2015 21:31:12 GMT
,只要發送請求時間是在Expires
之前,那麼本地緩存始終有效,否則就會去服務器發送請求獲取新的資源。如果同時出現Cache-Control:max-age
和Expires
,那麼max-age
優先級更高。他們可以這樣組合使用
Cache-Control: public
Expires: Wed, Jan 10 2018 00:27:04 GMT
3.3 所謂的緩存協商
當第一次請求時服務器返回的響應頭中存在以下情況時
- 沒有
Cache-Control
和Expires
Cache-Control
和Expires
過期了Cache-Control
的屬性設置爲no-cache
時
那麼瀏覽器第二次請求時就會與服務器進行協商,詢問瀏覽器中的緩存資源是不是舊版本,需不需要更新,此時,服務器就會做出判斷,如果緩存和服務端資源的最新版本是一致的,那麼就無需再次下載該資源,服務端直接返回
304 Not Modified
狀態碼,如果服務器發現瀏覽器中的緩存已經是舊版本了,那麼服務器就會把最新資源的完整內容返回給瀏覽器,狀態碼就是200 Ok
,那麼服務端是根據什麼來判斷瀏覽器的緩存是不是最新的呢?其實是根據HTTP
的另外兩組頭信息,分別是:Last-Modified/If-Modified-Since
與ETag/If-None-Match
。
Last-Modified 與 If-Modified-Since
- 瀏覽器第一次請求資源時,服務器會把資源的最新修改時間
Last-Modified:Thu, 29 Dec 2011 18:23:55 GMT
放在響應頭中返回給瀏覽器 - 第二次請求時,瀏覽器就會把上一次服務器返回的修改時間放在請求頭
If-Modified-Since:Thu, 29 Dec 2011 18:23:55
發送給服務器,服務器就會拿這個時間跟服務器上的資源的最新修改時間進行對比
如果兩者相等或者大於服務器上的最新修改時間,那麼表示瀏覽器的緩存是有效的,此時緩存會命中,服務器就不再返回內容給瀏覽器了,同時
Last-Modified
頭也不會返回,因爲資源沒被修改,返回了也沒什麼意義。如果沒命中緩存則最新修改的資源連同Last-Modified
頭一起返回
# 第一次請求返回的響應頭
Cache-Control:max-age=3600
Expires: Fri, Jan 12 2018 00:27:04 GMT
Last-Modified: Wed, Jan 10 2018 00:27:04 GMT
# 第二次請求的請求頭信息
If-Modified-Since: Wed, Jan 10 2018 00:27:04 GMT
這組頭信息是基於資源的修改時間來判斷資源有沒有更新,另一種方式就是根據資源的內容來判斷,就是接下來要討論的
ETag
與If-None-Match
ETag與If-None-Match
ETag/If-None-Match
與Last-Modified/If-Modified-Since
的流程其實是類似的,唯一的區別是它基於資源的內容的摘要信息(比如MD5 hash
)來判斷
瀏覽器發送第二次請求時,會把第一次的響應頭信息
ETag
的值放在If-None-Match
的請求頭中發送到服務器,與最新的資源的摘要信息對比,如果相等,取瀏覽器緩存,否則內容有更新,最新的資源連同最新的摘要信息返回。用ETag
的好處是如果因爲某種原因到時資源的修改時間沒改變,那麼用ETag
就能區分資源是不是有被更新。
# 第一次請求返回的響應頭:
Cache-Control: public, max-age=31536000
ETag: "15f0fff99ed5aae4edffdd6496d7131f"
# 第二次請求的請求頭信息:
If-None-Match: "15f0fff99ed5aae4edffdd6496d7131f"
#5.4 cookie 和 session
session
: 是一個抽象概念,開發者爲了實現中斷和繼續等操作,將user agent
和server
之間一對一的交互,抽象爲“會話”,進而衍生出“會話狀態”,也就是session
的概念cookie
:它是一個世紀存在的東西,http
協議中定義在header
中的字段,可以認爲是session
的一種後端無狀態實現
現在我們常說的
session
,是爲了繞開cookie
的各種限制,通常藉助cookie
本身和後端存儲實現的,一種更高級的會話狀態實現
session
的常見實現要藉助cookie
來發送 sessionID
#5.5 安全問題,如 XSS 和 CSRF
XSS
:跨站腳本攻擊,是一種網站應用程序的安全漏洞攻擊,是代碼注入的一種。常見方式是將惡意代碼注入合法代碼裏隱藏起來,再誘發惡意代碼,從而進行各種各樣的非法活動
防範:記住一點 “所有用戶輸入都是不可信的”,所以得做輸入過濾和轉義
CSRF
:跨站請求僞造,也稱XSRF
,是一種挾制用戶在當前已登錄的Web
應用程序上執行非本意的操作的攻擊方法。與XSS
相比,XSS
利用的是用戶對指定網站的信任,CSRF
利用的是網站對用戶網頁瀏覽器的信任。
防範:用戶操作驗證(驗證碼),額外驗證機制(
token
使用)等