面試準備 --- 持續更新中

React

1.key的做用

key幫助React識別哪些項目已更改,已添加或已刪除。也所以能夠高效的更新虛擬DOM列表,key值是用來判斷 VDOM 元素項的惟一依據css

  • key相同,若組件屬性有所變化,則react只更新組件對應的屬性;沒有變化則不更新。
  • key值不一樣,則react先銷燬該組件(有狀態組件的componentWillUnmount會執行),而後從新建立該組件(有狀態組件的constructor和componentWillUnmount都會執行)

2.vdom的更新策略diff

3.setState同步異步?

在React中,若是是由React引起的事件處理(好比經過onClick引起的事件處理)或者鉤子函數中,調用setState不會同步更新this.state,除此以外的setState調用會同步執行this.state。所謂「除此以外」,指的是繞過React經過addEventListener直接添加的事件處理函數,還有經過setTimeout/setInterval產生的異步調用。html

緣由:在React的setState函數實現中,會根據一個變量isBatchingUpdates判斷是直接更新this.state仍是放到隊列中回頭再說,而isBatchingUpdates默認是false,也就表示setState會同步更新this.state,可是,有一個函數batchedUpdates,這個函數會把isBatchingUpdates修改成true,而當React在調用事件處理函數以前就會調用這個batchedUpdates,形成的後果,就是由React控制的事件處理過程setState不會同步更新this.state。前端

class App extends React.Component {
  state = { val: 0 }

  componentDidMount() {
    this.setState({ val: this.state.val + 1 })
    console.log(this.state.val) // 0 在鉤子函數中不會當即更新

    this.setState({ val: this.state.val + 1 })
    console.log(this.state.val) // 0 在鉤子函數中不會當即更新

    setTimeout(_ => {
      this.setState({ val: this.state.val + 1 })
      console.log(this.state.val); // 2 這裏會當即更新,而且因爲setstate批量更新測量,componentDidMount的兩次setstate只會取最後一次,即1

      this.setState({ val: this.state.val + 1 })
      console.log(this.state.val) // 3 同上
    }, 0)
  }

  render() {
    return <div>{this.state.val}</div>
  }
}

4.什麼是錯誤邊界

在 React 中,咱們一般有一個組件樹。若是任何一個組件發生錯誤,它將破壞整個組件樹。沒有辦法捕捉這些錯誤,咱們能夠用錯誤邊界優雅地處理這些錯誤。node

錯誤邊界有兩個做用react

  • 若是發生錯誤,顯示回退UI
  • 記錄錯誤

下面是ErrorBoundary類的一個例子。若是類實現了 getDerivedStateFromErrorcomponentDidCatch 這兩個生命週期方法的任何一下,,那麼這個類就會成爲ErrorBoundary。前者返回{hasError: true}來呈現回退UI,後者用於記錄錯誤。jquery

export class ErrorBoundary extends React.Component {
    constructor(props) { 
        super(props); 
        this.state = { hasError: false }; 
     } 
     static getDerivedStateFromError(error) { 
         // Update state so the next render will show the fallback UI. 
         return { hasError: true }; 
     } 
     componentDidCatch(error, info) { 
        // You can also log the error to an error reporting service 
        console.log('Error::::', error); 
     } 
     render() { 
        if (this.state.hasError) { 
            // You can render any custom fallback UI               return <h1\>OOPS!. WE ARE LOOKING INTO IT.</h1\>; 
        } 
        return this.props.children; 
    } 
}

5.在構造函數調用super並將props做爲參數傳入的做用是啥?

在調用super()方法以前,子類構造函數沒法使用this引用,ES6 子類也是如此。將props參數傳遞給super()調用的主要緣由是在子構造函數中可以經過this.props來獲取傳入的propswebpack

傳遞 propsgit

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    console.log(this.props);  // { name: 'sudheer',age: 30 }
  }
}

沒有傳遞 propsweb

class MyComponent extends React.Component {
  constructor(props) {
    super();
    console.log(this.props); // undefined
    // 可是 Props 參數仍然可用
    console.log(props); // Prints { name: 'sudheer',age: 30 }
  }

  render() {
    // 構造函數外部不受影響
    console.log(this.props) // { name: 'sudheer',age: 30 }
  }
}

6.Fiber

查看正則表達式

Redux

Mobx

Webpack

1.webpack和gulp區別

gulp是基於任務和流的處理。放在之前好比我想用sass寫css, coffee寫js, 我必須手動的用相應的compiler去編譯各自的文件,而後各自minify。這時候designer給你了兩張新圖片,好嘞,接着用本身的小工具手動去壓縮圖片。
後來前端人不能忍了,搞出個自動化這個流程的 Grunt/Gulp, 好比你寫完代碼後要想發佈production版本,用一句gulp build就能夠

rm掉 dist文件夾中之前的舊文件
自動把sass編譯成css, coffee編譯成js
壓縮各自的文件,壓縮圖片,生成圖片sprite 
拷貝minified/uglified 文件到 dist 文件夾

webpack是基於入口的。webpack會自動地遞歸解析入口所須要加載的全部資源文件,而後用不一樣的Loader來處理不一樣的文件,用Plugin來擴展webpack功能。

2.loader 的執行順序爲何是後寫的先執行

loader中採起compose的洋蔥模型,內部採用reduceRight實現,所以是後寫的先執行

function compose(...loaders){
    return function(arg){
        return loaders.reduceRight((res, cur) => {
            return cur(res)
        }, arg)
    }
}

3.經常使用的loader和plugins以及他們的區別

常見的loader
  • file-loader:把文件輸出到一個文件夾中,在代碼中經過相對 URL 去引用輸出的文件
  • url-loader:和 file-loader 相似,可是能在文件很小的狀況下以 base64 的方式把文件內容注入到代碼中去
  • source-map-loader:加載額外的 Source Map 文件,以方便斷點調試
  • image-loader:加載而且壓縮圖片文件
  • babel-loader:把 ES6 轉換成 ES5
  • css-loader:加載 CSS,支持模塊化、壓縮、文件導入等特性
  • style-loader:把 CSS 代碼注入到 JavaScript 中,經過 DOM 操做去加載 CSS。
  • eslint-loader:經過 ESLint 檢查 JavaScript 代碼
常見的plugins
  • define-plugin:定義環境變量
  • commons-chunk-plugin:提取公共代碼
  • uglifyjs-webpack-plugin:經過UglifyES壓縮ES6代碼
不一樣的做用
  • Loader直譯爲"加載器"。Webpack將一切文件視爲模塊,可是webpack原生是隻能解析js文件,若是想將其餘文件也打包的話,就會用到loader。 因此Loader的做用是讓webpack擁有了加載和解析非js文件的能力。
  • Plugin直譯爲"插件"。Plugin能夠擴展webpack的功能,讓webpack具備更多的靈活性。 在 Webpack 運行的生命週期中會廣播出許多事件,Plugin 能夠監聽這些事件,在合適的時機經過 Webpack 提供的 API 改變輸出結果。
不一樣的用法
  • Loadermodule.rules中配置,也就是說他做爲模塊的解析規則而存在。 類型爲數組,每一項都是一個Object,裏面描述了對於什麼類型的文件(test),使用什麼加載(loader)和使用的參數(options
  • Pluginplugins中單獨配置。 類型爲數組,每一項是一個plugin的實例,參數都經過構造函數傳入。

3.webpack優化

優化Loader配置

因爲loader對於文件的轉換很耗時,因此須要讓儘可能少的文件被處理,經過修改test,use,include或者exclude配置來實現

module.exports = { 
    module : { 
        rules : [{
            //若是項目源碼中只有 文件,就不要寫成/\jsx?$/,以提高正則表達式的性能
            test: /\.js$/, 
            //babel -loader 支持緩存轉換出的結果,經過 cacheDirectory 選項開啓
            use: ['babel-loader?cacheDirectory'] , 
            //只對項目根目錄下 src 目錄中的文件採用 babel-loader
            include: path.resolve(__dirname,'src'),
        }],
    }
}
優化resolve.modules

resolve.modules默認值是['node_modules'],webpack查找模塊的機制和nodejs很相似,先從當前目錄的node_modules下去找,找不到再去上一級../node_modules,以此類推。前端大部分項目node_modules都是在根目錄,所以直接配置以下方式能夠減小查找

module.exports = {
        resolve: {
            modules: [path.resolve( __dirname,'node modules')] 
        },
    }
module.noParse

經過以下配置使得沒有模塊化的第三方庫無需被解析

module.exports = {
    module: {
        noParse: /jquery/,
    }
};
使用happyPack

因爲webpack是單線程處理,因此只能一個個處理任務。happyPack能夠將任務拆分紅多個分配給子進程併發處理,子進程都處理完了再通知主線程

const HappyPack = require('happypack')
    const os = require('os')
    const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })

    {
        test: /\.js$/,
        // loader: 'babel-loader',
        loader: 'happypack/loader?id=happy-babel-js', // 增長新的HappyPack構建loader
        include: [resolve('src')],
        exclude: /node_modules/,
    }
    
    plugins: [
        new HappyPack({
            id: 'happy-babel-js',
            loaders: ['babel-loader?cacheDirectory=true'],
            threadPool: happyThreadPool
        })
    ]
使用 ParallelUglifyPlugin壓縮

webpack提供的UglifyJS只能單線程的一個個壓縮js文件(壓縮JS代碼須要先把代碼解析成用Object抽象表示的AST語法樹,再去應用各類規則分析和處理AST,致使這個過程耗時很是大),而經過使用ParallelUglifyPlugin能夠開啓多個子進程並行使用UglifyJS壓縮每一個任務

使用 splitChunks 進行分包
tree-shaking

Tree Shaking 正常工做的前提是,提交給 Webpack 的 JavaScript 代碼必須採用了 ES6 的模塊化語法,由於 ES6 模塊化語法是靜態的,能夠進行靜態分析。
首先,爲了將採用 ES6 模塊化的代碼提交給 Webpack ,須要配置 Babel 以讓其保留 ES6 模塊化語句。修改 .babelrc 文件以下:

{
        'presets':[
            [
                'env',{ 
                    'module':false
                }
            ]
        ]
    }

第二個要求,須要使用UglifyJsPlugin插件。若是在mode:"production"模式,這個插件已經默認添加了,若是在其它模式下,能夠手工添加它。

另外要記住的是打開optimization.usedExports。在mode: "production"模式下,它也是默認打開了的。它告訴webpack每一個模塊明確使用exports。這樣以後,webpack會在打包文件中添加諸如/* unused harmony export */這樣的註釋,其後UglifyJsPlugin插件會對這些註釋做出理解。

module.exports = {
    mode: 'none',
    optimization: {
        minimize: true,
        minimizer: [
            new UglifyJsPlugin()
        ],
        usedExports: true,
        sideEffects: true
    }
}

4.webpack工做流程

Webpack 的運行流程是一個串行的過程,從啓動到結束會依次執行如下流程:

  1. 初始化參數:從配置文件和 Shell 語句中讀取與合併參數,得出最終的參數;
  2. 開始編譯:用上一步獲得的參數初始化 Compiler 對象,加載全部配置的插件,執行對象的 run 方法開始執行編譯;
  3. 肯定入口:根據配置中的 entry 找出全部的入口文件;
  4. 編譯模塊:從入口文件出發,調用全部配置的 Loader 對模塊進行翻譯。利用babel轉爲Ast->遍歷Ast->調用ImportDeclaration。找出該模塊依賴的模塊,再遞歸本步驟直到全部入口依賴的文件都通過了本步驟的處理;
  5. 完成模塊編譯:在通過第4步使用 Loader 翻譯完全部模塊後,獲得了每一個模塊被翻譯後的最終內容以及它們之間的依賴關係;
  6. 輸出資源:根據入口和模塊之間的依賴關係,組裝成一個個包含多個模塊的 Chunk,再把每一個 Chunk 轉換成一個單獨的文件加入到輸出列表,這步是能夠修改輸出內容的最後機會;
  7. 輸出完成:在肯定好輸出內容後,根據配置肯定輸出的路徑和文件名,把文件內容寫入到文件系統。

在以上過程當中,Webpack 會在特定的時間點廣播出特定的事件,插件在監聽到感興趣的事件後會執行特定的邏輯,而且插件能夠調用 Webpack 提供的 API 改變 Webpack 的運行結果。

5.webpack的熱更新是如何作到的?說明其原理?

瀏覽器

1.瀏覽器打開一個tab須要多少個進程

瀏覽器從關閉狀態進行啓動,而後新開 1 個頁面至少須要 1 個網絡進程、1 個瀏覽器進程、1 個 GPU 進程以及 1 個渲染進程,共 4 個進程;後續再新開標籤頁,瀏覽器、網絡進程、GPU進程是共享的,不會從新啓動,若是2個頁面屬於同一站點的話,而且從a頁面中打開的b頁面,那麼他們也會共用一個渲染進程,不然新開一個渲染進程。

最新的 Chrome 瀏覽器包括:1 個瀏覽器(Browser)主進程、1 個 GPU 進程、1 個網絡(NetWork)進程、多個渲染進程和多個插件進程。

  • 瀏覽器進程:主要負責界面顯示、用戶交互、子進程管理,同時提供存儲等功能。
  • 渲染進程:核心任務是將 HTML、CSS 和 JavaScript 轉換爲用戶能夠與之交互的網頁,排版引擎 Blink 和 JavaScript 引擎 V8 都是運行在該進程中,默認狀況下,Chrome 會爲每一個 Tab 標籤建立一個渲染進程。出於安全考慮,渲染進程都是運行在沙箱模式下。
  • GPU 進程:其實,Chrome 剛開始發佈的時候是沒有 GPU 進程的。而 GPU 的使用初衷是爲了實現 3D CSS 的效果,只是隨後網頁、Chrome 的 UI 界面都選擇採用 GPU 來繪製,這使得 GPU 成爲瀏覽器廣泛的需求。最後,Chrome 在其多進程架構上也引入了 GPU 進程。
  • 網絡進程:主要負責頁面的網絡資源加載,以前是做爲一個模塊運行在瀏覽器進程裏面的,直至最近才獨立出來,成爲一個單獨的進程。
  • 插件進程:主要是負責插件的運行,因插件易崩潰,因此須要經過插件進程來隔離,以保證插件進程崩潰不會對瀏覽器和頁面形成影響。

2.TCP三次握手四次揮手

創建TCP鏈接
  • 第一次握手:創建鏈接。客戶端發送鏈接請求報文段,將SYN位置爲1,Sequence Number爲x;而後,客戶端進入SYN_SEND狀態,等待服務器的確認;
  • 第二次握手:服務器收到SYN報文段。服務器收到客戶端的SYN報文段,須要對這個SYN報文段進行確認,設置Acknowledgment Number爲x+1(Sequence Number+1);同時,本身本身還要發送SYN請求信息,將SYN位置爲1,Sequence Number爲y;服務器端將上述全部信息放到一個報文段(即SYN+ACK報文段)中,一併發送給客戶端,此時服務器進入SYN_RECV狀態;
  • 第三次握手:客戶端收到服務器的SYN+ACK報文段。而後將Acknowledgment Number設置爲y+1,向服務器發送ACK報文段,這個報文段發送完畢之後,客戶端和服務器端都進入ESTABLISHED狀態,完成TCP三次握手。

簡言之,客戶端先向服務端發送消息,證實了客戶端的發送能力,服務端接受消息後向客戶端發送,證實了服務端的接收和發送能力,最後客戶端接收到消息向服務端告知,證實了客戶端的接受能力。
完成了三次握手,客戶端和服務器端就能夠開始傳送數據。

ACK:此標誌表示應答域有效,就是說前面所說的TCP應答號將會包含在TCP數據包中;有兩個取值:0和1,爲1的時候表示應答域有效,反之爲0。
TCP協議規定,只有ACK=1時有效,也規定鏈接創建後全部發送的報文的ACK必須爲1。
SYN(SYNchronization) : 在鏈接創建時用來同步序號。當SYN=1而ACK=0時,代表這是一個鏈接請求報文。對方若贊成創建鏈接,則應在響應報文中使SYN=1和ACK=1. 所以, SYN置1就表示這是一個鏈接請求或鏈接接受報文。
FIN (finis)即完,終結的意思, 用來釋放一個鏈接。當 FIN = 1 時,代表此報文段的發送方的數據已經發送完畢,並要求釋放鏈接。
關閉TCP鏈接
  • 第一次分手:主機1(可使客戶端,也能夠是服務器端),設置Sequence Number和Acknowledgment Number,向主機2發送一個FIN報文段;此時,主機1進入FIN_WAIT_1狀態;這表示主機1沒有數據要發送給主機2了;
  • 第二次分手:主機2收到了主機1發送的FIN報文段,向主機1回一個ACK報文段,Acknowledgment Number爲Sequence Number加1;主機1進入FIN_WAIT_2狀態;主機2告訴主機1,我「贊成」你的關閉請求;
  • 第三次分手:主機2向主機1發送FIN報文段,請求關閉鏈接,同時主機2進入LAST_ACK狀態;
  • 第四次分手:主機1收到主機2發送的FIN報文段,向主機2發送ACK報文段,而後主機1進入TIME_WAIT狀態;主機2收到主機1的ACK報文段之後,就關閉鏈接;此時,主機1等待2MSL後依然沒有收到回覆,則證實Server端已正常關閉,那好,主機1也能夠關閉鏈接了。

3.HTTPS的TCP握手

https的握手階段相比http多了一個ssl/tls協議。
該協議有這三個做用:
(1) 全部信息都是加密傳播,第三方沒法竊聽。
(2) 具備校驗機制,一旦被篡改,通訊雙方會馬上發現。
(3) 配備身份證書,防止身份被冒充。
ssl/tls協議的核心思想是採用公鑰加密。客戶端向服務器索要公鑰,利用公鑰對傳輸的信息加密,服務端收到密文後用私鑰解密。
但是這個過程會產生兩個問題
(1)如何保證公鑰不被篡改?

解決方法:將公鑰放在[數字證書](http://en.wikipedia.org/wiki/Digital_certificate)中。只要證書是可信的,公鑰就是可信的。

(2)公鑰加密計算量太大,如何減小耗用的時間?

解決方法:每一次對話(session),客戶端和服務器端都生成一個"對話密鑰"(session key),用它來加密信息。因爲"對話密鑰"是對稱加密,因此運算速度很是快,而服務器公鑰只用於加密"對話密鑰"自己,這樣就減小了加密運算的消耗時間。

所以ssl/tls協議的基本過程就是這樣的
(1) 客戶端向服務器端索要並驗證公鑰。
(2) 雙方協商生成"對話密鑰"。
(3) 雙方採用"對話密鑰"進行加密通訊。
而前兩步就是握手階段。這其中涉及四次握手,且都是明文

客戶端發出請求(ClientHello)

首先,客戶端(一般是瀏覽器)先向服務器發出加密通訊的請求,這被叫作ClientHello請求。

在這一步,客戶端主要向服務器提供如下信息。

(1) 支持的協議版本,好比TLS 1.0版。

(2) 一個客戶端生成的隨機數,稍後用於生成"對話密鑰"。

(3) 支持的加密方法,好比RSA公鑰加密。

(4) 支持的壓縮方法。

這裏須要注意的是,客戶端發送的信息之中不包括服務器的域名。也就是說,理論上服務器只能包含一個網站,不然會分不清應該向客戶端提供哪個網站的數字證書。這就是爲何一般一臺服務器只能有一張數字證書的緣由。

對於虛擬主機的用戶來講,這固然很不方便。2006年,TLS協議加入了一個Server Name Indication擴展,容許客戶端向服務器提供它所請求的域名。

服務器迴應(SeverHello)

服務器收到客戶端請求後,向客戶端發出迴應,這叫作SeverHello。服務器的迴應包含如下內容。

(1) 確認使用的加密通訊協議版本,好比TLS 1.0版本。若是瀏覽器與服務器支持的版本不一致,服務器關閉加密通訊。

(2) 一個服務器生成的隨機數,稍後用於生成"對話密鑰"。

(3) 確認使用的加密方法,好比RSA公鑰加密。

(4) 服務器證書。

除了上面這些信息,若是服務器須要確認客戶端的身份,就會再包含一項請求,要求客戶端提供"客戶端證書"。好比,金融機構每每只容許認證客戶連入本身的網絡,就會向正式客戶提供USB密鑰,裏面就包含了一張客戶端證書。

客戶端迴應

客戶端收到服務器迴應之後,首先驗證服務器證書。若是證書不是可信機構頒佈、或者證書中的域名與實際域名不一致、或者證書已通過期,就會向訪問者顯示一個警告,由其選擇是否還要繼續通訊。

若是證書沒有問題,客戶端就會從證書中取出服務器的公鑰。而後,向服務器發送下面三項信息。

(1) 一個隨機數。該隨機數用服務器公鑰加密,防止被竊聽。

(2) 編碼改變通知,表示隨後的信息都將用雙方商定的加密方法和密鑰發送。

(3) 客戶端握手結束通知,表示客戶端的握手階段已經結束。這一項同時也是前面發送的全部內容的hash值,用來供服務器校驗。

上面第一項的隨機數,是整個握手階段出現的第三個隨機數,又稱"pre-master key"。有了它之後,客戶端和服務器就同時有了三個隨機數,接着雙方就用事先商定的加密方法,各自生成本次會話所用的同一把"會話密鑰"。

至於爲何必定要用三個隨機數,來生成"會話密鑰",dog250解釋得很好:

"無論是客戶端仍是服務器,都須要隨機數,這樣生成的密鑰纔不會每次都同樣。因爲SSL協議中證書是靜態的,所以十分有必要引入一種隨機因素來保證協商出來的密鑰的隨機性。

對於RSA密鑰交換算法來講,pre-master-key自己就是一個隨機數,再加上hello消息中的隨機,三個隨機數經過一個密鑰導出器最終導出一個對稱密鑰。

pre master的存在在於SSL協議不信任每一個主機都能產生徹底隨機的隨機數,若是隨機數不隨機,那麼pre master secret就有可能被猜出來,那麼僅適用pre master secret做爲密鑰就不合適了,所以必須引入新的隨機因素,那麼客戶端和服務器加上pre master secret三個隨機數一同生成的密鑰就不容易被猜出了,一個僞隨機可能徹底不隨機,但是是三個僞隨機就十分接近隨機了,每增長一個自由度,隨機性增長的可不是一。"

此外,若是前一步,服務器要求客戶端證書,客戶端會在這一步發送證書及相關信息。

服務器的最後迴應

服務器收到客戶端的第三個隨機數pre-master key以後,計算生成本次會話所用的"會話密鑰"。而後,向客戶端最後發送下面信息。

(1)編碼改變通知,表示隨後的信息都將用雙方商定的加密方法和密鑰發送。

(2)服務器握手結束通知,表示服務器的握手階段已經結束。這一項同時也是前面發送的全部內容的hash值,用來供客戶端校驗。

至此,整個握手階段所有結束。接下來,客戶端與服務器進入加密通訊,就徹底是使用普通的HTTP協議,只不過用"會話密鑰"加密內容。

4.如何檢測頁面是否有卡頓

每秒中計算一次網頁的 FPS 值,得到一列數據,而後分析。通俗地解釋就是,經過 requestAnimationFrame API 來定時執行一些 JS 代碼,若是瀏覽器卡頓,沒法很好地保證渲染的頻率,1s 中 frame 沒法達到 60 幀,便可間接地反映瀏覽器的渲染幀率。

相關文章
相關標籤/搜索