key幫助React識別哪些項目已更改,已添加或已刪除。也所以能夠高效的更新虛擬DOM列表,key值是用來判斷 VDOM 元素項的惟一依據css
在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> } }
在 React 中,咱們一般有一個組件樹。若是任何一個組件發生錯誤,它將破壞整個組件樹。沒有辦法捕捉這些錯誤,咱們能夠用錯誤邊界優雅地處理這些錯誤。node
錯誤邊界有兩個做用react
下面是ErrorBoundary
類的一個例子。若是類實現了 getDerivedStateFromError
或componentDidCatch
這兩個生命週期方法的任何一下,,那麼這個類就會成爲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; } }
super
並將props
做爲參數傳入的做用是啥?在調用super()
方法以前,子類構造函數沒法使用this
引用,ES6 子類也是如此。將props
參數傳遞給super()
調用的主要緣由是在子構造函數中可以經過this.props
來獲取傳入的props
。webpack
傳遞 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 } } }
查看正則表達式
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功能。
loader中採起compose的洋蔥模型,內部採用reduceRight實現,所以是後寫的先執行
function compose(...loaders){ return function(arg){ return loaders.reduceRight((res, cur) => { return cur(res) }, arg) } }
UglifyES
壓縮ES6
代碼loader
。 因此Loader的做用是讓webpack擁有了加載和解析非js文件的能力。module.rules
中配置,也就是說他做爲模塊的解析規則而存在。 類型爲數組,每一項都是一個Object
,裏面描述了對於什麼類型的文件(test
),使用什麼加載(loader
)和使用的參數(options
)plugins
中單獨配置。 類型爲數組,每一項是一個plugin
的實例,參數都經過構造函數傳入。因爲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默認值是['node_modules'],webpack查找模塊的機制和nodejs很相似,先從當前目錄的node_modules下去找,找不到再去上一級../node_modules,以此類推。前端大部分項目node_modules都是在根目錄,所以直接配置以下方式能夠減小查找
module.exports = { resolve: { modules: [path.resolve( __dirname,'node modules')] }, }
經過以下配置使得沒有模塊化的第三方庫無需被解析
module.exports = { module: { noParse: /jquery/, } };
因爲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 }) ]
webpack提供的UglifyJS只能單線程的一個個壓縮js文件(壓縮JS代碼須要先把代碼解析成用Object抽象表示的AST語法樹,再去應用各類規則分析和處理AST,致使這個過程耗時很是大),而經過使用ParallelUglifyPlugin能夠開啓多個子進程並行使用UglifyJS壓縮每一個任務
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 } }
Webpack 的運行流程是一個串行的過程,從啓動到結束會依次執行如下流程:
在以上過程當中,Webpack 會在特定的時間點廣播出特定的事件,插件在監聽到感興趣的事件後會執行特定的邏輯,而且插件能夠調用 Webpack 提供的 API 改變 Webpack 的運行結果。
瀏覽器從關閉狀態進行啓動,而後新開 1 個頁面至少須要 1 個網絡進程、1 個瀏覽器進程、1 個 GPU 進程以及 1 個渲染進程,共 4 個進程;後續再新開標籤頁,瀏覽器、網絡進程、GPU進程是共享的,不會從新啓動,若是2個頁面屬於同一站點的話,而且從a頁面中打開的b頁面,那麼他們也會共用一個渲染進程,不然新開一個渲染進程。
最新的 Chrome 瀏覽器包括:1 個瀏覽器(Browser)主進程、1 個 GPU 進程、1 個網絡(NetWork)進程、多個渲染進程和多個插件進程。
簡言之,客戶端先向服務端發送消息,證實了客戶端的發送能力,服務端接受消息後向客戶端發送,證實了服務端的接收和發送能力,最後客戶端接收到消息向服務端告知,證實了客戶端的接受能力。
完成了三次握手,客戶端和服務器端就能夠開始傳送數據。
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 時,代表此報文段的發送方的數據已經發送完畢,並要求釋放鏈接。
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請求。
在這一步,客戶端主要向服務器提供如下信息。
(1) 支持的協議版本,好比TLS 1.0版。(2) 一個客戶端生成的隨機數,稍後用於生成"對話密鑰"。
(3) 支持的加密方法,好比RSA公鑰加密。
(4) 支持的壓縮方法。
這裏須要注意的是,客戶端發送的信息之中不包括服務器的域名。也就是說,理論上服務器只能包含一個網站,不然會分不清應該向客戶端提供哪個網站的數字證書。這就是爲何一般一臺服務器只能有一張數字證書的緣由。
對於虛擬主機的用戶來講,這固然很不方便。2006年,TLS協議加入了一個Server Name Indication擴展,容許客戶端向服務器提供它所請求的域名。
服務器收到客戶端請求後,向客戶端發出迴應,這叫作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協議,只不過用"會話密鑰"加密內容。
每秒中計算一次網頁的 FPS 值,得到一列數據,而後分析。通俗地解釋就是,經過 requestAnimationFrame API 來定時執行一些 JS 代碼,若是瀏覽器卡頓,沒法很好地保證渲染的頻率,1s 中 frame 沒法達到 60 幀,便可間接地反映瀏覽器的渲染幀率。