2020年前端面試初體驗

1.關於jsjavascript

(1)宏任務和微任務:css

宏任務分類:總體代碼的script  setTimeout   setInterval   requrestAnimationFramehtml

微任務分類:new promise().then(回調)   process.nextTick(在node運行環境中優先級高於promise())前端

實例:vue

console.log('開始');
setTimeout(function() {
            console.log('setTimeout');
        })
        
        new Promise(function(resolve) {
            console.log('promise');
        }).then(function() {
            console.log('then');
        })
        
        console.log('console');

運行結果: 開始   promise console then setTimetoutjava

解釋:  1  執行一個宏任務(棧中沒有,就從事件隊列中獲取)。node

2執行過程當中若是遇到微任務,就將它添加到微任務隊列中。react

3宏任務執行完畢後,就當即執行當前微任務隊列中全部的微任務(依次執行)。webpack

4.當前宏任務執行完畢,開始檢查渲染,而後GUI 線程接管渲染。css3

5.渲染完畢後,js線程繼續接管,開始完成下一共宏任務(從事件隊列中獲取)。


 

 

 

(2)事件循環機制:

1.同步和異步任務分別進入不一樣的執行」場所「,同步的進入主線程,異步的進入Event 列表並註冊函數。

2.當指定的事情完成時,Event 列表會將這個函數移入 event 隊列

3.當棧中代碼執行完畢,執行棧中的任務爲空時,就會讀取任務隊列中的事件,去執行對應的回調。

4.如此循環,造成js的事件循環機制

 

 

 

(3)如何理解閉包 ,閉包的使用場景:

閉包就是函數調用本身做用域外的變量,或者說成調用做用域鏈上的變量,這種引用就叫閉包。 閉包的做用,改變變量的做用域。
應用場景:好比 一個變量觸發某個事件的時候會改變值,而另外一個函數根據這個值作響應變化。

(4)es5繼承和es6繼承,區別:

 1.ES5先建立子類,在實例化父類並添加到子類this中
 2.ES6先建立父類,在實例化子集中經過調用super方法訪問父級後,在經過修改this實現繼承

es5繼承:實質是先建立子類元素child的實例對象,而後再把父類元素parent的原型對象中的屬性賦值給了子類元素chid的實例對象裏面,從而實現繼承。

1.原型繼承 :利用call和apply繼承this上面的屬性和方法

2.原型鏈繼承:將父類的實例做爲子類的原型

3.組合繼承:利用call apply 繼承屬性

      利用原型鏈繼承方法

 

es6繼承:es6中採用extend繼承

繼承用extends,當繼承後須要用super()來接收父類的constructor構造函數,否在報錯,當new一個子類的時候先把參數傳入子類構造函數再經過super()講父類的構造函數引入,就能夠調用父類。

 

(5)垃圾回收:

Js具備自動垃圾回收機制。垃圾收集器會按照固定的時間間隔週期性的執行。

標記清除:工做原理:是當變量進入環境時,將這個變量標記爲「進入環境」。當變量離開環境時,則將其標記爲「離開環境」。標記「離開環境」的就回收內存。

引用計數 :工做原理:跟蹤記錄每一個值被引用的次數。

(6)什麼狀況下會發生內存泄漏:

1.    意外的全局變量引發的內存泄漏。

緣由:全局變量,不會被回收。

解決:使用嚴格模式避免。

2.    閉包引發的內存泄漏

緣由:閉包能夠維持函數內局部變量,使其得不到釋放。

解決:將事件處理函數定義在外部,解除閉包,或者在定義事件處理函數的外部函數中,刪除對dom的引用。

3.    沒有清理的DOM元素引用

緣由:雖然別的地方刪除了,可是對象中還存在對dom的引用

解決:手動刪除。

4.    被遺忘的定時器或者回調

緣由:定時器中有dom的引用,即便dom刪除了,可是定時器還在,因此內存中仍是有這個dom。

解決:手動刪除定時器和dom。

5.    子元素存在引用引發的內存泄漏

緣由:div中的ul li  獲得這個div,會間接引用某個獲得的li,那麼此時由於div間接引用li,即便li被清空,也仍是在內存中,而且只要li不被刪除,他的父元素都不會被刪除。

解決:手動刪除清空。

(7)深拷貝和淺拷貝:

淺拷貝:淺拷貝就是拷貝了一層,除了對象時拷貝的引用類型,其餘都是直接值傳遞,有本身的內存空間的。

深拷貝:深拷貝時拷貝多層,即便是嵌套了對象,也會拷貝出來。

深拷貝的實現方式:

1.對象只有一層的話可使用上面的:手動複製:把一個對象的屬性複製給另外一個對象的屬性

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);

3.用把對象轉成字符串,再用把字符串轉成新的對象。Object.assign()函數是深拷貝 ,不然是淺拷貝。
2.
JSON.stringifyJSON.parse

  var obj1 = { body: { a: 10 } };
  var obj2 = JSON.parse(JSON.stringify(obj1));
  obj2.body.a = 20;
  console.log(obj1);
  // { body: { a: 10 } } <-- 沒被改到
  console.log(obj2);
  // { body: { a: 20 } }
  console.log(obj1 === obj2);
  // false
  console.log(obj1.body === obj2.body);

  可是這種方法也有很多壞處,譬如它會拋棄對象的constructor。也就是深拷貝以後,無論這個對象原來的構造函數是什麼,在深拷貝以後都會變成Object。

也就是說,只有能夠轉成JSON格式的對象才能夠這樣用,像function沒辦法轉成JSON。

4.遞歸
5.數組的化 .splice(0)也是深拷貝。

(8)變量提高,函數的提高:

1.變量聲明的提高:經過var定義(聲明)的變量,在定義語句以前就能夠訪問到值:undefined

2.函數聲明的提高:經過function(聲明)的函數,在以前就能夠直接調用值:函數定義(對象)

(9)call,apply,bind 區別:

相同點:

一、都是用來改變函數的this對象的指向的。
二、第一個參數都是this要指向的對象。
三、均可以利用後續參數傳參。

 

1.call的語法:函數名.call(obj,"參數1",」參數2「);

2. apply的語法:函數名.apply(obj,[參數1,參數2,參數3……]);

3.bind 的語法:函數名bind(obj,"參數1",」參數2「)();

(10)js設計模式,應用場景:

 1.單例模式:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點 

    應用場景: 一個redux只有一個store 就是單列模式。

2.策略模式:定義一系列的算法,把它們一個個封裝起來,而且使它們能夠相互替換

    應用場景: 表單驗證方法

優勢

能夠有效地避免多重條件語句,將一系列方法封裝起來也更直觀,利於維護

缺點

每每策略集會比較多,咱們須要事先就瞭解定義好全部的狀況

3.代理模式:爲一個對象提供一個代用品或佔位符,以便控制對它的訪問

代理模式主要有三種:保護代理、虛擬代理、緩存代理

保護代理應用場景:保護代理主要實現了訪問主體得限制行爲,以過濾字符做爲簡單得例子

虛擬代理模式應用場景:函數得節流,是虛擬代理。

緩存代理:爲一些開銷大得運算結果提供暫時得緩存,提高效率。 緩存加法操做。

4.迭代器模式:迭代器模式是指提供一種方法順序訪問一個聚合對象中的各個元素,而又不須要暴露該對象的內部表示

應用場景:map foreach

5.發佈-訂閱模式:也是觀察者模式,定義了對象間一種一對多得依賴關係,當一個對象得狀態發生改變時,全部依賴於它得對象都將獲得通知。

應用場景:redux就是訂閱者模式,事件監聽也訂閱者模式。組件自動發現props或者state變化自動更新,就是觀察者模式。

6.命令模式:用一種鬆耦合的方式來設計程序,使得請求發送者和請求接收者可以消除彼此之間的耦合關係

命令(command)指的是一個執行某些特定事情的指令

應用場景:使用對象字面量得形式定義一個命令。

7.組合模式: 用小得子對象來構建更大得對象,而這些小得子對象自己也許時由更小得」孫對象「構建成得。

應用場景:掃描文件夾種得文件

8.模板方法模式:模板方法模式由兩部分結構組成,第一部分時抽象父類,第二部分是具體得實現子類。

應用場景:繼承,子類直接調用父類得模板函數來執行。

9.享元模式:享元(flyweight)模式是一種用於性能優化的模式,它的目標是儘可能減小共享對象的數量

強調將對象的屬性劃分爲內部狀態(屬性)與外部狀態(屬性)。內部狀態用於對象的共享,一般不變;而外部狀態則剝離開來,由具體的場景決定

應用場景:一個班級男女測量身高,性別爲內部狀態,其餘屬性爲外部狀態。

10.職責鏈模式:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係,將這些對象連成一條鏈,並沿着這條鏈 傳遞該請求,直到有一個對象處理它爲止

應用場景:以展現不一樣類型的變量爲例,設置一條職責鏈,能夠免去多重if條件分支

11.中介者模式:全部的相關 對象都經過中介者對象來通訊,而不是互相引用,因此當一個對象發生改變時,只須要通知中介者對象便可

12.裝飾者模式:

13.狀態模式:

14.適配器模式:

15.外觀模式:

 

http://www.javashuo.com/article/p-ghjzqliu-k.html

(11)事件流的三個階段:

事件的處理過程主要有三個階段:捕獲階段,目標階段,冒泡階段;

1.捕獲階段:當咱們在 DOM 樹的某個節點發生了一些操做(例如單擊、鼠標移動上去),就會有一個事件發射過去。這個事件從 Window 發出,不斷通過下級節點直到觸發的目標節點。在到達目標節點以前的過程,就是捕獲階段(Capture Phase)。事件由頁面元素接收,逐級向下,到具體的元素。

2.目標階段:當事件不斷的傳遞直到目標節點的時候,最終在目標節點上觸發這個事件,就是目標階段。具體的元素自己

3.冒泡階段:事件冒泡即事件開始時,由最具體的元素接收(也就是事件發生所在的節點),而後逐級傳播到較爲不具體的節點。跟捕獲相反,具體元素自己,逐級向上,到頁面元素(咱們平時用的事件綁定就是利用的事件冒泡的原理)。

 

事件捕獲:當使用事件捕獲時,父級元素先觸發,子元素後觸發。

事件冒泡:當使用事件冒泡時,子級元素先觸發,父元素後觸發。

 W3C : 任何事件發生時,先從頂層開始進行事件捕獲,直到事件觸發到達事件源,再從事件源向上進行事件捕獲(事件冒泡)。

 

事件傳播的阻止方法:

在W3C中,使用stopPropagation()方法。

在IE下使用cancelBubble = true方法。

 

阻止默認行爲:

在W3c中,使用preventDefault()方法。

(12)判斷是不是數組的幾種實現方式:

1.Array.isArray(): 返回true

2.instanceof運算符:  這個運算符能夠判斷一個對象是不是在其原型鏈上原型構造函數中的屬性

let arr = []; console.log(arr instanceof Array); //true

3.constructor:這個屬性是返回對象相對應的構造函數 

 

let arr = []; console.log(arr.constructor == Array); //true

4.寫一個函數方法

let arr = []; var isType = function (obj) { return Object.prototype.toString.call(obj).slice(8,-1); } console.log(isType(arr) == 'Array'); //true

 

 

(13)MVC和MVVC模式的區別:

MVC:M仍是表示Modal層,負責與後臺交互數據,V表示View,負責頁面上DOM的渲染,C表示綁定在DOM元素上的事件,當Controllor中的事件被調用,會去調用Modal中的數據,而後交給View從新渲染數據

MVVC:react、vue這倆個框架的核心理念都是數據驅動頁面渲染,同時他們都是MVVM模式的框架,MVVM模式中的M仍是固定表示Modal,V仍是表死View,這倆個基本都是不會發生變化,一個頁面必然須要數據和渲染倆個部分,那麼變化的是如何將Modal渲染到View的過程變了,在MVVM模式中,將View和Modal綁定在一塊兒,只要Modal發生了變化,View就會自動更新,不須要咱們認爲的再去寫如何操做DOM更新的過程了

(14)數據類型是放入堆?仍是棧?

 

js中數據對於存儲能夠分爲兩種數據類型:基本類型和引用類型
基本類型:Number,String,Boolean,Null,Undefined
這些類型的值存放在棧區,函數調用時傳遞的是變量的值(值)。
引用類型:Object,Array,Function
這些類型的對象存放在堆區,對象的地址存放在棧區,函數調用時傳遞的是對象的地址(址)。

 

(15)函數的科裏化?

科裏化:是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數的技術。

 



或者:const curry = ( fn, arr = []) => { return (...args) => { return ( a => { //a是一個數組 if(a.length === fn.length) { return fn(...a) }else{ return curry(fn, a) } })([...arr, ...args]) //這裏把arr和args攤開成一個數組賦值給a } }const curry = ( fn, arr = []) => (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])

 

let curryPlus = curry((a,b,c,d)=>a+b+c+d) curryPlus(1,2,3)(4) //返回10 curryPlus(1,2)(4)(3) //返回10 curryPlus(1,2)(3,4) //返回10

 

(16)用原生的方式實現數組去重?

 

function merge(arr) {

 

if (!Array.isArray(arr) || arr.length == 0)

 

return [];

 

var ret = [] ;

 

for (var i = 0; i < arr.length; i++) {

 

// 或者 ret.indexOf(arr[i] == -1)

 

 if (arr.indexOf(arr[i]) == i)

 

{ret.push(arr[i]);}

 

}  

 

 return ret;

 

 } 

 

(17)防抖和節流?

防抖:觸發高頻事件後N秒內只會執行一次,若是n秒內高頻事件再次被觸發,則從新計算時間。

舉例:在百度搜索時,每次輸入以後都會有聯想詞彈出,這個控制聯想詞的方法就是不多是輸入框內容一改變就觸發的,他必定是當你結束輸入一段時間以後才觸發的。

 

function debounce(fn,delay){

//記錄上一次的延時器

var timer = null;

return function(){

//清除上一次延時器

clearTimeout(timer);

//從新設置新的延時器

timer = setTimeout(function(){

fn.apply(this);

},delay);

}

}

節流:高頻事件觸發,但在N秒內只會執行一次,因此節流會稀釋函數執行的頻率

舉例:預約一個函數只有在大於等於執行週期時才執行,週期內調用不執行,就像淘寶中搶購某一件熱賣商品時,你不斷的點擊刷新或者購買,但是總有一段時間點擊沒有效果,這裏就是節流。

 

代碼舉例:

 

function scall(fn,delay){

 

// 記錄上一次函數觸發的時間

 

var lastTime = 0;

 

return function (){

 

// var nowTime  = Date.now();

 

if(nowTime - lastTime>delay){

 

fn();

 

lastTime  = nowTime;

 

}

 

}

 

}

 

document.onscroll = scall(function(){ console.log('scroll事件被觸發了'+Date.now());},200)

 

(18)js異步編程的方法?

1.事件的回調函數

2.定時器

3.promise

4.async ...await 

5.generator 函數

 

2.關於es6

 

http://www.javashuo.com/article/p-gxswledk-nk.html

(1)箭頭函數

1.經過=>函數能夠更簡潔快速的定義一個函數

2.若是箭頭函數不須要傳參或須要多個傳參,就須要使用()括起來。

3.若是箭頭函數的代碼塊部分多於一條語句,就要使用大括號將他們括起來,並使用return語句返回。

4.若是箭頭函數只有一行語句,且不須要返回值,不用寫大括號了

 

(2)Promise的用法以及實現原理

 

(3)說出es6新特性

(4)set數據結構

Set自己是一個構造函數,用來生成 Set 數據結構,可用於數組去重

實例:

.add()  添加成員,返回Set結構自己
.size 返回Set實例的成員總數
.delete() 刪除某值,返回Set結構自己
.has() 返回一個布爾值,表示該值是否爲Set的成員
.clear() 清除全部成員,沒有返回值
.keys() 返回鍵名的遍歷器
.values() 返回鍵值的遍歷器
.entries() 返回鍵值對的遍歷器
.forEach() 使用回調函數遍歷每一個成員,沒有返回值

 

(5)map數據結構

在JavaScript中對象(Object)本質是鍵值對的集合,但只能用字符串看成鍵。而Map 數據結構的出現就是爲了解決這個限制,它相似於對象,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵

.set(key, value) 設置key對應的value,返回 Map 結構。若是key已經有值,則鍵值會被更新。
.get(key)  讀取key對應的鍵值,若是找不到key,返回undefined。
.has(key) 方法返回一個布爾值,表示某個鍵是否在當前 Map 對象之中。
.delete() 方法刪除某個鍵,返回true,若是刪除失敗,返回false。
.clear() 方法清除全部成員,沒有返回值。
.keys(): 返回鍵名的遍歷器。
.values():返回鍵值的遍歷器。
.entries():返回全部成員的遍歷器。
.forEach():遍歷 Map 的全部成員。

(6)let和const命令

  es5只有兩種聲明變量的方法:var命令和function命令。es6除了let和const命令,另外兩種聲明變量的方法: import命令和class命令。因此 es6一共有6種方法。

let和const相同的地方:1.只在聲明所在的塊級做用域內有效 。

                                       2.不可重複聲明。

                                       3.一樣存在暫時性死區,只能在聲明的位置後面使用。

(7)變量的解構賦值

1.數組解構賦值:

2.對象解構賦值:

3.字符串解構賦值:

4.默認值,無論是對象仍是數組,解構賦值都容許指定默認值:

用途:1.交換變量的值

例子1:

  1. let a = 1;
  2.   let b = 2;
  3.  
  4. [a, b] = [a, b];

  2.從函數返回多個值

  // 數組
  function example() {
  return [1, 2, 3];
  }
  let [a, b, c] = example();

  // 對象
  function example() {
  return {
  foo: 1,
  bar: 2
  };
  }
  let { foo, bar } = example();

  3.函數參數的定義

  1. let arr = [1, 2, 3];
  2. function f([x, y, z]) { ... }
  3. f(arr);
  4.  
  5. let obj = {z: 3, y: 2, x: 1};
  6. function f({x, y, z}) { ... } 
  7. f(obj);

  4.提取json數據

  1. let jsonData = {
  2. id: 1001,
  3. status: "yes",
  4. data: ['Joe', 23]
  5. };
  • let { id, status, data} = jsonData;
     
    5.遍歷Map結構

    const map = new Map();
    map.set('first', 'hello');
    map.set('second', 'world');

    for (let [key, value] of map) {
    console.log(key + " is " + value);
    }

    6.輸入模塊的指定方法

    const { fun1, fun2 } = require("funs");
     

 

3.關於css2和css3

(1)BFC:塊級格式化上下文"

佈局規則:

1.內部的BOX會在垂直方向,一個接一個的放置。

2.box垂直方向的距離由margin決定,屬於同一個BFC的兩個相鄰box的margin會發生重疊。

3.每一個盒子(塊盒與行盒)的margin box的左邊,與包含塊border box 的左邊相接觸(對於從左往右的格式化,不然相反)。即便存在浮動也是如此。

4.BFC的區域不會與float box重疊。

5.BFC就是頁面上的一個隔離的獨立容器,容器裏面的子元素不會影響到外面的元素。

6.計算bfc的高度時,浮動元素也參與計算。

建立BFC:一、float 的值不是none

        二、position的值不是static 或者relative

     三、display的值是inline-block、table-cell、flex、table-caption 或者inline-flex

                  四、overflow的值不是visible

1.BFC的做用:1.利用BFC避免margin重疊 

                         2.自適應兩欄佈局 left<div> : float :left,  right<div>: overflow:hidden

                         3.清除浮動:當咱們不給父節點設置高度,子節點設置浮動的時候,會發生高度塌                                  陷,這個時候咱們就要清楚浮動

總結:BFC就是頁面上的一個隔離的獨立容器,容器裏面的子元素不會影響到外面的元素。反之也如此  

由於BFC內部的元素和外部的元素絕對不會互相影響,所以, 當BFC外部存在浮動時,它不該該影響BFC內部Box的佈局,BFC會經過變窄,而不與浮動有重疊。一樣的,當BFC內部有浮動時,爲了避免影響外部元素的佈局,BFC計算高度時會包括浮動的高度。避免margin重疊也是這樣的一個道理

(2)清除浮動3種方式:

       1. clear:both
       2. overflow: auto
       3. 僞元素來清除浮動

(3)實現垂直水平居中(新老方式)

一、老方式

#wrap{

width:500px;

height:500px;

background:grey;

position:relative;

}

#wrap .box{

width:200px;

height:200px;

background:pick;

//方式1:

position:absolute;

top:50%;

left:50%;

margin-left:-100px;

margin-top:-100px;

//方式2:

position:absolute;

top:0;

left:0;

right:0;

bottom:0;

margin:auto;

//方式3

transform:translate(-50%,-50%);

}

二、新方式

#wrap{

display:flex;

justify-content:center;

align-items:center;

}

4.關於react

redux中間件原裏:改裝dispath

(1)hoc:

  定義:一種React的進階使用方法,主要仍是爲了便於組件的複用。HOC就是一個方法,獲取一個組件,返回一個更高級的組件

       何時使用hoc?

組件添加或者修改一些特定的props,一些權限的管理,或者一些其餘的優化之類的。而若是這個功能是針對多個組件的,同時每個組件都寫一套相同的代碼,明顯顯得不是很明智,因此就能夠考慮使用HOC。

       例子:react-redux的connect方法就是一個HOC,他獲取wrappedComponent,在connect中給wrappedComponent添加須要的props。

  hoc能夠作什麼?

  1.代碼複用,代碼模塊化

  2.增刪改props

  3.渲染劫持 :組件要在data沒有加載完的時候,顯示loading。。。

  Hoc有什麼用例?

  1.react redux的connect方法,經過這個hoc方法,監聽redux store ,而後把下級組件須要的state(經過mapStateToPtops獲取)和action creator(經過mapDispatchToProps 獲取)綁定到wrappedComponent的props上

  2.logger和debugger 能夠用來監控父級組件傳入的props的改變:

  3.頁面權限管理:能夠經過hoc對組件進行包裹,當跳轉到當前頁面的時候,檢查用戶是否含有對應的權限,若是有的話,渲染頁面,若是沒有的話,跳轉到其餘頁面(好比無權限頁面,或者登錄頁面)。

  使用Hoc須要注意什麼?

1.儘可能不要隨意修改下級組件須要的props

修改父級傳遞給下級的props是有必定風險的,可能會形成下級組件發生錯誤。

2.ref沒法獲取你想要的ref

由於這裏的component通過hoc的封裝,已是hoc裏面的那個component了,因此你沒法獲取你想要的那個ref(wrappedComponent的ref)。

解決方法:

a> 像react redux的connect方法同樣,在裏面添加一個參數,好比withRef,組件中檢查到這個flag了,就給下級組件添加一個ref ,並經過getWrappedinstance方法獲取。

b> 父級經過傳遞一個方法,來獲取ref

先看父級組件:

component上面綁定的Static方法會丟失

好比:原來在Component上面綁定了一些static方法,MyComponent.staticMethod = o=>o.可是因爲通過Hoc的包裹,父級組件拿到的已經不是原來的組件了,因此固然沒法獲取staicMethod方法了

https://segmentfault.com/a/1190000008112017?_ea=1553893

(2)hook:

(3)虛擬dom的缺點:

(4)dva和redux相比較優缺點:

dva是一個基於redux和redux-saga的數據流方案,dva還額外內置了react-router和fetch,因此也能夠理解爲一個輕量級的應用框架。

1.dva封裝了redux,減小不少重複代碼好比action reducers常量

2.dva操做都是在models層,經過namespace做爲key,標識不一樣的模塊state。state存儲數據。

3.reducers跟傳統的react-redux寫法一致,全部的操做放在reducers對象內。

4.異步操做寫在effects對象內:

其實*fetchList 就是function *fetchList ,是個Generator狀態機

call,put實際上是saga的寫法,dva集成了saga。

5.ui組件調用使用@connect包裹就能夠從this.props上調用方法和數據。

6.dva-loading能夠自動處理loading狀態,不用一遍遍的寫showLoading和hideLoading。

 

優勢

  • 框架: dva是個框架,集成了redux、redux-saga、react-router-redux、react-router
  • 快速初始化: 能夠快速實現項目的初始化,不須要繁瑣地配置
  • 簡化開發:將initState、saga、reducer集成到一個model裏面統一管理,避免文件散落在各個文件裏面,便於快速查找與開發
  • 簡潔的API:整個項目中只有dva、app.model、app.router、app.use、app.start幾個API
  • 無縫對接:跟react的生態沒有衝突,例如能夠直接使用redux devtool工具
  • 動態機制:app.start之後,仍然能夠註冊model,靈活性較高
缺點:
  • namespace不統一: dva中的action.type格式是namespace/XXX,且在model中不須要添加前綴namespace,可是在組件中dispatch,卻須要添加prefix
  • action問題:action會散落在兩個地方,一個是saga裏面,另一個是component dispatch的時候,固然這個問題在使用redux-saga的時候就會存在,只是dva仍然沒有很好地統一塊兒來。


做者:zhenhua-lee
連接:https://www.zhihu.com/question/51831855/answer/225446217
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

做者:zhenhua-lee
連接:https://www.zhihu.com/question/51831855/answer/225446217

 

(5)從3方面說說diff算法:

1.tree Diff

(1)react經過updateDepth對於Virtual DOM樹進行層級控制。

(2)對樹分層比較,兩棵樹只對同一層次節點進行比較,若是該節點不存在時,則該節點及其子節點會被徹底刪除,不會再進一步比較。

(3)只需遍歷一次,就 能完成整棵樹DOM樹的比較。

  (4)  diff只簡單考慮同層級的節點位置互換,若是時垮層級的話,只有建立節點和刪除節點的操做。

2.Component Diff

react對不一樣組件間的比較,有三種策略」

(1)同一個類型的兩個組件,就按原策略(層級比較)繼續比較Virtual DOM樹便可。

(2)同一類型的兩個組件,組件A變化爲組件B時,可能Virtual Dom沒有任何變化,若是知道這點(變換的過程當中,Virtual Dom沒有改變),可節省大量計算時間,因此用戶能夠經過shouldComponentUpdate()來判斷是否須要diff計算

(3)不一樣類型的組件,將一個(將被改變的)組件判斷爲dirty component (髒組件),從而替換整個組件的全部節點。

若是組件D和組件G的結構類似,可是react判斷時不一樣類型的組件,則不會比較其結構,而是刪除組件D及其子節點,建立組件G及其子節點。

3.Element Diff

當節點處於同一層級時,diff提供三種節點操做:刪除、插入、移動。

 

https://www.jianshu.com/p/3ba0822018cf

(6)react框架的原理:

 

react的設計原理就是其引入的虛擬dom機制:
一、react用javascript在瀏覽器端實現了一套虛擬dom api。
二、基於react開發的時候全部的dom構造都是基於虛擬dom進行的
三、每當有state更改的時候,react就從新render一整套虛擬dom樹,
react機制會將當前的整個dom樹和上一次的dom樹進行對比
取到diff,進行真實的dom更改。
四、其實state也有一部分實現的是數據、html片斷綁定,
直接更新的數據是包含的

深層次一點就是react的diff算法是怎麼理解的。
錯誤的看法:我曾經覺得diff算法,就是深層次的diff,算法運算時只比較
不一樣的。但其實當時淺顯的想法,確實是diff運算的結果,但不是
diff運算的算法。
一、tree diff
React對Virtual DOM樹進行層級控制,只會對相同層級的DOM節點進行比 

較,即同一個父元素下的全部子節點,當發現節點已經不存在了,則會刪除掉
該節點下全部的子節點,不會再進行比較。這樣只須要對DOM樹進行一次遍
歷,就能夠完成整個樹的比較。
即便說a節點以及他的子節點被移動,可是react只關注同級比較,在第二層
把a及其子節點刪了,在第三層再從新建立,因此diff運算量大,影響性能
不建議setState直接更改樹的結構。最好是state顆粒度小,只改變樹中
的某一個小的節點,那麼diff的時候只會深度比較這一個小節點。

二、componnet diff

假如說由於某個條件切換,因此要顯示不一樣的組件。
一、比較兩個組件的類型(D和G)
二、若是(D和G)不是同一類型,進行diff算法,分析會影響性能
直接刪掉上一個虛擬dom組件。 從新建立新的組件。
若是是同一類型的組件,會按照層級策略深層對比每一個節點。

三、element diff
精確的對屬於同一層級的節點diff時,提供了3種節點操做,分別爲INSERT_MARKUP(插入),MOVE_EXISTING(移動),REMOVE_NODE(刪除)。
若是同一層級,沒有這個新節點會新增插入
若是同一層級,若是有新節點,可是屬性不同,會複用節點,賦值屬性
若是同一層次,舊dom有,新dom沒有,會刪除這個節點。

原文連接:https://blog.csdn.net/running_shuai/java/article/details/80284698

 

總結:setState()觸發一次組件重繪,其實就是虛擬dom從新生成,除非在
shouldComponentUpdate()中實現了一些條件渲染邏輯。來容許和阻止是否須要
調用指定組件的 render 方法。其實這個深刻邏輯就是他觸發了render,只是是
否觸發了內部的diff算法,return false 的時候,不diff,render出來的新
舊dom同樣。diff算法的複雜度爲0。

 

 

 

 

(7)redux:

1>redux 是一個獨立專門用於作狀態管理的js庫,不是react插件庫

2>做用:集中式管理react應用中多個組件共享的狀態和從後臺獲取的數據。

react-redux簡化redux的編碼

redux-thunk實現redux的異步編程

使用redux devTools實現chrome 中redux的調試

(8)爲何虛擬dom會提升性能?

(9)this.setStatus 是異步仍是同步?

 

  1. setState 只在合成事件和鉤子函數中是「異步」的,在原生事件和 setTimeout 中都是同步的。
  2. setState的「異步」並非說內部由異步代碼實現,其實自己執行的過程和代碼都是同步的,只是合成事件和鉤子函數的調用順序在更新以前,致使在合成事件和鉤子函數中無法立馬拿到更新後的值,形式了所謂的「異步」,固然能夠經過第二個參數 setState(partialState, callback) 中的callback拿到更新後的結果。
  3. setState 的批量更新優化也是創建在「異步」(合成事件、鉤子函數)之上的,在原生事件和setTimeout 中不會批量更新,在「異步」中若是對同一個值進行屢次 setState , setState 的批量更新策略會對其進行覆蓋,取最後一次的執行,若是是同時 setState 多個不一樣的值,在更新時會對其進行合併批量更新。

 

(10)生命週期?

 

 

5.前端優化

 

6.前端工程化

 1.服務端模塊化 

1、commonJS 對模塊的定義很是簡單,主要分爲模塊引用,模塊定義和模塊標識3個部分。

1)模塊的引用

var add = require('./add.js);

2)  模塊定義 

module.exports.add  = function(){

... 

}

3)能夠在一個文件中引入模塊並導出另外一個模塊

var add = require('./add .js');

module.exports.increment = function(){

return add (val,1);

}

其實 ,一個文件表明一個模塊,一個模塊除了本身的函數做用域以外,最外層還有一個模塊做用域,module就是表明這個模塊,

exports是module的屬性,require也是這個模塊的上下文中,用來引入外部模塊。

3)模塊標識

模塊標識就是require()函數的參數,規範是這樣的

1.必須是字符串

2.能夠是以./ ../開頭的相對路徑

3.能夠是絕對路徑

4.能夠省略後綴名

2、node.js 模塊化實現

1.node中一個文件 就是一個模塊-----module

一個模塊就是一個Module的實例

2.node模塊分類:核心模塊和文件模塊

核心模塊:就是node內置的模塊好比http,path等。在node的源碼的編譯時,核心模塊就一塊兒被編譯進了二進制執行文件,部分核心模塊(內建模塊)被直接加載進內存中。

文件模塊:就是外部引入的模塊如node—modules裏面經過npm按裝模塊,或者咱們項目工程裏本身寫一個js文件或者json文件。

node模塊的引入過程,通常要通過三個步驟

路徑分析

文件定位

編譯執行

核心模塊:會省略 文件定位和編譯執行,而且在路徑分析中會優先判斷,加載速度比通常模塊更快。標識:require(‘http’);

文件模塊:三個步驟都要經歷。require('./c.js');以. / 或者.. /絕對路徑開頭。

3、AMD:異步模塊加載規範與CommonJS的主要區別就是異步模塊加載,就是模塊加載過程當中即便require的模塊尚未獲取到,也不會影響後面代碼的執行。

  • CommonJS通常用於服務端,AMD通常用於瀏覽器客戶端
  • CommonJS和AMD都是運行時加載

4、CMD:通用模塊規範,與AMD規範的主要區別在於定義模塊和依賴引入的部分,AMD須要在聲明模塊的時候指定全部的依賴,經過形參傳遞依賴到模塊內容中。在依賴示例部分,CMD支持動態引入,require、exports、module經過形參傳遞給模塊,在須要依賴模塊時,隨時調用require()引入便可。

5、UMD 通用模塊規範

6、es6模塊

1)導出一個變量

export  var name=‘pengpeng’;

2)導出一個函數

export function foo(x,y){}

3)經常使用導出方式(推薦)

const name = ‘dingman’;

const age = ‘18’;

export{name,age};

4)As用法

const s =1;

export {

s as t,

s as m,

}

能夠利用as將模塊輸出屢次。

Es6模塊使用-----import

1) 通常用法

import {name,age} from ‘./person,js';

2) As 用法

import {name as personName} from './person.js';

import命令具備提高效果,會提高到整個模塊的頭部,首先執行,以下也不會報錯:

getName();

import {getName} from ’person_module';

3)總體模塊加載*

export name = 'xixi';

export age = 23;

// 逐一加載

import  {age,name} from './person.js';

//  總體加載

import * as person from './person.js';

console.log(person.name);

console.log(person.age);

es6模塊使用 ------export default

使用export default 命令,須要注意的是使用export default命令時,import 是不須要加{}的,而不使用export default時,import是必須加{}。

示例以下:

//person.js export function getName() { ... } //my_module import {getName} from './person.js'; -----------------對比--------------------- //person.js export default function getName(){ ... } //my_module import getName from './person.js';

export default 實際上是導出一個叫作default的變量,因此其後面不能跟變量聲明語句。

//錯誤
export default var a = 1;

值得注意的是 咱們能夠同時使用export和export default




commonjs是運行時加載,es6是編譯時加載,又有什麼區別呢?

//person.js export name = 'dingman'; export default function getName(){ ... } //my_module import getName, { name } from './person.js';

ES6模塊的設計思想,是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。因此說ES6是編譯時加載,不一樣於CommonJS的運行時加載(實際加載的是一整個對象),ES6模塊不是對象,而是經過export命令顯式指定輸出的代碼,輸入時也採用靜態命令的形式

 

//ES6模塊 import { basename, dirname, parse } from 'path'; //CommonJS模塊 let { basename, dirname, parse } = require('path');

 七:webpack模塊化

 

 https://zhuanlan.zhihu.com/p/41568986

 https://zhuanlan.zhihu.com/p/42853909

7.websocket

(1)websocket和http區別?

 

http協議是用在應用層的協議,他是基於tcp協議的,http協議創建連接也必需要有三次握手才能發送信息。
http連接分爲短連接,長連接,短連接是每次請求都要三次握手才能發送本身的信息。即每個request對應一個response。長連接是在必定的期限內保持連接。保持TCP鏈接不斷開。客戶端與服務器通訊,必需要有客戶端發起而後服務器返回結果。客戶端是主動的,服務器是被動的。

 

WebSocket
WebSocket他是爲了解決客戶端發起多個http請求到服務器資源瀏覽器必需要通過長時間的輪訓問題而生的,他實現了多路複用,他是全雙工通訊。在webSocket協議下客服端和瀏覽器能夠同時發送信息。

 

創建了WebSocket以後服務器沒必要在瀏覽器發送request請求以後才能發送信息到瀏覽器。這時的服務器已有主動權想何時發就能夠發送信息到服務器。並且信息當中沒必要在帶有head的部分信息了與http的長連接通訊來講,這種方式,不只能下降服務器的壓力。並且信息當中也減小了部分多餘的信息。

 

(2)webSocket如何實現斷線重連?

 

var ws_heart_i = null;

 

/**
* websocket 每1分鐘發一次心跳
*/
function ws_heart() {
if (ws_heart_i) clearInterval(ws_heart_i);
ws_heart_i = setInterval(function () {
console.log('ws_heart');
var func = function () {
var data = {type: 'ping'};
ws.send(JSON.stringify(data));
};
ws_execute(func);
}, 60000);
}

 

 

 

 


原文連接:https://blog.csdn.net/sybil06/java/article/details/88821125

 

 

8.瀏覽器存儲

 

9.網絡問題

(1)http 請求的所有過程?

1.DNS解析:將域名地址解析爲ip地址

-瀏覽器DNS緩存

-系統DNS緩存

-路由DNS緩存

-網絡運營商DNS緩存

-遞歸搜索:blog.baidu.com

-.com域名下查找DNS解析

-.baidu域名下查找DNS解析

-blog域名下查找DNS解析

-出錯了

2.TCP鏈接,TCP三次握手

-第一次握手,由瀏覽器發起,告訴服務器我要發送請求了

-第二次握手,由服務器發起,告訴瀏覽器我準備接受了,你趕忙發送吧

-第三次握手,由瀏覽器發送,告訴服務器, 我立刻就發了,準備接受吧

3.發送請求

-請求報文,http協議的通訊內容

4.接受響應

-響應報文

5.渲染頁面

-碰見HTML標記,瀏覽器調用HTML解析器解析成Token並構建成dom樹

-碰見style/link標記,瀏覽器調用css解析器,處理css標記並構建cssom樹

-碰見script標記,調用JavaScript解析器,處理script代碼(綁定事件,修改dom樹/cssom樹)

-將dom樹和cssom樹合併成一個渲染樹

-根據渲染樹來計算佈局,計算每一個節點的幾何信息(佈局)

-將各個節點顏色繪製到屏幕上(渲染)

注意:

這個5個步驟不必定按照順序執行,若是dom樹或cssom樹被修改了,可能會執行屢次佈局和渲染,

每每實際頁面中,這些步驟會執行屢次的

6.斷開鏈接,TCP四次揮手

第一次揮手,由瀏覽器發起的,發送給服務器,我東西發送完了(請求報文),你準備關閉吧。

第二次揮手,由服務器發起的,告訴瀏覽器,我東西接收完了(請求報文),我準備關閉了,你也準備吧

第三次揮手,由服務器發起,告訴瀏覽器,我東西發送完了(響應報文),你準備關閉吧。

第四次揮手,由瀏覽器發起,告訴服務器,我東西接受完了,我準備關閉了(響應報文),你也準備吧。

 

 

(2)如何處理跨域問題?

(3)http緩存機制?

(4)http狀態碼

 

狀態碼

 

狀態代碼爲3位數字。
1xx:指示信息--表示請求已接收,繼續處理。
2xx:成功--表示請求已被成功接收、理解、接受。
3xx:重定向--要完成請求必須進行更進一步的操做。
4xx:客戶端錯誤--請求有語法錯誤或請求沒法實現。
5xx:服務器端錯誤--服務器未能實現合法的請求。

 

 

 

10 .移動端如何實現適配

11.react選型?

12.react和vue相比較

相同點:

1.都有組件化開發和virtual DOM

2.都支持props進行父子組件間數據通訊

3.都支持數據驅動視圖,不直接操做真實DOM,更新狀態數據界面就自動更新

4.都支持服務端渲染

5.都有支持native的方案,react的react native ,vue的weex

不一樣點:

1.數據綁定:vue實現了數據的雙向綁定,react數據流動時單向的。

2.組件寫法不同,react推薦的作法是JSX,也就是把html和css所有都寫進javascript了,即「all in js」 vue推薦的作法是webpack+vue-loader的單文件組件格式,即html,css,js 寫在同一個文件。

3.state對象在react應用中不可變的,須要使用setState方法更新狀態;在vue中,state對象不是必須的,數據由data屬性在vue對象中管理

4.virtual Dom不同,vue會跟蹤每個組件的依賴關係,不須要從新渲染整個組件樹,而對於react而言,每當應用的狀態被改變時,所有組件都會從新渲染,因此react中會須要shouldComponentUpdate 這個生命週期函數方法來進行控制

5.react嚴格上只針對MVC的view層,vue則是mvvm模式。

12.飛冰和antdesign?

相關文章
相關標籤/搜索