React基礎筆記(一)

這是我參與8月更文挑戰的第1天,活動詳情查看:8月更文挑戰javascript

前言

本文做爲本人學習總結之用,同時分享給你們,適合入門的react小白
由於我的技術有限,若是有發現錯誤或存在疑問之處,歡迎指出或指點!不勝感謝!html

React基本介紹

React介紹

介紹描述

中文官網: react.docschina.org/vue

英文官網: https://reactjs.org/java

  • 用於動態構建用戶界面的 JavaScript 庫(只關注於視圖)
  • 由Facebook開源

React特色

  1. 聲明式編碼
  2. 組件化編碼
  3. React Native 編寫原生應用
  4. 高效(優秀的Diffing算法)

高效緣由:react

1. 使用虛擬(virtual)DOM, 不老是直接操做頁面真實DOM。ajax

2. DOM Diffing算法, 最小化頁面重繪算法

React的基本使用

相關JS庫

  1. react.js:React核心庫。
  2. react-dom.js:提供操做DOM的react擴展庫
  3. babel.min.js:解析JSX語法代碼轉爲JS代碼的庫。

建立虛擬DOM的兩種方式

  • 純JS方式(通常不使用)
<script type="text/javascript" > 
    //1.建立虛擬DOM
    const VDOM = React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React'))
    //2.渲染虛擬DOM到頁面
    ReactDOM.render(VDOM,document.getElementById('test'))
</script>
複製代碼
  • JSX方式
<script type="text/babel" > /* 此處必定要寫babel */
    //1.建立虛擬DOM
    const VDOM = (  /* 此處必定不要寫引號,由於不是字符串 */
        <h1 id="title"> <span>Hello,React</span> </h1>
    )
    //2.渲染虛擬DOM到頁面
    ReactDOM.render(VDOM,document.getElementById('test'))
</script>
複製代碼

虛擬DOM與真實DOM

  1. React提供了一些API來建立一種 「特別」 的通常js對象

const VDOM = React.createElement('xx',{id:'xx'},'xx')
建立的就是一個簡單的虛擬DOM對象編程

  1. 虛擬DOM對象最終都會被React轉換爲真實的DOM
  2. 咱們編碼時基本只須要操做react的虛擬DOM相關數據, react會轉換爲真實DOM變化而更新界
//1.建立虛擬DOM
  const VDOM = ( 
    <h1 id="title"> <span>Hello,React</span> </h1>
  )
//2.渲染虛擬DOM到頁面
ReactDOM.render(VDOM,document.getElementById('test'))

const TDOM = document.getElementById('demo')
console.log('虛擬DOM',VDOM);   //虛擬DOM Object:{$$typeof: Symbol(react.element), type: "h1", key: null, ref: null, props: {…}, …}
console.log('真實DOM',TDOM);  //真實DOM <div id='demo'></div>
debugger;
console.log(typeof VDOM);  //object
console.log(VDOM instanceof Object); //true
複製代碼

關於虛擬DOM:
1.本質是Object類型的對象(通常對象)
2.虛擬DOM比較「輕」,真實DOM比較「重」,由於虛擬DOM是React內部在用,無需真實DOM上那麼多的屬性。
3.虛擬DOM最終會被React轉化爲真實DOM,呈如今頁面上。數組

React JSX

JSX

  1. 全稱:  JavaScript XML
  2. react定義的一種相似於XML的JS擴展語法: JS + XML本質是React.createElement(component, props, ...children)方法的語法糖
  3. 做用: 用來簡化建立虛擬DOM
    • 寫法:var ele =  <h1>Hello JSX!</h1>
    • 注意1:它不是字符串, 也不是HTML/XML標籤
    • 注意2:它最終產生的就是一個JS對象
  4. 標籤名任意: HTML標籤或其它標籤
  5. 標籤屬性任意: HTML標籤屬性或其它
  6. 基本語法規則

1.定義虛擬DOM時,不要寫引號。
2.標籤中混入JS表達式時要用{}。
3.樣式的類名指定不要用class,要用className。
4.內聯樣式,要用style={{key:value}}的形式去寫。
5.只有一個根標籤
6.標籤必須閉合
7.標籤首字母瀏覽器

  • 小寫字母開頭,則將該標籤轉爲html中同名元素,若html中無該標籤對應的同名元素,則報錯。
  • 字母開頭,react就去渲染對應的組件,若組件沒有定義,則報錯。
  1.  babel.js的做用
  • 瀏覽器不能直接解析JSX代碼, 須要babel轉譯爲純JS的代碼才能運行
  • 只要用了JSX,都要加上type="text/babel", 聲明須要babel來處理

渲染虛擬DOM元素

  1. 語法: ReactDOM.render(virtualDOM,containerDOM)
  2. 做用: 將虛擬DOM元素渲染到頁面中的真實容器DOM中顯示
  3. 參數說明
    • 參數一: 純js或jsx建立的虛擬dom對象
    • 參數二: 用來包含虛擬DOM元素的真實dom元素對象(通常是一個div)

React面向組件編程

基本理解和使用

注意

  1. 組件名必須首字母大寫
  2. 虛擬DOM元素只能有一個根元素
  3. 虛擬DOM元素必須有結束標籤

渲染類組件標籤的基本流程

  1. React內部會建立組件實例對象
  2. 調用render()獲得虛擬DOM, 並解析爲真實DOM
  3. 插入到指定的頁面元素內部

函數組件和類式組件

函數組件:

<script type="text/babel">
    //1.建立函數式組件
    function MyComponent(){
        console.log(this);  //此處的this是undefined,由於babel編譯後開啓了嚴格模式
        return <h2>我是用函數定義的組件(適用於【簡單組件】的定義)</h2>
    }
    //2.渲染組件到頁面
    ReactDOM.render(<MyComponent/>,document.getElementById('test'))
    /* 執行了ReactDOM.render(<MyComponent/>.......以後,發生了什麼? 1.React解析組件標籤,找到了MyComponent組件。 2.發現組件是使用函數定義的,隨後調用該函數,將返回的虛擬DOM轉爲真實DOM,隨後呈如今頁面中。 */
</script>
複製代碼

類式組件:        

<script type="text/babel">
    //1.建立類式組件
    class MyComponent extends React.Component {
        render(){
            //render是放在哪裏的?—— MyComponent的原型對象上,供實例使用。
            //render中的this是誰?—— MyComponent的實例對象 <=> MyComponent組件實例對象。
            console.log('render中的this:',this);
            //輸出爲 render中的this: MyComponent {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: FiberNode, …}
            return <h2>我是用類定義的組件(適用於【複雜組件】的定義)</h2>
        }
    }
    //2.渲染組件到頁面
    ReactDOM.render(<MyComponent/>,document.getElementById('test'))
    /* 執行了ReactDOM.render(<MyComponent/>.......以後,發生了什麼? 1.React解析組件標籤,找到了MyComponent組件。 2.發現組件是使用類定義的,隨後new出來該類的實例,並經過該實例調用到原型上的render方法。 3.將render返回的虛擬DOM轉爲真實DOM,隨後呈如今頁面中。 */
</script>
複製代碼

state組件三大核心屬性

理解

  1. state是組件對象最重要的屬性, 值是對象(能夠包含多個key-value的組合)
  2. 組件被稱爲"狀態機", 經過更新組件的state來更新對應的頁面顯示(從新渲染組件)

強烈注意

  1. 組件中render方法中的this爲組件實例對象
  2. 組件自定義的方法中this爲undefined,如何解決?
    • 強制綁定this: 經過函數對象的bind()
    • 箭頭函數
  3. 狀態數據,不能直接修改或更新

state基本使用

//1.建立組件
class Weather extends React.Component{
    //構造器調用幾回? ———— 1次
    constructor(props){
        console.log('constructor');
        super(props)
        //初始化狀態
        this.state = {isHot:false,wind:'微風',stat:{}}
        //解決changeWeather中this指向問題
        this.changeWeather = this.changeWeather.bind(this)
    }

    //render調用幾回? ———— 1+n次 1是初始化的那次 n是狀態更新的次數
    render(){
        console.log('render');
        //讀取狀態
        const {isHot,wind} = this.state
        return <h1 onClick={this.changeWeather}>今每天氣很{isHot ? '炎熱' : '涼爽'},{wind}</h1>
    }

    //changeWeather調用幾回? ———— 點幾回調幾回
    changeWeather(){
        //changeWeather放在哪裏? ———— Weather的原型對象上,供實例使用
        //因爲changeWeather是做爲onClick的回調,因此不是經過實例調用的,是直接調用
        //類中的方法默認開啓了局部的嚴格模式,因此changeWeather中的this爲undefined

        console.log('changeWeather');
        //獲取原來的isHot值
        const isHot = this.state.isHot
        //嚴重注意:狀態必須經過setState進行更新,且更新是一種合併,不是替換。
        this.setState({isHot:!isHot})
        console.log(this);

        //嚴重注意:狀態(state)不可直接更改,下面這行就是直接更改!!!
        //this.state.isHot = !isHot //這是錯誤的寫法
    }
}
//2.渲染組件到頁面
ReactDOM.render(<Weather/>,document.getElementById('test'))
複製代碼

state簡寫方式:簡寫只是把constructor去掉 初始化更加方便

class Weather extends React.Component{
    //初始化狀態
    state = {isHot:false,wind:'微風'}
    render(){
        const {isHot,wind} = this.state
        return <h1 onClick={this.changeWeather}>今每天氣很{isHot ? '炎熱' : '涼爽'},{wind}</h1>
    }
    //自定義方法————要用賦值語句的形式+箭頭函數
    changeWeather = ()=>{
        const isHot = this.state.isHot
        this.setState({isHot:!isHot})
    }
}
//2.渲染組件到頁面
ReactDOM.render(<Weather/>,document.getElementById('test'))
複製代碼

props組件三大核心屬性

理解

1. 每一個組件對象都會有props(properties的簡寫)屬性
2. 組件標籤的全部屬性都保存在props中

做用

1. 經過標籤屬性從組件外向組件內傳遞變化的數據
2. 注意: 組件內部不要修改props數據

編碼操做

  1. 內部讀取某個屬性值: this.props.name
  2. 對props中的屬性值進行類型限制和必要性限制
//第一種方式(React v15.5 開始已棄用):
Person.propTypes = {
    name: React.PropTypes.string.isRequired,
    age: React.PropTypes.number
}
//第二種方式(新):使用prop-types庫進限制(須要引入prop-types庫):
Person.propTypes = {
    name: PropTypes.string.isRequired,
    age: PropTypes.number. 
}
複製代碼
  1. 擴展屬性: 將對象的全部屬性經過props傳遞
  2. 默認屬性值
  3. 組件類的構造函數
//建立組件
class Person extends React.Component{
//5.組件類的構造函數
    constructor(props){
        //構造器是否接收props,是否傳遞給super,取決於:是否但願在構造器中經過this訪問props
        // console.log(props);
        super(props)
        console.log('constructor',this.props);
    }

//2對標籤屬性進行類型、必要性的限制
    static propTypes = {
        name:PropTypes.string.isRequired, //限制name必傳,且爲字符串
        sex:PropTypes.string,//限制sex爲字符串
        age:PropTypes.number,//限制age爲數值
    }

//4指定默認標籤屬性值
    static defaultProps = {
        sex:'男',//sex默認值爲男
        age:18 //age默認值爲18
    }
            
    render(){
//1內部讀取某個屬性值
        const {name,age,sex} = this.props
        //props是隻讀的
        //this.props.name = 'jack' //此行代碼會報錯,由於props是隻讀的
        return (
            <ul> <li>姓名:{name}</li> <li>性別:{sex}</li> <li>年齡:{age+1}</li> </ul>
        )
    }
}

//3擴展屬性
//渲染組件到頁面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
複製代碼

函數式組件使用 props

//建立組件
function Person (props){
    const {name,age,sex} = props
    return (
            <ul> <li>姓名:{name}</li> <li>性別:{sex}</li> <li>年齡:{age}</li> </ul>
        )
}
Person.propTypes = {
    name:PropTypes.string.isRequired, //限制name必傳,且爲字符串
    sex:PropTypes.string,//限制sex爲字符串
    age:PropTypes.number,//限制age爲數值
}

//指定默認標籤屬性值
Person.defaultProps = {
    sex:'男',//sex默認值爲男
    age:18 //age默認值爲18
}
//渲染組件到頁面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
複製代碼

refs與事件處理組件三大核心屬性

理解

組件內的標籤能夠定義ref屬性來標識本身不太建議過多使用React準備廢棄

編碼

1. 字符串形式的ref
2. 回調形式的ref
3. createRef建立ref容器

//字符串形式的ref
<input ref="input1" type="text" />
const {input1} = this.refs  //直接調用

//回調形式的ref
<input ref={c => this.input1 = c } type="text"/>
const {input1} = this   //調用賦值

//函數方式調用
    <input ref={this.saveInput} type="text"/>
  saveInput = (c)=>{
    this.input1 = c;
    console.log('@',c);
  }
  
  //createRef建立ref容器·
  //React.createRef調用後能夠返回一個容器,該容器能夠存儲被 ref所標識的節點,該容器是"專人專用"的
    myRef = React.createRef()
    <input ref={this.myRef} type="text"/>
複製代碼

事件處理

1. 經過onXxx屬性指定事件處理函數(注意大小寫)\

  • React使用的是自定義(合成)事件, 而不是使用的原生DOM事件———————爲了更好的兼容性
  • React中的事件是經過事件委託方式處理的(委託給組件最外層的元素)———— 爲了的高效

2. 經過event.target獲得發生事件的DOM元素對象 —————————不要過分使用ref

//事件處理
    showData = (event)=>{
      console.log(event.target);
      alert(this.myRef.current.value);
    }
    <button onClick={this.showData}>點我提示左側的數據</button>

//在父子組件中使用
export default class SliderList extends Component {
  sliderRef = React.createRef();
  componentDidMount(){
    this.sliderRef.current.doSlider();   //在父組件中調用
  }
  render() {
      return (
          <div className = 'slider-container'> <Slider ref={this.sliderRef}/> </div>
      );
  }
}
//子組件定義方法
export default class Slider extends Component {
  doSlider() {
    console.log(111111)
  }
  render() {
   return (
      <div>Slider</div>
    )
  }
}
複製代碼

受控組件和非受控組件

包含表單的組件分類:

  1. 受控組件 其實是Vue的雙向綁定
class Login extends React.Component{
    //初始化狀態
    state = {
        username:'', //用戶名
        password:'' //密碼
    }

    //保存用戶名到狀態中
    saveUsername = (event)=>{
        this.setState({username:event.target.value})
    }

    //保存密碼到狀態中
    savePassword = (event)=>{
        this.setState({password:event.target.value})
    }

    //表單提交的回調
    handleSubmit = (event)=>{
        event.preventDefault() //阻止表單提交
        const {username,password} = this.state
        alert(`你輸入的用戶名是:${username},你輸入的密碼是:${password}`)
    }

    render(){
        return(
            <form onSubmit={this.handleSubmit}> 用戶名:<input onChange={this.saveUsername} type="text" name="username"/> 密碼:<input onChange={this.savePassword} type="password" name="password"/> <button>登陸</button> </form>
        )
    }
}
複製代碼
  1. 非受控組件不太建議使用 ref使用較多
class Login extends React.Component{
    handleSubmit = (event)=>{
        event.preventDefault() //阻止表單提交
        const {username,password} = this
        alert(`你輸入的用戶名是:${username.value},你輸入的密碼是:${password.value}`)
    }
    render(){
        return(
            <form onSubmit={this.handleSubmit}> 用戶名:<input ref={c => this.username = c} type="text" name="username"/> 密碼:<input ref={c => this.password = c} type="password" name="password"/> <button>登陸</button> </form>
        )
    }
}
複製代碼

組件的生命週期

理解

1. 組件從建立到死亡它會經歷一些特定的階段。
2. React組件中包含一系列勾子函數(生命週期回調函數), 會在特定的時刻調用。
3. 咱們在定義組件時,會在特定的生命週期回調函數中,作特定的工做。
生命週期回調函數 <=> 生命週期鉤子函數 <=> 生命週期函數 <=> 生命週期鉤子

生命週期流程圖(舊)

1.初始化階段:由ReactDOM.render()觸發---初次渲染

  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()

2.更新階段:由組件內部this.setSate()或父組件從新render觸發

  • shouldComponentUpdate()
  • componentWillUpdate()
  • render()
  • componentDidUpdate()

3.卸載組件:由ReactDOM.unmountComponentAtNode()觸發

  • componentWillUnmount()

圖片1.png

生命週期流程圖(新)

1.初始化階段:由ReactDOM.render()觸發---初次渲染

  • constructor()
  • getDerivedStateFromProps
  • render()
  • componentDidMount() =====> 經常使用 通常在這個鉤子中作一些初始化的事,例如:開啓定時器、發送網絡請求、訂閱消息

2.更新階段:由組件內部this.setSate()或父組件從新render觸發

  • getDerivedStateFromProps
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate
  • componentDidUpdate()

3.卸載組件:由ReactDOM.unmountComponentAtNode()觸發

componentWillUnmount()====> 經常使用
通常在這個鉤子中作一些收尾的事,例如:關閉定時器、取消訂閱消息

圖片2.png

強制刷新:this.forceUpdate() ————跟vue強制刷新一致

class Count extends React.Component{
    //構造器
    constructor(props){
        console.log('Count---constructor');
        super(props)
        //初始化狀態
        this.state = {count:0}
    }

    //加1按鈕的回調
    add = ()=>{
        //獲取原狀態
        const {count} = this.state
        //更新狀態
        this.setState({count:count+1})
    }

    //卸載組件按鈕的回調
    death = ()=>{
        ReactDOM.unmountComponentAtNode(document.getElementById('test'))
    }

    //強制更新按鈕的回調
    force = ()=>{
        this.forceUpdate()
    }

    //若state的值在任什麼時候候都取決於props,那麼可使用getDerivedStateFromProps
    static getDerivedStateFromProps(props,state){
        console.log('getDerivedStateFromProps',props,state);
        return null
    }

    //在更新以前獲取快照
    getSnapshotBeforeUpdate(){
        console.log('getSnapshotBeforeUpdate');
        return 'atguigu'
    }

    //組件掛載完畢的鉤子
    componentDidMount(){
        console.log('Count---componentDidMount');
    }

    //組件將要卸載的鉤子
    componentWillUnmount(){
        console.log('Count---componentWillUnmount');
    }

    //控制組件更新的「閥門」
    shouldComponentUpdate(){
        console.log('Count---shouldComponentUpdate');
        return true
    }

    //組件更新完畢的鉤子
    componentDidUpdate(preProps,preState,snapshotValue){
        console.log('Count---componentDidUpdate',preProps,preState,snapshotValue);
    }

    render(){
        console.log('Count---render');
        const {count} = this.state
        return(
            <div> <h2>當前求和爲:{count}</h2> <button onClick={this.add}>點我+1</button> <button onClick={this.death}>卸載組件</button> <button onClick={this.force}>不更改任何狀態中的數據,強制更新一下</button> </div>
        )
    }
}

//渲染組件
ReactDOM.render(<Count count={199}/>,document.getElementById('test'))
複製代碼

重要的勾子(主要使用的函數)

  1. render:初始化渲染或更新渲染調用
  2. componentDidMount:開啓監聽, 發送ajax請求
  3. componentWillUnmount:作一些收尾工做, 如: 清理定時器

即將廢棄的勾子

  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate

如今使用會出現警告,下一個大版本須要加上UNSAFE_前綴才能使用,之後可能會被完全廢棄,不建議使用。

虛擬DOM與DOM Diffing算法

圖片3.png

關於diff算法的問題:

1). react/vue中的key有什麼做用?(key的內部原理是什麼?)
2). 爲何遍歷列表時,key最好不要用index?
複製代碼
  1. 虛擬DOM中key的做用:

詳細的說:當狀態中的數據發生變化時,react會根據【新數據】生成【新的虛擬DOM】, 隨後React進行【新虛擬DOM】與【舊虛擬DOM】的diff比較,比較規則以下:

a. 舊虛擬DOM中找到了與新虛擬DOM相同的key:

  1. 若虛擬DOM中內容沒變, 直接使用以前的真實DOM\
  2. 若虛擬DOM中內容變了, 則生成新的真實DOM,隨後替換掉頁面中以前的真實DOM\

b. 舊虛擬DOM中未找到與新虛擬DOM相同的key根據數據建立新的真實DOM,隨後渲染到到頁面

  1. 用index做爲key可能會引起的問題:

a.若對數據進行:逆序添加、逆序刪除等破壞順序操做:會產生沒有必要的真實DOM更新 ==> 界面效果沒問題, 但效率低
b.若是結構中還包含輸入類的DOM:會產生錯誤DOM更新 ==> 界面有問題。
c.注意!若是不存在對數據的逆序添加、逆序刪除等破壞順序操做,僅用於渲染列表用於展現,使用index做爲key是沒有問題的。

  1. 開發中如何選擇key?

最好使用每條數據的惟一標識做爲key, 好比id、手機號、身份證號、學號等惟一值。 若是肯定只是簡單的展現數據,用index也是能夠的。

相關文章
相關標籤/搜索