约定

本篇教程的所有HTML代码为以下格式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="https://cdn.bootcss.com/react/16.4.0/umd/react.development.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.min.js"></script>
<!--发送ajax请求会用到-->
<script type="text/javascript" src="https://cdn.bootcss.com/axios/0.17.1/axios.min.js"></script>
</body>
</html>

Hello World

代码

<script type="text/babel">
    //1.创建虚拟DOM
    const vDom = <h2>Hello World</h2>

    //2.渲染虚拟DOM到页面真实DOM容器中
    ReactDOM.render(vDom,document.getElementById("root"))
</script>

创建虚拟DOM的两种方式

方式一:纯JS创建虚拟DOM元素对象

<script type="text/babel">
    //方式一:纯JS创建虚拟DOM元素对象
    const vDomByJs = React.createElement('h2',{id:"js"},'Hello World!');

    ReactDOM.render(vDomByJs,document.getElementById("root"));
</script>

方式二:JSX创建虚拟DOM元素对象

<script type="text/babel">

    //方式二:JSX创建虚拟DOM元素对象

    const vDomByJSX = <h2>HELLO WORLD!</h2>
    ReactDOM.render(vDomByJSX,document.getElementById("root"));

</script>

组件

作用

组件可以将UI切分成一些的独立的、可复用的部件,这样你就只需专注于构建每一个单独的部件。

函数定义组件

<script type="text/babel">
    //方式一:函数定义组件(简单组件)
    function MyComponentByFun() {
        return <h2>hello world!</h2>
    }

    //渲染组件
    ReactDOM.render(<MyComponentByFun/>,document.getElementById("root"))
</script>

类定义组件

<script type="text/babel">
    //方式二:类定义组件(复杂组件)
    class MyComponentByClass extends React.Component{
        render(){
            return <h2>HELLO WORLD!</h2>
        }
    }

    //渲染组件
    ReactDOM.render(<MyComponentByClass/>,document.getElementById("root"))
</script>

组件三大属性之state

效果演示

代码

<script type="text/babel">
    class Like extends React.Component{
        //初始化状态
        constructor(props){
            super(props)
            this.state = {
                flag : true
            }
        }

        //更新状态
        handleOnClick(){
            this.setState({
                flag : !this.state.flag
            })
        }

        render(){
            const msg = this.state.flag ? "我喜欢你" : "你喜欢我";
            return (
                <div>
                    <h2>{msg}</h2>
                    <button onClick={() => this.handleOnClick()}>更新状态</button>
                </div>
            )
        }
    }

    ReactDOM.render(<Like />,document.getElementById("root"))
</script>

作用

  1. state是组件对象最重要的属性, 值是对象(可以包含多个数据)

  2. 组件被称为”状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)

语法

  1. 初始化状态
constructor (props) {
    super(props)
    this.state = {
      stateProp1 : value1,
      stateProp2 : value2
    }
  }
  1. 读取某个状态值
this.state.statePropertyName
  1. 更新状态—->组件界面更新
this.setState({
    stateProp1 : value1,
    stateProp2 : value2
  })

组件三大属性之props

代码

<script type="text/babel">
    class Person extends React.Component{
        render(){
            return(
                <div>
                    <ul>
                        <li>姓名:{this.props.pseson.name}</li>
                        <li>性别:{this.props.pseson.sex}</li>
                        <li>年龄:{this.props.pseson.age}</li>
                    </ul>
                </div>
            )
        }
    }
    
    const pseson = {
        name : "张三",
        sex : "",
        age : 17
    }
    
    ReactDOM.render(<Person pseson={pseson}/>,document.getElementById("root"))
</script>

理解

  1. 每个组件对象都会有props(properties的简写)属性
  2. 组件标签的所有属性都保存在props中

作用

  1. 通过标签属性从组件外向组件内传递变化的数据

注意点

  1. 组件内部不要修改props数据

组件三大属性之refs

代码

<script type="text/babel">
    /**
     * 获取输入框的值
     */
    class MyComponent extends React.Component{

        //自定义方法中的this默认为null,如何绑定this为组件对象?
        handleClick(){
            const  msg = this.msg.value ? this.msg.value : "还没有输入东西"
            alert(msg)
        }

        /**
         * 方式一:通过函数对象的bind()强制绑定this
         * 方式二:通过箭头函数绑定this
         * @param props
         */

        constructor(props){
            super(props)
            this.handleClick = this.handleClick.bind(this)
        }

        render(){
            return(
                <div>
                    <input type="text" ref={input => this.msg = input}/>
                    <button onClick={this.handleClick}>bind()获取数据</button>
                    <button onClick={() => this.handleClick()}>箭头函数获取数据</button>
                </div>
            )
        }
    }
    ReactDOM.render(<MyComponent/>,document.getElementById("root"))
</script>

作用

通过ref获取组件内容特定标签对象, 进行读取其相关数据

语法

<input type="text" ref={input => this.msg = input}/>

箭头函数将input元素绑定到msg对象中,用过this.msg得到真实DOM元素

注意点

  1. 组件内置的方法中的this为组件对象
  2. 组件自定义的方法中的thisnull

    a. 通关函数对象的bing()强制绑定this

     constructor(props){
             super(props)
             this.handleClick = this.handleClick.bind(this)
         }
            
     <button onClick={this.handleClick}>bind()获取数据</button>
    

    b. 通关箭头函数绑定this

     <button onClick={() => this.handleClick()}>箭头函数获取数据</button>
    

    获取表单数据的两种方式

代码

<script type="text/babel">

    /**
     * 获取表单数据的两种方式
     * 1.受控组件
     * 2.非受控组件
     */
    class LoginForm extends React.Component{

        constructor(props){
            super(props)
            this.state = {
                username : ''
            }
        }

        handleSubmit(event){
            const username = this.state.username
            const password = this.password.value
            alert(`准备提交的用户名为:${username},密码为:${password}`)
            //阻止事件的默认行为:提交表单
            event.preventDefault()
        }

        /**
         * 1.受控组件:通过改变状态获取表单的数据
         * 2.非受控组件:箭头函数将input元素赋给password对象,调用password.value获取表单数据
         * @param event
         */
        handleOnChange(event){
            this.setState({
                username: event.target.value
            })
        }

        render(){
            return(
                <form onSubmit={(event) => this.handleSubmit(event)} action="/test">
                    <label>
                        用户名:
                        <input type="text" onChange={(event) => this.handleOnChange(event)}/>
                    </label>
                    &nbsp;
                    <label>
                        密码:
                        <input type="password" ref={input => this.password = input}/>
                    </label>
                    &nbsp;
                    <input type="submit" value="登陆"/>
                </form>
            )
        }
    }

    ReactDOM.render(<LoginForm/>,document.getElementById("root"))
</script>

方式一:受控组件获取表单数据

onChange={(event) => this.handleOnChange(event)}

这里注意event要传进去

方式二:非受控组件获取表单数据

ref={input => this.password = input}

组件的组合

效果演示

代码

<script type="text/babel">

    //应用组件
    class App extends React.Component{

        constructor(props){
            super(props)
            this.state={
                todoList:['吃饭','喝水','睡觉']
            }
        }

        //将子组件传过来的数据添加到todoList里,并更新状态
        addTodo(todo){
            const todoList = this.state.todoList
            //添加数据
            todoList.unshift(todo)

            //更新状态
            this.setState({
                todoList:todoList
            })
        }

        render(){
            //console.log(this.state)
            //console.log(this.state.todoList)
            const todoList = this.state.todoList
            return(
                <div className="App">
                    <Heard />
                    <TodoAdd addTodo={(todo) => this.addTodo(todo)} length={todoList.length}/>
                    <List todoList={todoList}/>
                </div>
            )
        }
    }

    //头部组件
    class Heard extends React.Component{

        render(){
            return <h2>输入你要做的事</h2>
        }
    }

    //事情列表组件
    class List extends React.Component{
        render(){
            //获取父组件传过来的数据
            const todoList = this.props.todoList
            return(
                <div>
                    <ul>
                        {
                            todoList.map((todo,index) => <li key={index}>{todo}</li>)
                        }
                    </ul>
                </div>
            )
        }
    }

    //添加事情组件
    class TodoAdd extends React.Component{

        handleOnClick(){
            const msg = this.msg.value
            const todo = this.props.addTodo
            if(!msg){
                return false
            }else{
                todo(msg)
                this.msg.value = ''
            }
        }

        render(){
            const length = this.props.length
            return(
                <div className="TodoAdd">
                    <input type="text" ref={input => this.msg = input}/>&nbsp;
                    <button onClick={() => this.handleOnClick()}>添加#{length}</button>
                </div>
            )
        }
    }

    //渲染组件
    ReactDOM.render(<App/>,document.getElementById("root"))
</script>

作用

  1. 组件化开发是React的特性,也是主流前端框架的特性。
  2. 组件化开发无需再用模版代码,通过使用JavaScript编写的组件组合成页面。
  3. 组件化开发可以更好地传递数据,将应用状态和DOM拆分开来。

组件的生命周期

效果演示

代码

<script type="text/babel">
    class Life extends React.Component{

        constructor(props){
            super(props)
            console.log('constructor(): 创建组件对象')
            this.state = {
                opacity: 1
            }
        }

        componentDidMount(){
            console.log('componentDidMount(): 初始化已经挂载')
            this.intervalId = setInterval(function () {
                // 得到当前opacity
                let opacity = this.state.opacity
                //更新opacity
                opacity -= 0.1
                if(opacity <= 0){
                    opacity = 1
                }
                this.setState({
                    opacity:opacity
                })
            }.bind(this),200)
        }

        componentWillUpdate(){
            console.log('componentWillUpdate(): 将要更新');
        }

        componentDidUpdate(){
            console.log('componentDidUpdate(): 已经更新');
        }

        componentWillUnmount(){
            console.log('componentWillUnmount(): 将要被移除')
            clearInterval(this.intervalId)
        }

        removeComponent(){
            ReactDOM.unmountComponentAtNode(document.getElementById("root"))
        }

        render(){
            return (
                <div>
                    <h2 style=>{this.props.name}</h2>
                    <button onClick={() => this.removeComponent()}>移除组件</button>
                </div>
            )
        }
    }
    ReactDOM.render(<Life name="Hello World!"/>,document.getElementById("root"))
</script>

生命周期的作用

组件对象从创建到死亡它会经历特定的生命周期阶段,我们在定义组件时, 可以重写特定的生命周期回调函数, 做特定的工作。

周期 作用
*Mount 插入真实 DOM
*Update 被重新渲染
*Unmount 被移出真实 DOM

生命周期的流程

  1. 第一次初始化

ReactDOM.render():渲染显示

*constructor():创建对象初始化state

*componentWillMount() :将要插入回调

*render() :用于插入虚拟DOM回调

*componentDidMount():已经插入回调

  1. 每次更新state

*componentWillUpdate():将要更新回调

*render():更新(重新渲染)

*componentDidUpdate():已经更新回调

  1. 移除组件

ReactDOM.unmountComponentAtNode(containerDom)

*componentWillUnmount():组件将要被移除回调

ajax请求

效果演示

image

代码

<script type="text/babel">
    class App extends React.Component{

        constructor(props){
            super(props)
            this.state = {
                repo : {
                    reponame : '',
                    repourl : ''
                }
            }
        }

        //获取搜索的内容
        getSearch(search){
            const url = `https://api.github.com/search/repositories?q=${search}&sort=stars`;
            axios.get(url)
                .then(response => {
                    const result = response.data;
                    this.setState({
                        repo:{
                            reponame: result.items[0].name,
                            repourl: result.items[0].html_url
                        }
                    });
                })
                .catch(error => {
                    alert("请求失败"+error.message);
                })
        }

        render(){
            return(
                <div className="app">
                    <Heard/>
                    <Search getSearch={(search) => this.getSearch(search)}/>
                    <Result repo={this.state.repo}/>
                </div>
            )
        }
    }

    //头部信息
    class Heard extends React.Component{
        render(){
            return <h2 className="heard">搜索GitHub上最受欢迎的项目</h2>
        }
    }

    //搜索框
    class Search extends React.Component{

        handleOnClick(){
            const msg = this.msg.value;
            if (!msg){
                return
            } else{
                this.props.getSearch(msg);
                this.msg.value = '';
            }
        }

        render(){
            return (
                <div className="search">
                    <input ref={ input => this.msg = input }/>&nbsp;
                    <button onClick={() => this.handleOnClick()}>搜索</button>
                </div>
            )
        }
    }

    //结果展示
    class Result extends React.Component{
        render(){
            const reponame = this.props.repo.reponame
            const repourl = this.props.repo.repourl
            return (
                <div className="result">
                    <h2><a href={repourl}>{reponame}</a></h2>
                </div>
            )
        }
    }

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

说明

  1. React本身只关注于界面, 并不包含发送ajax请求的代码
  2. 前端应用需要通过ajax请求与后台进行交互(json数据)
  3. react应用中需要集成第三方ajax库(或自己封装)

常用的ajax请求库

  1. jQuery: 比较重, 如果需要另外引入不建议使用
  2. axios: 轻量级, 建议使用
  3. fetch: 原生函数, 但老版本浏览器不支持

axios的get请求和post请求

  1. Get请求
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

或者

axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
  1. Post请求
axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
})
.then(function (response) {
  console.log(response);
})
.catch(function (error) {
  console.log(error);
});

原文链接:https://miansen.wang/2018/09/15/1/