React01-开始
在记录react的学习之路开始,我想先谈谈为什么会有react
假如有一个页面,页面上有一个dom
,要操作这个dom
的内容,有如下步骤:
- 在js中获取dom
- 修改dom的内容
- 把内容回填到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
里面的值也会跟着改变,要解决这个问题,最简单的方法就是再声明一个变量和函数来分别给box1
和box2
调用,但是如果页面上有多个相同的功能模块呢?那就得声明多个一样的变量和函数,这样的代码明显是不好的。
先来看看如何给函数传参
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的规则有一点多,目前有如下几点:
-
class
必须extends React.Component
-
class
里的局部变量必须写在constructor
的this.state={变量的key:变量的value}
里,并且必须要在constructor
里写this.state={}
-
constructor
里的第一行必须是super()
这个是ES6的规定 - 如果要在
class
的函数里操作this.state={}
里的变量,必须用this.setState({修改变量})
- 外部传入的值默认挂在
props
下,props
是父类React.Component
构造出来的一个属性,使用时用{this.props.键名}
- 如果要在函数里使用
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({})
会将大批量的更新合并成一次(很有可能是异步更新)