javascript
React简介、虚拟DOM、Diff算法、创建React项目、JSX语法、组件、组件声明方式、组件传值props和state、组件的生命周期
React簡介:
前面只是簡單介紹移動APP開發(fā),后面還會繼續(xù)深入介紹移動app開發(fā);其中想要用ReactNative開發(fā)出更出色的應用,那么就得學好React,下面將介紹React:
React 是一個由 Facebook 開發(fā)用于構(gòu)建用戶界面的漸進式 JavaScript 庫,其特點:聲明式設計、高效、靈活、JSX、組件化、單向數(shù)據(jù)流。
React也是組件化的,與vue不同的是:React直接使用JS代碼編寫組件(結(jié)構(gòu)、樣式、邏輯混合在js代碼中)。
React是前端三大框架中誕生最早的框架,社區(qū)龐大,技術(shù)團隊實力雄厚。
虛擬DOM:
操作原生DOM是一件非常耗性能的事,操作虛擬DOM就不會那么耗性能了,操作虛擬DOM時采用Different算法,只更新變化的虛擬DOM部分;虛擬DOM指通過程序員手動模擬出來類似原生DOM的對象,如下面模擬一個帶有鏈接的p元素:
var p = {//定義一個對象tagName:'p',//標簽名為:pattrs:{//定義屬性:class:'font16'},children:[//定義內(nèi)容,相當于innerText'跳轉(zhuǎn)到:',{//在父標簽的children中再定義以對象,其方法和定義p一樣:tagName:'a',//標簽名為:aattrs:{//屬性:href:'https://www.baidu.com',},children:[//定義內(nèi)容:'百度']}]}Diff算法:
Diff算法用于對比新舊虛擬DOM的算法,其中有三部分:tree diff、component diff、element diff,其區(qū)別:
tree diff :新舊DOM樹逐層對比的方式,對比所有層節(jié)點來找到被更新的節(jié)點后修改舊DOM。
component diff:組件之間的對比,當對比組件的時候,如果兩個組件的類型相同,則這個組件暫時不需要被更新,如果組件的類型不同,則立即移除舊組件,新建一個組件,替換到被移除的位置。
element diff:組件中每個元素之間的對比。
使用虛擬DOM創(chuàng)建React項目(導入資源型):
使用React開發(fā)項目時,必須安裝兩個包:react、react-dom;react是用來創(chuàng)建組件、組件生命周期等。react-dom用來操作DOM。創(chuàng)建react項目步驟:
//1.新建一個項目文件夾,并在文件夾打開終端鍵入:npm init -y 初始化一個package.json文件//2.終端輸入:cnpm install react react-dom --save 安裝react和react-dom到運行里,ReactNative開發(fā)中不建議使用cnpm裝包//3.新建src文件夾并新建main.js文件并引入:react和react-dom:開始編寫react中js主文件(并新建index.html文件,文件中留渲染的div)import React from 'react';import ReactDOM from 'react-dom';//4.在main.js文件中使用React提供的API操作元素://4-1使用React.createElement()創(chuàng)建一個虛擬DOM,接收至少三個參數(shù):參數(shù)1:字符串(標簽類型),參數(shù)2:對象(標簽屬性),參數(shù)3開始:當前元素子節(jié)點,可放多個虛擬dom,如:var mydiv = React.createElement('div',{class:'mydiv',id:'box'},'這是一個div元素');//<div class='mydiv' id='box'>這是一個div元素</div>var spanp = React.createElement('span',{class:'spans'},'被span標簽包裹的文本');//<span class='spans'>被span標簽包裹的文本</span>var textp = React.createElement('p',{class:'tp'},'p標簽文本中',spanp);//<p class='tp'>p標簽文本中<span class='spans'>被span標簽包裹的文本</span></p>// 4-2使用ReactDOM.render()將虛擬DOM渲染到頁面中,參數(shù)1:渲染DOM內(nèi)容,參數(shù)2:渲染的dom元素位置(獲取DOM的方式),如:ReactDOM.render(mydiv,document.getElementById('app'));//這里app表示index.htlm文件中一個id值為app的標簽,如<div id='app'></div>//4.webpack打包構(gòu)建后,在dist目錄下的文件是正常可以訪問的。JSX:
不難發(fā)現(xiàn)使用js創(chuàng)建元素的方式是非常繁瑣的,因此這里介紹一款可以解決這個問題語法:JSX;
HTML 語言直接寫在 JavaScript 語言中,不加任何引號,這就是 JSX 語法,它允許 HTML 與 JavaScript 的混寫;
它是 一種 JavaScript 的語法擴展, 我們推薦在 React 中使用 JSX 來描述用戶界面,JSX 是在 JavaScript 內(nèi)部實現(xiàn)的;元素是構(gòu)成 React 應用的最小單位,JSX 就是用來聲明 React 當中的元素(底層實際就是通過上面js創(chuàng)建元素的)使用JSX語法時首先要安裝:(cnpm install babel-preset-react -D)并配置在.babelrc文件中,babel-preset-react 用來轉(zhuǎn)換JSX代碼。(注意新版本:babel-preset-react-app,此環(huán)境應該基于上面環(huán)境)
與瀏覽器的 DOM 元素不同,React 當中的元素事實上是普通的對象,如:
// 注意:想要正常運行JSX語法:還需要以下兩步://1.在項目目錄下新建:.babelic文件,其配置代碼如下:{"presets": ["env", "stage-0", "react"],"plugins": ["transform-runtime"]}//2.cnpm i babel-preset-env babel-preset-stage-0 babel-plugin-transform-runtime --save ,下載上面配置依賴的包。//3.配置完以上環(huán)境后,應該使用webpack打包后才可以以files的方式打正常訪問react項目import React from 'react';import ReactDOM from 'react-dom';var titles = '這是一個提示';var elements =<div>{/* JSX語法中允許有一個根節(jié)點,根節(jié)點中可以嵌套其它元素 */}<p title={titles}>hello</p>{/*JSX中使用變量用{}包裹,實際指使用js語法時用{}包裹*/}<a href="#">hello</a><p className='textp'>hello</p>{/*在JSX中使用className代替class屬性,因為class在js中只一個關(guān)鍵字*/}<label htmlFor="">hello</label>{/* 在JSX中for使用htmlFor代替 */}</div>const box = document.getElementById('box');ReactDOM.render(elements,box);//總結(jié)://1.當編譯時遇到尖括號<>當JSX執(zhí)行,當遇到大括號{}當js執(zhí)行//2.JSX語法中使用className代替class屬性//3.在JSX中for使用htmlFor代替//4.jsx語法中只能使用一個根元素//5.jsx中數(shù)組會自動展開JSX 中注釋:
寫法一:
{// 注釋// ...}寫法二(單行推薦):
{/* 單行注釋 */}寫法三(多行推薦):
{/** 多行注釋*/}JSX 不同寫法:
Babel 會把 JSX 通過React.createElement() 函數(shù)編譯,每個 React 元素都是一個真實的 JavaScript 對象;下面幾種方式是等價的,如:
const element = (<h1 className="greeting">Hello, world!</h1>);const element = React.createElement('h1',{className: 'greeting'},'Hello, world!');const element = {type: 'h1',props: {className: 'greeting',children: 'Hello, world'}};const element = {tagName:'h1',attrs:{className: 'greeting'},children:['Hello, world']}組件:
React 允許將代碼封裝成組件,然后像插入普通 HTML 標簽一樣,在網(wǎng)頁中插入這個組件即可使用;組件規(guī)則注意事項:組件名的第一個首字母必須大寫,class聲明式組件必須有 render 方法,組件內(nèi)必須有且只有一個根節(jié)點,組件的屬性可以在組件內(nèi)通過props 獲取(函數(shù)需要傳遞參數(shù):props;類直接通過: this.props),如:
函數(shù)式聲明組件(無狀態(tài)):
名字不能用小寫,React 在解析的時候,是以標簽的首字母來區(qū)分的,如果首字母是小寫則當作 HTML 來解析,如果首字母是大寫則當作組件來解析。
// 函數(shù)名大寫,組件名為函數(shù)名// 函數(shù)式聲明組件,必須有return關(guān)鍵字,即使沒內(nèi)容也應該:return nullfunction Header(props){return (//當返回多行代碼時,建議使用小括號括起來<div><p>這是header組件{props.name}</p>{/* 這里直接通過{屬性名}的方式是不能拿到屬性值的,需要給函數(shù)傳遞一個參數(shù)如:props等,在通過這個參數(shù)點出屬性:{props.name},且這些屬性只讀 */}</div>)}const box = document.getElementById('box');// 通過 <函數(shù)名/> 的方式定義組件名;組件中傳遞參數(shù)使用屬性的方式,如:name={變量名},age={變量名};分開傳遞參數(shù)很不方便,可以使用es6中屬性擴散語法傳遞一個對象達到同樣效果:var obj = {name:'jack',age:'28',gender:'男'}var cont = <div><Header {...obj}/> {/* es6中屬性擴散語法 */}</div>ReactDOM.render(cont,box);抽離組件:
上面組件聲明在同一個js文件中,沒有達到減少主文件代碼量的問題,想要減少主文件代碼量,就得將組件抽離出來,如:
// 被抽離的組件header.js文件:(擴展:組件可以使用jsx后綴名,但是需要在webpack.config.js文件中配置解析jsx文件的loader:loader和js一樣,可連寫為:( js|jsx)$ 、jsx?$ )import React from 'react';//導入React,注意:React首字母必須大寫function Header(props){return (<div><p>這是header組件{props.name}</p></div>)}// 抽離組件需要暴露出:export default Header;//或直接在函數(shù)或類前面加 export default;// 在主文件(main.js)中使用這個組件:// 導入組件:import Header from './components/header.js';// 在主文件中使用 <Header><Header/> (簡寫:<Header/>)使用組件即可類方式聲明組件(有狀態(tài)):
// 通過class定義一個Header組件,extends 繼承了React中Componentclass Header extends React.Component { render() { //使用render函數(shù),函數(shù)中返回組件類容:return (<div><p>這是header組件{this.props.names}</p>{/*class聲明的組件拿傳遞的值通過:this.props點屬性名,也是只可讀的*/}</div>)}}; ReactDOM.render(<Header names='jack'/>, document.getElementById('box'));// 抽離組件的方式和函數(shù)聲明組件的方式中一樣組件傳值props和state:
state 和 props區(qū)別在于props 是不可變的,而 state 可以改變。函數(shù)式組件只能通過 props 來傳遞數(shù)據(jù),不能通過state傳值;class定義的組件都可以使用它們傳值。
class Header extends React.Component { constructor(props){//class定義的類中如果實現(xiàn)了繼承,默認就有constructor函數(shù),只是看不見;當寫出這個構(gòu)造函數(shù)時,要通過super調(diào)用:super(props);//表示父類的構(gòu)造函數(shù)console.log(props);//在constructor中是不能直接使用props的,想要使用,就得在constructor中傳遞props// constructor中的this.state表示私有數(shù)據(jù)對象,類似vue中data(){},this.state中定義的數(shù)據(jù)是可改變的,通過this.state點屬性拿到,如:this.state={messages:'hello',names:'jack'}}render() { return (<div><p>這是header組件{this.props}</p><p>這是header組件{this.state.messages}</p><button onClick={this.change}>變更messages的值</button>{/* jsx中使用事件時,必須使用駝峰命名法,且處理函數(shù)名前面加this. */}</div>)}//React函數(shù)中this默認指向undefined,若想this指向class類,可以使用箭頭函數(shù):如:// change(){// this.state.messages = '修改hello為word';// }change=()=>{//這里是將箭頭函數(shù)賦值給change,箭頭函數(shù)中this依舊指向class類// this.state.messages = '修改hello為word';//通過this.state方式修改數(shù)據(jù)只是單向的,內(nèi)存中數(shù)據(jù)是修改了,但是頁面沒有自動刷新,因此也不推薦,推薦setState()方法異步修改,如// this.setState({//setState()方法中傳遞一個對象,對象中傳入要修改的屬性,如:// messages:'word',// names:'lucky'// })// this.setState(function(prevState,props){//setState也支持傳遞一個函數(shù),但是函數(shù)必須return一個對象;函數(shù)中第一個參數(shù)表示修改數(shù)據(jù)之前的數(shù)據(jù),第二個參數(shù)是外界傳遞過來的數(shù)據(jù)屬性// return {// messages:'word'// }// })this.setState(function(prevState,props){//setState()因為setState是異步修改數(shù)據(jù)的,想要確保拿到最新的數(shù)據(jù),那么,可以給setState繼續(xù)傳遞一個參數(shù)為:回調(diào)函數(shù),在回調(diào)函數(shù)里面拿到的數(shù)據(jù)是比較保險的,如:return {messages:'word'}},function(){console.log(this.state.messages);})}}; ReactDOM.render(<Header/>, document.getElementById('box'));// 抽離組件的方式和函數(shù)聲明組件的方式中一樣組件的生命周期:
組件從創(chuàng)建到運行再到銷毀,這期間伴隨著各種各樣的事件,這些事件統(tǒng)稱為組件的生命周期函數(shù);
組件生命周期分為三部分:組件創(chuàng)建階段(命周期函數(shù)一生只創(chuàng)建一次)、組件運行階段(生命周期函數(shù)根據(jù)props和state的狀態(tài)是否改變運行0-N次)、組件銷毀階段(生命周期函數(shù)一生只執(zhí)行一次)。
import React from 'react';import ReactDOM from 'react-dom';import ReactTypes from 'prop-types';//導入數(shù)據(jù)校驗模塊class Header extends React.Component { constructor(props){super(props);//1.使用:this.state={}初始化組件私有狀態(tài),初始化組件私有數(shù)據(jù)this.state={messages:'hello',numstate:props.num//因為props中的值時只讀的,想要修改值,就得使用state存值,但是state存的值是固定的,因此這里可以傳遞一個動態(tài)的值,即props的值,此時頁面的值應該為對應的state值。}}//2.使用:static defaultProps = {}定義默認的值供程序正常運行,如:static defaultProps = {num:0}//3.使用:static propTypes = {}做數(shù)據(jù)校驗,防止傳過來的數(shù)據(jù)無效,如:static propTypes = {//特別注意:做校驗需要安裝一個包prop-types: npm install prop-types ,并在開頭導入num:ReactTypes.number//如果需要檢驗其他數(shù)據(jù)類型,可閱讀prop-types原文;當做完數(shù)據(jù)校驗時,在組件中傳遞過來的值類型不符合時會報警告}// 4.使用:componentWillMount(){}組件即將被掛載到頁面時觸發(fā)函數(shù),虛擬DOM也沒創(chuàng)建(不能被拿到),因此此頁面不能操作頁面,可以操作數(shù)據(jù),如:componentWillMount(){console.log(this.state.messages);//數(shù)據(jù)可被拿到console.log(this.props.num);//數(shù)據(jù)可被拿到this.myFunction();//函數(shù)可被調(diào)用console.log(document.getElementById('testp'));//不能被獲取}// 5.使用:render(){}即將渲染內(nèi)存中的虛擬DOM,該函數(shù)執(zhí)行完后內(nèi)存中已經(jīng)渲染好數(shù)據(jù)了,但是還沒有被掛載到頁面上,如:render() { //render中不能使用this.setState方法,否則會進入死循環(huán)return (<div>{/* <p id='testp'>num的值:{this.props.num}</p> */}<p id='testp' onClick={this.addnumstate}>num的值自增為:{this.state.numstate}</p>{/*修改為可改變的state值*/}<span ref='spans'>hello</span>{/* 利用refs屬性可以快速獲取到該節(jié)點 */}</div>)}// 6.使用:componentDidMount(){}頁面已經(jīng)有可見的DOM元素了,此時可以拿到render中的DOM,如:componentDidMount(){console.log(document.getElementById('testp'));//可以獲取DOM元素,可對其進行操作(原生DOM型,但是需要注意this指向問題,可使用箭頭函數(shù),但是不推薦原生方式),如:// document.getElementById('testp').οnclick=()=>{// this.setState({// numstate:this.state.numstate++// })// }//推薦React中事件綁定的方式,如testp標簽中}//7.使用:conmponentWillReceiveProps(){}當子組件的屬性props改變時觸發(fā)此方法,但是第一次渲染時不會被觸發(fā),如:conmponentWillReceiveProps(nextProps){//這里的數(shù)據(jù)是舊的,但是通過第一個參數(shù)nextProps.屬性名,拿到的是最新的數(shù)據(jù)console.log('當外界傳遞過來新的props時,才會觸發(fā)該事件執(zhí)行');console.log(next.Props.num);}addnumstate=()=>{//使用箭頭函數(shù)改變this指向為類的實例;注意:在dom中掉用次函數(shù)時在函數(shù)名前加thisthis.setState({numstate:this.state.numstate++})}myFunction(){console.log('測試componentWillMount是否可以調(diào)用外部函數(shù)');}// 以上是組件創(chuàng)建階段生命周期,下面將介紹組件運行階段生命周期:// 1.使用:shouldComponentUpdate(){}里面return一個布爾值,判斷是否要更新頁面上數(shù)據(jù),當為true時可以改變頁面上數(shù)據(jù),當為false時不能改變頁面上的數(shù)據(jù);無論是true或false,內(nèi)存中數(shù)據(jù)都是改變重新渲染了shouldComponentUpdate(nextProps,nextState){//此方法中可傳入兩個參數(shù):第一個表示最新的props,第二個表示最新的stateconsole.log(nextState.numstate);return true;//這里應該使用傳入?yún)?shù)做出邏輯后return 布爾值}// 2.當shouldComponentUpdate中返回false時,會重新返回到shouldComponentUpdate執(zhí)行,若果返回的是true,則執(zhí)行下面的:componentWillUpdate(){}將要更新數(shù)據(jù),此時頁面和內(nèi)存中數(shù)據(jù)都未被更新,如:componentWillUpdate(){//此時頁面上數(shù)據(jù)都是舊的console.log(this.refs.spans.innerHTML);//使用refs獲取ref設置的值}// 3.這里有一個運行時的render方法,但是里面數(shù)據(jù)h還是舊的。// 4.使用:componentDidUpdate(){}更新了頁面和內(nèi)存中的數(shù)據(jù),此時數(shù)據(jù)都是最新的,如:componentDidUpdate(){//此時頁面上數(shù)據(jù)都是新的console.log(this.refs.spans.innerHTML);}}; ReactDOM.render(<Header num='校驗時傳入無效數(shù)據(jù)時,會報警告'/>, document.getElementById('box'));對上面組件生命周期流程總結(jié)如下圖:
提示:本文圖片等素材來源于網(wǎng)絡,若有侵權(quán),請發(fā)郵件至郵箱:810665436@qq.com聯(lián)系筆者刪除。
筆者:苦海
總結(jié)
以上是生活随笔為你收集整理的React简介、虚拟DOM、Diff算法、创建React项目、JSX语法、组件、组件声明方式、组件传值props和state、组件的生命周期的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 表单及数据提交、表单的作用、服务端接收提
- 下一篇: javascript中parseFloa