七篇前端面試必考知識點,助你一臂之力

前言

文章大部份內容是作一些知識點的總結,不會面面俱到,對於一些具體的實現步驟和底層原理的代碼並不會貼出來,否則篇幅實在是太長啦css

不過沒必要擔憂,相關知識點的詳細講解會貼出文章連接供你們參考,這些都是博主日常寫的筆記和看過的一些優秀的博文,但願可以幫助你查漏補缺,梳理起你的前端知識體系~html

文章內容較多,建議先 mark 再看喲。前端

1、HTML 基礎篇

一、doctype 的做用是什麼?

  • DOCTYPE 是 html5 標準網頁聲明,且必須聲明在HTML文檔的第一行。來告知瀏覽器的解析器用什麼文檔標準解析這個文檔,不一樣的渲染模式會影響到瀏覽器對於 CSS 代碼甚至 JavaScript 腳本的解析。

二、HTML、XHTML、XML 有什麼區別?

  • HTML(超文本標記語言): 在 html4.0 以前 HTML 先有實現再有標準,致使 HTML 很是混亂和鬆散
  • XML(可擴展標記語言): 主要用於存儲數據和結構,JSON做用相似,但更加輕量高效
  • XHTML(可擴展超文本標記語言): 基於上面二者而來

三、HTML 語義化的理解?

  • 語義化:指使用恰當語義的 html 標籤,如 header 標籤 表明頭部,article 標籤表明正文等
  • 好處:加強了可讀性、有利於SEO優化

四、經常使用的 meta 標籤?

  • charset,用於描述 HTML 文檔的編碼形式
<meta charset="UTF-8" >
複製代碼
  • http-equiv,至關於http 的文件頭做用,好比下面的代碼就能夠設置 http 的緩存過時日期
<meta http-equiv="expires" content="Wed, 20 Jun 2019 22:33:00 GMT"複製代碼
  • viewport,控制視口的大小和比例
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
複製代碼

五、script 標籤中 defer 和 async 的區別?

  • defer:script 被異步加載後並不會馬上執行,而是等待文檔被解析完畢後執行。
  • async:腳本加載完畢後當即執行

六、前端儲存的方式?

  • cookies: 兼容性好,請求頭自帶 cookie 方便,缺點是大小隻有4k,自動請求頭加入 cookie 浪費流量,每一個 domain 限制20個 cookie,使用起來麻煩須要自行封裝
  • localStorage:HTML5 加入的以鍵值對(Key-Value)爲標準的方式,優勢是操做方便,永久性儲存(除非手動刪除),大小爲5M,兼容IE8+
  • sessionStorage:與 localStorage 基本相似,區別是 sessionStorage 當頁面關閉後會被清理,並且與 cookie、localStorage 不一樣,他不能在全部同源窗口中共享,是會話級別的儲存方式
  • IndexedDB:NoSQL 數據庫,用鍵值對進行儲存,能夠進行快速讀取操做,很是適合 web 場景,同時用 JavaScript 進行操做會很是方便。

2、CSS 篇

一、CSS 盒模型

標準模型:寬高計算不包含 padding 和 border ;經過 box-sizing: content-box; 來設置(瀏覽器默認)。vue

IE模型:寬高計算包含 padding 和 border ;經過 box-sizing: border-box; 來設置。html5

二、BFC(塊狀格式化上下文)

特色:node

  • 是一個獨立的容器,裏面的元素和外面的元素互不影響;
  • BFC垂直方向的邊距會發生重疊;
  • BFC 區域不會與浮動元素區域重疊;
  • 計算 BFC 高度時,浮動元素也參與計算。

建立方式:react

  • float 值不爲 none;
  • position 的值不爲 static 或 relative;
  • display 爲 inline-box, table, table-cell 等;
  • overflow 不爲 visible

做用:webpack

  • 清除浮動
  • 防止同一 BFC 容器中的相鄰元素間的外邊距重疊問題

三、實現垂直居中佈局

  • 寬高固定
div.parent {
    position: relative; 
}

div.child {
    width: 100px;
    height: 100px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: -50px;
    margin-top: -50px;
}
或
div.child {
    width: 100px;
    height: 100px;
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    margin: auto;
}

複製代碼
  • 寬高不固定
div.parent {
    display: flex;
    justify-content: center;
    align-items: center;
}
或
div.parent{
  display:flex;
}
div.child{
  margin:auto;
}
或
div.parent {
    position: relative; 
}
div.child {
    position: absolute; 
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);  
}
或
div.parent {
    display: grid;
}
div.child {
    justify-self: center;
    align-self: center;
}
複製代碼

更多佈局類型可參考:乾貨!各類常見佈局實現+知名網站實例分析git

四、分析比較 opacity: 0、visibility: hidden、display: none 優劣和適用場景。

  • 結構上:display:none 會從渲染樹中消失,元素不佔據空間且沒法點擊;visibility: hidden 不會從渲染樹中消失,元素繼續佔據空間但沒法點擊;opacity: 0 不會從渲染樹消失,元素佔據空間且可點擊。
  • 繼承性:display: none 和 opacity: 0 是非繼承屬性;父元素設置了 display:none 或 opacity: 0,子元素不管怎麼設置都沒法顯示;visibility: hidden 會被子元素繼承,而且子元素能夠經過設置設置 visibility: visible; 來取消隱藏。
  • 性能:display: none 會引發重排,性能消耗較大;visibility: hidden 會引發重繪,性能消耗相對較小; opacity: 0 會重建圖層,性能較高

五、 link 標籤和 import 標籤的區別

  • link 屬於html 標籤,而 @import 是 css 提供的;
  • 頁面被加載時,link 會同時被加載,而 @import 引用的 css 會等到頁面加載結束後加載;
  • link 方式樣式的權重高於 @import 的;
  • link 可使用 js 動態引入,@import不行;
  • link 此沒有兼容性要求,而 @import 要求 IE5 以上才能識別。

六、移動端 Retina 1px 像素問題的解決方案

  • viewport + rem
  • background-image
  • 僞元素 + transform scale()
  • box-shadow

更多內容可參考:7 種方法解決移動端 Retina 屏幕 1px 邊框問題es6

七、文本顯示行數控制

  • 單行
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
複製代碼
  • 多行
overflow: hidden;
text-overflow: ellipsis;        // 超出顯示'...'
display: -webkit-box;           // 將元素做爲彈性伸縮盒子模型顯示 。
-webkit-line-clamp: 2;          // 用來限制在一個塊元素顯示的文本的行數
-webkit-box-orient: vertical;   // 設置或檢索伸縮盒對象的子元素的排列方式
複製代碼

八、清除浮動的方式

一、
.clearfix:after {
  visibility: hidden;
  display: block;
  font-size: 0;
  content: " ";
  clear: both;
  height: 0;
}
二、clear:both
三、overflow:hidden
複製代碼

九、transition 和 animate 有何區別?

  • transition:用於作過渡效果,沒有幀概念,只有開始和結束狀態,性能開銷較小
  • animate:用於作動畫,有幀的概念,能夠重複觸發且有中間狀態,性能開銷較大

十、實現一個扇形

.sector {
  width: 0;
  height: 0;
  border-width: 50px;
  border-style: solid;
  border-color: red transparent transparent;
  border-radius: 50px;
}
複製代碼

3、JS篇

一、JS 的內置類型

  • 基本類型:null、undefined、boolean、number、string、symbol
  • 對象(Object):引用類型

tips: NaN 也屬於 number 類型,而且 NaN 不等於自身。

二、類型判斷

  • Typeof
console.log(typeof 1);                  // number
console.log(typeof 'a');                // string
console.log(typeof true);               // boolean
console.log(typeof undefined);          // undefined
console.log(typeof function fn(){});    // function
console.log(typeof {});                 // object
console.log(typeof null);               // object
console.log(typeof []);                 // object
console.log(typeof new Error());        // object
複製代碼

tips:typeof 對於基本類型,除了 null 均可以顯示正確的類型;對於對象,除了函數都會顯示 object

  • Object.prototype.toString
var number = 1;             // [object Number]
var string = '123';         // [object String]
var boolean = true;         // [object Boolean]
var und = undefined;        // [object Undefined]
var nul = null;             // [object Null]
var obj = {a: 1}            // [object Object]
var array = [1, 2, 3];      // [object Array]
var date = new Date();      // [object Date]
var error = new Error();    // [object Error]
var reg = /a/g;             // [object RegExp]
var func = function a(){};  // [object Function]

function checkType() {
    for (var i = 0; i < arguments.length; i++) {
        console.log(Object.prototype.toString.call(arguments[i]))
    }
}

checkType(number, string, boolean, und, nul, obj, array, date, error, reg, func)
複製代碼

更多內容可參考:JavaScript溫故而知新——類型判斷

三、原型和原型鏈的理解

  • 原型:每一個函數都有 prototype 屬性,該屬性指向原型對象;使用原型對象的好處是全部對象實例共享它所包含的屬性和方法。
  • 原型鏈:主要解決了繼承的問題;每一個對象都擁有一個原型對象,經過__proto__ 指針指向其原型對象,並從中繼承方法和屬性,同時原型對象也可能擁有原型,這樣一層一層,最終指向 null。
  • 下圖展現了構造函數、原型對象、實例和原型鏈的關係:

更多內容可參考:JavaScript溫故而知新——原型和原型鏈

四、執行上下文

  • 全局執行上下文
  • 函數執行上下文
  • eval執行上下文

更多內容可參考:JavaScript溫故而知新——執行環境和做用域

五、閉包

  • 閉包是指有權訪問另外一個函數做用域中的變量的函數。
  • 閉包會使得函數內部的變量都被保存在內存中,形成較大的內存開銷,所以不要濫用閉包。解決的方法是在退出函數以前將不使用的局部變量置爲 null ;

經典面試題:改造下面的代碼,使之輸出0 - 9

for (var i = 0; i< 10; i++){
	setTimeout(() => {
		console.log(i);
    }, 1000)
}

方法1、利用 setTimeout 函數的第三個參數,會做爲回調函數的第一個參數傳入
for (var i = 0; i < 10; i++) {
  setTimeout(i => {
    console.log(i);
  }, 1000, i)
}

方法2、使用 let 變量 的特性
for (let i = 0; i < 10; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000)
}
等價於
for (let i = 0; i < 10; i++) {
  let _i = i;// const _i = i;
  setTimeout(() => {
    console.log(_i);
  }, 1000)
}

方法3、利用函數自執行的方式,把當前 for 循環過程當中的 i 傳遞進去,構建出塊級做用域。
for (var i = 0; i < 10; i++) {
  (i => {
    setTimeout(() => {
      console.log(i);
    }, 1000)
  })(i)
}
複製代碼

六、this 指向的問題

this 的指向取決於函數以哪一種方式調用:

  • 做用函數調用:非嚴格模式下 this 指向全局對象,嚴格模式爲 undefined
  • 做用方法調用:this 指向調用函數的對象
  • 構造函數調用:this 指向 new 建立出來的實例對象
  • call()和apply:它們的第一個參數爲 this 的指向

具體可參考:JavaScript溫故而知新——函數的4種調用方式

  • 補充:箭頭函數中的 this
function a() {
    return () => {
        return () => {
        	console.log(this)
        }
    }
}
console.log(a()()())
複製代碼

箭頭函數實際上是沒有 this 的,這個函數中的 this 只取決於他外面的第一個不是箭頭函數的函數的 this。在這個例子中,由於調用 a 符合前面代碼中的第一個狀況,因此 this 是 window。而且 this 一旦綁定了上下文,就不會被任何代碼改變。

七、call 和 apply 的實現

Function.prototype.call2 = function(context) {
    var context = context || window;
    context.fn = this;

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
    
    var result = eval('context.fn(' + args + ')');
    
    delete context.fn
    return result;
}

Function.prototype.apply = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;

    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')')
    }

    delete context.fn
    return result;
}
複製代碼

實現細節可參考:JavaScript溫故而知新——call()和apply()的實現

八、bind 的實現

Function.prototype.bind2 = function (context) {

    if (typeof this !== "function") {
      throw new TypeError("error");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    
    // 經過一個空函數做一箇中轉,避免綁定函數的 prototype 的屬性被修改
    var fNOP = function () {};

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}
複製代碼

實現細節可參考:JavaScript溫故而知新——bind()方法的實現

九、new 的實現原理

new 操做符作了什麼?

  • 建立一個空對象
  • 而後讓這個空對象的__proto__指向函數的原型prototype
  • 執行構造函數中的代碼,構造函數中的this指向該對象
  • 若是構造函數有返回值,則以該對象做爲返回值。若沒有return或return了基本類型,則將新對象做爲返回值
function objectFactory() {

    var obj = new Object(),

    Constructor = [].shift.call(arguments);

    obj.__proto__ = Constructor.prototype;

    var ret = Constructor.apply(obj, arguments);

    return typeof ret === 'object' ? ret : obj;

};
複製代碼

實現細節可參考:JavaScript溫故而知新——new操做符的實現

九、instanceof 的實現

function instance_of(L, R) {
  //L 表示左表達式,R 表示右表達式
  var O = R.prototype; // 取 R 的顯示原型
  L = L.__proto__; // 取 L 的隱式原型
  while (true) {
    if (L === null) return false;
    if (O === L)
      // 這裏重點:當 O 嚴格等於 L 時,返回 true
      return true;
    L = L.__proto__;
  }
}
複製代碼

十、深淺拷貝

  • 淺拷貝——若是被拷貝對象的元素是基本類型,就會拷貝出一份,而且互不影響。而若是被拷貝對象的元素是對象或者數組,就只會拷貝對象和數組的引用,此時如果在新舊對象上進行修改,都會相互影響。
// 數組淺拷貝:slice()、concat()
// 對象淺拷貝:Object.assign()、ES6的擴展運算符
複製代碼
  • 深拷貝——徹底的拷貝一個對象,即便嵌套了對象,二者也互相分離,修改對象的屬性,也不會影響到另外一個。
// 遞歸實現
function clone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
            if (typeof source[i] === 'object') {
                target[i] = clone(source[i]); // 若是是引用類型,則繼續遍歷
            } else {
                target[i] = source[i];
            }
        }
    }

    return target;
}
複製代碼

固然這只是簡單的實現,沒有考慮到特殊的狀況,如對象或數組中的函數,正則等特殊類型的拷貝等。

// JSON.parse(JSON.stringify)
var arr = [
   { value: 1 },
   { value: 2 },
   { value: 3 }
];
var copyArr = JSON.parse(JSON.stringify(arr))
copyArr[0].value = 0;
console.log(arr);       // [{value: 1}, { value: 2 }, { value: 3 }]
console.log(copyArr);   // [{value: 0}, { value: 2 }, { value: 3 }]
複製代碼

上面這種方法簡單粗暴,缺點是不能拷貝函數。

瞭解深拷貝更多實現細節,能夠參考:深拷貝的終極探索(90%的人都不知道)

十一、防抖和節流的實現(簡易版)

  • 防抖:觸發高頻事件後n秒內函數只會執行一次,若是n秒內高頻事件再次被觸發,則從新計算時間
funtion debounce(fn) {
    // 建立一個標記用來存放定時器的返回值
    let timeout = null;
    return function() {
        // 每次觸發事件時都取消以前的延時調用方法
        clearTimeout(timeout);
        // 而後又建立一個新的 setTimeout, 這樣就能保證 1000ms 間隔內若是重複觸發就不會執行 fn 函數
        timeout = setTimeout(() => {
            fn.apply(this, arguments);
        }, 1000);
    };
}
複製代碼
  • 節流:高頻事件觸發,但在n秒內只會執行一次,因此節流會稀釋函數的執行頻率
function throttle(fn) {
    // 經過閉包保存一個標記
    let canRun = true;
    return function(){
        // 每次開始執行函數時都先判斷標記是否爲 true,不爲 truereturn
        if (!canRun) return;
        // 上一次定時器執行完後 canRun 爲 true,因此要先設置爲false
        canRun = false;
        setTimeout(() => {
            fn.apply(this, arguments);
            // 最後在 setTimeout 執行完畢後再把標記設置爲true(關鍵)表示能夠執行下一次循環了。當定時器沒有執行的時候標記永遠是 false,在開頭被 return 掉
            canRun = true;
        }, 1000)
    }
}
複製代碼

十二、ES5繼承的實現

// 組合繼承
function SuperType(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function() {
    console.log(this.name);
}

function SubType(name, age) {
    SuperType.call(this, name);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;

SubType.prototype.sayAge = function() {
    console.log(this.age);
}
複製代碼

1三、JS 異步解決方案的發展歷程以及優缺點

  • 回調函數(callback)
ajax('XXX1', () => {
    // callback 函數體
    ajax('XXX2', () => {
        // callback 函數體
        ajax('XXX3', () => {
            // callback 函數體
        })
    })
})
複製代碼

優勢:解決了同步的問題

缺點:回調地獄,不能用 try catch 捕獲錯誤,不能 return

  • Promise
ajax('XXX1')
  .then(res => {
      // 操做邏輯
      return ajax('XXX2')
  }).then(res => {
      // 操做邏輯
      return ajax('XXX3')
  }).then(res => {
      // 操做邏輯
  })
複製代碼

優勢:解決了回調地獄的問題

缺點:沒法取消 Promise ,錯誤須要經過回調函數來捕獲

  • Generator
function *fetch() {
    yield ajax('XXX1', () => {})
    yield ajax('XXX2', () => {})
    yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()

// 配合 co 庫使用
const co = require('co')

function *fetch() {
    yield ajax('XXX1', () => {})
    yield ajax('XXX2', () => {})
    yield ajax('XXX3', () => {})
}

co(fetch()).then(data => {
    //code
}).fetch(err => {
    //code
})

複製代碼

優勢:能夠控制函數的執行,配合自動執行器 co 模塊 簡化了手動執行的步驟

缺點:不配合 co 函數庫的話使用起來比較麻煩

  • async/await
// async實際上是一個語法糖,它的實現就是將 Generator 函數和自動執行器(co),包裝在一個函數中
async function test() {
  // 如下代碼沒有依賴性的話,徹底可使用 Promise.all 的方式
  // 若是有依賴性的話,其實就是解決回調地獄的例子了
  await fetch('XXX1')
  await fetch('XXX2')
  await fetch('XXX3')
}
read().then((data) => {
    //code
}).catch(err => {
    //code
});

複製代碼

優勢:代碼清晰,不用像 Promise 寫一大堆 then 鏈,處理了回調地獄的問題

缺點:await 將異步代碼改形成同步代碼,若是多個異步操做沒有依賴性而使用 await 會致使性能上的下降。

1四、setTimeout、Promise、Async/Await 的區別

對於這個問題首先要弄明白 JS 的事件循環(Event Loop)機制,推薦你們先看一下這篇文章:這一次,完全弄懂 JavaScript 執行機制

  • setTimeout —— setTimeout的回調函數會放到宏任務隊列裏,等到執行棧清空之後執行
console.log('script start')	//1. 打印 script start
setTimeout(function() {
    console.log('settimeout')	// 4. 打印 settimeout
})	// 2. 調用 setTimeout 函數,並定義其完成後執行的回調函數
console.log('script end')	//3. 打印 script start
// 輸出順序:script start->script end->settimeout
複製代碼
  • Promise —— Promise自己是同步的當即執行函數, 當在 executor 中執行resolve或者reject的時候, 此時是異步操做, 會先執行then/catch等,當主棧完成後,纔會去調用resolve/reject中存放的方法執行。
console.log('script start')
let promise1 = new Promise(function (resolve) {
    console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function () {
    console.log('promise2')
})
setTimeout(function() {
    console.log('settimeout')
})
console.log('script end')
// 輸出順序: script start->promise1->promise1 end->script end->promise2->settimeout
複製代碼
  • async/await —— async 函數返回一個 Promise 對象,當函數執行的時候,一旦遇到 await 就會先返回,等到觸發的異步操做完成,再執行函數體內後面的語句。能夠理解爲,是讓出了線程,跳出了 async 函數體。
async function async1() {
   console.log('async1 start');
    await async2();
    console.log('async1 end')
}
async function async2() {
    console.log('async2')
}

console.log('script start');
async1();
console.log('script end')

// 輸出順序:script start->async1 start->async2->script end->async1 end
複製代碼

1五、Promise的簡單實現

promise 的使用(有關 Promise 的詳細用法,可參考 阮一峯老師的ES6文檔

var promise = new Promise((resolve,reject) => {
    if (操做成功) {
        resolve(value)
    } else {
        reject(error)
    }
})
promise.then(function (value) {
    // success
},function (value) {
    // failure
})
複製代碼

簡單實現

function myPromise(constructor) {
    let self = this;
    self.status = "pending"   // 定義狀態改變前的初始狀態
    self.value = undefined;   // 定義狀態爲resolved的時候的狀態
    self.reason = undefined;  // 定義狀態爲rejected的時候的狀態
    function resolve(value) {
       if(self.status === "pending") {
          self.value = value;
          self.status = "resolved";
       }
    }
    function reject(reason) {
       if(self.status === "pending") {
          self.reason = reason;
          self.status = "rejected";
       }
    }
    // 捕獲構造異常
    try {
       constructor(resolve,reject);
    } catch(e) {
       reject(e);
    }
}
複製代碼

添加 then 方法

myPromise.prototype.then = function(onFullfilled,onRejected) {
   let self = this;
   switch(self.status) {
      case "resolved":
        onFullfilled(self.value);
        break;
      case "rejected":
        onRejected(self.reason);
        break;
      default:       
   }
}

var p = new myPromise(function(resolve,reject) {
    resolve(1)
});
p.then(function(x) {
    console.log(x) // 1
})
複製代碼

有關 Promise 原理的更多細節推薦 Promise實現原理(附源碼),這篇文章講的很仔細,比較好理解。

1六、前端模塊化發展歷程

  • IIFE: 使用自執行函數來編寫模塊化,特色:在一個單獨的函數做用域中執行代碼,避免變量衝突。
(function(){
  return {
	data:[]
  }
})()
複製代碼
  • AMD: 使用 requireJS 來編寫模塊化,特色:依賴必須提早聲明好。
define('./index.js',function(code){
	// code 就是index.js 返回的內容
})
複製代碼
  • CMD: 使用 seaJS 來編寫模塊化,特色:對於依賴的模塊是延遲執行,依賴能夠就近書寫,等到須要用這個依賴的時候再引入這個依賴,支持動態引入依賴文件。
define(function(require, exports, module) {  
  var indexCode = require('./index.js');
});
複製代碼
  • CommonJS: nodejs 中自帶的模塊化。
var fs = require('fs');
複製代碼
  • ES Modules: ES6 引入的模塊化,支持 import 來引入另外一個 js 。
import a from 'a';
複製代碼

1七、ES6 模塊和 CommonJS 模塊的差別?

  • ES6模塊在編譯時,就能肯定模塊的依賴關係,以及輸入和輸出的變量;CommonJS 模塊,運行時加載。
  • ES6 模塊自動採用嚴格模式,不管模塊頭部是否寫了 "use strict";
  • require 能夠作動態加載,import 語句作不到,import 語句必須位於頂層做用域中。
  • ES6 模塊中頂層的 this 指向 undefined,CommonJS 模塊的頂層 this 指向當前模塊。
  • CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。

4、網絡基礎篇

一、HTTP 協議的主要特色

  • 簡單快速、靈活、無鏈接、無狀態

二、HTTP 報文的組成部分

  • 請求報文:
    • 請求行 ( http 方法 + 頁面地址 + http 協議 + 版本)
    • 請求頭( key + value 值)
    • 空行(服務端經過空行來判斷下一部分再也不是請求頭,而當作請求體來解析)
    • 請求體(數據部分)
  • 響應報文:狀態行 + 響應頭 + 空行 + 響應體

三、HTTP 方法

  • GET => 獲取資源
  • POST => 傳輸資源
  • PUT => 更新資源
  • DELETE => 刪除資源
  • HEAD => 得到報文首部

四、POST 和 GET 的區別

  • GET在瀏覽器回退時是無害的,而POST會再次提交請求 *
  • GET請求會被瀏覽器主動緩存,而POST不會,除非手動設置 *
  • GET請求參數會被完整保留在瀏覽器的歷史記錄裏,而POST中的參數不會被保留 *
  • GET請求在URL中傳送的參數是有長度限制的,而POST沒有限制 *
  • GET參數經過URL傳遞,POST放在Request body中 *
  • GET請求只能進行 url 編碼,而POST支持多種編碼方式
  • GET產生的URL地址能夠被收藏,而POST不能夠
  • 對參數的數據類型,GET只接受ASCII字符,而POST沒有限制
  • GET比POST更不安全,由於參數直接暴露在URL上,因此不能用來傳遞敏感信息

五、HTTP 狀態碼

  • 1xx:指示信息 —— 表示請求已接受,繼續處理

  • 2xx: 成功 —— 表示請求已被成功接受

    • 200:OK,表示從客戶端發來的請求在服務器端被正確處理
    • 204:No content,表示請求成功,但響應報文不含實體的主體部分
    • 205:Reset Content,表示請求成功,但響應報文不含實體的主體部分,可是與 204 響應不一樣在於要求請求方重置內容
    • 206:Partial Content,客戶發送了一個帶有Range頭的GET請求,服務器完成了它(例如請求較大的文件,如用vedie標籤或audio標籤播放音視頻地址)
  • 3xx:重定向 —— 要完成請求必須進行更進一步操做

    • 301:moved permanently,永久性重定向,表示資源已被分配了新的 URL
    • 302:found,臨時性重定向,表示資源臨時被分配了新的 URL
    • 303:see other,表示資源存在着另外一個 URL,應使用 GET 方法獲取資源
    • 304:not modified,表示服務器容許訪問資源,但因發生請求未知足條件的狀況
    • 301:temporary redirect,臨時重定向,和302含義相似,可是指望客戶端保持請求方法不變向新的地址發出請求
  • 4xx:客戶端錯誤——請求有語法錯誤或請求沒法實現

    • 400:bad request,請求報文存在語法錯誤
    • 401:unauthorized,表示發送的請求須要有經過 HTTP 認證的認證信息
    • 403:forbidden,表示對請求資源的訪問被服務器拒絕
    • 404:not found,表示在服務器上沒有找到請求的資源
  • 5xx:服務端錯誤——服務器未能實現合法的請求

    • 500:internal sever error,表示服務器端在執行請求時發生了錯誤
    • 501:Not Implemented,表示服務器不支持當前請求所須要的某個功能
    • 503:service unavailable,代表服務器暫時處於超負載或正在停機維護,沒法處理請求

六、TCP 三次握手和四次揮手的理解

三次握手和四次揮手能夠模擬成對講機通話的過程

  • 三次握手:
A: 你好,我是A
B:收到,我是B
A:好的,咱們能夠開始通話啦
複製代碼
  • 四次揮手:
A:我已經沒什麼話說了,結束通話吧
B:稍等,我還有最後一句話要說
B:我已經說完啦
A:好的,你能夠關掉對講機了,不用回覆了(而後A等待2MSL無回覆,也關掉對講機)
複製代碼

更詳細的內容可參考:面試 -- 網絡 TCP/IP

七、HTTPS

HTTPS 仍是經過了 HTTP 來傳輸信息,可是信息經過 TLS 協議進行了加密。

TLS 中的加密:

  • 對稱加密 —— 兩邊擁有相同的祕鑰,兩邊都知道如何將密文加密解密。
  • 非對稱加密 —— 有公鑰私鑰之分,公鑰全部人均可以知道,能夠將數據用公鑰加密,可是將數據解密必須使用私鑰解密,私鑰只有分發公鑰的一方纔知道。

HTTPS 握手過程:

  • 第一步,客戶端給出協議版本號、一個客戶端生成的隨機數(Client random),以及客戶端支持的加密方法。
  • 第二步,服務端確認雙方使用的加密方法,並給出數字證書、以及一個服務器生成的隨機數(Server random)。
  • 第三步,客戶端確認數字證書有效,而後生成一個新的隨機數(Premaster secret),並使用數字證書中的公鑰,加密這個隨機數,發給服務端。
  • 第四步,服務端使用本身的私鑰,獲取客戶端發來的隨機數(即Premaster secret)。
  • 第五步,客戶端和服務端根據約定的加密方法,使用前面的三個隨機數,生成"對話密鑰"(session key),用來加密接下來的整個對話過程。

七、HTTP 2.0的特性

  • 二進制傳輸:將全部傳輸的信息分割爲更小的消息和幀,並對它們採用二進制格式的編碼
  • 多路複用:一個 TCP 鏈接中能夠發送多個請求
  • Header 壓縮
  • 服務器推送:服務器能夠額外的向客戶端推送資源,而無需客戶端明確的請求

http1.0 http1.1 http2.0特性及區別

八、用戶輸入 url 到頁面呈現的過程

  • 用戶輸入url
  • 瀏覽器查找域名的 IP 地址 域名解析(DNS解析)
  • 找到 IP 地址後,創建 TCP 三次握手 ,與目標服務器創建鏈接
  • 握手成功後,經過規定的協議(http),瀏覽器向目標主機發送 http 請求,請求數據包
  • 服務器處理收到的請求,將數據返回至瀏覽器
  • 瀏覽器收到 HTTP 響應報文
  • 關閉鏈接 瀏覽器解析文檔
  • 讀取頁面內容,瀏覽器渲染,解析html源碼
  • 生成 Dom 樹、解析 css 樣式、js 交互
  • 在生成 Render 樹的過程當中,瀏覽器就開始調用 GPU 繪製,合成圖層,將內容顯示在屏幕上了

5、瀏覽器與 Web 安全篇

一、事件機制

  • 事件觸發的三個階段:
捕獲階段 —— window 往事件觸發處傳播,遇到註冊的捕獲事件會觸發
目標階段 —— 傳播到事件觸發處時觸發註冊的事件
冒泡階段 —— 從事件觸發處往 window 傳播,遇到註冊的冒泡事件會觸發
複製代碼
  • 捕獲DOM事件的具體流程:
window對象 => document對象 => html標籤 => body標籤 => ... => 目標元素(冒泡反之)
複製代碼
  • Event 對象的常見應用
event.preventDefault()              // 阻止默認事件,例如a標籤的跳轉行爲
event.stopPropagation()             // 阻止冒泡
event.stopImmediatePropagation()    // 事件響應優先級:例如同一元素綁定不一樣事件時,觸發a事件不讓b事件觸發
event.currentTarget                 // 當前綁定事件的元素
event.target                        // 當前被點擊的元素
複製代碼

二、跨域

同源策略:

  • 瀏覽器出去安全考慮所作的限制,協議、域名、端口有一個不一樣就是跨域,此時 Ajax 請求不能發送,Cookie、LocalStorage 和 IndexDB 沒法獲取,DOM也沒法獲取。

跨域的幾種解決方法:

  • JSONP —— 利用 <script> 標籤沒有跨域限制的特色,經過 <script> 標籤指向一個須要訪問的地址並提供一個回調函數來接收數據。
<script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
<script>
    function jsonp(data) {
    	console.log(data)
	}
</script>
複製代碼

封裝一個 jsonp 方法

function jsonp({url, params, cb}) {
    return new Promise((resolve, reject) => {
        //建立script標籤
        let script = document.createElement('script');
        //將回調函數掛在 window 上
        window[cb] = function(data) {
            resolve(data);
            //代碼執行後,刪除插入的script標籤
            document.body.removeChild(script);
        }
        //回調函數加在請求地址上
        params = {...params, cb}
        let arrs = [];
        for(let key in params) {
            arrs.push(`${key}=${params[key]}`);
        }
        script.src = `${url}?${arrs.join('&')}`;
        document.body.appendChild(script);
    });
}
//使用
function sayHi(data) {
    console.log(data);
}
jsonp({
    url: 'http://localhost:3000/say',
    params: {
        //code
    },
    cb: 'sayHi'
}).then(data => {
    console.log(data);
});
複製代碼
  • CORS —— 服務端設置 Access-Control-Allow-Origin 就能夠開啓 CORS。 該屬性表示哪些域名能夠訪問資源,若是設置通配符則表示全部網站均可以訪問資源。
  • document.domain —— 只適用於二級域名相同的狀況,如a.test.comb.test.com,只須要給頁面添加 document.domain = 'test.com' 表示二級域名都相同就能夠實現跨域。
  • postMessage —— H5新增的跨域通訊方式,如窗口 A(http:A.com) 向跨域的窗口 B(http:B.com) 發送信息
# 在A中發送數據
window.postMessage('data', 'http://B.com');
# 在窗口B中監聽
window.addEventListener('message', function(event){
    console.log(event.origin);
    console.log(event.source);
    console.log(event.data);
}, false)
複製代碼
  • Hash:url 地址中 '#' 後面的部分,hash 的改變頁面不會刷新
# 使用場景:當頁面A經過iframe或frame嵌入了跨域的頁面B
# 在A中的代碼:
var B = document.getElementByTagName('iframe');
B.src = B.src + '#' + 'data';
# 在B中的代碼:
window.onhashchange = function () {
    var data = window.location.hash;
}
複製代碼

三、渲染機制

  • 瀏覽器渲染過程:
    • 處理 HTML 並構建 DOM 樹
    • 處理 CSS 構建 CSS 規則樹
    • 將 DOM 樹 與 CSS 規則樹 合併成一個渲染樹
    • 根據渲染樹來佈局,計算每一個節點的位置
    • 調用 GPU 繪製,合成圖層,顯示在屏幕上

  • 重繪(Repaint)和迴流(Reflow)
    • 重繪 —— 當節點須要更改外觀而不會影響佈局的,好比改變 color
    • 迴流 —— 佈局或者幾何屬性須要改變
  • 什麼時候發生重繪和迴流(迴流一定會發生重繪,重繪不必定會引起迴流。)
    • 添加或刪除可見的DOM元素
    • 元素的位置發生變化
    • 元素的尺寸發生變化(包括外邊距、內邊框、邊框大小、高度和寬度等)
    • 內容發生變化,好比文本變化或圖片被另外一個不一樣尺寸的圖片所替代。
    • 瀏覽器的窗口尺寸變化(由於迴流是根據視口的大小來計算元素的位置和大小的)
  • 減小重繪和迴流
    • 使用 translate 替代 top
    • 使用 visibility 替換 display: none ,由於前者只會引發重繪,後者會引起迴流(改變了佈局)
    • 把 DOM 離線後修改,好比:先把 DOM 給 display:none (有一次 Reflow),而後你修改 100 次,而後再把它顯示出來
    • 不要把 DOM 結點的屬性值放在一個循環裏當成循環裏的變量
    • 不要使用 table 佈局,可能很小的一個小改動會形成整個 table 的從新佈局
    • 動畫實現的速度的選擇,動畫速度越快,迴流次數越多,也能夠選擇使用 requestAnimationFrame
    • CSS 選擇符從右往左匹配查找,避免 DOM 深度過深
    • 將頻繁運行的動畫變爲圖層,圖層可以阻止該節點回流影響別的元素。好比對於 video 標籤,瀏覽器會自動將該節點變爲圖層。

重繪和迴流更詳細的內容可參考:你真的瞭解迴流和重繪嗎

四、瀏覽器的緩存機制

緩存的原理 —— 將請求來的資源存放到本地磁盤當中,下次獲取資源則直接在磁盤當中讀取而再也不去向服務器發送請求。

緩存的分類:

  • 強緩存 —— 經過兩種響應頭實現:ExpiresCache-Control 。強緩存表示在緩存期間不須要請求,state code 爲 200
Expires: Wed, 22 Oct 2018 08:41:00 GMT
// Expires 是 HTTP / 1.0 的產物,表示資源會在 Wed, 22 Oct 2018 08:41:00 GMT 後過時,須要再次請求。
// 而且 Expires 受限於本地時間,若是修改了本地時間,可能會形成緩存失效。

Cache-control: max-age=30
// Cache-Control 出現於 HTTP / 1.1,優先級高於 Expires 。該屬性表示資源會在 30 秒後過時,須要再次請求。
複製代碼
  • 協商緩存 —— 若是緩存過時了,咱們就可使用協商緩存來解決問題。協商緩存須要請求,若是緩存有效會返回 304。 協商緩存須要客戶端和服務端共同實現
    • Last-ModifiedIf-Modified-Since —— 表示本地文件最後修改日期,If-Modified-Since 會將 Last-Modified 的值發送給服務器,詢問服務器在該日期後資源是否有更新,有更新的話就會將新的資源發送回來。(可是若是在本地打開緩存文件,就會形成 Last-Modified 被修改,因此在 HTTP / 1.1 出現了 ETag 。)
    • ETagIf-None-Match —— ETag 相似於文件指紋,If-None-Match 會將當前 ETag 發送給服務器,詢問該資源 ETag 是否變更,有變更的話就將新的資源發送回來。而且 ETag 優先級比 Last-Modified

瞭解更多有關瀏覽器緩存機制可參考:深刻理解瀏覽器的緩存機制

五、Web 安全

XSS

  • 基本概念:XSS,也稱跨域腳本攻擊,英文名 Cross-site scripting
  • 如何攻擊:向頁面注入惡意的標籤或 js 代碼來攻擊網站。
  • 如何防護:讓惡意插入的標籤或js代碼不可執行,如轉義輸入輸出的內容

CSRF

  • 基本概念:CSRF,也稱跨站請求僞造,英文名 Cross-site request forgery
  • 如何攻擊:利用用戶的登陸態發起惡意請求。
  • 如何防護:
    • Get 請求不對數據進行修改
    • 不讓第三方網站訪問到用戶 Cookie
    • 阻止第三方網站請求接口
    • 請求時附帶驗證信息,好比驗證碼或者 token

關於 Web 安全更多內容可參考:【面試篇】寒冬求職之你必需要懂的Web安全

6、性能優化篇

一、網絡相關

  • DNS 預解析
<link rel="dns-prefetch" href="//host_name_to_prefetch.com" />
複製代碼
  • 緩存(見上文瀏覽器緩存機制)
  • 使用 HTTP / 2.0
  • 預加載 —— 能夠將一些不影響首屏但重要的文件延後加載,可以下降首屏加載的時間,缺點是兼容性很差
<link rel="preload" href="http://example.com" />
複製代碼
  • 預渲染 —— 將要下載的文件預先在後臺渲染
<link rel="prerender" href="http://example.com" />
複製代碼

二、優化渲染過程

  • 代碼層面的優化(參考瀏覽器篇如何減小重繪和迴流)
  • 懶執行 —— 將某些邏輯放到使用時再進行,能夠經過定時器或事件進行喚醒
  • 懶加載 —— 將不關鍵的資源延後加載,如圖片、視頻資源等。

三、文件優化

圖片優化:

  • 能夠用 css 模擬代替的儘可能不要用圖片
  • 小圖片用 base64 格式
  • 雪碧圖
  • 選擇正確的圖片格式
    • 選擇使用 WebP 格式,體積較小,缺點是兼容性很差
    • 小圖使用 PNG ,圖標類可使用 SVG 代替
    • 照片使用 JPEG

其餘文件優化:

  • CSS 文件放在 head
  • 服務端開啓文件壓縮功能
  • 將 script 標籤放在 body 底部,由於 JS 文件執行會阻塞渲染。
  • script 文件異步加載
    • defer:在 script 標籤上加上 defer 屬性,defer 是在 HTML 解析完以後纔會執行,若是是多個,按照加載的順序依次執行
    • async:在 script 標籤上加上 async 屬性,async 是在加載完以後當即執行,若是是多個,執行順序和加載順序無關
  • 對於須要不少時間計算的代碼能夠考慮使用 Webworker,Webworker 可讓咱們另開一個線程執行腳本而不影響渲染。
  • 使用CDN

四、其餘

使用 Webpack 優化項目:

  • 對於 Webpack4,打包項目使用 production 模式,這樣會自動開啓代碼壓縮
  • 開啓 tree shaking,移除沒有用的代碼
  • 優化圖片,對於小圖可使用 base64 的方式寫入文件中
  • 按照路由拆分代碼,實現按需加載
  • 給打包出來的文件名添加哈希,實現瀏覽器緩存文件

錯誤監控:

  • 即時運行錯誤
    • try...catch
    • window.onerror
  • 資源加載錯誤
    • object.onerror
    • 高級瀏覽器下的 performance.getEntries() 能夠獲取已經加載完成的資源進而監控加載失敗的資源
  • 跨域的 js 運行錯誤 —— 在 script 標籤增長 crossorigin 屬性,而後設置 js 資源響應頭 Access-Control-Allow-Origin
  • 打包時生成 sourceMap 文件便於 debug

錯誤上報

  • 採用 Ajax 通訊的方式上報
  • 經過 img 標籤的 src 發起一個請求。

7、框架篇

一、如何理解MVVM模式?

顧名思義,即 Model-View-ViewModel 模式

  • View:界面
  • Model:數據模型
  • ViewModel:做爲橋樑負責溝通 View 和 Model

優勢:

  • 分離視圖 (View) 和模型 (Model),下降代碼耦合,提升視圖或邏輯的重用性
  • 自動更新 DOM,避免了對 DOM 的頻繁操做
  • 提升可測試性

缺點:

  • Bug 難以調試,好比在 View 寫了一些指令,但這是沒辦法經過 debug 的手段去調試的
  • 模塊較大時,Model 也會變大,容易形成較高的內存開銷
  • 對於大型的圖形應用層序,視圖狀態較多,ViewModel 的構建和維護成本也會較高

二、Vue 響應式原理

Vue 採用數據劫持結合發佈—訂閱模式的方法,經過 Object.defineProperty() 來劫持各個屬性的 setter,getter,在數據變更時發佈消息給訂閱者,觸發相應的監聽回調。

  • Observer 遍歷數據對象,給全部屬性加上 setter 和 getter,監聽數據的變化
  • compile 解析模板指令,將模板中的變量替換成數據,而後初始化渲染頁面視圖,並將每一個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變更,收到通知,更新視圖
  • Watcher 訂閱者是 Observer 和 Compile 之間通訊的橋樑,主要作的事情:
    • 在自身實例化時往屬性訂閱器 (dep) 裏面添加本身
    • 待屬性變更 dep.notice() 通知時,調用自身的 update() 方法,並觸發 Compile 中綁定的回調

模擬實現可參考 面試題:你能寫一個Vue的雙向數據綁定嗎?,這裏就不貼代碼了。

三、虛擬DOM(Virtual Dom)實現原理

  • 經過 JS 對象模擬實現 DOM
  • Dom 樹的 diff 算法,同層對比(由於實際業務中不多會跨層移動 DOM 元素),須要判斷三種狀況
    • 沒有新的節點,則什麼都不用作
    • 新的節點的 tagName 和 key 和舊的不一樣,直接替換節點
    • 新的節點的 tagName 和 key(可能沒有)和舊的相同,則判斷屬性是否變動,並繼續遍歷子樹
      • 判斷屬性是否變動
        • 遍歷舊的屬性列表,查看每一個屬性是否還存在於新的屬性列表中
        • 遍歷新的屬性列表,判斷都存在的屬性值是否有變化(同時查看是否出現新的屬性)
  • 渲染差別
    • 經過 diff 算法能夠獲得兩個樹的差別
    • 深度遍歷樹,根據差別來進行 DOM 的更新操做

在遍歷子節點的時候還有一個列表對比的算法,是針對帶有 key而且新舊列表同時存在的節點作處理的。當子節點僅僅只是發生位置改變的狀況,若是按照同層對比,它們就會被替換掉,形成較大的 DOM 開銷,而列表對比算法會經過對比新舊節點列表的順序來移動節點進行 DOM 的更新,在一些場合下就能夠起到必定的性能優化的做用。

對於 Virtual Dom 詳細的代碼實現可參考:深度剖析:如何實現一個 Virtual DOM 算法

四、前端路由原理

本質:監聽 URL 的變化,而後匹配路由規則,而且無需刷新頁面。

實現方式:

  • 基於 Hash —— 兼容性更好,但存在 '#' 不夠美觀
    • 點擊跳轉或瀏覽器歷史跳轉:觸發 hashchange 事件 -> 解析 url -> 匹配到對應的路由規則
    • 手動刷新: 觸發 load 事件 -> ...
  • 基於 History API —— HTML5 新路由方案,更加方即可讀,兼容性較差
    • 瀏覽器動做,如前進後退:觸發 popstate 事件 -> 解析 url -> 匹配到對應的路由規則
    • 點擊跳轉:調用 pushState 函數向瀏覽器歷史添加一個狀態 -> ...
    • 刷新頁面或輸入 URL:會向服務器請求,因此使用 history 須要後端配合重定向 -> ...

詳細內容可參考:面試官: 你瞭解前端路由嗎?

五、Proxy與Object.defineProperty的優劣勢對比

Proxy的優點:

  • 能夠直接監聽對象而非屬性
  • 能夠直接監聽數組的變化
  • 攔截方式較多
  • Proxy返回一個新對象,能夠只操做新對象達到目的,而Object.defineProperty只能遍歷對象屬性直接修改
  • Proxy做爲新標準將受到瀏覽器廠商重點持續的性能優化

Object.defineProperty的優點以下:

  • 兼容性好,支持IE9

六、Vue 生命週期

指建立初始化數據、編譯模板、掛載 DOM、渲染、更新、卸載一系列的過程。

  • beforeCreate:組件實例被建立之初,組件的屬性生效以前
  • created:組件實例已經徹底建立,屬性也綁定,但真實DOM還未生成,$el不可用
  • beforeMount: 在掛載開始以前被調用,相關的render函數首次被調用
  • mounted: el被建立的vm.$el替換,並掛載到實例上去以後調用
  • beforeUpdate: 組件數據更新以前調用,發生在虛擬DOM打補丁以前
  • update:組件數據更新以後
  • activited:keep-alive專屬,組件被激活時調用
  • deadctivated: keep-alive專屬,組件被銷燬時調用
  • beforeDestory:組件銷燬前調用
  • destoryed:組件銷燬後調用

七、v-if 和 v-show 的區別

  • v-if 切換狀態時會形成 dom 的銷燬和重建,初始渲染條件爲 false 時,將不會渲染元素;
  • v-show 只是簡單的控制顯隱藏,無論初始條件如何,元素總會被渲染;
  • v-if適用於不多改變條件的場景,v-show適用於頻繁切換條件的場景。

八、computed 和 watch 的區別

  • computed:經常使用於比較消耗性能的計算場景,具備緩存性,getter執行後會被緩存,只有它依賴的屬性值變化後,下一次獲取的computed值纔會從新計算
  • watch:經常使用於某些數據的監聽,觀察props$emit或本組件值的變化來執行回調進行後續操做,無緩存性,頁面從新渲染時值不變化也會執行。

九、Vue 中的 key 有什麼用

  • 帶上惟一標識的 key 能夠提升 diff 算法的效率,而且在一些複雜場景下的列表組件,能夠更加準確的進行更新渲染
  • 在渲染簡單的無狀態列表組件的場景下,不帶 key 實際上性能要比帶 key 要好,由於不帶 key 的狀況下,vue 默認會對節點進行復用,省去了銷燬/建立組件的開銷。

推薦使用惟一標識做爲 key 的緣由:

  • 帶上惟一的 key 能夠保證更新組件的狀態是正確的,避免了一些場景下會出現 bug,雖然會增長性能開銷,但對於用戶而言基本感覺不到差距。

十、nextTick

nextTick 可讓咱們在下次 DOM 更新循環結束以後執行延遲迴調,用於得到更新後的 DOM。

  • Vue2.4 前使用的是 microtasks
  • 新版本默認使用 microtasks,v-on 中使用 macrotasks
  • macrotasks 的實現:會先判斷是否能使用 setImmediate ,不能的話降級爲 MessageChannel ,以上都不行的話就使用 setTimeout

十一、vue 組件間通訊的方法

  • props / $emit
  • $children / $parent —— 父組件中用 this.$children 獲取到子組件實例數組,子組件中可使用 this.$parent 獲取父組件實例對象。
    • tips:在 #app 上獲取 $parent 獲得的是 new Vue()的實例,在這之上再獲取 $parent 則是 undefined ;而底層子組件獲取 $children 獲得的是空數組。
  • provide / inject —— vue2.2 新增的 api ,父組件經過 provide 屬性來提供變量,而後在子組件中用 inject 來注入變量。
  • ref / $refs —— ref 在普通 DOM 元素上使用,引用的就是 DOM 元素;若是在子組件上,引用的是組件實例
  • $attrs / $listeners —— vue2.4新增,能夠進行跨級的組件通訊
  • eventBus
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()

// A.vue
<template>
  <div>
    <button @click="sendFirstName"></button>    
  </div>
</template>

<script>
import {EventBus} from './event-bus.js'
export default {
  data(){
    return{
      firstName:'leborn'
    }
  },

  methods:{
    sendFirstName(){
      EventBus.$emit('getFirstName', {
        firstName:this.firstName
      })
    }
  }
}
</script>


// B.vue
<template>
  <div>姓名: {{name}}</div>
</template>

<script>
import { EventBus } from './event-bus.js'
export default {
  data() {
    return {
      name: ''
    }
  },

  mounted() {
    EventBus.$on('getFirstName', param => {
      this.name = param.firstName + 'james';
    })
  }
}
</script>
複製代碼

十二、vue 項目性能優化

代碼層面:

  • 合理適用 v-if 和 v-show
  • 區分 computed 和 watch 的使用
  • v-for 遍歷爲 item 添加 key
  • v-for 遍歷避免同時使用 v-if
  • 經過 addEventListener 添加的事件在組件銷燬時要用 removeEventListener 手動移除這些事件的監聽
  • 圖片懶加載
  • 路由懶加載
  • 第三方插件按需引入
  • SSR服務端渲染,首屏加載速度快,SEO效果好

Webpack 層面優化:

  • 對圖片進行壓縮
  • 使用 CommonsChunkPlugin 插件提取公共代碼
  • 提取組件的 CSS
  • 優化 SourceMap
  • 構建結果輸出分析,利用 webpack-bundle-analyzer 可視化分析工具

基礎的Web技術優化:

  • 開啓gzip壓縮
  • 瀏覽器緩存
  • CDN的使用
  • 使用Chrome Performance分析性能

詳細內容可參考:Vue 項目性能優化 — 實踐指南(網上最全 / 詳細)

1三、React 生命週期

class ExampleComponent extends React.Component {
  // 用於初始化 state
  constructor() {}
  // 用於替換 `componentWillReceiveProps` ,該函數會在初始化和 `update` 時被調用
  // 由於該函數是靜態函數,因此取不到 `this`
  // 若是須要對比 `prevProps` 須要單獨在 `state` 中維護
  static getDerivedStateFromProps(nextProps, prevState) {}
  // 判斷是否須要更新組件,多用於組件性能優化
  shouldComponentUpdate(nextProps, nextState) {}
  // 組件掛載後調用
  // 能夠在該函數中進行請求或者訂閱
  componentDidMount() {}
  // 用於得到最新的 DOM 數據
  getSnapshotBeforeUpdate() {}
  // 組件即將銷燬
  // 能夠在此處移除訂閱,定時器等等
  componentWillUnmount() {}
  // 組件銷燬後調用
  componentDidUnMount() {}
  // 組件更新後調用
  componentDidUpdate() {}
  // 渲染組件函數
  render() {}
  // 如下函數不建議使用
  UNSAFE_componentWillMount() {}
  UNSAFE_componentWillUpdate(nextProps, nextState) {}
  UNSAFE_componentWillReceiveProps(nextProps) {}
}
複製代碼

1四、React 中 setState 何時是同步的,何時是異步的?

  • 由 React 引起的事件處理(如 onClick ),調用 setState 不會同步更新 this.state,除此以外(addEventListener,setTimeout/setInterval)的 setState 調用會同步執行 this.state 。

緣由:在 setState 函數實現中,會根據 isBatchingUpdates 這個變量來決定是否同步更新 this.state。isBatchingUpdates 默認是 false 表示 setState 會同步更新 this.state。但 React 在調用事件處理函數時會調用一個 batchedUpdates 函數,將 isBatchingUpdates 改成 true,此時 setState 不會同步更新 this.state

  • 一道 setState 的筆試題:下面代碼會輸出什麼
class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      val: 0
    };
  }
  
  componentDidMount() {
    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 1 次 log

    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 2 次 log

    setTimeout(() => {
      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 3 次 log

      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 4 次 log
    }, 0);
  }

  render() {
    return null;
  }
};
複製代碼
  • 第一次和第二次都是在 react 自身生命週期內,isBatchingUpdates 爲 true,此時不會直接更新 state,都是輸出 0
  • setTimeout 外的兩次 setState 會被合併只執行一次,此時 state.val 爲 1
  • setTimeout 中會觸發 isBatchingUpdates 爲 false,此時 setState 同步執行,輸出 2, 3

輸出:0 0 2 3

結尾

前端的知識點實在是太多太雜了,對於一些沒有涉及到的內容,例如 webpack,babel 什麼的後續可能會補上。本篇文章若是能幫助到你的話點贊就完事兒了,沒能幫到你也敬請見諒哈~

最後建議你們在看一些技術文章的時候能夠本身一邊作作總結,否則很容易過目即忘的,沒錯博主就是如此,而後文章若是有不嚴謹的地方,歡迎你們評論區指出。

參考

相關文章
相關標籤/搜索