React01-开始

2019-01-04  本文已影响0人  我也不知道啊丶

在记录react的学习之路开始,我想先谈谈为什么会有react
假如有一个页面,页面上有一个dom,要操作这个dom的内容,有如下步骤:

  1. 在js中获取dom
  2. 修改dom的内容
  3. 把内容回填到dom里,通知页面更新

代码示例:

<body>
    <span id="result">0</span>
    <button id="add">+</button>
    <button id="minus">-</button>
</body>
<script>
    let result = document.querySelector('#result')
    let add = document.querySelector('#add')
    let minus = document.querySelector('#minus')

    add.addEventListener('click',function(){
        let number = parseInt(result.innerHTML,10)
        number += 1
        result.innerHTML = number
    })

    minus.addEventListener('click',function(){
        let number = parseInt(result.innerHTML,10)
        number -= 1
        result.innerHTML = number
    })
</script>

这段代码就是从页面获取dom,接着修改dom内容然后回填到dom里去
虽然简单,但是 可能有点繁琐
那么有没有可能省略其中的某些步骤呢
很明显
修改内容、把内容回填到dom中不能省略
那么就只能把获取dom省去
既然不获取dom,那么干脆开始在页面中就不写dom,等操作完数据(内容)后直接插入页面中就行了
于是,有了react.js

正式开始之前

在正式开始之前,想说一下jsx
jsx可以让你像写html一样写js

<div className='box'>
    <span className='num'>{变量}</span>
    <button onClick={函数}>+</button>
    <button onClick={函数}>-</button>
</div>

比如这段代码,看起来是不是很神奇又很眼熟?
我们来用React来改写最初的那个功能

<body>
    <div id='root'>

    </div>
</body>
// html部分就写完了,只需要一个id告诉React虚拟DOM该插到哪里
<script>
    let number = 0;
    function add(){
        number += 1;
        render()
    }
    function minus(){
        number -= 1;
        render()
    }
    render()
    function render(){
        ReactDOM.render(
            <div>
                <span>{number}</span>
                <button onClick={add}>+</button>
                <button onClick={minus}>-</button>
            </div>,
            document.getElementById('root')
        )
    }

ok,就这样写完了一个
逻辑是不是比上面的版本清晰很多?
React牛逼的地方不仅如此
还是来看看上面的代码,因为只是实现一个很简单的功能,所以ReactDOM.render里只用写那几行代码,
如果是构建一个大型页面呢?如果还是用这种方法,那么ReactDOM.render里可能就要写几千行代码来描述DOM,这是不能忍受的,维护起来也十分麻烦,所以需要一个东西把这些结构分块包裹起来
最简单的就是用函数包裹,来看看

function App(){
    return (
        <div>
            <span>{number}</span>
            <button onClick={add}>+</button>
            <button onClick={minus}>-</button>
        </div>
    )
}

然后下面改成

function render(){
    ReactDOM.render(
        React.createElement(App),
        document.getElementById('root')
    )
}

React.createElement()用来创造虚拟DOM
但是我们用的是JSX,所以可以改成下面这种写法

function render(){
    ReactDOM.render(
        <App />,
        document.getElementById('root')
    )
}

再往下看,如果页面上有多个这种功能

function App(){
    return (
        <div>
            <box1 />
            <box2 />
        </div>
    )
}
function Box1(){
    return (
        <div>
            <span>{number}</span>
            <button onClick={add}>+</button>
            <button onClick={minus}>-</button>
        </div>
    )
}
function Box2(){
    return (
        <div>
            <span>{number}</span>
            <button onClick={add}>+</button>
            <button onClick={minus}>-</button>
        </div>
    )
}

最简单的React的函数组件
这样写会有一个问题,当点击box1中的+按钮时,box2里面的值也会跟着改变,要解决这个问题,最简单的方法就是再声明一个变量和函数来分别给box1box2调用,但是如果页面上有多个相同的功能模块呢?那就得声明多个一样的变量和函数,这样的代码明显是不好的。
先来看看如何给函数传参

function App(){
    return (
        <div>
            <box1 name='加一' />
            <box2 />
        </div>
    )
}
function Box1(obj){
    return (
        <div>
            我的name是:{obj.name}
            <span>{number}</span>
            <button onClick={add}>+</button>
            <button onClick={minus}>-</button>
        </div>
    )
}

注意,在React里,最好不要在函数里修改传入的属性
再回头来看上面的问题,想想在js里,有什么东西既能满足函数的功能,又有自己的作用域

要注意的是,在React里使用class,必须要继承 React.Component

class App2 extends React.Component{
    render(){ //局部render
        return(
            <div>
                {this.props.name}
            </div>
    )
}
function render(){
    ReactDOM.render(
        <App2 name='我是App2'/>,
        document.getElementById('root')
    )
}

props是由继承的React.Component构造的,必须使用
来设置一下自己的局部变量

class App2 extends React.Component{
    constructor(props){  //必须这样写
        super(props) //必须加上这句
        this.state = {
            number : 0
        }
    }
    add(){
        this.setState({ //必须写在this.setState里,为什么可以看ES6里的set和get
            number : this.state.number + 1 //如果这里要使用this.state.number,在下面引用函数时必须使用this.add.bin(this)或() => this.add
        })
    }
    minus(){
        this.setState({
            number : this.state.number - 1
        })
    }
    render(){ //局部render
        return(
            <div>
                {this.props.name}
                <br/>
                <span>{this.state.number}</span>
                <button onClick={this.add.bind(this)}>+</button>
                <button onClick={this.minus.bind(this)}>-</button>
            </div>
        )
    }
}

function render(){
    ReactDOM.render(
        <App2 name='我是App2'/>,
        document.getElementById('root')
    )
}

可以看到虽然class的功能更加强大,但是React里使用class的规则有一点多,目前有如下几点:

  1. class必须extends React.Component
  2. class里的局部变量必须写在constructorthis.state={变量的key:变量的value}里,并且必须要在constructor里写this.state={}
  3. constructor里的第一行必须是super()这个是ES6的规定
  4. 如果要在class的函数里操作this.state={}里的变量,必须用this.setState({修改变量})
  5. 外部传入的值默认挂在props下,props是父类React.Component构造出来的一个属性,使用时用{this.props.键名}
  6. 如果要在函数里使用this.state={}里的变量,用this.state.变量名这种方法时,在调用函数时必须用
    this.函数名.bind(this)或者() =>this.函数名,这是因为React默认在调用函数时是以函数.call(undefined,方法),所以如果不用bind()传入this或者用箭头函数,this就会是undefined
function App(){
    return (
        <div>
            <Box1 name='我是class1' />
            <Box2 name='我是class2' />
        </div>
    )
}

class Box1 extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            number : 0
        }
    }
    add(){
        this.setState({
            number : this.state.number + 1
        })
    }
    minus(){
        this.setState({
            number : this.state.number - 1
        })
    }
    render(){
        return (
            <div className='box1'>
                <p>{this.props.name}</p>
                <span>{this.state.number}</span>
                <button onClick={this.add.bind(this)}>+</button>
                <button onClick={this.minus.bind(this)}>-</button>
            </div>
        )
    }
}

class Box2 extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            number : 0
        }
    }
    add(){
        this.setState({
            number : this.state.number + 2
        })
    }
    minus(){
        this.setState({
            number : this.state.number - 2
        })
    }
    render(){
        return (
            <div className='box2'>
                <p>{this.props.name}</p>
                <span>{this.state.number}</span>
                <button onClick={this.add.bind(this)}>+2</button>
                <button onClick={this.minus.bind(this)}>-2</button>
            </div>
        )
    }
}

function render(){
    ReactDOM.render(
        <App />,
        document.getElementById('root')
    )
}

render()

React的class组件
来说一下为什么在函数中不自己render()而是用setState({})
如果是自己来render(),可能会在短时间内多次render(),导致页面卡顿
setState({})会将大批量的更新合并成一次(很有可能是异步更新)

上一篇下一篇

猜你喜欢

热点阅读