非dom屬性?
dangerouslySetInnerHTML,ref,key
非dom標準屬性,也就是說dom標準裡面沒有規定的屬性,react引入了三個非dom屬性,如上。
dangerouslySetInnerHTML:字面意思,危險的設定內部html,這個屬性的作用就是在jsx中,直接插入html程式碼。我們為什麼要用這個屬性插入html程式碼呢?而不是在寫程式碼的時候直接寫入呢?因為有的時候我們在寫程式碼的時候,無法確實要插入什麼程式碼,也就是說這部分html程式碼是動態產生的。或者說不是由我們來寫的,為什麼這中行為是危險的呢?有一個詞語叫做跨站攻擊,之所以會產生跨站攻擊,就是因為有這樣的直接寫入代碼功能,我們舉個例子,如果頁面上要顯示一個內容,這個內容來自於使用者的輸入,假設使用者的輸入包含了程式碼,比如說js程式碼,html程式碼,如果我們不經過偵測就直接使用,把這個dom插入到頁面中,那麼其他人訪問頁面的時候,就會執行這個人寫入的代碼,由於我們無法判斷這個用戶的意圖,所以他有可能寫入很危險的代碼,比如說添加一個連接,連接到一個木馬,那麼造訪這個頁面的用戶,就會在不知不覺中中病毒或下載木馬,這是非常危險的,但是有些時候我們確實需要動態的寫入程式碼,所以react還是提供了這個屬性,只是明確的在名字裡面告訴你這個屬性是很危險的,盡量不要去使用。
ref:reference的縮寫,父組件引用子組件,我們在編寫組件的時候,經常會用到嵌套的情況,父組件如果嵌套了子組件,父組件引用子組件的時候,就需要用ref,在實際的使用中ref其實是在父組件中維護了很多引用,每一個引用都引用到了一個對應的子組件,這樣我們在父組件中就可以通過這些引用來操作子組件。為什麼我們不能操作父元件的父元件呢?這個其實不是程式碼問題,而是設計問題,在react中我們使用元件的目的就是讓程式碼模組化,每個元件只需要考慮自己自身的功能,邏輯,不需要在乎誰在使用它,因此元件不需要引用他本身的父元件,父元件和元件之間的互動透過屬性的傳遞來實現的,我們後面會說。但是這樣的傳遞是單向的,也就是說,屬性總是由上往下傳遞,下面的元件不會對上面的元件進行控制,這是為了讓邏輯更加清晰,
key:提高渲染效能。 react特點之一就是效能好,這是因為他去除了手動的dom操作,完全由自動來實現,但是自動實現就說明他有一套演算法,也就說在頁面發生變化的時候,react需要應用這套演算法來判斷如何有效率的修改頁面,以反映出對應的變化,這個演算法通常被稱為diff,也就是different的縮寫,表示計算兩個狀態之間的差異。
react diff演算法
react diff演算法的流程圖
首先需要明確我們現在進行比較的是兩個元件,react中所有的東西都是元件,所以我們只要明白了兩個元件之間是如何比較的,其他的結構都可以分解成組件進行比較。
最左邊是開始,開始以後第一個判斷就是節點是否相同,相同的意思是div和div是相同的,div和p就是不同的,或者說我們自訂的HelloMessage和HelloMessage是相同的,HellowMessage和HelloWorld就不是相同的元件,節點如果相同才會進行後面的比較,如果節點不同,react會直接拋棄舊的節點,產生一個全新的節點,在這裡react是基於一個假設,如果兩個節點不同,那麼他們的內容很大程度不同。如果節點不同我們就直接結束比較,產生全新的節點,如果節點相同,下一步需要判斷的是,這個幾點是自訂的節點還是dom的標準節點,也就是說,他是div還是HelloWorld,如果說不是自訂的節點,是標準的節點,那麼react下一步要做的就是比較兩個節點的屬性,比如說class,id等,如果屬性完全相同,就表示兩個節點是相同的,結束比較;如果屬性不同,就把不同的屬性記下來,並且應用改動,比如說多了一個新屬性,就加入一個新屬性,少了一個屬性就刪掉一個屬性,類似這樣的操作。也就是react並不會刪掉舊的節點,只會在他上面操作。若果是自訂的節點,react會重新渲染他,我們後面會學習屬性和狀態,一個元件有很多狀態,如果是同一個自訂元件,那麼新元件可能只是舊元件的一個狀態,所說reac會把新的狀態傳入到舊的元件中,然後比較元件的渲染結果,並進行改動,也就是說react仍然不會重新產生元件,而是在舊的元件上進行改動,這就是diff演算法的整個流程。那麼key到底有什麼用呢? key作用主要體現在節點的比較,假設我們有一個類似列表的節點,也就是父節點裡面有多個子節點,那麼如果沒有key的時候,我們進行改動,react會比較傻的從左到右進行比較,比如說在改動之前只有節點1,在改動之後我們插入一個節點2,變成了節點2,節點1,那麼react執行的操作是,刪除節點1,添加節點2,添加節點1,也就是說,react無法判斷新狀態中的節點1是不是就狀態中的節點1,所以他只能把不同的全部刪掉,即使他們可能相同,這樣就導致性能上的問題,那麼引入key的目的就是給每個節點加上唯一的標識,這樣的話react透過比較key,就可以知道到底哪些節點是原有節點,哪些節點是新加入的節點,針對我們剛才說的例子, 節點1變成了節點2節點1,這時候,react只需要做一個操作,就是插入節點2,因為節點1的key是一樣的,說明他們兩個是同一個節點,沒有變化。
了解這個原理,對於我們寫react元件有什麼啟示呢?
第一點就是如果兩個組件非常類似,那就盡量把他們寫成一個組件,因為我們在流程中看到,兩個不相同的組件一定會被重新生成,即使他們的內容非常的相似,。第二個啟示就是如果使用類似的列表來展示元素的話,那麼元素盡量加上key,這樣可以大大提高效率,避免不必要的效能損耗。
如何使用非dom屬性?
dangerously的實例
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>hello world</title> </head> <body> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/react.js"></script> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/JSXTransformer.js"></script> <script type="text/jsx"> var style={ color:"red", border:"1px solid #f09", }; var rawHTML={ __html:"<h1>I am inner HTML</h1>" }; var HelloWorld=React.createClass({ render: function(){ return <p>Hello,world</p> } }); React.render(<div style={style} dangerouslySetInnerHTML={rawHTML}></div>,document.body); </script> </body> </html>
ref實例,要注意透過引用拿到的並不是這個dom節點本身,也就說我們並不能進行dom之間的操作,比如說設定文本,這樣是不行的,我們拿到的只是一個虛擬的dom節點,也就是react展示給我們的dom節點,如果想要拿到真正的dom節點,還需要呼叫一個方法,後面我們會說,不過react並不會鼓勵我們這樣做,除非在特殊的情況下,我們需要操作dom節點,其他情況react會幫助我我們進行操作,
這個例子不全,後面我們會繼續講解。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>hello world</title> </head> <body> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/react.js"></script> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/JSXTransformer.js"></script> <script type="text/jsx"> var style={ color:"red", border:"1px solid #f09", }; var rawHTML={ __html:"<h1>I am inner HTML</h1>" }; var HelloWorld=React.createClass({ render: function(){ this.refs.childp return <p ref="childp">Hello,world</p> } }); React.render(<div style={style} dangerouslySetInnerHTML={rawHTML}></div>,document.body); </script> </body> </html>
key實例:注意在每個組件的內部,key的值必須是不同的,注意是組件內部!兩個組件之間就沒有這個限制了。
記住兩點:1,內容類似的元件盡量合併稱為同一個元件,2列表類型的元素,一定要加上唯一的key,做到這兩點,就能避免很多效能問題。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>hello world</title> </head> <body> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/react.js"></script> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/JSXTransformer.js"></script> <script type="text/jsx"> var style={ color:"red", border:"1px solid #f09", }; var rawHTML={ __html:"<h1>I am inner HTML</h1>" }; var HelloWorld=React.createClass({ render: function(){ return <ul> <li key="1">1</li> <li key="2">2</li> <li key="3">3</li> </ul> } }); React.render(<div style={style} dangerouslySetInnerHTML={rawHTML}></div>,document.body); </script> </body> </html>