React 技術分享
1. react 介紹 和 想法
- A declarative, efficient, and flexible JavaScript library for building user interfaces
- react是一個專注於UI的庫
- 狀態機輸入 =>元件 => 輸出
- 元件化 react元件對映 為原生android元件 原生ios 元件react vr
2. jsx 語法
JSX就是Javascript和XML結合的一種格式,要使用需要在babelrc裡配置,Babel 轉譯器會把 JSX 轉換成一個名為 React.createElement() 的方法呼叫
// 最外層必須有一個元素包裹 <div>hello world!</div> // 屬性都要駝峰 <div className="haha" ></div> // 樣式 <div style={{ marginTop: '10px'}}></div> // 變數 <div> {user.name }</div> // 事件 <button onClick={}> click me </button> // 遍歷 <ul> { list.map( ({name, value}) => <li key={name} value={value}>{name}</li> ) } </ul> // 展開物件 const props = {name: "tom" } <div {...props}></div>
3 虛擬DOM 和 diff 演算法
a. 虛擬DOM(Virtual DOM)機制:對於每一個元件,React會在記憶體中構建一個相對應的DOM樹,基於React開發時所有的DOM構造都是通過虛擬DOM進行,每當元件的狀態發生變化時,React都會重新構建整個DOM資料,然後將當前的整個DOM樹和上一次的DOM樹進行對比,得出DOM結構變化的部分(Patchs),然後將這些Patchs 再更新到真實DOM中
b. 兩個不同型別的元素會產生不同的樹(根元素不同結構樹一定不同)
c. 開發者可以通過key屬性指定不同樹中沒有發生改變的子元素
d. diff演算法的核心就是同級比較
4. state 和 props
- react 資料是單向流動的資料自頂向下流動 ,
- props是傳遞來的引數,從父級元件向子元件傳遞的資料, 是隻讀的
- state是自己的內部狀態
- 在react更新生命週期setState會導致死迴圈
// 預設引數 Main.defaultProps = { list: [ ] } // setState 是非同步的 this.setState( (prevState,props) => ({ text: prev.text + "-ha" }))
5. 生命週期
Note: 父子元件的掛載順序,更新順序
6 函數語言程式設計
- 函數語言程式設計,函式作為一等公民,而不是類和物件
- 純函式 一個函式的返回結果只依賴於它的引數,並且在執行過程裡面沒有副作用,我們就把這個函式叫做純函式
- 高階函式 函式的引數可以是函式,返回值也可以是函式, 如陣列的
forEach map filter some reduce ,setTimeout setInterval等
- 柯里化是指這樣一個函式(假設叫做createCurry),他接收函式A作為引數,執行後能夠返回一個新的函式。並且這個新的函式能夠處理函式A的剩餘引數
6 無狀態元件
顧名思義,它沒有state,接收props,它是一種只負責展示的純元件 使用函式式的方式宣告,會使得程式碼的可讀性更好,並能大大減少程式碼量,多寫無狀態元件
function HelloComponent(props) { return <div>Hello {props.name}</div> } ReactDOM.render(<HelloComponent name="marlon" />, mountNode)
7 使用 fragment 碎片
Jsx 要求返回的元素,必須有東西包裹,使用碎片可以減少一層DOM
<React.Fragment> <td>Hello</td> <td>World</td> </React.Fragment> // 可以簡寫為 <> <td>Hello</td> <td>World</td> </>
8 使用context
Context 通過元件樹提供了一個傳遞資料的方法,從而避免了在每一個層級手動的傳遞 props 屬性,React.createContext 建立一個context,一個context包含provider(資料提供者), comsumer(資料消費者), Provider 有一個value引數,用來覆蓋建立時的預設值
// 建立一個 theme Context,預設 theme 的值為 light const ThemeContext = React.createContext ('light'); function ThemedButton(props) { // ThemedButton 元件從 context 接收 theme return ( <ThemeContext.Consumer> {theme => <Button {...props} theme={theme} />} </ThemeContext.Consumer> ); } // 中間元件 function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } class App extends React.Component { render() { return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } }
9.mobx
推薦一個react模組的目錄結構
/states 狀態 /(widgets|components)各類元件、彈窗等 index.js index.less service 建議放在config裡,全域性統一 Computed用於生成計算資料,autorun類似用來監聽某一資料改變而作出反映 import {action, observable ,autorun} from 'mobx'; import StateAdd from "./add" class State { constructor(){ this.$add = new StateAdd(this); } // 顯示 @observable show = false; // query @observable query = { trade: null, type: 1 } /** * @name獲取列表 */ @action getList = () => { } } const instance = new State(); autorun(() => { if(instance.show){ console.log("開啟彈窗了") } }) export default instance
10 高階元件
主要有兩種實現方式屬性proxy、繼承實現
通常使用裝飾器模式呼叫,常見的有mobx的 @observer, react-router的withRouter等
------屬性proxy 實現如下 import React from "react" export default class extends React.Component { render = () => <div> <div> <Hoc1_text name={'Hoc1_text'}/> </div> <div> <Hoc2_text /> </div> </div> } class Text1 extends React.Component { render = () => <div> this is simple text <br/> {this.props.name} </div> } /** * @name 操作props */ function Hoc1(Component){ return class extends React.Component { render = () => <div className='hoc1'> <Component {...Object.assign({} , this.props, {gen : '111'})}/> </div> } } const Hoc1_text = Hoc1(Text1); class Text2 extends React.Component { render = () => <div> <input type="text" value={this.props.value} onChange={this.props.change}/> </div> } /** *@name 抽離state */ function Hoc2(Component){ return class extends React.Component { state = { value: '11' } change = e => { this.setState({ value: e.target.value }) } render = () => <div> <Component value={this.state.value} change={this.change}/> </div> } } const Hoc2_text = Hoc2(Text2); function Hoc3(Component){ return class extends React.Component { getRef = _ref => { this.refs = _ref; } render = () => <div> <Component ref={this.getRef}/> </div> } } ------繼承元件 實現如下 ------- class Text extends React.Component { render(){ return ( <div> this is text </div> ) } } // 生命週期可以覆蓋掉 const hoc2 = Component => { return class NewHoc extends Component { static displayName = `NewHoc-${Component.displayName || Component.name}` render(){ const element= super.render(); console.log(element) const style = { color: element.type === 'div' ? 'red' : 'green' } const newProps = {...this.props, style} return React.cloneElement(element, newProps, element.props.children) } } } const NewCom = hoc2(Text)
11 注意事項
- React元件名字大寫
- 不能直接修改state props
- 更新函式裡不能做狀態操作(死迴圈)
- 多用無狀態元件
- 多用pureComponent React.PureComponent 通過prop和state的淺對比來實現 shouldComponentUpate()。
- 程式碼清晰,一目瞭然,註釋寫好
- 過度封裝不如不封裝
- 使用 ImmutableJS 處理資料提升效能