前端百問

問:如何理解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-celltable-captioninline-block之一。
  • position的值不爲staticreleative中的任何一個。

問:你經常使用的清除浮動方式是什麼?

.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: centerline-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的變量有哪些類型?

  • 分爲兩種:基礎類型和引用類型。基礎類型目前有六種,分別是booleannullundefinednumberstringsymbol
  • 除了以上的基礎類型以外,其餘就是引用類型了,有ArrayObjectFunction

問:基礎類型和引用的區別?

  • 它們在內存中存儲的方式不一樣。基礎類型存儲的是值,而引用類型存儲的是指向內存中某個空間的指針;
  • 基礎類型賦值就是把值賦給另一個變量,而引用類型的賦值是賦值的原來變量的指針,因此當引用類型發生改變時,只要是指向同一個指針的變量的都會發生改變。

問:函數參數是對象時會發生什麼問題?

  • 函數參數是對象時,至關因而將對象的指針傳遞給了函數,若是在函數的內部改變了對象的值,外面對象的值也會發生改變,數組也是如此。

問:typeofinstanceof判斷變量類型的區別?

  • 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);
  })
}

問:字符串的testmatchsearch它們之間的區別?

`test`是檢測字符串是否匹配某個正則,返回布爾值;
/[a-z]/.test(1);  // false

`match`是返回檢測字符匹配正則的數組結果集合,沒有返回`null`;
'1AbC2d'.match(/[a-z]/ig);  // ['A', 'b', 'C', 'd']

`search`是返回正則匹配到的下標,沒有返回`-1`。
'1AbC2d'.search(/[a-z]/);  // 2

問:字符串的slicesubstringsubstr它們之間的區別?

`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 == nullvar === undefined || var === null的簡寫,其他狀況一概使用===

問:是否===就徹底靠譜?

  • 也是不必定的,例如0 === -0就爲trueNaN === NaNfalse,判斷兩個變量是否徹底相等可使用ES6新增的APIObject.is(0, -0)Object.is(NaN, NaN)就能夠準確區分。

問:在類型轉換中哪些值會被轉爲true

  • 除了undefinednullfalseNaN''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施法的寒冰箭,再讀條的過程當中,你身子抖動打斷了施法,再次觸發技能時麻煩您從新讀條。
  • 函數節流:火衝爲瞬發技能,不過你規定cd8s,因此即便8s內按了10次,也只能來1發,節省點體力吧。

問:請手寫一下map、call、new、instanceof、Events、深拷貝、節流、Promise等?

問:varletconst的區別 ?

  • var類型會有變量提高的狀況,也就是說聲明會首先提高到當前做用域的頂端,在使用到時再讀取定義的值,並且在全局做用域下定義的變量會掛載到window下,而letconst並不會。
  • letconst沒有變量提高的狀況,必需要先聲明再使用,不然就會出現暫時性死區的狀況。
  • 並且它們的做用域存在最近的大括號以內,也就是塊級做用域,而且一經定義後,同一個做用域內不能再次定義。
  • constlet的區別在於一經定義後不得再次改變const定義的值,若是是引用類型只要不改變指針,改變裏面的值是沒問題的。
  • const定義時必須賦值,let沒必要。

問:SetWeakSet的區別?

  • Set類型內存儲的是不會重複的值,建議存儲基礎類型的值,由於引用類型的指針都不一樣。
  • WeakSet只能存儲對象參數,不然會報錯,並且是存儲的引用類型的弱引用。
  • WeakSet不可被迭代,不支持forEachfor-ofkeysvalues方法,沒有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

問:MapWeakMap的區別?

  • 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以前不管是定義仍是繼承都好理解了不少。繼承主要是使用extendssuper關鍵字,本質相似於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構造函數內的代碼是同步執行的,而以後thencatch方法是異步執行的,構造函數接受兩個函數參數resolvereject,它們執行時接受的參數分別會傳遞給thencatch表示成功的回調以及失敗回調接受到的值。
  • Promise一共有三種狀態pending等待狀態、resolved已完成狀態、rejected已拒絕狀態,狀態的改變只能由等待轉爲已完成或等待轉爲已拒絕狀態,並且狀態的改變只會發生一次。
  • 必需要實現then方法且方法裏必需要返回一個Promise對象,若是是返回其餘的類型會嘗試包裝成Promise對象;
  • then能夠被鏈式的調用。
  • 缺點是Promise鏈中途沒法取消;錯誤須要經過回調函數捕獲。

問:談談對ES-Module的理解 ?

  • ES-ModuleES6原生支持模塊化方案,經過import來引入模塊,經過export defaultexport來導出模塊。

問:談談對Proxy的理解 ?

  • Object.defineProperty有些相似,它的做用是用來自定義對象中操做。Proxy的構造函數接受兩個參數,第一個參數是須要代理的對象,第二個參數是一個對象,裏面會定義getset方法,當代理對象中的某個值被訪問或從新賦值就會觸發相應的getset方法。vue3.0就拋棄了Object.defineProperty而擁抱了Proxy,它的優勢是隻須要代理一次,對象內的值發生了改變就會被感知到,再也不須要像之前爲對象的每一個值進行數據劫持;並且之前對象的新增,數組的下標設置0清空等狀況均可以被感知到,在響應式裏也不在須要爲數組和對象收集兩次依賴,相信會大大提高性能。

談談對Generator的理解?

  • JavaScript方便建立迭代器的新語法,在方法名前面添加*號,表示這個方法是一個生成器函數,在函數內部配合yield關鍵字指定next()方法返回值及順序。
  • yield相似與在函數內部打上了斷點,yield就是每一處的debugger,執行next()方法後進入下一個斷點。
  • 不能使用箭頭函數來建立生成器。

問:談談對asyncawait的理解 ?

  • Genneator的語法糖形式,解決的問題是以同步的形式寫異步代碼,讓代碼流程能很好的表示執行流程。在函數的前面加上async代表是一個異步函數,函數的內部須要配合await關鍵字使用,每個await關鍵字至關因而yield,會暫停函數的執行,直到異步函數執行完畢後內部會自動執行next()方法,執行以後的代碼,函數的返回結果是一個Promise對象。由於是以同步的形式書寫異步代碼,因此錯誤捕獲是使用try/catch的形式。

問:談談對Event-Loop的理解 ?

  • JavaScript的執行機制簡單來講就先執行同步代碼,而後執行異步代碼,而異步的代碼裏面又分爲宏任務代碼和微任務代碼,先執行微任務,而後執行宏任務。首先會將全部JavaScript做爲一個宏任務執行,遇到同步的代碼就執行,而後開始分配任務,遇到宏任務就將它的回調分配到宏任務的隊列裏,遇到微任務的回調就分配到微任務的隊列裏,而後開始執行全部的微任務。執行微任務的過程仍是遵循先同步而後分配異步任務的順序,微任務執行完畢以後,一次Event-LoopTick就算完成了。接着挨個去執行分配好的宏任務,在每一個宏任務裏又先同步後分配異步任務,完成下一次Tick,循環往復直到全部的任務所有執行完成。
  • 微任務包括:process.nextTickpromiseMutationObserver
  • 宏任務包括:scriptsetTimeoutsetIntervalsetImmediateI/OUI rendering

問:對瀏覽器或元素的各類距離參數你知道哪些?

  • document.documentElement.clientHeight:當前窗口內容區 + 內邊距的高度
  • window.innerHeight: 當前窗口內容區 + 內邊距 + 邊框 + 滾動條高度
  • window.outerHeight:整個瀏覽器的高度(包括工具欄)
  • clientHeight: 當前元素內容區 + 內邊距的高度
  • clientTop: 當前元素上邊框的寬度
  • offsetHeight: 當前元素內容區 + 內邊距 + 邊框 + 滾動條的高度
  • offsetTop: 當前元素的邊框距離父元素上外邊距的距離
  • scrollHeight: 當前內部能夠滾動區域的高度,若是不能滾動則爲本身內容區 + 內邊距的高度
  • scrollTop: 當前元素滾動離頂部的距離

問:怎麼肯定當前瀏覽器的類型?

  • 經過navigator.userAgent獲取瀏覽器信息,根據裏面的關鍵字來肯定。

問:什麼是簡單請求和複雜請求?

  • 簡單請求:
  1. 請求方法僅限getheadpost
  2. Content-type僅限text/plainmultipart/form-dataapplication/x-www-form-urlencoded
  • 複雜請求:

不符合以上條件者就爲複雜請求,首先會發起一個option方法的預檢請求,來知道服務端是否容許跨域請求。
有一個坑就是服務端設置了CORS,但當客戶端發其複雜請求時會驗證Authorization字段,可是客戶端並無,因此須要將option方法過濾掉。html

問:從輸入域名到頁面顯示都經歷了什麼?

  • 首先將域名DNS解析爲對應的IP地址,而後經過Socket發送數據,通過tcp協議的三次握手,向該地址發起HTTP請求,服務器處理,瀏覽器收到HTTP響應的數據,關閉tcp鏈接,開始渲染。

問:談談瀏覽器的渲染機制?

  • 書寫的JavaScriptCssHtml在網絡傳輸中都是01的字節流,因此瀏覽器首先會把接受到的這些字節流轉爲字符串。而後首先將html字節流解析爲字符串,對字符串進行標記化,肯定標籤名以及裏面的內容,而後生成對應的node節點,根據節點的結構關係生成DOM樹。而後開始解析css,和解析html相似,css通常有嵌套或繼承的狀況,瀏覽器會從裏到外的遞歸來肯定每一個節點的樣式是什麼,從而生成一顆CSSOM樹。最後是將這兩顆樹結合起來生成一顆渲染樹,瀏覽器根據渲染樹進行佈局,調用GPU繪製生成頁面顯示在屏幕上。

問:什麼是重繪和迴流?

  • 重繪是節點的外觀發生改變而不改變佈局時,如改變了color這個行爲;迴流是指改變佈局或幾何屬性發生改變時引發的行爲,如添加移除Dom,改變尺寸。它們頻繁的觸發會影響頁面性能。
  • 迴流必定觸發重繪,而重繪不必定引發迴流。迴流的成本比重繪高不少,並且子節點的迴流,可能引發父節點一系列的迴流。

問:如何減小重繪和迴流?

  • 使用transform替代位移,使用translate3d開啓GPU加速
  • 儘可能使用visibility替代display:none
  • 不要使用tanle佈局
  • 不要在循環裏讀取節點的屬性值
  • 動畫速度越快,迴流次數越少

問:什麼是事件流/模型?

  • 當某一個事件被觸發時,分爲三個階段:
  • 1.事件經過捕獲從window => document => body => 目標元素
  • 2.事件到達註冊的目標上
  • 3.目標元素經過冒泡返回到window,沿途觸發相同類型的事件

問:什麼是事件代理?

  • 利用事件流的冒泡特性,將子節點的事件綁定在父節點上,而後在回調裏面使用事件對象進行區分,優勢是節省內存且不須要給子節點銷燬事件。

問:什麼是事件對象?

  • 這個對象裏面存放着觸發事件的狀態,如觸發事件的當前元素,鍵盤事件是哪一個按鍵觸發的,滾動事件的位置等等。

問:什麼是跨域?

  • 也就瀏覽器的同源策略,出於安全的考慮,只要是協議、域名、端口有一個不一樣就算是跨域,ajax請求就會失敗。瀏覽器有同源策略主要是爲了防止CSRF攻擊,防止利用戶的登陸狀態發起惡意請求。

問:你知道的解決跨域的方式有幾種?

  • JSONP: 利用script標籤不受同源策略限制,具體能夠參考40行封裝一個jsonp包
  • CORS:使用自定義的HTTP頭部讓瀏覽器和服務器進行溝通,實現CORS的關鍵是後端,服務端設置Access-Control-Allow-Origin就能夠開啓,表示哪些域名能夠訪問資源。
  • document.domain:當二級域名相同時,例如a.test.htmlb.test.html,只須要給兩個頁面都設置document.domain = 'test.html',就能夠實現跨域。
  • postMessage:a.html頁面經過iframe嵌入了b.html頁面,其中一個能夠經過postMessage方法發送信息,另外一頁面經過監聽message事件判斷來源並接受消息。

問:cookiesession分別是什麼?

  • cookie是服務器發送到用戶瀏覽器並保存在本地的一小塊數據,它會在瀏覽器發起請求時被攜帶併發送到服務器,它一般是告知服務端兩個請求是否來自同一瀏覽器,保持用戶的登陸狀態。
  • session表明着服務器在和客戶端一次會話的過程,存儲着用戶會話所需的屬性及配置信息,當用戶在不一樣頁面之間跳轉時,使整個用戶會話一直存在。

問:cookiesession有什麼不一樣?

  • 做用範圍不一樣:cookie存儲在客戶端,session保存在服務器端。
  • 存取的方式不一樣:cookie只能保存ASCⅡsession能夠存取任意數據類型。
  • 有效期不一樣:cookie可設置長時間保持,session通常失效時間較短,或客戶端關閉就會失效。
  • 存儲大小不一樣:單個cookie保存的數據不能超過4ksession可存儲數據遠高於cookie

問:爲何須要cookiesession

  • 讓服務器知道根它打交道的用戶是誰以及用戶的狀態,瀏覽器發起第一次請求後服務端會返回一個sessionID存儲到cookie中,當再次發起請求時服務端根據攜帶的cookie裏的sessionID來查找對應的session信息,沒有找到就說明沒登陸或登陸失效,找到說明已經登陸,能夠進行以後的操做。

問:若是瀏覽器禁止了cookie怎麼辦?

  • 每次請求都攜帶一個SessionID的參數;或者使用token令牌,登陸後服務端生成一個Token返回給客戶端,之後客戶端攜帶token進行數據請求。

問:使用cookie有哪些注意點?

  • 不建議做爲存儲方式使用。首先會隨請求攜帶,影響請求的性能,其次存儲空間也過小,最後一些屬性的使用也須要注意。value:若是做用於登陸狀態,須要加密。http-only:不能經過JavaScript訪問到cookie,防止XSS攻擊。same-site:不能在跨域請求中攜帶cookie,防止CSRF攻擊。

問:先後端實現登陸的方式有哪些?

  • cookie + session:前端登陸後,後端會種一個httpOnlycookie在前端,裏面就有這個用戶對應的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-HeaderExpiresCache-Control實現。強緩存表示在緩存期間不須要發起請求,狀態碼爲200
  • Expires:HTTP/1的產物,值爲緩存的過時時間,若是資源過時了就須要再次發起請求,若是修改本地時間,可能會形成緩存失效。
  • Cache-control:出現於HTTP/1.1,若是和Expires同時設置,優先級會更高。能夠在請求頭或者響應頭中設置,能夠組合多種指令使用。

cache.png

  • 協商緩存:經過設置HTTP-HeaderLast-ModifiedETag實現。若是緩存過時了,就須要發起請求驗證資源是否有更新。若是發起請求驗證資源沒有改變,返回狀態304,而且更新瀏覽器緩存的有效期。
  • Last-Modified和If-Modified-SinceLast-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,使瀏覽器每次都請求服務器,而後配合ETagLast-Modified來驗證資源是否有效,雖然請求數量沒少,不過能顯著減小響應的數據大小。
  • 不頻繁變更的資源:通常使用打包工具會爲生成的jscss等文件最後加上哈希值,只有當代碼修改後纔會生成新的文件名。因此咱們能夠給這些文件設置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,設置cookieSameSite屬性,不讓cookie隨跨域請求攜帶;組織第三方網站請求接口,驗證RefererToken驗證,登錄後服務器下發一個隨機token,以後的請求帶上。

問:什麼是點擊劫持?

  • 是一種視覺欺騙的攻擊手段,攻擊者將須要攻擊的網站經過iframe嵌入的方式嵌入到本身的網頁裏,將iframe設置爲透明,在頁面中透出一個按鈕誘導用戶點擊。

問:如何防範點擊劫持?

  • 現代瀏覽器設置HTTP響應頭X-FRAME-OPTIONS,這個響應頭就是爲了防護點擊劫持的;遠古瀏覽器使用js防護,當經過iframe的方式加載頁面時,讓攻擊者網站不顯示內容。

問:什麼是中間人攻擊?

  • 就是在攻擊者在服務端和客戶端創建了鏈接,並讓對方認爲鏈接是安全的,攻擊者不只能得到雙方的通訊信息,還能修改通訊信息。

問:若是防範中間人攻擊?

  • 不要使用公共wifi;只使用https協議,並關閉http的訪問。

問:你知道的性能優化方式有哪些?

    1. 文件壓縮,減少資源大小
    1. 異步組件,按需加載
    1. 小圖片轉base64,減小請求
    1. 雪碧圖,減小請求
    1. 選擇合適的圖片格式和尺寸
    1. 懶加載,按需加載
    1. css放最上面,js放在body最下面,渲染優化
    1. 事件節流,減小操做
    1. 減小Dom操做和避免迴流,渲染優化
    1. 瀏覽器緩存,減小請求次數或響應數據
    1. 減小cookie的使用,減小請求攜帶大小

問:babel是如何將ES6代碼轉爲ES5的?

  • 首先解析ES6的代碼字符串,生成 ES6AST語法樹;
  • 而後將ES6AST轉爲ES5 AST語法樹;
  • 最後將ES5AST轉換成字符串代碼。

問:有哪些方式能夠提高webpack的打包速度?

  • loader:使用includeexclude指定搜索文件的範圍。
  • 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.0http1.1的區別?

  1. http1.1加如了持久鏈接。
  2. 加入了更多的請求頭、響應頭(緩存方面的)。
  3. 新增了一些錯誤狀態碼、如409(請求的資源和資源當前狀態發生衝突)、410(服務器上的某個資源被永久性的刪除)。

問:httphttps的區別?

url開頭不一致是最明顯的區分;其次http沒有https安全,http沒有通過SSL/TLS加密、身份驗證;還有默認的端口不同,http80https443https須要證書,https是防止中間人攻擊方式的一種。前端

問:爲何https更安全?

  • 通常使用公鑰加密或私鑰加密:
  1. 公鑰加密雙方都須要事先知道一個都知道加密方式的密鑰,優勢是加密速度快,缺點是過程當中可能會被竊取,安全性沒有非對稱加密高。
  2. 非對稱加密會加入公鑰和私鑰,客戶端使用第三方證書去服務端獲取公鑰,而後用獲取到的公鑰把共享密鑰進行加密發送給服務端,服務端使用私鑰解密出共享密鑰,服務端用獲取到的共享密鑰進行消息的加密,客戶端用用共享密鑰進行解密。

問:常見的響應狀態碼有哪些?

  • 大體能夠分爲2開頭表示成功、3開頭表示重定向、4開頭客戶端錯誤、5開頭服務器錯誤。
  • 204:如當瀏覽器發出請求處理後,返回204響應,表示客戶端顯示的頁面不發生更新。
  • 206:客戶端只要想要響應內容中的一部分,服務端成功執行了此次響應。響應報文中的Content-Range則指定了哪部分的內容。
  • 301:永久重定向,表示請求的資源已被分配到了新的URI,之後使用新的吧
  • 302:臨時重定向,表示請求的資源已被分配到了新的URI,如今使用新的吧
  • 303:臨時重定向,表示請求的資源已被分配到了新的URI,請使用get方法獲取資源。
  • 304:服務端找不到根據客戶端發送附帶條件的請求。(附帶條件指get請求報文中包含If-MatchIf-Modified-SinceIf-None-MatchIf-RangeIf-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函數。
  • 接下來會初始化一些狀態,好比injectdatacomputedwatchprovide等,掛載到當前實例this下,並完成數據的響應式,緊接着執行created鉤子函數。
  • 開始組件的掛載階段,若是是帶編譯器版本且須要編譯時,這個時候就開始將模板編譯render函數,完畢以後執行beforeMount鉤子。
  • 接下來執行render函數,獲得VNode,以後執行patch由內而外的將VNode轉爲真實Dom,完成以後執行mounted鉤子。
  • 若是在patch的過程當中遇到了子組件的VNode,就會轉爲去執行子組件的初始化到真實Dom的狀態過程,完畢以後才執行父組件的mounted鉤子。

問:vue生命週期鉤子有哪些,每一個鉤子階段都作了什麼?

  • beforeCreate:創建了組件的父子關係,完成了事件的初始化,還不能訪問到定義的狀態。
  • created:完成了provide/inject和狀態的初始化,能夠訪問到dataprops等狀態。
  • beforeMount:內部執行了$mount函數,找到掛載點後執行beforeMount,主要做用是做爲掛載到完成階段性能檢測的起始時間以及將模板編譯爲render函數。
  • mounted:開始將render函數轉爲VNode,並建立爲真實的DOM,掛載完成以後執行mounted
  • beforeUpdate:對於已經掛載完成的組件更新了組件模板的數據時,在異步更新以前觸發。
  • updated:更新完成以後執行。更新相關的鉤子連官網都不推薦使用,推薦使用watch監聽數據變化。
  • activated:keep-alive包裹的組件,由於不會銷燬,因此也不會重建,它的createdmounted鉤子不會觸發。被緩存的組件激活時執行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-showv-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

  1. beforeRouteLeave
  2. beforeEach
  3. beforeRouteUpdate
  4. beforeEnter
  5. beforeRouteEnter
  6. beforeResolve
  7. 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,發佈者徹底不用感知到訂閱者,事件的註冊和觸發都發生在事件總線上,實現了徹底的解耦。
  • DepWatcher就是觀察者模式,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;
  }
}
相關文章
相關標籤/搜索