一、CSS
#1. 盒模型
頁面渲染時,
dom
元素所採用的 佈局模型。可通過box-sizing
進行設置。根據計算寬高的區域可分爲
content-box
(W3C
標準盒模型)border-box
(IE
盒模型)padding-box
margin-box
(瀏覽器未實現)
#2. BFC
塊級格式化上下文,是一個獨立的渲染區域,讓處於
BFC
內部的元素與外部的元素相互隔離,使內外元素的定位不會相互影響。
IE下爲
Layout
,可通過zoom:1
觸發
觸發條件:
- 根元素
position: absolute/fixed
display: inline-block / table
float
元素ovevflow !== visible
規則:
- 屬於同一個
BFC
的兩個相鄰Box
垂直排列 - 屬於同一個
BFC
的兩個相鄰Box
的margin
會發生重疊 BFC
中子元素的margin box
的左邊, 與包含塊 (BFC)border box
的左邊相接觸 (子元素absolute
除外)BFC
的區域不會與float
的元素區域重疊- 計算
BFC
的高度時,浮動子元素也參與計算 - 文字層不會被浮動層覆蓋,環繞於周圍
應用:
- 阻止
margin
重疊 - 可以包含浮動元素 —— 清除內部浮動(清除浮動的原理是兩個
div
都位於同一個BFC
區域之中) - 自適應兩欄佈局
- 可以阻止元素被浮動元素覆蓋
#3.層疊上下文
元素提升爲一個比較特殊的圖層,在三維空間中 (z軸) 高出普通元素一等。
觸發條件
- 根層疊上下文(
html
) position
css3
屬性flex
transform
opacity
filter
will-change
webkit-overflow-scrolling
層疊等級:層疊上下文在z軸上的排序
- 在同一層疊上下文中,層疊等級纔有意義
z-index
的優先級最高
#4. 居中佈局
水平居中
- 行內元素:
text-align: center
- 塊級元素:
margin: 0 auto
absolute + transform
flex + justify-content: center
垂直居中
line-height: height
absolute + transform
flex + align-items: center
table
水平垂直居中
absolute + transform
flex + justify-content + align-items
#5. 選擇器優先級
!important
> 行內樣式 >#id
>.class
>tag
>*
> 繼承 > 默認- 選擇器 從右往左 解析
#6.去除浮動影響,防止父級高度塌陷
- 通過增加尾元素清除浮動
:after / <br> : clear: both
- 創建父級
BFC
- 父級設置高度
#7.link 與 @import 的區別
link
功能較多,可以定義RSS
,定義Rel
等作用,而@import
只能用於加載css
- 當解析到
link
時,頁面會同步加載所引的css
,而@import
所引用的css
會等到頁面加載完才被加載 @import
需要IE5
以上才能使用link
可以使用js
動態引入,@import
不行
#8. CSS預處理器(Sass/Less/Postcss)
CSS
預處理器的原理: 是將類CSS
語言通過Webpack
編譯 轉成瀏覽器可讀的真正CSS
。在這層編譯之上,便可以賦予CSS
更多更強大的功能,常用功能:
- 嵌套
- 變量
- 循環語句
- 條件語句
- 自動前綴
- 單位轉換
mixin
複用
面試中一般不會重點考察該點,一般介紹下自己在實戰項目中的經驗即可~
#9.CSS動畫
transition: 過渡動畫
transition-property
: 屬性transition-duration
: 間隔transition-timing-function
: 曲線transition-delay
: 延遲- 常用鉤子:
transitionend
animation / keyframes
animation-name
: 動畫名稱,對應@keyframes
animation-duration
: 間隔animation-timing-function
: 曲線animation-delay
: 延遲animation-iteration-count
: 次數infinite
: 循環動畫
animation-direction
: 方向alternate
: 反向播放
animation-fill-mode
: 靜止模式forwards
: 停止時,保留最後一幀backwards
: 停止時,回到第一幀both
: 同時運用forwards / backwards
- 常用鉤子:
animationend
動畫屬性: 儘量使用動畫屬性進行動畫,能擁有較好的性能表現
translate
scale
rotate
skew
opacity
color
#二、JavaScript
#1. 原型 / 構造函數 / 實例
- 原型(
prototype
): 一個簡單的對象,用於實現對象的 屬性繼承。可以簡單的理解成對象的爹。在Firefox
和Chrome
中,每個JavaScript
對象中都包含一個__proto__
(非標準)的屬性指向它爹(該對象的原型),可obj.__proto__
進行訪問。 - 構造函數: 可以通過
new
來 新建一個對象 的函數。 - 實例: 通過構造函數和
new
創建出來的對象,便是實例。 實例通過__proto__
指向原型,通過constructor
指向構造函數。
以
Object
爲例,我們常用的Object
便是一個構造函數,因此我們可以通過它構建實例。
// 實例
const instance = new Object()
則此時, 實例爲
instance
, 構造函數爲Object
,我們知道,構造函數擁有一個prototype
的屬性指向原型,因此原型爲:
// 原型
const prototype = Object.prototype
這裏我們可以來看出三者的關係:
實例.__proto__ === 原型
原型.constructor === 構造函數
構造函數.prototype === 原型
// 這條線其實是是基於原型進行獲取的,可以理解成一條基於原型的映射線
// 例如:
// const o = new Object()
// o.constructor === Object --> true
// o.__proto__ = null;
// o.constructor === Object --> false
實例.constructor === 構造函數
#2.原型鏈:
原型鏈是由原型對象組成,每個對象都有
__proto__
屬性,指向了創建該對象的構造函數的原型,__proto__
將對象連接起來組成了原型鏈。是一個用來實現繼承和共享屬性的有限的對象鏈
- 屬性查找機制: 當查找對象的屬性時,如果實例對象自身不存在該屬性,則沿着原型鏈往上一級查找,找到時則輸出,不存在時,則繼續沿着原型鏈往上一級查找,直至最頂級的原型對象
Object.prototype
,如還是沒找到,則輸出undefined
; - 屬性修改機制: 只會修改實例對象本身的屬性,如果不存在,則進行添加該屬性,如果需要修改原型的屬性時,則可以用:
b.prototype.x = 2
;但是這樣會造成所有繼承於該對象的實例的屬性發生改變。
#3. 執行上下文(EC)
執行上下文可以簡單理解爲一個對象:
它包含三個部分:
- 變量對象(
VO
) - 作用域鏈(詞法作用域)
this
指向
它的類型:
- 全局執行上下文
- 函數執行上下文
eval
執行上下文
代碼執行過程:
- 創建 全局上下文 (
global EC
) - 全局執行上下文 (
caller
) 逐行 自上而下 執行。遇到函數時,函數執行上下文 (callee
) 被push
到執行棧頂層 - 函數執行上下文被激活,成爲
active EC
, 開始執行函數中的代碼,caller
被掛起 - 函數執行完後,
callee
被pop
移除出執行棧,控制權交還全局上下文 (caller
),繼續執行
#4.變量對象
- 變量對象,是執行上下文中的一部分,可以抽象爲一種 數據作用域,其實也可以理解爲就是一個簡單的對象,它存儲着該執行上下文中的所有 變量和函數聲明(不包含函數表達式)。
- 活動對象 (
AO
): 當變量對象所處的上下文爲active EC
時,稱爲活動對象。
#5. 作用域
執行上下文中還包含作用域鏈。理解作用域之前,先介紹下作用域。作用域其實可理解爲該上下文中聲明的 變量和聲明的作用範圍。可分爲 塊級作用域 和 函數作用域
特性:
- 聲明提前: 一個聲明在函數體內都是可見的, 函數優先於變量
- 非匿名自執行函數,函數變量爲 只讀 狀態,無法修改
let foo = function() { console.log(1) }
(function foo() {
foo = 10 // 由於foo在函數中只爲可讀,因此賦值無效
console.log(foo)
}())
// 結果打印: ƒ foo() { foo = 10 ; console.log(foo) }
#6.作用域鏈
我們知道,我們可以在執行上下文中訪問到父級甚至全局的變量,這便是作用域鏈的功勞。作用域鏈可以理解爲一組對象列表,包含 父級和自身的變量對象,因此我們便能通過作用域鏈訪問到父級裏聲明的變量或者函數。
由兩部分組成:
[[scope]]
屬性: 指向父級變量對象和作用域鏈,也就是包含了父級的[[scope]]
和AO
AO
: 自身活動對象
如此
[[scopr]]
包含[[scope]]
,便自上而下形成一條 鏈式作用域。
#7. 閉包
閉包屬於一種特殊的作用域,稱爲 靜態作用域。它的定義可以理解爲: 父函數被銷燬 的情況下,返回出的子函數的
[[scope]]
中仍然保留着父級的單變量對象和作用域鏈,因此可以繼續訪問到父級的變量對象,這樣的函數稱爲閉包。
閉包會產生一個很經典的問題:
多個子函數的
[[scope]]
都是同時指向父級,是完全共享的。因此當父級的變量對象被修改時,所有子函數都受到影響。
••解決:**
- 變量可以通過 函數參數的形式 傳入,避免使用默認的
[[scope]]
向上查找 - 使用
setTimeout
包裹,通過第三個參數傳入 - 使用 塊級作用域,讓變量成爲自己上下文的屬性,避免共享
#8. script 引入方式:
html
靜態<script>
引入js
動態插入<script>
<script defer>
: 異步加載,元素解析完成後執行<script async>
: 異步加載,但執行時會阻塞元素渲染
#9. 對象的拷貝
淺拷貝: 以賦值的形式拷貝引用對象,仍指向同一個地址,修改時原對象也會受到影響
Object.assign
- 展開運算符(
...
)
深拷貝: 完全拷貝一個新對象,修改時原對象不再受到任何影響
JSON.parse(JSON.stringify(obj))
: 性能最快- 具有循環引用的對象時,報錯
- 當值爲函數、
undefined
、或symbol
時,無法拷貝 - 遞歸進行逐一賦值
#10. new運算符的執行過程
- 新生成一個對象
- 鏈接到原型:
obj.__proto__ = Con.prototype
- 綁定
this: apply
- 返回新對象(如果構造函數有自己
retrun
時,則返回該值)
#11. instanceof原理
能在實例的 原型對象鏈 中找到該構造函數的
prototype
屬性所指向的 原型對象,就返回true
。即:
// __proto__: 代表原型對象鏈
instance.[__proto__...] === instance.constructor.prototype
// return true
#12. 代碼的複用
當你發現任何代碼開始寫第二遍時,就要開始考慮如何複用。一般有以下的方式:
- 函數封裝
- 繼承
- 複製
extend
- 混入
mixin
- 借用
apply/call
#13. 繼承
在 JS 中,繼承通常指的便是 原型鏈繼承,也就是通過指定原型,並可以通過原型鏈繼承原型上的屬性或者方法。
最優化: 聖盃模式
var inherit = (function(c,p){
var F = function(){};
return function(c,p){
F.prototype = p.prototype;
c.prototype = new F();
c.uber = p.prototype;
c.prototype.constructor = c;
}
})();
使用
ES6
的語法糖class / extends
#14. 類型轉換
大家都知道 JS 中在使用運算符號或者對比符時,會自帶隱式轉換,規則如下:
-、*、/、%
:一律轉換成數值後計算- +:
- 數字 + 字符串 = 字符串, 運算順序是從左到右
- 數字 + 對象, 優先調用對象的
valueOf -> toString
- 數字 +
boolean/null
-> 數字 - 數字 +
undefined
->NaN
[1].toString() === '1'
{}.toString() === '[object object]'
NaN !== NaN
、+undefined
爲NaN
#15. 類型判斷
判斷
Target
的類型,單單用typeof
並無法完全滿足,這其實並不是bug
,本質原因是JS
的萬物皆對象的理論。因此要真正完美判斷時,我們需要區分對待:
- 基本類型(
null
): 使用String(null)
- 基本類型(
string / number / boolean / undefined
) +function
: - 直接使用typeof
即可 - 其餘引用類型(
Array / Date / RegExp Error
): 調用toString
後根據[object XXX]
進行判斷
很穩的判斷封裝:
let class2type = {}
'Array Date RegExp Object Error'.split(' ').forEach(e => class2type[ '[object ' + e + ']' ] = e.toLowerCase())
function type(obj) {
if (obj == null) return String(obj)
return typeof obj === 'object' ? class2type[ Object.prototype.toString.call(obj) ] || 'object' : typeof obj
}
#16. 模塊化
模塊化開發在現代開發中已是必不可少的一部分,它大大提高了項目的可維護、可拓展和可協作性。通常,我們 在瀏覽器中使用
ES6
的模塊化支持,在Node
中使用commonjs
的模塊化支持。
分類:
es6: import / export
commonjs: require / module.exports / exports
amd: require / defined
require與import的區別
require
支持 動態導入,import
不支持,正在提案 (babel
下可支持)require
是 同步 導入,impor
t屬於 異步 導入require
是 值拷貝,導出值變化不會影響導入值;import
指向 內存地址,導入值會隨導出值而變化
#17. 防抖與節流
防抖與節流函數是一種最常用的 高頻觸發優化方式,能對性能有較大的幫助。
- 防抖 (debounce): 將多次高頻操作優化爲只在最後一次執行,通常使用的場景是:用戶輸入,只需再輸入完成後做一次輸入校驗即可。
function debounce(fn, wait, immediate) {
let timer = null
return function() {
let args = arguments
let context = this
if (immediate && !timer) {
fn.apply(context, args)
}
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}
- 節流(throttle): 每隔一段時間後執行一次,也就是降低頻率,將高頻操作優化成低頻操作,通常使用場景: 滾動條事件 或者
resize
事件,通常每隔100~500 ms
執行一次即可。
function throttle(fn, wait, immediate) {
let timer = null
let callNow = immediate
return function() {
let context = this,
args = arguments
if (callNow) {
fn.apply(context, args)
callNow = false
}
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args)
timer = null
}, wait)
}
}
}
#18. 函數執行改變this
- 由於 JS 的設計原理: 在函數中,可以引用運行環境中的變量。因此就需要一個機制來讓我們可以在函數體內部獲取當前的運行環境,這便是
this
。
因此要明白
this
指向,其實就是要搞清楚 函數的運行環境,說人話就是,誰調用了函數。例如
obj.fn()
,便是obj
調用了函數,既函數中的this === obj
fn()
,這裏可以看成window.fn()
,因此this === window
但這種機制並不完全能滿足我們的業務需求,因此提供了三種方式可以手動修改
this
的指向:
call: fn.call(target, 1, 2)
apply: fn.apply(target, [1, 2])
bind: fn.bind(target)(1,2)
#19. ES6/ES7
由於
Babel
的強大和普及,現在ES6/ES7
基本上已經是現代化開發的必備了。通過新的語法糖,能讓代碼整體更爲簡潔和易讀。
聲明
let / const
: 塊級作用域、不存在變量提升、暫時性死區、不允許重複聲明const
: 聲明常量,無法修改
解構賦值
class / extend: 類聲明與繼承
Set / Map: 新的數據結構
異步解決方案:
Promise
的使用與實現generator
:yield
: 暫停代碼next()
: 繼續執行代碼
function* helloWorld() {
yield 'hello';
yield 'world';
return 'ending';
}
const generator = helloWorld();
generator.next() // { value: 'hello', done: false }
generator.next() // { value: 'world', done: false }
generator.next() // { value: 'ending', done: true }
generator.next() // { value: undefined, done: true }
await / async
: 是generator
的語法糖,babel
中是基於promise
實現。
async function getUserByAsync(){
let user = await fetchUser();
return user;
}
const user = await getUserByAsync()
console.log(user)
#20. AST
抽象語法樹 (
Abstract Syntax Tree
),是將代碼逐字母解析成 樹狀對象 的形式。這是語言之間的轉換、代碼語法檢查,代碼風格檢查,代碼格式化,代碼高亮,代碼錯誤提示,代碼自動補全等等的基礎。例如:
function square(n){
return n * n
}
通過解析轉化成的AST如下圖:
#21. babel編譯原理
babylon
將ES6/ES7
代碼解析成AST
babel-traverse
對AST
進行遍歷轉譯,得到新的AST
- 新 AST 通過
babel-generator
轉換成ES5
#22. 函數柯里化
在一個函數中,首先填充幾個參數,然後再返回一個新的函數的技術,稱爲函數的柯里化。通常可用於在不侵入函數的前提下,爲函數 預置通用參數,供多次重複調用。
const add = function add(x) {
return function (y) {
return x + y
}
}
const add1 = add(1)
add1(2) === 3
add1(20) === 21
#23. 數組(array)
map
: 遍歷數組,返回回調返回值組成的新數組forEach
: 無法break
,可以用try/catch
中throw new Error
來停止filter
: 過濾some
: 有一項返回true
,則整體爲true
every
: 有一項返回false
,則整體爲false
join
: 通過指定連接符生成字符串push / pop
: 末尾推入和彈出,改變原數組, 返回推入/彈出項unshift / shift
: 頭部推入和彈出,改變原數組,返回操作項sort(fn) / reverse
: 排序與反轉,改變原數組concat
: 連接數組,不影響原數組, 淺拷貝slice(start, end)
: 返回截斷後的新數組,不改變原數組splice(start, number, value...)
: 返回刪除元素組成的數組,value
爲插入項,改變原數組indexOf / lastIndexOf(value, fromIndex)
: 查找數組項,返回對應的下標reduce / reduceRight(fn(prev, cur)
,defaultPrev)
: 兩兩執行,prev
爲上次化簡函數的return
值,cur
爲當前值(從第二項開始)
數組亂序:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function () {
return Math.random() - 0.5;
});
數組拆解: flat: [1,[2,3]] --> [1, 2, 3]
Array.prototype.flat = function() {
this.toString().split(',').map(item => +item )
}
#三、瀏覽器
#1. 跨標籤頁通訊
不同標籤頁間的通訊,本質原理就是去運用一些可以 共享的中間介質,因此比較常用的有以下方法:
- 通過父頁面
window.open()
和子頁面postMessage
- 異步下,通過
window.open('about: blank')
和tab.location.href = '*'
- 異步下,通過
- 設置同域下共享的
localStorage
與監聽window.onstorage
- 重複寫入相同的值無法觸發
- 會受到瀏覽器隱身模式等的限制
- 設置共享
cookie
與不斷輪詢髒檢查(setInterval
) - 藉助服務端或者中間層實現
#2. 瀏覽器架構
- 用戶界面
- 主進程
- 內核
- 渲染引擎
JS
引擎- 執行棧
- 事件觸發線程
- 消息隊列
- 微任務
- 宏任務
- 消息隊列
- 網絡異步線程
- 定時器線程
#3. 瀏覽器下事件循環(Event Loop)
事件循環是指: 執行一個宏任務,然後執行清空微任務列表,循環再執行宏任務,再清微任務列表
- 微任務
microtask(jobs): promise / ajax / Object.observe
(該方法已廢棄) - 宏任務
macrotask(task): setTimout / script / IO / UI Rendering
#4. 從輸入 url 到展示的過程
DNS
解析TCP
三次握手- 發送請求,分析
url
,設置請求報文(頭,主體) - 服務器返回請求的文件 (
html
) - 瀏覽器渲染
HTML parser
-->DOM Tree
- 標記化算法,進行元素狀態的標記
dom
樹構建
CSS parser --> Style Tree
- 解析
css
代碼,生成樣式樹
- 解析
attachment
-->Render Tree
- 結合 dom樹 與 style樹,生成渲染樹
layout
: 佈局GPU painting
: 像素繪製頁面
#5. 重繪與迴流
當元素的樣式發生變化時,瀏覽器需要觸發更新,重新繪製元素。這個過程中,有兩種類型的操作,即重繪與迴流。
- 重繪(repaint): 當元素樣式的改變不影響佈局時,瀏覽器將使用重繪對元素進行更新,此時由於只需要UI層面的重新像素繪製,因此 損耗較少
- 迴流(reflow): 當元素的尺寸、結構或觸發某些屬性時,瀏覽器會重新渲染頁面,稱爲迴流。此時,瀏覽器需要重新經過計算,計算後還需要重新頁面佈局,因此是較重的操作。會觸發迴流的操作:
- 頁面初次渲染
- 瀏覽器窗口大小改變
- 元素尺寸、位置、內容發生改變
- 元素字體大小變化
- 添加或者刪除可見的
dom
元素 - 激活
CSS
僞類(例如::hover
) - 查詢某些屬性或調用某些方法
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
getComputedStyle()
getBoundingClientRect()
scrollTo()
迴流必定觸發重繪,重繪不一定觸發迴流。重繪的開銷較小,迴流的代價較高。
最佳實踐:
css
- 避免使用
table
佈局 - 將動畫效果應用到
position
屬性爲absolute
或fixed
的元素上
javascript
- 避免頻繁操作樣式,可彙總後統一 一次修改
- 儘量使用
class
進行樣式修改 - 減少
dom
的增刪次數,可使用 字符串 或者documentFragment
一次性插入 - 極限優化時,修改樣式可將其
display: none
後修改 - 避免多次觸發上面提到的那些會觸發迴流的方法,可以的話儘量用 變量存住
#6. 存儲
我們經常需要對業務中的一些數據進行存儲,通常可以分爲 短暫性存儲 和 持久性儲存。
- 短暫性的時候,我們只需要將數據存在內存中,只在運行時可用
- 持久性存儲,可以分爲 瀏覽器端 與 服務器端
- 瀏覽器:
cookie
: 通常用於存儲用戶身份,登錄狀態等http
中自動攜帶, 體積上限爲4K
, 可自行設置過期時間
localStorage / sessionStorage
: 長久儲存/窗口關閉刪除, 體積限制爲4~5M
indexDB
- 服務器:
- 分佈式緩存
redis
- 數據庫
- 分佈式緩存
- 瀏覽器:
#7. Web Worker
現代瀏覽器爲
JavaScript
創造的 多線程環境。可以新建並將部分任務分配到worker
線程並行運行,兩個線程可 獨立運行,互不干擾,可通過自帶的 消息機制 相互通信。
基本用法:
// 創建 worker
const worker = new Worker('work.js');
// 向主進程推送消息
worker.postMessage('Hello World');
// 監聽主進程來的消息
worker.onmessage = function (event) {
console.log('Received message ' + event.data);
}
限制:
- 同源限制
- 無法使用
document / window / alert / confirm
- 無法加載本地資源
#8. 內存泄露
- 意外的全局變量: 無法被回收
- 定時器: 未被正確關閉,導致所引用的外部變量無法被釋放
- 事件監聽: 沒有正確銷燬 (低版本瀏覽器可能出現)
- 閉包: 會導致父級中的變量無法被釋放
dom
引用:dom
元素被刪除時,內存中的引用未被正確清空
可用
chrome
中的timeline
進行內存標記,可視化查看內存的變化情況,找出異常點。
#四、服務端與網絡
#1. http/https 協議
1.0 協議缺陷:
- 無法複用鏈接,完成即斷開,重新慢啓動和
TCP 3
次握手 head of line blocking
: 線頭阻塞,導致請求之間互相影響
1.1 改進:
- 長連接(默認
keep-alive
),複用 host
字段指定對應的虛擬站點- 新增功能:
- 斷點續傳
- 身份認證
- 狀態管理
cache
緩存Cache-Control
Expires
Last-Modified
Etag
2.0:
- 多路複用
- 二進制分幀層: 應用層和傳輸層之間
- 首部壓縮
- 服務端推送
https: 較爲安全的網絡傳輸協議
- 證書(公鑰)
SSL
加密- 端口
443
TCP:
- 三次握手
- 四次揮手
- 滑動窗口: 流量控制
- 擁塞處理
- 慢開始
- 擁塞避免
- 快速重傳
- 快速恢復
緩存策略: 可分爲 強緩存 和 協商緩存
Cache-Control/Expires
: 瀏覽器判斷緩存是否過期,未過期時,直接使用強緩存,Cache-Control
的max-age
優先級高於Expires
- 當緩存已經過期時,使用協商緩存
- 唯一標識方案:
Etag
(response
攜帶) &If-None-Match
(request
攜帶,上一次返回的Etag
): 服務器判斷資源是否被修改 - 最後一次修改時間:
Last-Modified(response) & If-Modified-Since
(request
,上一次返回的Last-Modified
)- 如果一致,則直接返回 304 通知瀏覽器使用緩存
- 如不一致,則服務端返回新的資源
- 唯一標識方案:
Last-Modified
缺點:- 週期性修改,但內容未變時,會導致緩存失效
- 最小粒度只到
s
,s
以內的改動無法檢測到
Etag
的優先級高於Last-Modified
#2. 常見狀態碼
1xx
: 接受,繼續處理200
: 成功,並返回數據201
: 已創建202
: 已接受203
: 成爲,但未授權204
: 成功,無內容205
: 成功,重置內容206
: 成功,部分內容301
: 永久移動,重定向302
: 臨時移動,可使用原有URI304
: 資源未修改,可使用緩存305
: 需代理訪問400
: 請求語法錯誤401
: 要求身份認證403
: 拒絕請求404
: 資源不存在500
: 服務器錯誤
#3. get / post
get
: 緩存、請求長度受限、會被歷史保存記錄- 無副作用(不修改資源),冪等(請求次數與資源無關)的場景
post
: 安全、大數據、更多編碼類型
#4. Websocket
Websocket
是一個 持久化的協議, 基於http
, 服務端可以 主動push
兼容:
FLASH Socket
- 長輪詢: 定時發送
ajax
long poll
: 發送 --> 有消息時再response
new WebSocket(url)
ws.onerror = fn
ws.onclose = fn
ws.onopen = fn
ws.onmessage = fn
ws.send()
#5. TCP三次握手
建立連接前,客戶端和服務端需要通過握手來確認對方:
- 客戶端發送
syn
(同步序列編號) 請求,進入syn_send
狀態,等待確認 - 服務端接收並確認
syn
包後發送syn+ack
包,進入syn_recv
狀態 - 客戶端接收
syn+ack
包後,發送ack
包,雙方進入established
狀態
#6. TCP四次揮手
- 客戶端 -- FIN --> 服務端, FIN—WAIT
- 服務端 -- ACK --> 客戶端, CLOSE-WAIT
- 服務端 -- ACK,FIN --> 客戶端, LAST-ACK
- 客戶端 -- ACK --> 服務端,CLOSED
#7. Node 的 Event Loop: 6個階段
timer
階段: 執行到期的setTimeout / setInterval
隊列回調I/O
階段: 執行上輪循環殘流的callback
idle
,prepare
poll
: 等待回調-
- 執行回調
-
- 執行定時器
- 如有到期的
setTimeout / setInterval
, 則返回timer
階段 - 如有
setImmediate
,則前往check
階段
-
check
- 執行
setImmediate
- 執行
close callbacks
#8. 跨域
JSONP
: 利用<script>
標籤不受跨域限制的特點,缺點是隻能支持get
請求
function jsonp(url, jsonpCallback, success) {
const script = document.createElement('script')
script.src = url
script.async = true
script.type = 'text/javascript'
window[jsonpCallback] = function(data) {
success && success(data)
}
document.body.appendChild(script)
}
- 設置
CORS: Access-Control-Allow-Origin:*
postMessage
#9. 安全
XSS
攻擊: 注入惡意代碼cookie
設置httpOnly
- 轉義頁面上的輸入內容和輸出內容
CSRF
: 跨站請求僞造,防護:get
不修改數據- 不被第三方網站訪問到用戶的
cookie
- 設置白名單,不被第三方網站請求
- 請求校驗
#五、框架:Vue
#1. nextTick
在下次
dom
更新循環結束之後執行延遲迴調,可用於獲取更新後的dom
狀態
- 新版本中默認是
mincrotasks
,v-on
中會使用macrotasks
macrotasks
任務的實現:setImmediate / MessageChannel / setTimeout
#2. 生命週期
init
initLifecycle/Event
,往vm上掛載各種屬性callHook: beforeCreated
: 實例剛創建initInjection/initState
: 初始化注入和data
響應性created: 創建完成,屬性已經綁定, 但還未生成真實
dom`- 進行元素的掛載:
$el / vm.$mount()
- 是否有
template
: 解析成render function
*.vue
文件:vue-loader
會將<template>
編譯成render function
beforeMount
: 模板編譯/掛載之前- 執行
render function
,生成真實的dom
,並替換到dom tree
中 mounted
: 組件已掛載
update
- 執行
diff
算法,比對改變是否需要觸發UI
更新 flushScheduleQueue
watcher.before
: 觸發beforeUpdate
鉤子 -watcher.run()
: 執行watcher
中的notify
,通知所有依賴項更新UI- 觸發
updated
鉤子: 組件已更新 actived / deactivated(keep-alive)
: 不銷燬,緩存,組件激活與失活destroy
beforeDestroy
: 銷燬開始- 銷燬自身且遞歸銷燬子組件以及事件監聽
remove()
: 刪除節點watcher.teardown()
: 清空依賴vm.$off()
: 解綁監聽
destroyed
: 完成後觸發鉤子
上面是vue的聲明週期的簡單梳理,接下來我們直接以代碼的形式來完成vue的初始化
new Vue({})
// 初始化Vue實例
function _init() {
// 掛載屬性
initLifeCycle(vm)
// 初始化事件系統,鉤子函數等
initEvent(vm)
// 編譯slot、vnode
initRender(vm)
// 觸發鉤子
callHook(vm, 'beforeCreate')
// 添加inject功能
initInjection(vm)
// 完成數據響應性 props/data/watch/computed/methods
initState(vm)
// 添加 provide 功能
initProvide(vm)
// 觸發鉤子
callHook(vm, 'created')
// 掛載節點
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
// 掛載節點實現
function mountComponent(vm) {
// 獲取 render function
if (!this.options.render) {
// template to render
// Vue.compile = compileToFunctions
let { render } = compileToFunctions()
this.options.render = render
}
// 觸發鉤子
callHook('beforeMounte')
// 初始化觀察者
// render 渲染 vdom,
vdom = vm.render()
// update: 根據 diff 出的 patchs 掛載成真實的 dom
vm._update(vdom)
// 觸發鉤子
callHook(vm, 'mounted')
}
// 更新節點實現
funtion queueWatcher(watcher) {
nextTick(flushScheduleQueue)
}
// 清空隊列
function flushScheduleQueue() {
// 遍歷隊列中所有修改
for(){
// beforeUpdate
watcher.before()
// 依賴局部更新節點
watcher.update()
callHook('updated')
}
}
// 銷燬實例實現
Vue.prototype.$destory = function() {
// 觸發鉤子
callHook(vm, 'beforeDestory')
// 自身及子節點
remove()
// 刪除依賴
watcher.teardown()
// 刪除監聽
vm.$off()
// 觸發鉤子
callHook(vm, 'destoryed')
}
#3. Proxy 相比於 defineProperty 的優勢
- 數組變化也能監聽到
- 不需要深度遍歷監聽
let data = { a: 1 }
let reactiveData = new Proxy(data, {
get: function(target, name){
// ...
},
// ...
})
#4. vue-router
mode
hash
history
跳轉
this.$router.push()
<router-link to=""></router-link>
佔位
<router-view></router-view>
#5. vuex
state
: 狀態中心mutations
: 更改狀態actions
: 異步更改狀態getters
: 獲取狀態modules
: 將state
分成多個modules
,便於管理