react

webpack --help -p :
shortcut for --optimize-minimize --define process.env.NODE_ENV="production"

理解JSX語法

JSX語法注意點(diǎn):

  • 是 XML 不是 HTML,所以所有的標(biāo)簽都要閉合,如 <img /> 或 <div />。
  • 每段 JSX 里的 XML 只能有一個(gè)根元素,不然就報(bào)錯(cuò)。
  • XML 里面可以用 {} 混入 任何 JS 代碼

JSX語法的理解:分為tagname,attributes,children三項(xiàng)來理解

例如: const jsx = <div>Hello Jsx</div>
經(jīng)過babel之后為:

var jsx = React.createElement(
  "div",
  null,
  "Hello Jsx"
);

這樣可以理解為:

{
  tagname: "div",
  attributes: null,
  children: "Hello Jsx"
}

組件的tagname就不是標(biāo)準(zhǔn)的html標(biāo)簽了,如果有props,那么attributes也不是null了,對(duì)于children來說,如果內(nèi)部還有嵌套標(biāo)簽,那么依次做一個(gè)遞歸。

組件

react聲明組件時(shí),第一個(gè)字母必須大寫

兩種寫法:

  1. class component
class Welcome extends React.Component {
    render () {
        return <h1>hello,{this.props.name}</h1>;
    }
}

或者:
2.functional component

function Welcome(props) {
    return <h1>hello,{props.name}</h1>
}

組件中的數(shù)據(jù)源

  1. props (props是父組件到子組件的,props should be pure,即不予許直接修改props)

  2. state (state是自身維護(hù)的數(shù)據(jù)狀態(tài),但也只可以通過setState修改)

組件中不可以改變props的值,state是組件中可以改變的東西

但是要使用this.setState()方法才能改變state的值

關(guān)于setState() , 參考

要理解2點(diǎn):

  • setState不會(huì)立刻改變React組件中state的值
  • 函數(shù)式的setState用法(即setState()方法可以接受一個(gè)函數(shù)作為參數(shù))
import React from 'react';
class Welcome extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            date: new Date()
        }
        setInterval(() => {
            this.setState({
                date: new Date()
            })
        })
    }
    render () {
        return (
            <div>
                <h1>hello, {this.props.name}</h1>
                <h2>{this.state.date.toString()}</h2>
            </div>
        )
    }
}

export default Welcome;


生命周期

聲明周期英文文檔

React 的生命周期包括三個(gè)階段:mount(掛載)、update(更新)和 unmount(移除)

mount

mount 就是第一次讓組件出現(xiàn)在頁面中的過程。這個(gè)過程的關(guān)鍵就是 render 方法。React 會(huì)將 render 的返回值(一般是虛擬 DOM,也可以是 DOM 或者 null)插入到頁面中。

這個(gè)過程會(huì)暴露幾個(gè)鉤子(hook)方便你往里面加代碼:

  • constructor() 初始化props 和 state
  • componentWillMount() 準(zhǔn)備插入render中return的內(nèi)容
  • render() 開始插入
  • componentDidMount() 插入之后想進(jìn)行的操作

update

mount 之后,如果數(shù)據(jù)有任何變動(dòng),就會(huì)來到 update 過程,這個(gè)過程有 5 個(gè)鉤子:

  • 1.componentWillReceiveProps(nextProps) - 我要讀取 props 啦!
    1. shouldComponentUpdate(nextProps, nextState) - 請(qǐng)問要不要更新組件?true / false
    1. componentWillUpdate() - 我要更新組件啦!
    1. render() - 更新!
    1. componentDidUpdate() - 更新完畢啦!

unmount

當(dāng)一個(gè)組件將要從頁面中移除時(shí),會(huì)進(jìn)入 unmount 過程,這個(gè)過程就一個(gè)鉤子:

  • componentWillUnmount() - 我要死啦!

你可以在這個(gè)組件死之前做一些清理工作。

一般在下列鉤子中應(yīng)用setState():

  • componentWillMount
  • componentDidMount
  • componentWillReceiveProps

事件綁定

  • 首先明確一個(gè)概念,就是在<div onClick=fn></div>這個(gè)DOM綁定中,onClick后面的fn是一個(gè)函數(shù),而不是一個(gè)函數(shù)執(zhí)行的結(jié)果,所以不能寫成<div onClick=fn()></div>。(重點(diǎn)?。?/li>

明確了函數(shù)綁定時(shí),寫的是一個(gè)函數(shù)而不是函數(shù)運(yùn)行的結(jié)果之后,繼續(xù)明確第二個(gè)概念:

  • bind(),對(duì)于一個(gè)函數(shù)fn來說,fn.bind(window)同樣是一個(gè)函數(shù),只不過運(yùn)行的Context指定為了window。fn.bind(window)本身并沒有執(zhí)行(所以仍然是一個(gè)函數(shù)而不是函數(shù)運(yùn)行的結(jié)果?。?/li>

明確了上述2點(diǎn)之后,再來看react中的事件綁定。

先來看一個(gè)App組件:

import React from 'react'
import ReactDom from 'react-dom'
import './index.css'

const rootDom = document.querySelector('#root')

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      isOn: true
    }
  }
  render () {
    return (
      //這里點(diǎn)擊的時(shí)候的this已經(jīng)不是當(dāng)前組件了,所以要bind為當(dāng)前組件,不然是沒有testClick函數(shù)的
      <div onClick={this.testClick.bind(this)}> 
        <button>{this.state.isOn ? 'ON' : 'OFF' }</button>
      </div>
    )
  }
  testClick () {
    this.setState( (prevState) => { //setState()也能傳函數(shù)作為參數(shù),沒什么好說的
      return {
        isOn: !prevState.isOn
      }
    })
  }
}
ReactDom.render(<App></App>,rootDom)

這么一看就能理解react中的事件綁定了。

  • 還有一個(gè)小問題,對(duì)于標(biāo)簽?zāi)J(rèn)行為的阻止,不像Vue一樣有.prevent這種修飾符,還是要自己寫的。
    來看個(gè)例子:
import React from 'react'
import ReactDom from 'react-dom'
import './index.css'

const rootDom = document.querySelector('#root')

let App = () => {
  function preventClick (e) { // 注意這里的e,是react自己傳過來的  本身沒做任何操作
    e.preventDefault() //
    console.log('default click has been prevent')
  }
  return (
    <div>
      {/*上面已經(jīng)說過,綁定事件只能寫函數(shù),不能寫函數(shù)的運(yùn)行結(jié)果,所以如果寫了preventClick()就會(huì)報(bào)錯(cuò)*/}
      <a  onClick={preventClick}>click me</a>
    </div>
  )
}
ReactDom.render(<App></App>,rootDom)

對(duì)于上述問題 又引申出了一個(gè)小問題,如果我想在阻止a標(biāo)簽的同時(shí),事件處理函數(shù)還想接收一些其他的參數(shù)進(jìn)行處理怎么寫呢。

對(duì)于React,上面例子的e.preventDefault()中的e是React自己傳過來的。我們?nèi)绻认雽慹,還想有自己規(guī)定的一些參數(shù)的話,要這么寫:

let App = () => {
  function preventClick (str,e) { // 如果有其他自定義的參數(shù),那么e永遠(yuǎn)是最后一項(xiàng),而且同樣也是React自己傳過來的。
    e.preventDefault() //
    console.log(str)
  }
  return (
    <div>
      {/*上面已經(jīng)說過,綁定事件只能寫函數(shù),不能寫函數(shù)的運(yùn)行結(jié)果,所以如果寫了preventClick()就會(huì)報(bào)錯(cuò)*/}
      <a  onClick={preventClick.bind(this,'testString')}>click me</a>
      {/*這里的preventClick.bind(this,'testString')同樣也是一個(gè)函數(shù),并不是函數(shù)運(yùn)行結(jié)果*/}
    </div>
  )
}

上面的例子中,我們想在preventClick()中傳一些自己用的參數(shù),那么在點(diǎn)擊的時(shí)候,就要bind到組件本身上,然后再傳入想要的參數(shù),注意e是不用我們自己寫到參數(shù)中去的,回到函數(shù)本身的參數(shù)上,自定義的參數(shù)在前,e永遠(yuǎn)是最后一項(xiàng)。

擺脫React中操蛋的bind(this)的方法

上面的事件綁定中已經(jīng)提到了bind,因?yàn)?strong>事件觸發(fā)時(shí)的this已經(jīng)不是當(dāng)前組件本身了,所以我們要將處理函數(shù)的this重新設(shè)置為當(dāng)前組件,所以有了隨便一個(gè)函數(shù)后面都要bind(this)的情況。

先來看個(gè)例子:

class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render (){
    return (
      <div>
        <button onClick={this.testClick.bind(this)}>click me</button>
      </div>
    )
  }
  testClick() { // 普通函數(shù)在被調(diào)用的時(shí)候,就有了this,所以要加bind()到App組件上
    console.log(this) 
  }
}

上述例子中,testClick是App組件上的函數(shù),點(diǎn)擊時(shí)的this已經(jīng)不是App組件了,所以不能正確調(diào)用。要將其寫為this.testClick.bind(this)而不是this.testClick。

為了擺脫上述這種煩人的寫法,我們可以有如下幾種方法:

  1. constructor中將當(dāng)前函數(shù)重新賦值。
class App extends React.Component {
  constructor(props) {
    super(props)
    this.testClick = this.testClick.bind(this) //這的this是App組件,直接將testClick重新賦值一下
  }
  render (){
    return (
      <div>
        <button onClick={this.testClick}>click me</button>
      </div>
    )
  }
  testClick() { // 普通函數(shù)在被調(diào)用的時(shí)候,就有了this,所以要加bind()到App組件上
    console.log(this)
  }
}

我們?cè)赾onstructor中給testClick重新賦了一次值,testClick變?yōu)榱艘粋€(gè)運(yùn)行上下文為App組件的函數(shù),下面直接onClick={this.testClick}即可。

  1. public class fields syntax(不知道怎么翻譯,就是利用箭頭函數(shù)沒有this的特性)
class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render (){
    return (
      <div>
        <button onClick={this.testClick}>click me</button>
      </div>
    )
  }
  testClick = () => { //箭頭函數(shù)沒有本身的this 所以定義的時(shí)候this已經(jīng)確定為App組件了,所以不用bind
    console.log(this)
  }
}

我們利用箭頭函數(shù)本身沒有this的特性,在App中定義testClick的時(shí)候,testClick的this就已經(jīng)確定為當(dāng)前上下文App組件了。所以后面直接調(diào)用onClick={this.testClick}即可。

  1. 在回調(diào)中使用箭頭函數(shù)
class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render (){
    return (
      <div>
        <button onClick={(e) => {return this.testClick(e)}}>click me</button>
      </div>
    )
  }
  testClick() { 
    console.log(this)
  }
}

每次渲染組件時(shí),都會(huì)生成一個(gè)全新的回調(diào)。但在有時(shí)候?qū)⑵渥鳛閜rop傳給子組件時(shí),會(huì)引發(fā)一次額外的渲染。所以,建議用constructor 和public class fields syntax兩種方式避免bind(this)這種寫法。

值得一提的是:

上面說的想給事件處理函數(shù)傳遞另外自定義的參數(shù)時(shí),綁定事件的時(shí)候是不用寫e的,因?yàn)閞eact是自動(dòng)幫你把e作為最后一個(gè)參數(shù)傳遞的

<button onClick={this.testClick.bind(this, id)}>click me</button>

就像上述例子一樣,我們想傳一個(gè)額外的參數(shù)id,在id后面是不需要寫e的,this.testClick.bind(this, id,e)這種是不用寫的。

但是! 在我們提到的第3種方法,回調(diào)中使用函數(shù)的情況下,是需要開發(fā)者自己寫上e的!

<button onClick={(e) => this.testClick(id, e)}>click me</button>

就像這樣,自定義參數(shù)id的后面還要寫上e。

詳情參考文檔:With an arrow function, we have to pass it explicitly, but with bind any further arguments are automatically forwarded.

文檔地址

React中的ref

想直接修改原生DOM或者是組件的時(shí)候,可以使用ref。

  1. 原生DOM上
class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render() {
    return (
      <div>
        {/*ref callback*/}
        <input type="text" ref={(input) => this.testRef = input} />
        <input type="button" onClick={this.focus.bind(this)} value="Click" />
      </div>
    )
  }
  focus() {
    this.testRef.focus();
  }
}

上述例子就是一個(gè)原生DOM上使用ref的例子,react在原生的input加載完成后,通過一個(gè)回調(diào)函數(shù),(input) => this.testRef = input,這里的回調(diào)參數(shù)input就是底層的DOM,接受DOM作為參數(shù),存到testRef中。引用的時(shí)候,直接就引用到了原生的DOM。

  1. ref ====> class Component
// ref ===> class Component
class App extends React.Component {
  render() {
    return (
      <TestComponent ref={(testComponent) => this.classCom = testComponent}></TestComponent>
    )
  }
  componentDidMount () {
    console.log(this.classCom.state)
  }
}

class TestComponent extends React.Component {
  constructor() {
    super()
    this.state = {
      name: 'children Component'
    }
  }
  render() {
    return (
      <div>
        <input type="text"/>
      </div>
    )
  }
}

這里的ref回調(diào)函數(shù)中,(testComponent) => this.classCom = testComponent的參數(shù)為已經(jīng)加載的 React 實(shí)例,我們可以在父組件中通過this.classCom訪問到它。

  1. ref ====> functional Component

你不能在函數(shù)式組件上使用 ref 屬性,因?yàn)樗鼈儧]有實(shí)例,但是可以對(duì)其內(nèi)部的原生DOM使用ref,參見第一條。

React中突變數(shù)據(jù)的處理

經(jīng)常會(huì)遇到設(shè)置了state之后,進(jìn)行setState()了之后,頁面沒有進(jìn)行渲染的情況。(尤其是使用PureComponent的時(shí)候)。

這種情況經(jīng)常是由于操作習(xí)慣不好造成了原數(shù)據(jù)結(jié)構(gòu)的突變。

  • 先來解釋什么是原數(shù)據(jù)突變
    let arr = [1]
    arr.push(2)
    console.log(arr) //[1, 2]

我們有一個(gè)數(shù)組,進(jìn)行了push操作之后,原先的數(shù)組的值改變了,由[1]變?yōu)榱?code>[1,2]。

看下一個(gè)例子:

    let arr2 = [1]
    let arr3 = arr2.concat([2])
    console.log(arr2) //[1]
    console.log(arr3) //[1, 2]

同樣是一個(gè)數(shù)組,進(jìn)行了concat操作之后,原先的arr2的值并沒有發(fā)生改變,我們把concat之后的結(jié)果賦給了一個(gè)新變量arr3來儲(chǔ)存。

  • React中setState()時(shí)我們?cè)撛趺磳憯?shù)據(jù)

上面我們提到了push操作會(huì)改變?cè)瓟?shù)據(jù),這在React中是不應(yīng)該的。

setState時(shí),我們應(yīng)該return的是一個(gè)新對(duì)象,而不是原對(duì)象。所以我們要避免那些會(huì)修改原數(shù)據(jù)的操作,例如push。

比如說,我們?cè)谝粋€(gè)組件中通過push()修改了數(shù)組,然后有一個(gè)PureComponent子組件把這個(gè)數(shù)組作為props渲染頁面,這時(shí)候雖然我們?cè)诟附M件中修改了數(shù)組的值,但是setState()return的時(shí)候,原數(shù)據(jù)結(jié)構(gòu)已經(jīng)變了,這時(shí)候雖然本身的state已經(jīng)改變了,但是傳到子組件的props并沒有改變。

為了避免這種情況發(fā)生,我們一般可以采用concat等不會(huì)引起原數(shù)據(jù)突變的操作。

如果非要用push的話,可以通過Object.assgin()或者...spread語法來進(jìn)行一份數(shù)據(jù)的拷貝,在進(jìn)行操作。

比如說上述的arr,我們可以這么操作:

    this.state = {
      arr: [1]
    };
//進(jìn)行setState()
    this.setState( (prevState) => {
      return {
        arr: [...prevState.arr,2]
      }
    })

如果目標(biāo)數(shù)據(jù)是對(duì)象的話,可以通過Object.assgin()來進(jìn)行操作

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 說在前面 關(guān)于 react 的總結(jié)過去半年就一直碎碎念著要搞起來,各(wo)種(tai)原(lan)因(le)。心...
    陳嘻嘻啊閱讀 7,034評(píng)論 7 41
  • 自己最近的項(xiàng)目是基于react的,于是讀了一遍react的文檔,做了一些記錄(除了REFERENCE部分還沒開始讀...
    潘逸飛閱讀 3,741評(píng)論 1 10
  • 3. JSX JSX是對(duì)JavaScript語言的一個(gè)擴(kuò)展語法, 用于生產(chǎn)React“元素”,建議在描述UI的時(shí)候...
    pixels閱讀 2,979評(píng)論 0 24
  • 深入JSX date:20170412筆記原文其實(shí)JSX是React.createElement(componen...
    gaoer1938閱讀 8,183評(píng)論 2 35
  • 使用 create-react-app 快速構(gòu)建 React 開發(fā)環(huán)境 項(xiàng)目的目錄結(jié)構(gòu)如下: React JSX ...
    majun00閱讀 573評(píng)論 0 0

友情鏈接更多精彩內(nèi)容