react快速入门(一)

215 阅读8分钟

Hello World

最简单的代码示例如下

ReactDOM.render(<h1>Hello world</h1>, document.getElementById('root'))

什么是jsx

概念

jsx是javascript的语法扩展,通常与react一起使用,用来描述UI的外观,jsx具有javascript的全部功能。

简单示例

这是一个简单的jsx示例

const element = <h1>Hello world</h1>

等同于

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

为什么选择jsx

react本质上是一个渲染器,通过将标记与逻辑松散耦合的组件来实现,react不需要使用jsx,也可以使用React.createElement去描述UI,但是jsx用来描述UI更加方便简洁,而且还允许react显示更多有用的错误和警告信息。

复杂示例

jsx编译后成为常规的javascript函数调用,这意味着如果使用函数方法,可以在jsx中使用if语句和for循环进行变量的处理,jsx中还可以嵌入表达式、指定属性、绑定事件等

const formatName = (user) => {
    return user.name1 + ' ' + user.name2
}

const buttonClick = (e) => {
    alert(e)
}

const user = {
    name1: 'name1',
    name2: 'name2',
}

const buttonType = "button"

const element = (
            <button
                type={buttonType}
                onClick={buttonClick}
            >
                Hello, {formatName(user)}
            </button>
            )


ReactDOM.render(element, document.getElementById('root'))

进阶示例

下方示例展示了

  • 绑定事件
  • 使用表达式
  • 遍历数据
  • 嵌套结构
  • 嵌入变量
  • 设置属性
// 使用表达式
const formatName = (user) => {
    return user.name1 + ' ' + user.name2
}

// 绑定事件
const buttonClick = (e) => {
    alert(e)
}

// 遍历数据
const list = ['list1', 'list2', 'list3', 'list4']
const renderList = (list) => {
    return list.map((item,index)=>{
        return <span key={index}>{item}</span>
    })
}

// 按钮内容
const user = {
    name1: 'name1',
    name2: 'name2',
}

// 按钮颜色
const color = '#ccc'

// 设置属性
const buttonType = "button"

const element = (
        <div>
            {renderList(list)}
            <button
                // 设置css
                style={{'color': color}}
                // 设置属性
                type={buttonType}
                // 绑定事件
                onClick={buttonClick}
            >
                Hello, {formatName(user)}
            </button>
        </div>
        )

ReactDOM.render(element, document.getElementById('root'))

当然,上面的示例都只是一些简单的示例,后面还有更复杂的示例

组件

概念

组件就像javascript函数,它们接受任意的输入(props),并返回描述应该在屏幕上显示的内容的react元素

需要注意的是,组件需要以大写为开头

在react中组件的形式分为两种,一种是函数式组件,另外一种是类组件

函数式组件

函数式组件有以下特性:

  • 自身没有状态
  • 返回一个元素
  • 没有生命周期

在react中所有的函数组件,必须是纯函数,即不改变原有属性

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

类组件

类组件有以下特性:

  • 使用es6的class方式声明组件
  • 需要继承 React.Component 来使用react的生命周期
  • 有声明周期
  • 自身有状态
  • 可以通过调用setState方法去更新组件
  • render函数中通过 retur一个元素进行渲染
class Welcom extends React.Component {
    
    render(){
    
        return <h1>Hello, {this.props.name}</h1>
    }
}

示例

在jsx中不只是html标签可以使用,我们声明的组件也可以使用


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

const element = <Welcome name="name1" />

ReactDOM.render(
    element,
    document.getElementById('root')
)

复杂示例

组件可以复用,可以嵌套,我们可以把结构相同或者某一功能封装成一个组件进行使用,不要害怕将组件拆分成较小的组件


function Nav (props){
    return (
        <div className="nav">
            <img src={props.logo} alt="logo" />
            <ul>
                <li><a href={props.nav[0].href}>{props.nav[0].name}</a></li>
                <li><a href={props.nav[1].href}>{props.nav[1].name}</a></li>
                <li><a href={props.nav[2].href}>{props.nav[2].name}</a></li>
                <li><a href={props.nav[3].href}>{props.nav[3].name}</a></li>
            </ul>
            <div className="user">
                <img src={props.user.pic} alt="user" />
                <span>{props.user.name}</span>
            </div>
        </div>
    )
}

const element = (
    <div className="nav-warp">
        <Nav logo="" nav={[]} user={{pic:'', name:''}} />
    </div>
)

ReactDOM.render(
    element,
    document.getElementById('root')
)

上面是一个导航栏的结构,我们可以将它细分一下


function Logo(props) {
    return <img src={props.logo} alt="logo" />
}

function NavList(props) {
    return (
        <ul>
            {props.nav.map((item, index) => {
                return (
                    <li key={index}><a href={item.href}>{item.name}</a></li>
                )
            })}
        </ul>
    )
}

function User(props) {
    return (
        <div className="user">
            <img src={props.user.pic} alt="user" />
            <span>{props.user.name}</span>
        </div>
    )
}

function Nav(props){
    return (
        <div className="nav">
            <Logo logo="" />
            <NavList nav={[]} />
            <User user={{pic:'', name:''}}/>
        </div>
    )
}

const element = (
    <div className="nav-warp">
        <Nav />
    </div>
)

ReactDOM.render(
    element,
    document.getElementById('root')
)

状态和生命周期

以es6的class声明的组件继承了 React.Component 会拥有自己的状态和生命周期

state && setState

state是指组件自身的状态

用状态控制组件变化 可以把一个组件看做一个状态机, 每一次状态对应于组件的一个 ui

需要注意的是setState是一个异步函数,并不是更改以后就立刻会生效的

import React, { Component } from 'react';

class StateDemo extends Component {

  state = {
    secondsElapsed: 0
  }

  tick(){
    this.setState({ secondsElapsed: this.state.secondsElapsed + 1 });
  }

  componentDidMount(){
    this.interval = setInterval( this.tick.bind(this), 1000 );
  }

  componentWillUnmount(){
    clearInterval(this.interval);
  }

  render(){
    return (
      <div>目前已经计时:{this.state.secondsElapsed}秒</div>
    )
  }
}

export default StateDemo;

props

props是指父组件传递给子组件的状态

通过 this.props 可以获取传递给该组件的属性值,还可以通过定义 getDefaultProps 来指定默认属性值(这是ES5的写法,ES6定义组件的默认props可以直接写props) 下面几个是props的常用API: this.props.children this.props.map this.props.filter props是调用组件的时候传递进去的数据,一般用于组件树数据传递

import React, { Component } from 'react';

class PropsDemo extends Component {
  props = {
    title: '这是默认的title属性值'
  }
  render(){
    console.log(this.props);
    return <b>{this.props.title}</b>
  }
}

export default PropsDemo;

// 组件调用方式
// <PropsDemo title="设置的标题" />

函数组件更新页面

在不使用生命周期的情况下更更新状态我们只能通过不断的去执行ReactDOM.render函数去进行更新

var count = 0

function buttonClick() {
    count = count + 1
    render()
}

const render = () => {
    const element = (
        <div className="warp">
            <div>点击了{count}次</div>
            <button type="button" onClick={buttonClick}>点击</button>
        </div>
    )
    ReactDOM.render(
        element,
        document.getElementById('root')
    )
}

render()

类组件更新状态

在内组件内部可通过调用 setState 函数去更新状态,以达到更新页面的目的

class App extends React.Component {

    state = {
        count: 0
    }

    buttonClick = () => {
        this.setState({
            count: this.state.count + 1
        })
    }

    render() {
      alert(this.state.count)
        return (
            <div className="warp">
                <div>点击了{this.state.count}次</div>
                <button type="button" onClick={this.buttonClick}>点击</button>
            </div>
        )
    }
}

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

生命周期

在上面的环节里,我们使用函数组件以及类组件实现了一个计数器的组件,使用到了render以及setState,在实际应用中,我们需要对组件的状态有更新详细的掌控,对组件性能的优化,以及实现更多的需求,这个时候就需要用到react的生命周期函数

下面篇幅较多,如果想看生命周期图,请直接往下翻

React生命周期主要包括三个阶段:初始化阶段、运行中阶段和销毁阶段;

初始化阶段

getDefaultProps()

设置组件的默认属性;可以用static defaultProps设置默认的props

getInitialState()

在使用es6的class语法时是没有这个钩子函数的,使用constructor代替;

一个react组件需要继承react Component这个基类,才能有render(),生命周期等方法可以使用,这也是为什么函数组件不能使用这些方法的原因; 而constructor里的super(props)用来调用基类的构造方法, 也将父组件的props注入给子组件,供子组件读取(组件中props只读不可变,state可变)。 而constructor()用来做一些组件的初始化工作,如定义this.state的初始内容,使用bing()方法绑定一些函数,使其this指向当前的这个类,这样就可以在函数中访问到这个类的所有方法;

componentWillMount() react 16.7移除

组件即将被渲染到页面之前触发;只会调用一次,调用this.setState()不会触发render 因为componentWillMount()将被删除,所以官方推荐使用constructor()替代该方法

render()

组件渲染到页面中,此时可以进行开启定时器、向服务器发送请求等操作 父组件重新render,props、state改变都会重新render,需要return出一个返回结果

componentDidMount()

组件已经被渲染到页面中后触发;只会调用一次; 此时页面中有了真正的DOM的元素,可以进行DOM相关的操作; 服务器端渲染时不会被触发;

运行中阶段

componentWillReceiveProps()

组件接收到属性时触发;react 16.7中被 static getDerivedStateFromProps() 替代

shouldComponentUpdate()

react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候

一个父组件的重新更新会造成它旗下所有的子组件重新执行render()方法,形成新的虚拟DOM,再用diff算法对新旧虚拟DOM进行结构和属性的比较,决定组件是否需要重新渲染。

例如React中的就提供了一个PureComponent的类,当我们的组件继承于它时,组件更新时就会默认先比较新旧属性和状态,从而决定组件是否更新。值得注意的是,PureComponent进行的是浅比较,所以组件状态或属性改变时,都需要返回一个新的对象或数组

常见的用例有:根据state的变化设置变量,派发事件,开始动画

componentWillUpdate()

组件即将被更新时触发;react 16.7中被 getSnapshotBeforeUpdate() 替代

被调用于render之后,可以读取但无法使用DOM的时候。可以在可能更改之前从DOM捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()的第三个参数。

componentDidUpdate()

组件被更新完成后触发。页面中产生了新的DOM的元素,可以进行DOM操作 有两个参数,分别是更新前的props和更新前的state

销毁阶段

componentWillUnmount()

组件被销毁时触发。这里我们可以进行一些清理操作,取消Redux的订阅事件,删除在componentDidMount或其他地方添加的事件监听,断开网络请求,清空计时器,清理在componentDidMount中创建的 DOM 元素

整个流程如下图所示

16.7之前

16.7之后

有兴趣的可以加群 885356143 react前端交流群