概況: 通過本篇文章你可以對react的重點有個整體的認識。 關於react是什麼,優點,解決什麼問題等,網上一大推就不啰嗦了。 瞭解虛擬DOM的實現,參考這篇文章 簡單講,其實就是用一個輕量級的dom結構(用js模擬實現),來模擬重量級的dom結構,通過比對輕量級結構後,在操作重量級dom結構提高 ...
概況:
通過本篇文章你可以對react的重點有個整體的認識。 關於react是什麼,優點,解決什麼問題等,網上一大推就不啰嗦了。
瞭解虛擬DOM的實現,參考這篇文章
[虛擬DOM]
(https://www.zhihu.com/question/29504639)
簡單講,其實就是用一個輕量級的dom結構(用js模擬實現),來模擬重量級的dom結構,通過比對輕量級結構後,在操作重量級dom結構提高性能,從而到達性能優化的目的。
生命周期:
快速學習react 先瞭解它的重中之重----生命周期, 一個組件在不同時期會調用不同時期的函數介面也就是對應的生命周期函數
裝載時期的函數
getDefaultProps(是設置預設props)getInitialState(廢棄,設置預設State) 依次執行以下函數
• constructor
• componentWillMount
• render
• componentDidMount
更新時期的函數
如果組件的數據有變化了(porp,state), 依次執行以下函數
• componentWillReceiveProps
• shouldComponentUpdate
• componentWillUpdate
• render
• componentDidUpdate
卸載時期的函數
銷毀組件
•componentWillUnmount
1. import React,{ Component } from 'react'; 2. class Demo extends Component { 3. constructor(props) { 4. // 構造函數,要創造一個組件類的實例,會調用對應的構造函數, 5. //一個react組件需要構造函數,往往為了兩個目的. 6. //1:初始化state.2.綁定成員函數this環境。 7. // 無狀態的函數就不需要構造函數, 8. super(props) 9. console.log("---初始化組件---") 10. this.state = { 11. test:'想要顯示一段不一樣的文字' 12. //定義state,存放頁面的數據,通過this.setState()方法修改 13. //.this.setState()函數所做的事情,首先是改變this.state的值,然後驅動組件經歷更新過程,這樣才有機會讓this.state里新的值出現在界面上。 14. } 15. } 16. componentWillMount () { 17. console.log("---組件掛載前---最先執行的函數") 18. } 19. componentDidMount () { 20. console.log("---組件掛載後---") 21. } 22. componentWillReceiveProps (nextProps) { 23. console.log("---父組件重新渲染---") 24. 值得註意的是,更新時期的componentWillReceiveProps函數, 25. 只要父組件的render函數被調用,在render函數裡面被渲染的子組件就會經歷更新過程,不管父組件傳給子組件的Props有沒有改變,都會觸發子組件的componentWillReceiveProps函數,但是自身的this.setState方法觸發的更新過程不會調用這個函數。 26. } 27. shouldComponentUpdate (nextProps,nextState) { 28. console.log("---組件接受到重繪狀態---") 29. 它決定了一個組件什麼時候不渲染。 30. 在更新過程中shouldComponemtUpdata 返回 false那就立刻停止更新。 31. this.setState函數後會執行shouldComponemtUpdata 然後在決定我要不要更新。 32. 相反 shouldComponemtUpdata 函數返回 TRUE,接下來就會依次調用 33. componentWillUpdata,render,componetDidUpdata函數,它把render像夾心麵包似得夾在了中間。 34. } 35. componentWillUpdate (nextProps,nextState) { 36. console.log("---組件將要更新---") 37. } 38. componentDidUpdate (prevProps,prevState) { 39. console.log("---組件更新完畢---") 40. } 41. render () { 42. console.log("---組件渲染---") 43. return ( 44. <div>{this.state.test}</div> 45. ) 46. } 47. componentWillUnmount () { 48. console.log("---組件銷毀---") 49. } 50. } 51. export default Demo;
componentWillMount 和componentDidMount的區別:componentWillMount 可以在伺服器調用,也可以在瀏覽器調用但是componentDidMount只能在瀏覽器被調用,因為裝載是一個組件放到DOM樹上的過程,那麼真正的裝載是不可能在伺服器上完成的,伺服器的渲染並不會產生DOM樹。所以我們可以利用這一點。在componentDidMount被調用時候,組件已經被裝載到DOM樹上了,可放心的去操作渲染出來的任何DOM。
編寫組件:
組件間的傳遞通過props進行傳遞,看下麵例子
import React from 'react'; // 一級父組件 class Level1 extends React.Component{ render(){ return <Level2 color='red'/> } } // 二級子組件 class Level2 extends React.Component{ render(){ return <Level3 color={this.props.color}/> } } // 三級孫子組件 class Level3 extends React.Component{ render(){ return <div color={{color: this.props.color}}/> } }
也可以這樣創建
-
import React from 'react'; const Level1 = React.createClass({ render() { return ( <div></div> ); } }); export default Level1 ;
React.createClass和extends Component的區別:
Component{}是ES6的寫法,會自動繼承Component裡面的屬性 createClass({})是React的ES5寫法,會生成一個React Component 語法區別
• propType 和 getDefaultProps
• 狀態的區別
• this區別
• Mixins
參考這篇文章
[React.createClass和extends Component的區別]
(https://segmentfault.com/a/1190000005863630)
如果你的組件是無狀態的,純輸出組件也可以直接寫成函數如下
-
function Pure(props){ return( <div>{props.xxxx}</div> ) }
react 組件必須一級一級傳遞 ,如果想要越級傳遞,1直接到5,那麼需要用到redux
redux
redux之前最好瞭解下flux,但是redux更加優秀。 react和redux事實上是兩個獨立的東西,如果你兩者單獨使用推薦react-redux庫,我們從redux 使用方法開始,循序漸進過渡到react-redux,這個庫可以讓我們簡化代碼的書寫
在redux框架下,一個react組件是這樣運行的
讀取Store的狀態,用於初始化組件的狀態,同時監聽Store的狀態改變,當Store狀態發生改變時候,就需要更新組件的狀態,從而驅動渲染。當需要更新store狀態時,就要派發action對象。 根據當前props,和state,渲染用戶界面。
項目結構
actions--->用戶行為 components--->組件 containers--->容器 reducer--->一個純函數返回新的state狀態 store--> store裡面負責分發action行為 index.html ---> 模板文件 webpack---> 打包配置文件
actions:
• 是一個行為的抽象
• 是普通JS對象
• 一般由方法生成
• 必須有一個type 我要添加一本書這個行為可以如下:
-
const addTodo = (text) =>{ retrun { type:'Add', id: nextTodoId++, text, } }
reducer:
• 是響應的抽象
• 是純方法
• 傳入舊的狀態和action
• 返回新的狀態
簽名函數:reducer(state, action) state 是當前狀態,action是接受到的action, 註意不能改變參數state和action
-
const todo = (state, action) =>{ switch (action.type){ case "Add_Book": return { text: action.text, } }
用一個例子串起來:
設計一個具有加減功能的項目:
Actions.js:
export const increment = (counterCaption) => { return { type: increment, counterCaption: counterCaption }; }; export const decrement = (counterCaption) => { return { type: decrement, counterCaption,//es6寫法等同於counterCaption: counterCaption }; }; Reducer.js: export default (state, action) => { const {counterCaption} = action;//等同於const counterCaption= action.counterCaption; switch (action.type) { case increment: return {...state, [counterCaption]: state[counterCaption] + 1}; case decrement: return {...state, [counterCaption]: state[counterCaption] - 1}; default: return state } } //return {...state, [counterCaption]: state[counterCaption] - 1};等同於 //const newState = Object.assign({},state); //newState[counterCaption]--; //return newState;
Store.js:
import {createStore} from 'redux'; import reducer from './Reducer.js'; const initValues = { 'First': 0, 'Second': 10, 'Third': 20 }; const store = createStore(reducer, initValues); export default store; //createStore是redux庫提供的函數第一個參數是更新狀態的reducer,第二參數是初始值
views(容器):
import React, { Component } from 'react'; import Counter from './Counter.js'; class ControlPanel extends Component { render() { return ( <div> <Counter caption="First" /> <Counter caption="Second" /> <Counter caption="Third" /> </div> ); } } export default ControlPanel;
Counter.js(組件):
import React, { Component, PropTypes } from 'react'; import store from '../Store.js'; import * as Actions from '../Actions.js'; const buttonStyle = { margin: '10px' }; class Counter extends Component { render() { const {caption, onIncrement, onDecrement, value} = this.props; return ( <div> <button style={buttonStyle} onClick={onIncrement}>+</button> <button style={buttonStyle} onClick={onDecrement}>-</button> <span>{caption} count: {value}</span> </div> ); } } //以下是對參數類型的定義,開啟eslint需要寫一下代碼。 Counter.propTypes = { caption: PropTypes.string.isRequired,//表示caption是string類型,必填 onIncrement: PropTypes.func.isRequired, onDecrement: PropTypes.func.isRequired, value: PropTypes.number.isRequired }; class CounterContainer extends Component { constructor(props) { super(props); this.onIncrement = this.onIncrement.bind(this); this.onDecrement = this.onDecrement.bind(this); this.onChange = this.onChange.bind(this); this.getOwnState = this.getOwnState.bind(this); this.state = this.getOwnState(); } getOwnState() { return { value: store.getState()[this.props.caption] }; } onIncrement() { store.dispatch(Actions.increment(this.props.caption)); } onDecrement() { store.dispatch(Actions.decrement(this.props.caption)); } onChange() { //為了保持Store上的狀態和this.state的同步 this.setState(this.getOwnState()); } shouldComponentUpdate(nextProps, nextState) { return (nextProps.caption !== this.props.caption) || (nextState.value !== this.state.value); } componentDidMount() { //為了保持Store上的狀態和this.state的同步 store.subscribe(this.onChange); } componentWillUnmount() { //為了保持Store上的狀態和this.state的同步 store.unsubscribe(this.onChange); } render() { //Counter 在上面 return <Counter caption={this.props.caption} onIncrement={this.onIncrement} onDecrement={this.onDecrement} value={this.state.value} /> } } CounterContainer.propTypes = { caption: PropTypes.string.isRequired }; export default CounterContainer;
通常我們會把容器放在container文件夾下,把組件放在component下
ControlPanel 根本就沒有使用store,如果僅僅為了傳遞prop給組件counter就要求支持state prop,顯然不合理,其中react提供了Context的功能可以解決這個問題;
Context:
我們增加Provider.js,代碼如下:
import {PropTypes, Component} from 'react'; class Provider extends Component { getChildContext() { return { store: this.props.store }; } render() { return this.props.children; //Provider包裹的子元素輸出出來 } } Provider.contextTypes = { store: PropTypes.object } export default Provider;
index.js 文件引入Provider
import React from 'react'; import ReactDOM from 'react-dom'; import ControlPanel from './views/ControlPanel'; import store from './Store.js'; import Provider from './Provider.js'; ReactDOM.render( <Provider store={store}> <ControlPanel /> </Provider>, document.getElementById('root') );
最後我們在修改ControlPanel中的Counter組件,
React-Redux:
如果理解上面的例子之後你會發現有些復用的部分可以提取出來,各個組件關心自己的部分就行了,react-redux庫就是解決這個事情的,讓你開發爽到飛起
react-redux 規定,所有的 UI 組件都由用戶提供,容器組件則是由 React-Redux 自動生成。也就是說,用戶負責視覺層,狀態管理則是全部交給它。
首先我們先瞭解一下重要的函數connect,React-Redux 提供connect方法,用於從 UI 組件生成容器組件,就是將這兩種組件連起來。 connect方法的完整 API
import { connect } from 'react-redux' const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList)
connect方法接受兩個參數:mapStateToProps和mapDispatchToProps。它們定義了 UI 組件的業務邏輯。前者負責輸入邏輯,即將state映射到 UI 組件的參數(props),後者負責輸出邏輯,即將用戶對 UI 組件的操作映射成 Action。
此時index.js文件變成:
import React from 'react'; import ReactDOM from 'react-dom'; import {Provider} from 'react-redux'; import ControlPanel from './views/ControlPanel'; import store from './Store.js'; ReactDOM.render( <Provider store={store}> <ControlPanel/> </Provider>, document.getElementById('root') ); //Provider在根組件外麵包了一層,這樣一來,App的所有子組件就預設都可以拿到state了
Counter.js文件變成
import React, { PropTypes } from 'react'; import * as Actions from '../Actions.js'; import {connect} from 'react-redux'; const buttonStyle = { margin: '10px' }; function Counter({caption, onIncrement, onDecrement, value}) { return ( <div> <button style={buttonStyle} onClick={onIncrement}>+</button> <button style={buttonStyle} onClick={onDecrement}>-</button> <span>{caption} count: {value}</span> </div> ); } Counter.propTypes = { caption: PropTypes.string.isRequired, onIncrement: PropTypes.func.isRequired, onDecrement: PropTypes.func.isRequired, value: PropTypes.number.isRequired }; function mapStateToProps(state, ownProps) { return { value: state[ownProps.caption] } } function mapDispatchToProps(dispatch, ownProps) { return { onIncrement: () => { dispatch(Actions.increment(ownProps.caption)); }, onDecrement: () => { dispatch(Actions.decrement(ownProps.caption)); } } } export default connect(mapStateToProps, mapDispatchToProps)(Counter);
connect函數實際上是個高階函數,瞭解可以參考這邊文章
[Higher-Order Components](https://reactjs.org/docs/higher-order-comonents.html)
關於react 路由可以參考這邊文章
[路由](http://www.ruanyifeng.com/blog/2016/05/react_router.html?utm_source=tool.lu)