一、真實DOM和虛擬DOM|缺點和優點|子組件向父組件傳值|React組件3個部分|ref可以設置回調函數|為什么不能用index做key|狀態提聲|跨多級組件傳參原理 1、真實DOM和虛擬DOM (1)真實DOM,用戶每次操作DOM(文檔對象模型),都會引起BOM(瀏覽器對象模型)重新渲染 (2)虛擬DOM,用戶每次操作DOM(文檔對象模型),都會改變虛擬DOM(堆內存的一個對象),最后一次性地渲染到BOM上。 2、React的缺點和優點 (1)缺點: (A)React本身只是一個V,不是一個完整的框架,不是一套完整的框架 (B)需要加上React-Router和React-Redux才能成為一套完整的框架 (2)優點: (A)單向數據流動; (B)虛擬DOM取代物理DOM作為操作對象; (C)用JSX語法取代HTML模板,在JavaScript里聲明式地描述UI。 3、react子組件向父組件傳值 (1)react父組件向子組件傳值:屬性傳參 (2)react子組件向父組件傳值:在父組件定義一個函數並綁定父組件的this,在函數體里,將函數的參數賦值給父組件this的一個屬性;通過屬性傳參的方式把這個函數傳給子組件。在子組件里,給這個函數傳參並執行,實現子組件向父組件傳值! 4、React組件3個部分 (1)React組件基本上由3個部分組成——屬性(props)、狀態(state)以及生命周期方法 (2)React組件一旦接收到的參數(即props)或自身狀態有所改變,React組件就會執行相應的生命周期方法,最后渲染。 (3)整個過程完全符合傳統組件所定義的組件職責(“屬性更新”與“狀態改變”)。 (4)以上內容來自《深入React技術棧》第18和30頁。 5、ref可以設置回調函數 (1)<input type="text" ref="myInput"/> this.refs.myInput.value ="22"; //this.$refs.myInput.value ="22" 減少獲取dom節點的消耗 (2)ref屬性可以設置為一個回調函數,這也是官方強烈推薦的用法;這個函數執行的時機為: (3)組件被掛載后,回調函數被立即執行,回調函數的參數為該組件的具體實例。 (4)組件被卸載或者原有的ref屬性本身發生變化時,回調也會被立即執行,此時回調函數參數為null,以確保內存泄露。 6、為什么不能用index做key? (1)react會根據key來決定是否重新構建該組件 (2)刪除和添加操作,會使一個組件使用另一個的組件的index,進而key,進而data 7、狀態提聲(父組件的函數作為屬性傳給子組件) (1)在父組件的constructor中定義狀態 (2)在父組件的方法中執行this.setState({}) (3)把父組件的方法作為屬性fromParent傳給子組件 (4)在子組件的方法中加上this.props.fromParent(e.target.value); (5)觸發子組件的事件,執行子組件的方法,改變父組件的狀態 8、跨多級組件傳參原理 //祖父組件聲明自己支持 context static childContextTypes = { color:PropTypes.string, callback:PropTypes.func, } //祖父組件提供一個函數,用來返回相應的 context 對象 getChildContext(){ return{ color:"red", callback:this.callback.bind(this) } } //孫子組件聲明自己需要使用 context static contextTypes = { color:PropTypes.string, callback:PropTypes.func, } 二、約束性組件和非約束性組件(受控組件和非受控組件) 1、約束性組件 <input type="text" value={this.state.name} onChange={this.handleChange} /> handleChange: function(e) { this.setState({name: e.target.value}); } //用戶輸入內容A>觸發onChange事件>handleChange中設置state.name="A"渲染input使他的value變成A 2、非約束性組件 <input type="text" defaultValue="a" />//用戶輸入A -> input 中顯示A 3、React 把 input,textarea 和 select 三個組件做了抽象和封裝,他們有統一的 value 屬性 和 onChange 事件。 <input type='text' name='intro' id='intro' value={this.state.email} onChange={this.handleEmail} /> <textarea type='text' name='intro' id='intro' value={this.state.intro} onChange={this.handleIntro} /> <textarea type='text' name='intro' id='intro' value={this.state.intro} onChange={this.handleIntro} /> 4、checkbox改變的不是 value ,而是 checked 狀態。 <input type='radio' name='gender' checked={this.state.male==='MALE'} onChange={this.handleGender} value='MALE' /> <input type='radio' name='gender' checked={this.state.male==='FEMALE'} onChange={this.handleGender} value='FEMALE' /> handleGender(e){ this.setState({ male:e.target.value }) } 三、setState 1、this.setState接收兩種參數 (1)對象+函數(可選):傳入的對象淺層合並到新的state中 (2)函數+函數(可選):第一個函數接受兩個參數,第一個是當前state,第二個是當前props,該函數返回一個對象,和直接傳遞對象參數是一樣的,就是要修改的state;第二個函數參數是state改變后觸發的回調。 2、this.setState何時同步何時異步? (1)異步,由react控制的事件處理程序 (2)同步,react控制之外的事件中調用setState是同步更新的。比如原生js綁定的事件,setTimeout/setInterval等。 (3)大部分開發中用到的都是react封裝的事件,比如onChange、onClick、onTouchMove等,這些事件處理程序中的setState都是異步處理的。 3、this.setState何時渲染 (1)死循環:this.setState執行時,會根據_compositeLifeCycleState是否為null,來決定是否重新渲染,因此在有的生命周期里,會產生死循環。 (2)只生效一次:this.setState最后執行隊列時,先用變量獲取並存儲state,后來直接修改變量,最后把變量賦值給state,頁面渲染。 handleClick() { //const count = this.state.count 下面 this.setState 多次執行,但只生效一次。因為似乎存在此行代碼。 this.setState({ count: count + 1 }) this.setState({ count: count + 1 }) } 四、React生命周期 1、React15、React16完整生命周期比較 A、實例化 (1)React15之ES5寫法:propTypes = {}; getDefaultProps(){return{}}; //React16之ES6寫法:static propTypes = {}; static defaultProps = {}; (2)React15之ES5寫法:getInitialState(){return{}}; //React16之ES6寫法:constructor (3)React15之ES5寫法:componentWillMount//執行setState會合並到初始化狀態中;獲取從屬性生成的狀態;此后生命狀態會被重置為null; //React16之ES6寫法:static getDerivedStateFromProps//執行setState會合並到初始化狀態中;獲取從屬性生成的狀態 (4)render//執行setState會發起updateComponent導致-死循環 (5)componentDidMount//執行setState會導致更新;這是發起異步請求去API獲取數據的絕佳時期 B、存在期 (1)React15之ES5寫法:componentWillReceiveProps//執行setState會合並到狀態中;此后生命狀態會被重置為null //React16之ES6寫法:static getDerivedStateFromProps//執行setState會合並到狀態中; (2)shouldComponentUpdate//執行setState會發起updateComponent導致-死循環 (3)React15之ES5寫法:componentWillUpdate//執行setState會發起updateComponent導致-死循環 //React16之ES6寫法:此處沒有componentWillUpdate (4)render//執行setState會發起updateComponent導致-死循環 //React16之ES6寫法:此處新增getSnapshotBeforeUpdate//執行setState會發起updateComponent導致-死循環 (5)componentDidUpdate//可以有條件地執行setState C、銷毀期 (1)componentWillUnmount//等待頁面卸載,改變state沒意義。 2、React16生命周期(過渡) (1)沿用了3個componentWillMount,componentWillReceiveProps,componentWillUpdate (2)新增了2個getDerivedStateFromProps,getSnapshotBeforeUpdate (3)在實際項目中,沿用方案和新增方案只能二選一,不能混用 3、React17生命周期(全新) (1)棄用了3個componentWillMount,componentWillReceiveProps,componentWillUpdate (2)啟用了2個getDerivedStateFromProps,getSnapshotBeforeUpdate (3)新增了1個componentDidCatch(處理錯誤) 來源:https://blog.csdn.net/liuqiao0327/article/details/107297106 五、React組件寫法 1、React15.6.1版組件之ES5寫法 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>React實例</title> <script src="https://lib.baomitu.com/react/15.6.1/react.js"></script> <script src="https://lib.baomitu.com/react/15.6.1/react-dom.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Button = React.createClass({ setNewNumber(number,event) { this.setState({number: this.state.number + 1}) }, getDefaultProps() { return { name: "計數器" }; }, getInitialState() { return{number: 0}; }, render() { return ( <div> <button onClick = {this.setNewNumber.bind(null,this.state.number,event)}>點擊{this.props.name}</button> <Text myNumber = {this.state.number}></Text> </div> ); } }) var Text = React.createClass({ //一、以下實例化時期 getDefaultProps() { console.log("1.getDefaultProps 獲取默認屬性"); return { }; }, getInitialState() { console.log("2.getInitialState 獲取初始狀態"); return { }; }, componentWillMount() { console.log("3.componentWillMount 此組件將要被渲染到目標組件內部"); }, componentDidMount() { console.log("5.componentWillMount 此組件已經被渲染到目標組件內部"); }, //二、以下存在時期 componentWillReceiveProps() { console.log("6.componentWillReceiveProps 此組件將要收到屬性"); }, shouldComponentUpdate(newProps, newState) { console.log("7.shouldComponentUpdate 組件是否應該被更新"); return true; }, componentWillUpdate() { console.log("8.componentWillUpdate 組件將要被更新"); }, componentDidUpdate() { console.log("10.componentDidUpdate 組件已經更新完成"); }, //三、以下銷毀時期 componentWillUnmount() { console.log("11.componentWillUnmount 組件將要銷毀"); }, render() { console.log("4和9.render 組件將要渲染"); return ( <div> <h3>{this.props.myNumber}</h3> </div> ); } }) ReactDOM.render( <div> <Button /> </div>, document.getElementById('example') ); </script> </body> </html> 2、React16.4.0版組件之ES6寫法 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>React實例</title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> class Button extends React.Component { //name="計算器";state = {number: 0}; //上下寫法,二選一 constructor(props) { super(props); this.name="計算器"; this.state = {number: 0}; }; setNewNumber(number,event) { this.setState({number: this.state.number + 1}) }; render() { return ( <div> <button onClick = {this.setNewNumber.bind(this,this.state.number,event)}>點擊{this.name}</button> <Text myNumber = {this.state.number}></Text> </div> ); } } class Text extends React.Component { //一、以下實例化時期 constructor(props) { super(props); console.log("2.constructor 獲取初始狀態"); } componentWillMount() { console.log("3.componentWillMount 此組件將要被渲染到目標組件內部"); } componentDidMount() { console.log("5.componentWillMount 此組件已經被渲染到目標組件內部"); } //二、以下存在時期 componentWillReceiveProps() { console.log("6.componentWillReceiveProps 此組件將要收到屬性"); } shouldComponentUpdate(newProps, newState) { console.log("7.shouldComponentUpdate 組件是否應該被更新"); return true; } componentWillUpdate() { console.log("8.componentWillUpdate 組件將要被更新"); } componentDidUpdate() { console.log("10.componentDidUpdate 組件已經更新完成"); } //三、以下銷毀時期 componentWillUnmount() { console.log("11.componentWillUnmount 組件將要銷毀"); } render() { console.log("4和9.render 組件將要渲染"); return ( <div> <h3>{this.props.myNumber}</h3> </div> ); } } ReactDOM.render( <div> <Button /> </div>, document.getElementById('example') ); </script> </body> </html> 來源 https://www.runoob.com/try/try.php?filename=try_react_life_cycle2 六、插槽 1、portal插槽 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>React插槽實例</title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="container"></div> <div id="outer"></div> </body> <script type="text/babel"> const container = document.getElementById('container'); const outer = document.getElementById('outer'); class Modal extends React.Component { constructor(props) { super(props); this.div = document.createElement('div'); } componentDidMount() { outer.appendChild(this.div); } componentWillUnmount() { outer.removeChild(this.div); } render() { return ReactDOM.createPortal( this.props.children, this.div ); } } class Parent extends React.Component { constructor(props) { super(props); this.state = {clicks: 0}; this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(state => ({ clicks: state.clicks + 1 })); } render() { return ( <div onClick={this.handleClick}> <div>{this.state.clicks}</div> <div><button>Click</button></div> <Modal> <div> <button className="modal">Click</button> </div> </Modal> </div> ); } } ReactDOM.render(<Parent/>, container); </script> 2、React.Children插槽 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>React插槽實例</title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="container"></div> <div id="outer"></div> </body> <script type="text/babel"> class Parent extends React.Component { constructor(props) { super(props); this.state = {clicks: 0}; this.handleClick = this.handleClick.bind(this); } handleClick(event) { console.log(event) this.setState(state => ({ clicks: state.clicks + 1 })); } render() { var that = this; return ( <div> <div>{this.state.clicks}</div> <div><button onClick={this.handleClick}>clicks</button></div> <ul> { React.Children.map(this.props.children,function(item,index){ if(index !=1){ return <li onClick={that.handleClick}>{item}</li> }else{ return <li onClick={that.handleClick}>{item}---{index+1}</li> } }) } </ul> </div> ); } } ReactDOM.render(<Parent> <span style={{cursor:'pointer',userSelect: 'none'}}>插槽一</span> <span style={{cursor:'pointer',userSelect: 'none'}}>插槽二</span> <span style={{cursor:'pointer',userSelect: 'none'}}>插槽三</span> </Parent>, document.getElementById('container')); </script> </html> 七、React.15.6.0源碼外框 /** * React v15.6.0 */ (function (allFn) { if (typeof exports === "object" && typeof module !== "undefined") { module.exports = allFn() } else if (typeof define === "function" && define.amd) { define([], allFn) } else { var tempGlobal; if (typeof window !== "undefined") { tempGlobal = window } else if (typeof global !== "undefined") { tempGlobal = global } else if (typeof self !== "undefined") { tempGlobal = self } else { tempGlobal = this } tempGlobal.React = allFn() } })(function () { var define, module, exports; return (function outerFn(first, second, third) { function recursion(number) { if (!second[number]) { if (!first[number]) { var error = new Error("Cannot find module '" + number + "'"); throw error.code = "MODULE_NOT_FOUND", error } var module = second[number] = { exports: {} }; first[number][0].call(module.exports, function (key) { var value = first[number][1][key]; return recursion(value ? value : key) }, module, module.exports, outerFn, first, second, third) } return second[number].exports//在react實例化的過程中,這行代碼不但因獲取依賴而多次執行,而且還因獲取react實例而最后執行。 } for (var number = 0; number < third.length; number++) recursion(third[number]);//fn(16)第1次執行,執行結果沒有變量接收 return recursion //執行到這,整個邏輯就快結束了。前兩行可以合並為一行:return recursion(third[0]),同時下面的"(48)"應當刪掉。 })({ 2: [function (_dereq_, module, exports) { var thisVar = _dereq_(138) }, { "25": 25, "30": 30 }], }, {}, [16])(16)// fn(16)第2次執行,因為n[num]為真,所以直接返回n[num].exports並被掛在g.React上 }); 八、ant-design-pro腳手架的構成 Pro的底座是umi,umi是一個(基於)webpack之上的(自動化)整合工具。 Pro的核心是umi,umi的核心是webpack。 1、web 技術 2、Umi-前端應用框架(可整個或部分復用的軟件) (1)Node.js 前端開發基礎環境 (2)Webpack 前端必學必會的打包工具 (3)React Router 路由庫,被dva內置 (4)proxy 反向代理工具 (5)dva 輕量級的應用框架(可整個或部分復用的軟件) (6)fabric 嚴格但是不嚴苛的 lint 規則集 (7)TypeScript 帶類型的 JavaScript 3、Ant Design 前端組件庫 4、ProComponents 模板組件 5、useModel 簡易數據流 6、編譯時和運行時 (1)編譯時,環境是node環境,可以使用fs,path等功能;沒有使用webpack,不能使用jsx,不能引入圖片 (2)運行時,編譯完成,開始在瀏覽器環境運行,不能使用fs、path,有跨域的問題;這個環境被webpack編譯過,可以寫jsx,導入圖片 7、Umi的插件 (1)plugin-access,權限管理 (2)plugin-analytics,統計管理 (3)plugin-antd,整合 antd UI 組件 (4)plugin-initial-state,初始化數據管理 (5)plugin-layout,配置啟用 ant-design-pro 的布局 (6)plugin-locale,國際化能力 (7)plugin-model,基於 hooks 的簡易數據流 (8)plugin-request,基於 umi-request 和 umi-hooks 的請求方案 8、Umi其它 (1)配置:開發配置和(瀏覽器)運行配置 (2)路由:配置路由和約定式路由 (3)插件:id和key,每個插件都會對應一個id和一個key,id是路徑的簡寫,key是進一步簡化后用於配置的唯一值。 九、前端路由:url有變化,但不向后台發送請求, 1、它的作用是 (1)記錄當前頁面的狀態; (2)可以使用瀏覽器的前進后退功能; 2、hash模式幫我們實現這兩個功能,因為它能做到: (1)改變url且不讓瀏覽器向服務器發出請求; (2)監測 url 的變化; (3)截獲 url 地址,並解析出需要的信息來匹配路由規則。 3、history 模式改變 url 的方式會導致瀏覽器向服務器發送請求。 4、react前端路由觸發條件 (1)<Link to={item.url}>{item.location}</Link> (2)browserHistory.push('/aaa') 暫時留存 四、React組件生命周期 1、ES5寫法 ES5寫法之實例化時期 (1)propTypes = {}; getDefaultProps(){return{}}; (2)getInitialState(){return{}}; (3)componentWillMount//執行setState會合並到初始化狀態中;獲取從屬性生成的狀態 -----執行到這里,生命狀態會被重置為null (4)render//執行setState會發起updateComponent導致-死循環 (5)componentDidMount//執行setState會導致更新;這是發起異步請求去API獲取數據的絕佳時期 ES5寫法之存在期 (1)componentWillReceiveProps//執行setState會合並到狀態中; -----執行到這里,生命狀態會被重置為null (2)shouldComponentUpdate//執行setState會發起updateComponent導致-死循環 (3)componentWillUpdate//執行setState會發起updateComponent導致-死循環 (4)render//執行setState會發起updateComponent導致-死循環 (5)componentDidUpdate//可以有條件地執行setState ES5寫法之銷毀時期 (1)componentWillUnmount//等待頁面卸載,改變state沒意義。 2、ES6寫法(舊,React v16.3-) ES6寫法之實例化時期 (1)static propTypes = {}; static defaultProps = {}; (2)constructor (3)componentWillMount//執行setState會合並到初始化狀態中;獲取從屬性生成的狀態 -----執行到這里,生命狀態會被重置為null (4)render//執行setState會發起updateComponent導致-死循環 (5)componentDidMount//執行setState會導致更新;這是發起異步請求去API獲取數據的絕佳時期 ES6寫法之存在期 (1)componentWillReceiveProps//執行setState會合並到狀態中;----將有變動 -----執行到這里,生命狀態會被重置為null (2)shouldComponentUpdate//執行setState會發起updateComponent導致-死循環 (3)componentWillUpdate//執行setState會發起updateComponent導致-死循環 (4)render//執行setState會發起updateComponent導致-死循環 (5)componentDidUpdate//可以有條件地執行setState ES6寫法之銷毀時期 (1)componentWillUnmount//等待頁面卸載,改變state沒意義。 3、ES6寫法(新,React v16.4+) ES6寫法之實例化時期 (1)static propTypes = {}; static defaultProps = {}; (2)constructor (3)static getDerivedStateFromProps//執行setState會合並到初始化狀態中;獲取從屬性生成的狀態 -----執行到這里,生命狀態會被重置為null (4)render//執行setState會發起updateComponent導致-死循環 (5)componentDidMount//執行setState會導致更新;這是發起異步請求去API獲取數據的絕佳時期 ES6寫法之存在期 (1)static getDerivedStateFromProps//執行setState會合並到狀態中;----已有變動 -----執行到這里,生命狀態會被重置為null (2)shouldComponentUpdate//執行setState會發起updateComponent導致-死循環 //此處沒有componentWillUpdate (3)render//執行setState會發起updateComponent導致-死循環 //下(4)為新增 (4)getSnapshotBeforeUpdate//執行setState會發起updateComponent導致-死循環 (5)componentDidUpdate//可以有條件地執行setState 銷毀時期 (1)componentWillUnmount//等待頁面卸載,改變state沒意義。 另外==== (1)getDerivedStateFromProps//返回一個對象來更新state,如果返回 null 則不更新任何內容。