問:如何理解
html
標籤語義化?
html5
新出的標籤,每一個標籤都有本身語義,什麼標籤作什麼事。讓人看的懂,也讓機器能夠看的懂,利於SEO
。問:
css
權重是什麼?
important
:無限高1000
id
選擇器:權重值爲100
10
1
問:盒模型有幾種,它們區別是什麼?
使用box-sizing
屬性設置:border-box
:怪異盒模型、content-box
:標準盒模型。css
問:什麼是
BFC
?
BFC
能夠清除浮動、讓margin
不重疊。問:如何觸發
BFC
?
float
的值不爲none
。overflow
的值不爲visible
。display
的值爲table-cell
、table-caption
和inline-block
之一。position
的值不爲static
或releative
中的任何一個。問:你經常使用的清除浮動方式是什麼?
.clear:after, .clear:before { content: ' '; display: table; } .clear:after { clear: both; }
問:em、rem的區別?
em
:若是父級有設置字體大小,1em就是父級的大小,沒有1em等於自身默認的字體大小。rem
:相對於html
標籤的字體大小。問:不使用
border
屬性畫一條1px
的線?
<div style='height: 1px; background: #666; overflow: hidden;'></div> <hr size='1'></hr>
問:移動端
1px
問題?
box-shadow: 0 -1px 1px -1px #e5e5e5, //上邊線 1px 0 1px -1px #e5e5e5, //右邊線 0 1px 1px -1px #e5e5e5, //下邊線 -1px 0 1px -1px #e5e5e5; //左邊線 0 0 0 1px #e5e5e5; //四條線
問:定位的方式有哪幾種,它們的區別是什麼?
relative:
相較於自身定位,設置的位置相對於本身進行位移。不脫離文檔流。absolute:
相較於最近有定位的父節點定位,設置的位置相較於父節點。會脫離文檔流,致使父節點高度塌陷。fixed:
相較於當前窗口進行定位,設置的位置相較於窗口。脫離文檔流。問:垂直水平居中的實現方式有哪些?
text-align: center
和line-height
等同高度。position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);
position: absolute;top:0;left:0;right:0;bottom:0;margin:auto;
display: table
,子節點設置display:table-cell;text-align:center;vertical-align:middle;
display: flex;justify-content:center;align-items:center;
display: grid;
,子節點設置:align-self:center;justify-self: center;
問:你知道的左右寬度固定,中間自適應的三欄佈局方案有哪些?
浮動: .parent {overflow: hidden;} .left {float: left; width: 100px;} .right: {float: right; width: 100px;} <div class='parent'> <div class='left'></div> <div class='right'></div> <div class='center'></div> </div> 定位1: .parent {postion: relative}; .left {position: absolute; left: 0; width: 100px}; .right {position: absolute; right: 0; width: 100px}; .center {postion: absolute; left: 100px; right: 100px}; 定位2: .parent {postion: relative}; .left {position: absolute; left: 0; width: 100px}; .right {position: absolute; right: 0; top: 0; width: 100px}; .center {margin: 0 100px 0 100px}; 表格: .parent {dispaly: table; width: 100%;} .left {display: table-cell; width: 100px;} .center {display: table-cell;} .right {display: table-cell; width: 100px;} 彈性: .parent {display: flex;} .left {width: 100px;} .center {flex: 1;} .right {width: 100px;} 網格: .parent { display: grid; width: 100%; grid-template-rows: 100px; grid-template-columns: 100px auto 100px; }
問:實現三個圓形的水平自適應佈局?
難點在於高度的自適應 .parent { display: table; width: 100%; } .child { display: table-cell; padding-top: 33.33%; background: red; border-radius: 50%; } .parent { overflow: hidden; } .child { float: left; width: 33.33%; padding-top: 33.33%; border-radius: 50%; background: red; } .parent { display: flex; } .child { flex: 1; padding-top: 33.33%; border-radius: 50%; background: red; }
問:介紹下
flex
佈局?
主軸方向:水平排列(默認) | 水平反向排列 | 垂直排列 | 垂直反向排列 flex-direction: row | row-reverse | column | column-reverse; 換行:不換行(默認) | 換行 | 反向換行(第一行在最後面) flex-wrap: nowrap | wrap | wrap-reverse; flex-direction屬性和flex-wrap屬性的簡寫形式,默認值爲row nowrap flex-flow: <flex-direction> || <flex-wrap>; 主軸對齊方式:起點對齊(默認) | 終點對齊 | 居中對齊 | 兩端對齊 | 分散對齊 justify-content: flex-start | flex-end | center | space-between | space-around; 交叉軸對齊方式:拉伸對齊(默認) | 起點對齊 | 終點對齊 | 居中對齊 | 第一行文字的基線對齊 align-items: stretch | flex-start | flex-end | center | baseline; 多根軸線對齊方式:拉伸對齊(默認) | 起點對齊 | 終點對齊 | 居中對齊 | 兩端對齊 | 分散對齊 align-content: stretch | flex-start | flex-end | center | space-between | space-around;
問:
JavaScript
的變量有哪些類型?
boolean
、null
、undefined
、number
、string
、symbol
。Array
、Object
、Function
。問:基礎類型和引用的區別?
問:函數參數是對象時會發生什麼問題?
問:
typeof
和instanceof
判斷變量類型的區別?
typeof
對於基礎類型除了null
之外均可以顯示正確的類型,對於數組和對象都會顯示object
,對於函數會顯示function
。instanceof
主要是用來判斷引用類型,它的原理是根據原型鏈來查找。問:有沒有更好的判斷變量類型的方法?
Object.prototype.toString.call(var)
,能夠更加準確的判斷某個變量的類型。問:類數組轉爲數組的方式有哪些?
[].slice.call(arguments) Array.from(arguments) [...arguments]
問:如何判斷一個變量是不是數組?
arr instanceof Array Array.prototype.isPrototypeOf(arr) Array.isArray(arr) Object.prototype.toString.call(arr) === '[object Array]' arr.constructor === Array
問:將多維數組扁平化?
function flatten(arr) { return [].concat(...arr.map(v => { return Array.isArray(v) ? flatten(v) : v; })) } function flatten(arr) { return arr.reduce((pre, cur) => { return pre.concat(Array.isArray(cur) ? flatten(cur) : cur); }, []) } function flatten(arr) { return arr.flat(Infinity); } function flatten(arr) { // 純數字 return arr.toString().split(',').map(Number); } function flatten(arr) { const ret = []; while (arr.length) { const item = arr.shift(); if (Array.isArray(item)) { arr.unshift(...item); } else { ret.push(item); } } return ret; }
問:數組去重?
function unique(arr) { return [...new Set(arr)]; } function unique(arr) { return arr.filter((v, i, a) => { return a.indexOf(v) === i; }) } function unique(arr) { const tmp = new Map(); return arr.filter(v => { return !tmp.has(v) && tmp.set(v); }) }
問:字符串的
test
、match
、search
它們之間的區別?
`test`是檢測字符串是否匹配某個正則,返回布爾值; /[a-z]/.test(1); // false `match`是返回檢測字符匹配正則的數組結果集合,沒有返回`null`; '1AbC2d'.match(/[a-z]/ig); // ['A', 'b', 'C', 'd'] `search`是返回正則匹配到的下標,沒有返回`-1`。 '1AbC2d'.search(/[a-z]/); // 2
問:字符串的
slice
、substring
、substr
它們之間的區別?
`slice`是返回字符串開始至結束下標減去開始下標個數的新字符串,下標是負數爲倒數; 'abcdefg'.slice(2,3); // c // 3 - 2 'abcdefg'.slice(3,2); // '' // 2 - 3 'abcdefg'.slice(-2,-1); // f // -1 - -2 `substring`和`slice`正常截取字符串時相同,負數爲0,且下標值小的爲開始下標; 'abcdefg'.substring(2,3); //c // 3 - 2 'abcdefg'.substring(3,2); // c // 3 - 2 'abcdefg'.substring(3,-3); // abc // 3 - 0 `substr`返回開始下標開始加第二個參數(不能爲負數)個數的新字符串。 'abcdefg'.substr(2, 3); // cde 'abcdefg'.substr(3, 2); // de 'abcdefg'.substr(-3, 2); // ef
問:
==
和===
的區別?
===
會判斷兩邊變量的類型和值是否所有相等,==
會存在變量類型轉換的問題,因此並不推薦使用,只用一種狀況會被使用,var == null
是var === undefined || var === null
的簡寫,其他狀況一概使用===
。問:是否
===
就徹底靠譜?
0 === -0
就爲true
,NaN === NaN
爲false
,判斷兩個變量是否徹底相等可使用ES6
新增的API
,Object.is(0, -0)
,Object.is(NaN, NaN)
就能夠準確區分。問:在類型轉換中哪些值會被轉爲
true
?
undefined
、null
、false
、NaN
、''
、0
、-0
之外的值都會被轉爲true
,包括全部引用類型,即便是空的。問:談談對
this
的理解?
this
表示爲當前的函數調用方,在運行時才能決定。如誰調用了某個方法,誰就是這個方法執行時的this
。問:改變當前調用
this
的方式?
call
:會當即執行調用call
方法的函數,不過是以第一個參數爲this
的狀況下調用,方法內能夠傳遞不等的參數,做爲調用call
方法的參數。apply
:運行方式和call
是一致的,只是接受的參數不一樣,不能是不定參數,得是一個數組。bind
:會改變當前的this
,接受不定參數,不過不會立刻執行調用bind
方法的函數,而是返回一個函數做爲結果,執行後纔是調用函數的結果。問:談談對閉包的理解?
return
關鍵字。問:談談對原型以及原型鏈的理解?
JavaScript
引用類型(數組/對象/函數)都有一個__proto__
屬性,這個屬性是一個對象格式,也就是原型屬性。在原型屬性裏面有一個constructor
屬性,這個屬性是這個引用類型的構造函數,在constructor
裏面又有一個prototype
的屬性,這個屬性又指回了引用類型的原型屬性。__proto__
屬性層層鏈接起來造成的,而構造函數的prototype
是一個對象屬性,再構造函數實例化時就會將這個屬性賦值給實例化後對象的__proto__
屬性,因此函數的繼承也會相應的構造出對象的原型鏈。問:原型繼承的方式有哪些?
function Parent(name) { this.name = name; } function Child(name, age) { Parent.call(this, name); this.age = age; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child
問:如何解決引用類型變量共享的問題?
JSON.parse(JSON.stringify(obj))
,不過也會存在諸多問題,更加完善的深拷貝須要手寫遞歸方法對不一樣參數分別處理,參考深拷貝的終極探索(90%的人都不知道)。問:函數防抖和節流的區別?
wow
爲例:2.5s
施法的寒冰箭,再讀條的過程當中,你身子抖動打斷了施法,再次觸發技能時麻煩您從新讀條。cd
爲8s
,因此即便8s
內按了10
次,也只能來1
發,節省點體力吧。問:請手寫一下map、call、new、instanceof、Events、深拷貝、節流、Promise等?
問:
var
、let
、const
的區別 ?
var
類型會有變量提高的狀況,也就是說聲明會首先提高到當前做用域的頂端,在使用到時再讀取定義的值,並且在全局做用域下定義的變量會掛載到window
下,而let
和const
並不會。let
和const
沒有變量提高的狀況,必需要先聲明再使用,不然就會出現暫時性死區的狀況。const
和let
的區別在於一經定義後不得再次改變const
定義的值,若是是引用類型只要不改變指針,改變裏面的值是沒問題的。const
定義時必須賦值,let
沒必要。問:
Set
、WeakSet
的區別?
Set
類型內存儲的是不會重複的值,建議存儲基礎類型的值,由於引用類型的指針都不一樣。WeakSet
只能存儲對象參數,不然會報錯,並且是存儲的引用類型的弱引用。WeakSet
不可被迭代,不支持forEach
、for-of
、keys
、values
方法,沒有size
屬性。const set = new Set(); const obj = {name: 'cc'}; set.add(obj); obj = null; [...set][0]; // {name: 'cc'} 轉數組後依然能夠訪問到 const weakSet = new WeakSet(); const obj = {}; weakSet.add(obj); obj = null; // 會移除引用 weakSet.has(obj); // false
問:
Map
、WeakMap
的區別?
Map
是解決了對象key
會被自動轉爲字符串的一種加強key/value
集合。WeakMap
是弱引用的Map
集合,key
必須是非null
的對象格式,一樣不能夠被迭代。const obj = Object.create(null); obj[1] = 'cc'; obj['1']; // cc const map = new Map(); map.set(1, 'cc'); map.has('1'); // false 1 和 '1'不會被轉換
問:箭頭函數和普通函數的區別?
this
是由包裹它的普通函數的this
來決定;Generator
函數;arguments
訪問,須要使用Es6
的不定參數訪問;bind
方法無效。問:請實現plus(1)(2)(3)(4)等於8?
方法1: function plus(n) { debugger let sum = n; const _plus = function (n) { sum += n; return _plus; }; _plus.toString = function () { return sum; }; return _plus; } 方法2: function multi() { const args = [].slice.call(arguments); const fn = function () { const newArgs = args.concat([].slice.call(arguments)); return multi.apply(this, newArgs); } fn.toString = function () { return args.reduce(function (a, b) { return a + b; }) } return fn; }
問:談談對
class
的理解 ?
JavaScript
沒有真正的類,一直也是經過函數加原型的形式來模擬,class
也不例外,只是語法糖,本質仍是函數。須要先聲明再使用,內部的方法不會被遍歷,且沒有函數的prototype
屬性。不過相較ES6
以前不管是定義仍是繼承都好理解了不少。繼承主要是使用extends
和super
關鍵字,本質相似於ES5
的寄生組合繼承:class Parent { constructor(name) { this.name = name; } } class Child extends Parent { constructor(name, age) { super(name); // 至關於Parent.call(this, name) this.age = age; } }
問:談談對
Promise
的理解 ?
Promise
主要解決的問題就是異步回調嵌套過深形成代碼難以維護和理解。Promise
構造函數內的代碼是同步執行的,而以後then
或catch
方法是異步執行的,構造函數接受兩個函數參數resolve
和reject
,它們執行時接受的參數分別會傳遞給then
和catch
表示成功的回調以及失敗回調接受到的值。Promise
一共有三種狀態pending
等待狀態、resolved
已完成狀態、rejected
已拒絕狀態,狀態的改變只能由等待轉爲已完成或等待轉爲已拒絕狀態,並且狀態的改變只會發生一次。then
方法且方法裏必需要返回一個Promise
對象,若是是返回其餘的類型會嘗試包裝成Promise
對象;then
能夠被鏈式的調用。Promise
鏈中途沒法取消;錯誤須要經過回調函數捕獲。問:談談對
ES-Module
的理解 ?
ES-Module
是ES6
原生支持模塊化方案,經過import
來引入模塊,經過export default
或export
來導出模塊。問:談談對
Proxy
的理解 ?
Object.defineProperty
有些相似,它的做用是用來自定義對象中操做。Proxy
的構造函數接受兩個參數,第一個參數是須要代理的對象,第二個參數是一個對象,裏面會定義get
和set
方法,當代理對象中的某個值被訪問或從新賦值就會觸發相應的get
和set
方法。vue3.0
就拋棄了Object.defineProperty
而擁抱了Proxy
,它的優勢是隻須要代理一次,對象內的值發生了改變就會被感知到,再也不須要像之前爲對象的每一個值進行數據劫持;並且之前對象的新增,數組的下標設置0
清空等狀況均可以被感知到,在響應式裏也不在須要爲數組和對象收集兩次依賴,相信會大大提高性能。談談對
Generator
的理解?
JavaScript
方便建立迭代器的新語法,在方法名前面添加*
號,表示這個方法是一個生成器函數,在函數內部配合yield
關鍵字指定next()
方法返回值及順序。yield
相似與在函數內部打上了斷點,yield
就是每一處的debugger
,執行next()
方法後進入下一個斷點。問:談談對
async
及await
的理解 ?
Genneator
的語法糖形式,解決的問題是以同步的形式寫異步代碼,讓代碼流程能很好的表示執行流程。在函數的前面加上async
代表是一個異步函數,函數的內部須要配合await
關鍵字使用,每個await
關鍵字至關因而yield
,會暫停函數的執行,直到異步函數執行完畢後內部會自動執行next()
方法,執行以後的代碼,函數的返回結果是一個Promise
對象。由於是以同步的形式書寫異步代碼,因此錯誤捕獲是使用try/catch
的形式。問:談談對
Event-Loop
的理解 ?
JavaScript
的執行機制簡單來講就先執行同步代碼,而後執行異步代碼,而異步的代碼裏面又分爲宏任務代碼和微任務代碼,先執行微任務,而後執行宏任務。首先會將全部JavaScript
做爲一個宏任務執行,遇到同步的代碼就執行,而後開始分配任務,遇到宏任務就將它的回調分配到宏任務的隊列裏,遇到微任務的回調就分配到微任務的隊列裏,而後開始執行全部的微任務。執行微任務的過程仍是遵循先同步而後分配異步任務的順序,微任務執行完畢以後,一次Event-Loop
的Tick
就算完成了。接着挨個去執行分配好的宏任務,在每一個宏任務裏又先同步後分配異步任務,完成下一次Tick
,循環往復直到全部的任務所有執行完成。process.nextTick
,promise
,MutationObserver
。script
, setTimeout
,setInterval
,setImmediate
,I/O
,UI rendering
。問:對瀏覽器或元素的各類距離參數你知道哪些?
document.documentElement.clientHeight
:當前窗口內容區 + 內邊距的高度window.innerHeight
: 當前窗口內容區 + 內邊距 + 邊框 + 滾動條高度window.outerHeight
:整個瀏覽器的高度(包括工具欄)clientHeight
: 當前元素內容區 + 內邊距的高度clientTop
: 當前元素上邊框的寬度offsetHeight
: 當前元素內容區 + 內邊距 + 邊框 + 滾動條的高度offsetTop
: 當前元素的邊框距離父元素上外邊距的距離scrollHeight
: 當前內部能夠滾動區域的高度,若是不能滾動則爲本身內容區 + 內邊距的高度scrollTop
: 當前元素滾動離頂部的距離問:怎麼肯定當前瀏覽器的類型?
navigator.userAgent
獲取瀏覽器信息,根據裏面的關鍵字來肯定。問:什麼是簡單請求和複雜請求?
get
、head
、post
。Content-type
僅限text/plain
、multipart/form-data
、application/x-www-form-urlencoded
。不符合以上條件者就爲複雜請求,首先會發起一個option
方法的預檢請求,來知道服務端是否容許跨域請求。
有一個坑就是服務端設置了CORS
,但當客戶端發其複雜請求時會驗證Authorization
字段,可是客戶端並無,因此須要將option
方法過濾掉。html
問:從輸入域名到頁面顯示都經歷了什麼?
DNS
解析爲對應的IP
地址,而後經過Socket
發送數據,通過tcp
協議的三次握手,向該地址發起HTTP
請求,服務器處理,瀏覽器收到HTTP
響應的數據,關閉tcp
鏈接,開始渲染。問:談談瀏覽器的渲染機制?
JavaScript
、Css
、Html
在網絡傳輸中都是0
和1
的字節流,因此瀏覽器首先會把接受到的這些字節流轉爲字符串。而後首先將html
字節流解析爲字符串,對字符串進行標記化,肯定標籤名以及裏面的內容,而後生成對應的node
節點,根據節點的結構關係生成DOM
樹。而後開始解析css
,和解析html
相似,css
通常有嵌套或繼承的狀況,瀏覽器會從裏到外的遞歸來肯定每一個節點的樣式是什麼,從而生成一顆CSSOM
樹。最後是將這兩顆樹結合起來生成一顆渲染樹,瀏覽器根據渲染樹進行佈局,調用GPU
繪製生成頁面顯示在屏幕上。問:什麼是重繪和迴流?
color
這個行爲;迴流是指改變佈局或幾何屬性發生改變時引發的行爲,如添加移除Dom
,改變尺寸。它們頻繁的觸發會影響頁面性能。問:如何減小重繪和迴流?
transform
替代位移,使用translate3d
開啓GPU
加速visibility
替代display:none
tanle
佈局問:什麼是事件流/模型?
window
=> document
=> body
=> 目標元素window
,沿途觸發相同類型的事件問:什麼是事件代理?
問:什麼是事件對象?
問:什麼是跨域?
ajax
請求就會失敗。瀏覽器有同源策略主要是爲了防止CSRF
攻擊,防止利用戶的登陸狀態發起惡意請求。問:你知道的解決跨域的方式有幾種?
JSONP:
利用script
標籤不受同源策略限制,具體能夠參考40行封裝一個jsonp包。CORS:
使用自定義的HTTP
頭部讓瀏覽器和服務器進行溝通,實現CORS
的關鍵是後端,服務端設置Access-Control-Allow-Origin
就能夠開啓,表示哪些域名能夠訪問資源。document.domain:
當二級域名相同時,例如a.test.html
和b.test.html
,只須要給兩個頁面都設置document.domain = 'test.html'
,就能夠實現跨域。postMessage:
如a.html
頁面經過iframe
嵌入了b.html
頁面,其中一個能夠經過postMessage
方法發送信息,另外一頁面經過監聽message
事件判斷來源並接受消息。問:
cookie
和session
分別是什麼?
cookie
是服務器發送到用戶瀏覽器並保存在本地的一小塊數據,它會在瀏覽器發起請求時被攜帶併發送到服務器,它一般是告知服務端兩個請求是否來自同一瀏覽器,保持用戶的登陸狀態。session
表明着服務器在和客戶端一次會話的過程,存儲着用戶會話所需的屬性及配置信息,當用戶在不一樣頁面之間跳轉時,使整個用戶會話一直存在。問:
cookie
和session
有什麼不一樣?
cookie
存儲在客戶端,session
保存在服務器端。cookie
只能保存ASCⅡ
,session
能夠存取任意數據類型。cookie
可設置長時間保持,session
通常失效時間較短,或客戶端關閉就會失效。cookie
保存的數據不能超過4k
,session
可存儲數據遠高於cookie
。問:爲何須要
cookie
和session
?
sessionID
存儲到cookie
中,當再次發起請求時服務端根據攜帶的cookie
裏的sessionID
來查找對應的session
信息,沒有找到就說明沒登陸或登陸失效,找到說明已經登陸,能夠進行以後的操做。問:若是瀏覽器禁止了
cookie
怎麼辦?
SessionID
的參數;或者使用token
令牌,登陸後服務端生成一個Token
返回給客戶端,之後客戶端攜帶token
進行數據請求。問:使用
cookie
有哪些注意點?
value
:若是做用於登陸狀態,須要加密。http-only
:不能經過JavaScript
訪問到cookie
,防止XSS
攻擊。same-site
:不能在跨域請求中攜帶cookie
,防止CSRF
攻擊。問:先後端實現登陸的方式有哪些?
cookie
+ session
:前端登陸後,後端會種一個httpOnly
的cookie
在前端,裏面就有這個用戶對應的sessionId
,之後每一次前端發起請求會攜帶上這個cookie
,後端從裏面解析到sessionId
後找到對應的session
信息,就知道是誰再操做了。缺點是後端須要空間存儲session
,用戶多了,服務器多了都不方便,這種方式基本屬於淘汰邊緣。jwt
+ token
:前端登陸後,後端會返回一個包括用戶信息加密的token
字符串(可能還有過時時間,手機端有設備惟一碼等信息),客戶端本身保存了,將這個token
設置到header
裏的Authorization
,以後每次請求都帶上,服務器解碼這個token
以後就知道是誰在訪問了。優勢是不佔存儲空間,後端解碼便可。問:瀏覽器實現本地存儲的方式有哪幾種?
cookie:
存儲大小4kb
,會隨請求發送到服務端,可設置過時時間。localStorage:
存儲大小爲5M
,不參與請求,除非被清理,不然一直存在。sessionStorage:
存儲大小爲5M
,不參與請求,頁面關閉清除。indexDB:
存儲大小沒限制,不參與請求,除非被清理,不然一直存在,運行在瀏覽器上的非關係型數據庫。問:瞭解
Service Worker
嘛?
HTTPS
。使用Service-Worker
實現緩存功能通常分爲三個步驟:首先註冊,而後監聽install
事件緩存須要的文件,最後攔截請求事件,若是緩存中已經有請求的數據就直接使用。問:談談對瀏覽器緩存的理解?
問:從哪些地方能夠讀取到瀏覽器緩存?
Service Worker:
和瀏覽器其餘內建緩存機制不一樣,它可讓咱們自由控制緩存哪些文件、如何匹配緩存、如何讀取緩存且緩存是持續性的。Memory Cache:
從內存中讀取,速度快,不過緩存的持續性並不高,關閉頁面後內存中的緩存會被釋放,什麼東西能進內存肯定不了。Disk Cache:
速度沒有內存快,不過存儲的容量和持續性會高不少,在瀏覽器緩存中硬盤的覆蓋面是最大的。能夠根據HTTP Header
中的字段判斷哪些資源須要緩存,哪些能夠不請求直接使用,哪些已過時須要從新請求。而且即便在跨站點的狀況下,相同地址的資源一旦被硬盤緩存下來,就不會再次去請求數據。Push Cache:
是HTTP/2
中的內容,存儲時間很短暫,只在會話中,一旦會話結束就被釋放。問:什麼是瀏覽器緩存策略?
HTTP-Header
來實現的,在強緩存沒有命中以後纔會嘗試協商緩存。HTTP-Header
的Expires
和Cache-Control
實現。強緩存表示在緩存期間不須要發起請求,狀態碼爲200
。Expires:
是HTTP/1
的產物,值爲緩存的過時時間,若是資源過時了就須要再次發起請求,若是修改本地時間,可能會形成緩存失效。Cache-control:
出現於HTTP/1.1
,若是和Expires
同時設置,優先級會更高。能夠在請求頭或者響應頭中設置,能夠組合多種指令使用。HTTP-Header
的Last-Modified
和ETag
實現。若是緩存過時了,就須要發起請求驗證資源是否有更新。若是發起請求驗證資源沒有改變,返回狀態304
,而且更新瀏覽器緩存的有效期。Last-Modified和If-Modified-Since
:Last-Modified
表示本地文件最後修改日期,If-Modified-Since
會將Last-Modified
的值發送給服務器,詢問該日期以後的資源是否有更新,有就將新資源發送來,沒有返回304
狀態碼。Last-Modified
的弊端:1. 若是本地硬盤打開了緩存文件,即便沒修改也會被誤認爲已經修改了,從而致使服務器不能準確命中緩存,致使發送相同的內容;2. 只能以秒計時,若是在不可感知的時間內修改完成文件,服務端會認爲資源仍是命中了,不會返回正確的資源。ETag和If-None-Match
:爲了解決Last-Modified
的弊端,ETag
出現於HTTP/1.1
,他的優先級比Last-Modified
高。ETag
相似於文件的指紋,If-None-Match
會將ETag
發送給服務器,詢問該ETag
是否變更,有變更的話就將新的資源發送回來。問:瀏覽器緩存適用的應用場景有哪些?
Cache-Control:no-cache
,使瀏覽器每次都請求服務器,而後配合ETag
或Last-Modified
來驗證資源是否有效,雖然請求數量沒少,不過能顯著減小響應的數據大小。js
、css
等文件最後加上哈希值,只有當代碼修改後纔會生成新的文件名。因此咱們能夠給這些文件設置Cache-Control:max-age=31536000
爲一年的有效期,文件名變動了就下載新的文件,不然一直使用緩存。問:什麼是
XSS
?
XSS
的類型分爲兩種:持久型和非持久型。URL
參數的方式加入攻擊代碼,從而誘導用戶訪問連接從而進行攻擊。問:如何防範
XSS
?
script
標籤;使用CSP
告訴瀏覽器限制外部資源能夠加載和執行,開啓CSP
有兩種方式:1. 設置HTTP-Header
中的Content-sesurity-Policy
。 2. 設置<meta>
標籤的方式<meta http-equiv="Content-Security-Policy">
。問:什麼是
CSRF
?
問:如何防範
CSRF
?
get
請求對數據進行修改;不讓第三方網站訪問到用戶的cookie
,設置cookie
的SameSite
屬性,不讓cookie
隨跨域請求攜帶;組織第三方網站請求接口,驗證Referer
;Token
驗證,登錄後服務器下發一個隨機token
,以後的請求帶上。問:什麼是點擊劫持?
iframe
嵌入的方式嵌入到本身的網頁裏,將iframe
設置爲透明,在頁面中透出一個按鈕誘導用戶點擊。問:如何防範點擊劫持?
HTTP
響應頭X-FRAME-OPTIONS
,這個響應頭就是爲了防護點擊劫持的;遠古瀏覽器使用js
防護,當經過iframe
的方式加載頁面時,讓攻擊者網站不顯示內容。問:什麼是中間人攻擊?
問:若是防範中間人攻擊?
wifi
;只使用https
協議,並關閉http
的訪問。問:你知道的性能優化方式有哪些?
base64
,減小請求css
放最上面,js
放在body
最下面,渲染優化Dom
操做和避免迴流,渲染優化cookie
的使用,減小請求攜帶大小問:
babel
是如何將ES6
代碼轉爲ES5
的?
ES6
的代碼字符串,生成 ES6
的AST
語法樹;ES6
的AST
轉爲ES5
的 AST
語法樹;ES5
的AST
轉換成字符串代碼。問:有哪些方式能夠提高
webpack
的打包速度?
loader:
使用include
和exclude
指定搜索文件的範圍。babel-loader:
配置後面加上loader:'babel-loader?cacheDirectory=true'
將編譯過的文件緩存起來,下次只須要編譯更改過的代碼文件便可。HappyPack:
使用這個插件開啓loader
多線程打包。DllPlugin:
將特定的類庫提早打包而後引入,減小打包類庫的次數,只有當類庫更新版本才進行從新打包。resolve.alias:
配置別名,更快找到路徑。module.noParse:
肯定這個文件沒有其餘依賴時,讓webpack
打包時不掃描該文件。問:有哪些方式能夠減少
webpack
的打包後的體積?
Scope Hoisting:
分析模塊之間的依賴關係,儘量的把打包出來的模塊合併到一個函數裏。Tree Shaking:
刪除項目中未被引用的代碼。問:對
HTTP
協議的理解?
TCP/IP
協議家族的子集,屬於通訊傳輸流中鏈路層、網絡層、傳輸層、應用層中的應用層,主要職責是生成針對目標web
服務器的HTTP
請求報文和請求內容的處理。HTTP
是無狀態的協議,不對請求和響應之間的通訊狀態進行保存,響應完成後就會斷開鏈接。問:什麼是持久鏈接以及管線化?
HTTP/1.1
以前的時代,每一次HTTP
請求就須要先TCP
創建三次握手,傳輸完畢後就斷開鏈接,會增長不少的通訊開銷。HTTP/1.1
增長了持久鏈接,也就是說在一次TCP
鏈接裏面能夠發送屢次HTTP
請求,只要任意一端沒有明確提出斷開鏈接,則保持TCP
的鏈接狀態,也就是響應頭裏面的Connection:keep-alive
。HTTP
的方式是,發送響應完成後才能發起下一個請求,而管線化解決的問題是能夠一次發起多個HTTP
請求,且能夠同時返回屢次響應結果。問:爲何發起
HTTP
請求前須要TCP
三次握手?
HTTP
請求就能夠傳輸數據了。問:爲何關閉
HTTP
請求前須要TCP
四次揮手?
問:
HTTP
請求報文和響應報文裏分別有什麼?
ETag
、日期、內容類型等,以及響應的報文主體。問:
http1.0
和http1.1
的區別?
http1.1
加如了持久鏈接。問:
http
和https
的區別?
url
開頭不一致是最明顯的區分;其次http
沒有https
安全,http
沒有通過SSL/TLS
加密、身份驗證;還有默認的端口不同,http
是80
、https
是443
,https
須要證書,https
是防止中間人攻擊方式的一種。前端
問:爲何
https
更安全?
問:常見的響應狀態碼有哪些?
2
開頭表示成功、3
開頭表示重定向、4
開頭客戶端錯誤、5
開頭服務器錯誤。204:
如當瀏覽器發出請求處理後,返回204
響應,表示客戶端顯示的頁面不發生更新。206:
客戶端只要想要響應內容中的一部分,服務端成功執行了此次響應。響應報文中的Content-Range
則指定了哪部分的內容。301:
永久重定向,表示請求的資源已被分配到了新的URI
,之後使用新的吧302:
臨時重定向,表示請求的資源已被分配到了新的URI
,如今使用新的吧303:
臨時重定向,表示請求的資源已被分配到了新的URI
,請使用get
方法獲取資源。304:
服務端找不到根據客戶端發送附帶條件的請求。(附帶條件指get
請求報文中包含If-Match
、If-Modified-Since
、If-None-Match
、If-Range
、If-Unmodified-Since
中的一個)400:
請求報文存在語法錯誤。403:
請求資源被服務器拒絕。503:
代表服務器暫時處於超負載或正在停機維護,沒法處理請求。問:get和post的區別?
get
回退不會從新發起請求,post
會;get
默認會被瀏覽器主動緩存,post
不會;get
只能進行url
編碼,post
支持多種編碼方式;get
的請求參數會被拼接到url
後面,post
放在request-body
中;get
產生一個tcp
數據包,post
會產生兩個tcp
包;get
主要是應用爲獲取資源,post
主要是應用於傳輸或修改資源。問:什麼是
UDP
協議?
UDP
是面向無鏈接的,傳輸雙方沒有確認機制,也就是說你要傳就傳吧,沒有HTTP
那樣須要事先三從握手。問:談談
vue
初始化從數據到視圖的過程,能詳細些嗎?
vue
的內部會執行_init()
方法,進行一系列的初始化,首先會進行配置的合併,將用戶傳入的對象和自身的方法屬性進行合併。render
函數內的數據轉爲VNode
的方法(手寫render
函數裏的h
方法),接着執行第一個生命週期鉤子beforeCreate
函數。inject
、data
、computed
、watch
、provide
等,掛載到當前實例this
下,並完成數據的響應式,緊接着執行created
鉤子函數。render
函數,完畢以後執行beforeMount
鉤子。render
函數,獲得VNode
,以後執行patch
由內而外的將VNode
轉爲真實Dom
,完成以後執行mounted
鉤子。patch
的過程當中遇到了子組件的VNode
,就會轉爲去執行子組件的初始化到真實Dom
的狀態過程,完畢以後才執行父組件的mounted
鉤子。問:vue生命週期鉤子有哪些,每一個鉤子階段都作了什麼?
beforeCreate:
創建了組件的父子關係,完成了事件的初始化,還不能訪問到定義的狀態。created:
完成了provide/inject
和狀態的初始化,能夠訪問到data
、props
等狀態。beforeMount:
內部執行了$mount
函數,找到掛載點後執行beforeMount
,主要做用是做爲掛載到完成階段性能檢測的起始時間以及將模板編譯爲render
函數。mounted:
開始將render
函數轉爲VNode
,並建立爲真實的DOM
,掛載完成以後執行mounted
。beforeUpdate:
對於已經掛載完成的組件更新了組件模板的數據時,在異步更新以前觸發。updated:
更新完成以後執行。更新相關的鉤子連官網都不推薦使用,推薦使用watch
監聽數據變化。activated:
被keep-alive
包裹的組件,由於不會銷燬,因此也不會重建,它的created
、mounted
鉤子不會觸發。被緩存的組件激活時執行activated
鉤子。deactivated:
離開當前激活的組件時觸發。beforeDestroy:
組件銷燬以前執行,這個時候尚未銷燬任何東西,全部狀態均可以訪問到。destroyed:
當前組件的子組件都銷燬完,當前組件也銷燬完以後執行。問:組件之間通訊方式有哪些?
$emit
觸發,傳值給父組件的回調使用。props
給子組件;父組件使用ref
引用子組件實例,訪問子組件的數據和方法。$children
屬性,經過name
找到對應的兄弟組件實例。provide/inject
,父組件能夠向全部子組件傳值。vuex
或者Event Bus
;當前組件找到須要傳值組件的實例,使用$on
和$emit
傳值。問:父子組件如何完成數據雙向綁定?
v-model形式: 只能綁定一個數據 父組件: <template> <Child v-model='msg'/> /* 可使用value和@input的形式 */ </template> export default { data() { return { msg: '' } } } 子組件: <template> <input v-model='currentMsg'/> </template> export default { props: ['value'], data() { return { currentMsg: this.value } }, watch: { value(newVal) { this.currentMsg = newVal }, currentMsg(newVal) { this.$emit('input', newVal) } } } sync形式: 綁定數據條數沒限制 父組件: <template> <Child :name.sync='name' :sex.sync='sex'/> </template> export default { data() { return { name: '', sex: '' } } } 子組件: <template> <div> <input v-model='currentName'/> <input v-model='currentSex'/> </div> </template> export default { props: ['name', 'sex'], data() { return { currentName: this.name, currentSex: this.sex } }, watch: { name(newName) { this.currentName= newName }, sex(newSex) { this.currentSex = newSex }, currentName(newName) { this.$emit('update:name', newName) }, currentSex(newSex) { this.$emit('update:sex', newSex) } } }
問:什麼是插槽、具名插槽、做用域插槽?
默認插槽: <button> <slot>插槽默認屬性</slot> </button> 具名插槽: <div> <slot name="head">Head</slot> <slot name="center">Center</slot> <slot name="footer">Footer</slot> <slot>Default</slot> </div> <TestComp> <template v-slot:head> <div>添加到head</div> </template> <template #center> // 簡寫 <div>添加到center</div> </template> <template #footer> <div>添加到footer</div> </template> <template>添加到默認</template> </TestComp> 做用域插槽: <div> <slot :user="user">{{user.lastName}}</slot> </div> <TestComp v-slot="{user}"> <div>{{user.firstName}}</div> </TestComp> 具名插槽加做用域插槽 <div> <slot :user="user" name="info">{{user.lastName}}</slot> </div> <TestComp #info="{ user }"> <div>{{user.firstName}}</div> </TestComp>
問:
v-show
與v-if
區別?
v-if:條件渲染
,當條件爲false
時,渲染時根本就不會渲染出Dom
。v-show:條件展現
,不管條件是什麼,都會進行渲染出Dom
,顯隱由display:none
切換。v-show
,若是不頻繁就使用v-if
。問:
vue
裏數組綁定class的用法?
key/value
的形式,value
是一個布爾值。computed: { classes() { return [ `${prefixCls}`, { [`${prefixCls}-${this.type}`]: this.type !== '' } ] } }
問:
vue
裏的動畫?css動畫鉤子
1.v-enter
:定義進入過渡的開始狀態。在元素被插入以前生效,在元素被插入以後的下一幀移除。
2.v-enter-active
:定義進入過渡生效時的狀態。在整個進入過渡的階段中應用,在元素被插入以前生效,在過渡/動畫完成以後移除。這個類能夠被用來定義進入過渡的過程時間,延遲和曲線函數。
3.v-enter-to
: 2.1.8版及以上 定義進入過渡的結束狀態。在元素被插入以後下一幀生效 (與此同時 v-enter 被移除),在過渡/動畫完成以後移除。
4.v-leave
: 定義離開過渡的開始狀態。在離開過渡被觸發時馬上生效,下一幀被移除。
5.v-leave-active
:定義離開過渡生效時的狀態。在整個離開過渡的階段中應用,在離開過渡被觸發時馬上生效,在過渡/動畫完成以後移除。這個類能夠被用來定義離開過渡的過程時間,延遲和曲線函數。
6.v-leave-to
: 2.1.8版及以上 定義離開過渡的結束狀態。在離開過渡被觸發以後下一幀生效 (與此同時 v-leave 被刪除),在過渡/動畫完成以後移除。vue
JavaScript動畫鉤子v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
在 enter 和 leave 中必須使用 done 進行回調,不然,它們將被同步調用,過渡會當即完成。
appear會完成初始渲染。html5
問:如何實現一個自定義過濾器?
Vue.filter
方法進行自定義過濾器的全局註冊,第一個參數是過濾器的名稱,第二個參數是一個回調,回調內的第一個參數就是須要處理的文本值。同一個文本能夠通過多個過濾器,使用 |
進行分割便可,過濾器也能夠像使用方法般接受參數。問:如何實現一個自定義指令?
Dom
時,會更加的方便,通常是使用Vue.directive
方法進行自定義指令的全局註冊。註冊自定義時接受兩個參數,第一個是指令的名稱(不加v-
),第二個是一個對象,裏面主要是一些指令的鉤子函數,如bind
(綁定到節點時執行)、inserted
(渲染到父節點時執行)、update
(指令所在組件更新時)、componentUpdated
(所在組件及其子組件更新時)、unbind
(解除綁定時),在這些鉤子的內部第一個參數就是指令綁定對應的真實Dom
,第二個參數binding
,指令對應的一些信息或傳的參數。在鉤子函數裏完成對Dom
的操做就是自定義指令作的事情。問:談談對
vuex
的理解?
state
內,能夠被全部組件所引用,一經修改後引用state
內狀態的組件都會響應式的更新。state
不能被直接修改,必須commit
內提交mutation
才行,且mutation
必須是同步函數。提交action
能夠在內部進行異步操做,可同時提交多個mutation
。store
內的state
能夠經過this.$store.state.xxx
訪問。store
內的getter
能夠經過this.$store.getters.xxx
訪問。store
內的mutation
能夠經過this.$store.commit('xxx')
提交。store
內的action
能夠經過this.$store.dispatch('yyy')
提交。問:
vuex
語法糖方法有哪些以及如何使用?
computed: { ...mapState(['xxx', 'yyy']) } computed: { ...mapGetters(['xxx', 'yyy']) } methods: { ...mapMutations({ zzz: 'XXX_YYY' }) } methods: { ...mapActions(['yyy']) }
問:
vue-router
如何傳遞參數?
方式1: { path:"/home/:id", component:Home } this.$router.push({ path:`/home/${id}`, }) 在Home組件中獲取參數 this.$route.params.id 方式2:須要命名路由 { path:'/home', name: 'home' component:Home } this.$router.push({ name:'home', params:{id:9527} }) 在Home組件中獲取參數 this.$route.params.id 方式3: { path:"/home", component:Home } this.$router.push({ path:'/home', query:{id:9527} }) 在Home組件中獲取參數 this.$route.query.id
問:
vue-router
有哪些導航守衛鉤子?以及它們的執行順序?
beforeEach
:只要當某個導航被觸發時,就會執行這個鉤子。beforeResolve
:在路由的beforeEnter
和組件的beforeRouteEnter
執行以後執行。afterEach
:在全部的導航守衛執行完畢以後執行,沒有next
方法。node
beforeEnter
:在路由內定義,在全局beforeEach
以後執行。webpack
beforeRouteEnter
:在渲染組件對應路由被確認以前調用,不能訪問this
,在路由beforeEnter
鉤子以後執行。beforeRouteUpdate
:在當前路由改變但組件被複用時調用,例如在動態子路由以前調轉時。beforeRouteLeave
:導航離開該路由時調用。web
beforeRouteLeave
beforeEach
beforeRouteUpdate
beforeEnter
beforeRouteEnter
beforeResolve
afterEach
問:如何實現異步組件?
方式1: const Home= () => import('components/home') export default new Router({ routes: [ { path: '/home', component: Home, }, ] }) 方式2:能夠指定多個路由爲相同`chunk`名,會打包在一塊兒 export default new Router({ [{ path: '/home', component: r => require.ensure([], () => r(require('../components/home')), 'home' /* chunk名 */) }] }) 方式3: export default new Router({ [{ path: '/promisedemo', component: resolve => require(['../components/home'], resolve) } ]}) 方式4:高級異步組件,帶`loading`和`error`組件 const Home= () => lazyLoadView(import('components/home')) export default new Router({ routes: [ { path: '/home', component: Home, }, ] }) function lazyLoadView(AsyncView) { const AsyncHandler = () => ({ component: AsyncView, loading: require('@/components/loading').default, error: require('@/components/error').default, delay: 200, timeout: 10000 }); return Promise.resolve({ functional: true, render(h, { data, children }) { return h(AsyncHandler, data, children); } }); }
問:請實現一個最小化
vue
響應式示例?
class Watcher { update() { console.log('更新~'); } } class Dep { constructor() { this._watchers = new Set(); } add(watcher) { if(!this._watchers.has(watcher)) { this._watchers.add(watcher); } } notify() { this._watchers.forEach(watch => { watch.update(); }) } } Dep.target = new Watcher(); function observer(target) { if (typeof target === 'object' && target !== null) { Object.keys(target).forEach(key => { defineReactive(target, key, target[key]); }) } } function defineReactive(target, key, val) { const dep = new Dep(); if (typeof val === 'object' && val !== null) { observer(val); } Object.defineProperty(target, key, { get() { dep.add(Dep.target); return val; }, set(newVal) { dep.notify(); val = newVal; } }) }
問:工廠模式?
function Coder(name, age) { this.name = name this.age = age this.career = 'coder' this.work = ['寫代碼', '寫系分', '修Bug'] } function ProductManager(name, age) { this.name = name this.age = age this.career = 'product manager' this.work = ['訂會議室', '寫PRD', '催更'] } ... 簡單封裝,不一樣再去一個個的new具體的角色 function Factory(name, age, career) { if (career === 'coder') { return new Coder(name, age); } else if (career === 'product manager') { return new ProductManager(name, age); } ... } 將角色抽象成User類,使用工廠進一步封裝 function User(name, age, career, work) { this.name = name; this.age = age; this.career = career; this.work = work; } function Factory(name, age, career) { let work; if (career === 'coder') { word = ['寫代碼', '寫細分', '修Bug']; } else if (career === 'product manager') { word = ['訂會議室', '寫PRD', '催更']; } ... return new User(name, age, career, word); }
問:單例模式?
vue
裏的插件,安裝一次以後不會再次安裝,直接返回以前已經實例化的結果。實現Storage類,使得該對象爲單例,基於localStorage進行封裝。實現方法 setItem(key,value) 和 getItem(key)?
靜態方法版: class Storage { static create() { if (!Storage.instance) { Storage.instance = new Storage(); } return Storage.instance; } setItem(key, value) { return localStorage.setItem(key, value); } getItem(key, value) { return localStorage.getItem(key, value); } } 閉包版: function Storage() { } Storage.prototype.setItem = function (key, value) { return localStorage.setItem(key, value); } Storage.prototype.getItem = function (key) { return localStorage.getItem(key, value); } const createStorage = (function () { let instance; return function () { if (!instance) { instance = new Storage(); } return instance } })()
問:實現一個全局惟一的模態框?
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>modal</title> <style> #modal { position: fixed; height: 200px; width: 200px; line-height: 200px; top: 50%; left: 50%; transform: translate(-50%, -50%); border: 1px solid #000; text-align: center; } </style> </head> <body> <button id='open'>打開模態框</button> <button id='close'>關閉模態框</button> <script> const Model = (function () { let box; return function () { if (!box) { box = document.createElement('div'); box.innerHTML = '惟一'; box.id = 'modal'; box.style.display = 'none'; document.body.appendChild(box); } return box; } })() document.getElementById('open').addEventListener('click', () => { const model = Model(); model.style.display = 'block'; }) document.getElementById('close').addEventListener('click', () => { const model = Model(); model.style.display = 'none'; }) </script> </body> </html>
問:觀察者模式和發佈訂閱模式的區別?
vue
內的自定義事件的Event Emitter
,發佈者徹底不用感知到訂閱者,事件的註冊和觸發都發生在事件總線上,實現了徹底的解耦。Dep
和Watcher
就是觀察者模式,Dep
直接add
以及notify
觸發watcher
的更新。觀察者模式: class Subject { constructor() { this.observers = []; } add(observer) { this.observers.push(observer); } remove(observer) { const index = this.observers.indexOf(observer); if (index > -1) { this.observers.splice(index, 1); } } notify() { const obs = this.observers; for (let i = 0; i < obs.length; i++) { obs[i].update(); } } } class Observer { constructor(name) { this.name = name; } update() { console.log('my name is' + this.name); } } 發佈訂閱模式: class Events { constructor() { this._evnets = Object.create(null); } on(event, fn) { // 往事件中心添加事件 if (Array.isArray(event)) { for (let i = 0; i < event.length; i++) { this.on(evnet[i], fn); } } else { (this._evnets[event] || (this._evnets[event] = [])).push(fn); } } emit(event, ...args) { // 觸發事件中心對應事件 const cbs = this._evnets[event]; if (cbs) { for (let i = 0; i < cbs.length; i++) { cbs[i].apply(this, args); } } } off(event, fn) { // 移除事件 if (!arguments) { this._evnets = Object.create(null); return this; } if (Array.isArray(event)) { for (let i = 0; i < event.length; i++) { this.off(event[i], fn); } return this; } if (!fn) { this._evnets[event] = null; return this; } const cbs = this._evnets[event]; let i = cbs.length; while (i--) { const cb = cbs[i]; if (cb === fn || cb.fn === fn) { cbs.splice(i, 1); break; } } return this; } once(evnet, fn) { // 只執行一次 function on() { this.off(evnet, on); fn.apply(this, arguments); } on.fn = fn; this.on(evnet, on); return this; } }