React面向组件编程
- 基本理解和使用
- 组件三大核心属性1: state
- 效果
- 理解
- 强烈注意
 
- 组件三大核心属性2: props
- 效果
- 理解
- 作用
- 编码操作
 
- 组件三大核心属性3: refs与事件处理
- 效果
- 理解
- 编码
- 2.4.4. 事件处理
 
- 收集表单数据
- 效果
- 理解
 
- 组件的生命周期(重要)
- 效果
- 理解
- 生命周期流程图(旧)
- 生命周期流程图(新)
- 重要的勾子
- 即将废弃的勾子
 
基本理解和使用
使用React开发者工具调试
 
 效果
 函数式组件:
 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 1. 创建函数式组件,组件首字母大写
        function MyComponent(){
            console.log(this)       //  此处的this时undefined,因为Babel编译后开启了严格模式
            return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
        }
        // 2.渲染组件到页面
        ReactDOM.render(<MyComponent/>, document.getElementById('test'))
        /**
         *  执行了ReactDOM.render(<MyComponent/>, document.getElementById('test')))之后发生了什么
         *      1.react会解析组件标签,找到了MyComponent组件
         *      2.发现组件是使用函数定义的,随后调用该函数,蒋返回的虚拟DOM转为真实DOM,随后呈现在页面中
         * **/
    </script>
</body>
</html>
类式组件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 1. 创建类式组件,必须要继承React.Component
        class MyComponent extends React.Component{
            render(){
                // render方法放在了那里?- MyComponent类的原型对象上,供实例对象使用
                // render中的this实施诶---MyComponent组件实例对象
                console.log("render中的this:", this)
                return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
            }
        }
        // 2.渲染组件到页面
        ReactDOM.render(<MyComponent/>, document.getElementById('test'))
        /**
         *  执行了ReactDOM.render(<MyComponent/>, document.getElementById('test'))之后发生了什么
         *      1.react会解析组件标签,找到了MyComponent组件
         *      2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法
         *      3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中
         * **/
         /**
          * 组件的三大属性:
            props:{}
            refs:{}
            state:null
          * **/
    </script>
</body>
</html>
复杂组件的状态state如何理解:
 组件中的状态驱动页面渲染
 组件实例对象生成的state
注意
 1.组件名必须首字母大写
 2.虚拟DOM元素只能有一个根元素
 3.虚拟DOM元素必须有结束标签
渲染类组件标签的基本流程
 1.React内部会创建组件实例对象
 2.调用render()得到虚拟DOM, 并解析为真实DOM
 3.插入到指定的页面元素内部
组件三大核心属性1: state
效果
需求: 定义一个展示天气信息的组件
 1.默认展示天气炎热 或 凉爽
2.点击文字切换天气
state.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // let that
        
        // 1.创建组件
        class Weather extends React.Component{
            // 参数取决于new的时候需要传递的内容
            // 构造器调用几次?----- 1次
            constructor(props){
                console.log('constructor')
                super(props)
                // 初始化状态
                this.state = {isHot:false, wind:'微风'}
                // that = this
                // 解决changeWeather中this指向问题,将原型上的changeWeather挂在到实例对象上
                this.changeWeather = this.changeWeather.bind(this)
            }
            
            // render调用几次-----1+n次,1是初始化次数,n是状态更新的次数
            render(){
                console.log('render')
                // 读取状态
                const {isHot, wind} = this.state
                // react中onClick要大写,函数后面不需要加括号,只用指定函数,由react调用
                return <h1 onClick = {this.changeWeather}> 今天天气很{isHot ? '炎热':'凉爽'} , {wind}</h1>
            }
            
            // changeWeaher调用几次?点几次调几次
            changeWeather(){
                // changeWeather放在那里-----类的原型对象上,供实例使用
                // 通过Weather实例对象调用changeWeather时,changeWeather中的this就是Weather实例
                // 为什么这里this.state是undefined,因为他不是通过实例调用
                // 由于changeWeather是作为onClick回调,所以不是通过实例嗲偶用的,是直接调用
                // 类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
                // console.log(this.state.isHot)
                // 获取原来的isHot值
                const isHot = this.state.isHot
                // 严重注意:状态state不能直接更改,下面这行就是直接更改,要借助一个内置API
                // this.state.isHot = !isHot   // 这是错误写法
                // 严重注意,状态必须通过setState更改,且更新是一种合并的操作
                this.setState({isHot:!isHot})
            }
        }
        // 2.渲染组件到页面
        ReactDOM.render(<Weather/>, document.getElementById('test'))
        // const title = document.getElementById('title')
        // title.addEventListener('click', ()=>{
        //     console.log("标题被点击了")
        // })
        // const title = document.getElementById('title')
        // title.onclick = ()=>{
        //     console.log("标题被点击了")
        // }
        // function changeWeather(){
        //     alert('此处修改isHot的值')
        //     // 这里的this是window
        //     // const {isHot} = this.state
        //     // console.log(isHot)
        //     console.log(that.state.isHot)
        // }
    </script>
</body>
</html>
间写
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        class Weather extends React.Component{
        
            // 初始化状态
            state = {isHot:false, wind:'微风'}
            
            render(){
                const {isHot, wind} = this.state
                return <h1 onClick = {this.changeWeather}> 今天天气很{isHot ? '炎热':'凉爽'} , {wind}</h1>
            }
            
            // 自定义方法-----要用赋值语句的形式+箭头函数
            // 将function改成了箭头函数,可以实现,箭头函数没有自己的this,
            // 在里面写this不会报错,它会找外层函数的this,作为箭头函数的this去使用
            changeWeather = ()=>{
                const isHot = this.state.isHot
                this.setState({isHot:!isHot})
            }
        }
        // 2.渲染组件到页面
        ReactDOM.render(<Weather/>, document.getElementById('test'))
    </script>
</body>
</html>
理解
1.state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
 2.组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
强烈注意
1.组件中render方法中的this为组件实例对象
 2.组件自定义的方法中this为undefined,如何解决?
 a)强制绑定this: 通过函数对象的bind()
 b)箭头函数
 3.状态数据,不能直接修改或更新
组件三大核心属性2: props
效果
需求: 自定义用来显示一个人员信息的组件
1.姓名必须指定,且为字符串类型;
 2.性别为字符串类型,如果性别没有指定,默认为男
 3.年龄为字符串类型,且为数字类型,默认值为18

理解
1.每个组件对象都会有props(properties的简写)属性
 2.组件标签的所有属性都保存在props中
作用
1.通过标签属性从组件外向组件内传递变化的数据
 2.注意: 组件内部不要修改props数据
编码操作
1.内部读取某个属性值
this.props.name
2.对props中的属性值进行类型限制和必要性限制
 第一种方式(React v15.5 开始已弃用):
Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number
}
第二种方式(新):使用prop-types库进限制(需要引入prop-types库)
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number. 
}
3.扩展属性: 将对象的所有属性通过props传递
<Person {...person}/>
4.默认属性值:
Person.defaultProps = {
  age: 18,
  sex:'男'
}
5.组件类的构造函数
constructor(props){
  super(props)
  console.log(props)//打印所有属性
}
props基本使用
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test1">
    </div>
    <div id="test2">
    </div>
    <div id="test3">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 1.创建组件
        class Person extends React.Component{
            render(){
                console.log(this.props)
                const {name, sex, age} = this.props
                return (
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                    </ul>
                )
            }
        }
        // 类似html标签key-value的形式,,注意要传递number类型的参数,不能直接使用引号,需要用大括号括起来
        // 对传递的标签属性age进行类型鉴定,name进行必要性鉴定,性别需要指定默认值
        ReactDOM.render(<Person name="tom" age={19} sex='女'/>, document.getElementById('test1'))
        ReactDOM.render(<Person name="java" age={20} sex='男'/>, document.getElementById('test2'))
        // 要保证两者属性名全部相同
        //仅适用于标签属性的传递
        const p = {name:'老刘', age:18, sex:'女'}
        ReactDOM.render(<Person {...p}/>, document.getElementById('test3'))
        // 传递props,就是传递标签属性
        
    </script>
</body>
</html>
props进行限制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test1">
    </div>
    <div id="test2">
    </div>
    <div id="test3">
    </div>
    <!--react的核心库,React对象-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM, ReactDom对象-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!---引入prop-types,用于对组件标签属性进行限制,PropType对象-->
    <script type="text/javascript" src="../js/prop-types.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 1.创建组件
        class Person extends React.Component{
            render(){
                console.log(this.props)
                const {name, sex, age} = this.props
                // props是只读的
                //this.props.name='java'      // 此行代码会报错,因为props是只读的
                return (
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                    </ul>
                )
            }
            
        }
        //对标签属性进行类型、必要性的限制
        Person.propTypes = {
            name:PropTypes.string.isRequired,           //限制name必传,且为字符串
            sex:PropTypes.string,       //限制sex字符串
            age:PropTypes.number,       //限制age为数值
            speak:PropTypes.func        //限制speak为函数
        }
        // 指定默认标签属性值
        Person.defaultProps = {
            sex:'男' ,     // sex默认值为男
            age:18         //age默认值为18
        }
        ReactDOM.render(<Person name="tom" age={19} sex='女'/>, document.getElementById('test1'))
        ReactDOM.render(<Person name="java" speak={speak}/>, document.getElementById('test2'))
        const p = {name:'老刘', age:18, sex:'女'}
        ReactDOM.render(<Person {...p}/>, document.getElementById('test3'))
        function speak(){
            console.log('我说话了')
        }
    </script>
</body>
</html>
props简写方式
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test1">
    </div>
    <div id="test2">
    </div>
    <div id="test3">
    </div>
    <!--react的核心库,React对象-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM, ReactDom对象-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!---引入prop-types,用于对组件标签属性进行限制,PropType对象-->
    <script type="text/javascript" src="../js/prop-types.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 1.创建组件
        class Person extends React.Component {
            //对标签属性进行类型、必要性的限制
            static propTypes = {
                name: PropTypes.string.isRequired,           //限制name必传,且为字符串
                sex: PropTypes.string,       //限制sex字符串
                age: PropTypes.number,       //限制age为数值
                speak: PropTypes.func        //限制speak为函数
            }
            // 指定默认标签属性值
            static defaultProps = {
                sex: '男',     // sex默认值为男
                age: 18         //age默认值为18
            }
            render() {
                console.log(this.props)
                const { name, sex, age } = this.props
                // props是只读的
                //this.props.name = 'java'      // 此行代码会报错,因为props是只读的
                return (
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age + 1}</li>
                    </ul>
                )
            }
        }
        ReactDOM.render(<Person name="tom" age={19} sex='女' />, document.getElementById('test1'))
        ReactDOM.render(<Person name="java" speak={speak} />, document.getElementById('test2'))
        const p = { name: '老刘', age: 18, sex: '女' }
        ReactDOM.render(<Person {...p} />, document.getElementById('test3'))
        function speak() {
            console.log('我说话了')
        }
    </script>
</body>
</html>
props与构造器
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test1">
    </div>
    <div id="test2">
    </div>
    <div id="test3">
    </div>
    <!--react的核心库,React对象-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM, ReactDom对象-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!---引入prop-types,用于对组件标签属性进行限制,PropType对象-->
    <script type="text/javascript" src="../js/prop-types.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 1.创建组件
        class Person extends React.Component {
            // 类中的构造器有什么作用呢?有必要传props吗?
            // 在react中,构造函数仅用于以下两种情况
            // 1.通过给this.state赋值对象来初始化内部state,2.为事件处理函数绑定实例
            // 类中的构造器可有可无
            // 在react组件挂在致癌案,会调用它的构造函数,在为react.component子类实现构造函数时,应在
            // 其他语句之前调用super(props),否则this.props在构造函数中可能会出现为定义异常
            constructor(props) {
                // 构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
                console.log(props)
                super(props)
            }
            //对标签属性进行类型、必要性的限制
            static propTypes = {
                name: PropTypes.string.isRequired,           //限制name必传,且为字符串
                sex: PropTypes.string,       //限制sex字符串
                age: PropTypes.number,       //限制age为数值
                speak: PropTypes.func        //限制speak为函数
            }
            // 指定默认标签属性值
            static defaultProps = {
                sex: '男',     // sex默认值为男
                age: 18         //age默认值为18
            }
            render() {
                return (
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age + 1}</li>
                    </ul>
                )
            }
        }
        ReactDOM.render(<Person name="tom"/>, document.getElementById('test1'))
    </script>
</body>
</html>
函数式组件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test1">
    </div>
    <div id="test2">
    </div>
    <div id="test3">
    </div>
    <!--react的核心库,React对象-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM, ReactDom对象-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!---引入prop-types,用于对组件标签属性进行限制,PropType对象-->
    <script type="text/javascript" src="../js/prop-types.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 1.函数式组件只能使用props
        function Person(props) {
            const { name, age, sex } = props
            return (
                <ul>
                    <li>姓名:{name}</li>
                    <li>性别:{sex}</li>
                    <li>年龄:{age + 1}</li>
                </ul>
            )
        }
        //对标签属性进行类型、必要性的限制
        Person.propTypes = {
            name: PropTypes.string.isRequired,           //限制name必传,且为字符串
            sex: PropTypes.string,       //限制sex字符串
            age: PropTypes.number,       //限制age为数值
            speak: PropTypes.func        //限制speak为函数
        }
        // 指定默认标签属性值
        Person.defaultProps = {
            sex: '男',     // sex默认值为男
            age: 18         //age默认值为18
        }
        ReactDOM.render(<Person name="tom" />, document.getElementById('test1'))
    </script>
</body>
</html>
组件三大核心属性3: refs与事件处理
效果
需求: 自定义组件, 功能说明如下:
点击按钮, 提示第一个输入框中的值
 当第2个输入框失去焦点时, 提示这个输入框中的值

理解
组件内的标签可以定义ref属性来标识自己(类似id)
编码
1.字符串形式的ref
<input ref="input1"/>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 1. 创建类式组件,必须要继承React.Component
        class Demo extends React.Component{
            // 展示左侧输入框的数据
            showData = ()=>{
                // 注意收集出来的属性是refs
                const {input1} = this.refs
                console(input1)
                alert(input1.value)
                
            }
            // 展示右侧输入框的数据
            showData2 = ()=>{
                const {input2} = this.refs
                alert(input2.value)
                
            }
            render(){
                return (
                    // 打了标识之后,都收集到组件对象的refs属性中
                    <div>
                        <input ref = "input1" type="text" placeholder="点击按钮提示数据"/>
                        <button ref = "button1" onClick={this.showData}>点我提示左侧的数据</button>
                        <input onBlur = {this.showData2} ref = "input2" type="text" placeholder="失去焦点提示数据"/>  
                    </div>
                )
            }
        }
        // 2.渲染组件到页面
        ReactDOM.render(<Demo/>, document.getElementById('test'))
    </script>
</body>
</html>
2.回调形式的ref
<input ref={(c)=>{this.input1 = c}}/>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 1. 创建类式组件,必须要继承React.Component
        class Demo extends React.Component{
            // 展示左侧输入框的数据
            showData = ()=>{
                const{input1} = this
                alert(input1.value)
            }
            // 展示右侧输入框的数据
            showData2 = ()=>{
                const {input2} = this
                alert(input2.value)
                
            }
            render(){
                return (
                    // 打了标识之后,都收集到组件对象的refs属性中
                    /**
                     * <input ref = {(a)=>{this.input1 = a}} type="text" placeholder="点击按钮提示数据"/>
                     * 这句的含义是把ref当前所处的节点挂在了当前实例自身上,并且取了个名字叫input1
                     * react会自动帮你调用回调函数
                     * **/
                    <div>
                        
                        <input ref = {c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>
                        <button ref = "" onClick={this.showData}>点我提示左侧的数据</button>
                        <input onBlur = {this.showData2} ref = {c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
        }
        // 2.渲染组件到页面
        ReactDOM.render(<Demo/>, document.getElementById('test'))
    </script>
</body>
</html>
回调中的函数执行次数
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 1. 创建类式组件,必须要继承React.Component
        class Demo extends React.Component{
            state = {isHot:true}
            showInfo = ()=>{
                const {input1} = this
                alert(input1.value)
            }
            changeWeather = ()=>{
                const {isHot} = this.state
                // 获取原来的天气
                this.setState({isHot:!isHot})
            }
            saveInput = (c)=>{
                this.input1 = c;
                console.log('@',c)
            }
            render(){
                const {isHot} = this.state
                return (
                    /**
                     * 如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。
                     * 这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。
                     * 通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。
                     * **/
                    <div>
                        <h2>今天天气很{isHot ? '炎热':'寒冷'} </h2>
                        {/*<input ref = {(c) => {this.input1 = c;console.log('@', c);}} type="text" />*/}
                        <input ref = {this.saveInput} type="text" />
                        <button onClick={this.showInfo}>点我提示左侧的数据</button>
                        <button onClick={this.changeWeather}>点我切换天气</button>
                    </div>
                )
            }
        }
        // 2.渲染组件到页面
        ReactDOM.render(<Demo/>, document.getElementById('test'))
    </script>
</body>
</html>
3.createRef创建ref容器·
myRef = React.createRef() 
<input ref={this.myRef}/>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 1. 创建类式组件,必须要继承React.Component
        class Demo extends React.Component{
            /**
             * 它本身是一个函数,React.createRef()调用后可以返回一个容器,调用后可以存储被ref所标识的节点,
             * 改容器是专人专用的
             * **/
            myRef = React.createRef()
            myRef2 = React.createRef()
            // 展示左侧输入框的数据
            showData = ()=>{
                alert(this.myRef.current.value)
            }
            // 展示右侧输入框的数据
            showData2 = ()=>{
                alert(this.myRef2.current.value)
                
            }
            render(){
                return (
                    <div>
                        
                        <input ref = {this.myRef} type="text" placeholder="点击按钮提示数据"/>
                        <button ref = "" onClick={this.showData}>点我提示左侧的数据</button>
                        <input onBlur={this.showData2} ref = {this.myRef2} type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
        }
        // 2.渲染组件到页面
        ReactDOM.render(<Demo/>, document.getElementById('test'))
    </script>
</body>
</html>
2.4.4. 事件处理
1.通过onXxx属性指定事件处理函数(注意大小写)
- 1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件
- 2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
2.通过event.target得到发生事件的DOM元素对象
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/babel">
        // 1. 创建类式组件
        class Demo extends React.Component {
            /**
             *  1.通过onXxx属性指定事件处理函数(注意大小写,react是大写,原生是小写),
                    1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件        为了更好的兼容性
                    2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)   为了更高效
                2.通过event.target得到发生事件的DOM元素对象     ----不要过度使用ref
             * **/
            // 创建ref容器
            myRef = React.createRef()
            myRef2 = React.createRef()
            // 展示左侧输入框的数据
            showData = () => {
                alert(this.myRef.current.value)
            }
            // 展示右侧输入框的数据
            showData2 = () => {
                alert(event.target.value)
            }
            render() {
                return (
                    <div>
                        <input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
                        <button ref="" onClick={this.showData}>点我提示左侧的数据</button>
                        {/**发生事件的元素是正要操作的元素,ref可以省略**/}
                        <input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
                    </div>
                )
            }
        }
        // 2.渲染组件到页面
        ReactDOM.render(<Demo />, document.getElementById('test'))
    </script>
</body>
</html>
收集表单数据
效果
需求: 定义一个包含表单的组件 输入用户名密码后, 点击登录提示输入信息
 
理解
包含表单的组件分类
1.受控组件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 随着输入维护状态,就称之为非受控组件
        // 创建组件
        class Login extends React.Component{
            
            state = {
                username:'',        //用户名
                password:''         //密码
            }
            handleSubmit = (event)=>{
                event.preventDefault()  //  阻止表单提交,不会跳转到新的页面
                const {username, password} = this.state
                alert(`输入的用户名是:${username}, 你输入的密码是:${password}`)
            }
            // 保存用户名到状态中
            saveUsername = (event)=>{
                this.setState({username: event.target.value})
            }
            // 保存用户名到密码中
            savePassword = (event)=>{
                this.setState({password: event.target.value})
            }
            render() {
                return (
                    // 不指定的话,默认发get请求,带的参数默认是get参数
                    <form action="http://www.baidu.com" onSubmit={this.handleSubmit}>
                        用户名:<input onChange={this.saveUsername} type = "text" name ="username"/>
                        密码:<input onChange={this.savePassword} type = "password" name="password"/>
                        <button>登陆</button>
                    </form>
                )
            }
        }
        ReactDOM.render(<Login/>, document.getElementById('test'))
    </script>
</body>
</html>
2.非受控组件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- babel将jsx代码翻译为js代码 -->
    <script type="text/babel">
        // 创建组件
        class Login extends React.Component{
            // 页面所有输入类的dom,都是现用现取,就是非受控组件
            // 
            handleSubmit = (event)=>{
                event.preventDefault()  //  阻止表单提交,不会跳转到新的页面
                const {username, password} = this
                alert(`输入的用户名是:${username.value}, 你输入的密码是:${password.value}`)
            }
            render() {
                return (
                    // 不指定的话,默认发get请求,带的参数默认是get参数
                    <form action="http://www.baidu.com" onSubmit={this.handleSubmit}>
                        用户名:<input ref={c=>this.username=c} type = "text" name ="username"/>
                        密码:<input ref={c=>this.password=c} type = "password" name="password"/>
                        <button>登陆</button>
                    </form>
                )
            }
        }
        ReactDOM.render(<Login/>, document.getElementById('test'))
    </script>
</body>
</html>
组件的生命周期(重要)
效果
需求:定义组件实现以下功能:
让指定的文本做显示 / 隐藏的渐变动画
 从完全可见,到彻底消失,耗时2S
 点击“不活了”按钮从界面中卸载组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/babel">
        // 创建组件
        // 生命周期回调函数<===>生命周期钩子函数
        // react会在合适的时机把函数勾起来执行。
        class Life extends React.Component {
            // 状态的数据驱动页面的更新
            state = { opacity: 1 }
            death = () => {
                // 卸载组件unmount
                ReactDOM.unmountComponentAtNode(document.getElementById('test'))
            }
            // action =()=>{
            //     setInterval(()=>{
            //         // 获取原来状态
            //         let {opacity} = this.state
            //         // 减小0.1
            //         opacity -= 1
            //         if (opacity <= 0) opacity = 1
            //         // 设置新的透明度
            //         this.setState({opacity})
            //     }, 200);
            // }
            // 名字一定不要写错
            // 组件挂载完毕之后调用
            componentDidMount() {
                
                this.timer = setInterval(() => {
                    // 获取原来状态
                    let { opacity } = this.state
                    // 减小0.1
                    opacity -= 0.1
                    if (opacity <= 0) opacity = 1
                    // 设置新的透明度
                    this.setState({opacity})
                }, 200);
            }
            
            //组件将要卸载
            componentWillUnmount(){
                // 清空定时器
                clearInterval(this.timer)
            }
            // render调用1+n次,调用的时机:初始化渲染,状态更新之后
            render() {
                // 无线递归,定时器每隔200s都会更新状态,更新状态就会导致组件重新挂在,render再次执行,定时器又重新初始化了
                // 
                console.log('render')
                // setInterval(()=>{
                //     // 获取原来状态
                //     let {opacity} = this.state
                //     // 减小0.1
                //     opacity -= 1
                //     if (opacity <= 0) opacity = 1
                //     // 设置新的透明度
                //     this.setState({opacity})
                // }, 200);
                return (
                    <div>
                        <h2 style={{ opacity: this.state.opacity }}>React学不会怎么办</h2>
                        <button onClick={this.death}>不活了</button>
                        <button onClick={this.action}>开始变化</button>
                    </div>
                )
            }
        }
        // 渲染组件,实际上就是挂载(mount),
        ReactDOM.render(<Life />, document.getElementById('test'))
    </script>
</body>
</html>
理解
1.组件从创建到死亡它会经历一些特定的阶段。
 2.React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
 3.我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
生命周期流程图(旧)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用jsx创建虚拟DOM</title>
</head>
<body>
    <!--准备好一个容器-->
    <div id="test">
    </div>
    <!--react的核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--react的扩展库,专门用于操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于解析jsx语法为原生js语法-->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/babel">
        /**
         *  componentDidMount().  =====》常用
                一般在这个钩子中做一些初始化的事,例如:开启定时器,发送网络请求,订阅消息
            componentWillUnmount() ====》常用
                一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
         * 
         * **/
        class Count extends React.Component {
            // 构造器
            constructor(props) {
                console.log('Count-constructor')
                super(props)
                // 初始化状态
                this.state = { count: 0 }
            }
            //加1按钮的回调
            add = () => {
                // 获取原来的状态
                const { count } = this.state
                this.setState({ count: count + 1 })
            }
            // 卸载组件的回调
            death = () => {
                // 卸载组件unmount
                ReactDOM.unmountComponentAtNode(document.getElementById('test'))
            }
            // 强制更新按钮的回调
            force = () => {
                this.forceUpdate()
            }
            // 组件将要挂在的钩子
            componentWillMount() {
                console.log('Count-componentWillMount')
            }
            // 组件挂载完毕的钩子
            componentDidMount() {
                console.log('Count-componentDidMount')
            }
            // 组件将要卸载的钩子
            componentWillUnmount() {
                console.log('Count-componentWillUnmount')
            }
            // 控制组件更新的阀门
            shouldComponentUpdate() {
                console.log('Count-shouldComponentUpdate')
                // 不写返回值,默认为true
                // 如果是true,每次更新state的时候,都会调用一次render,如果是false,包含render在内的3个函数都不会调用
                return true
            }
            // 组件将要更新的钩子
            componentWillUpdate() {
                console.log('Count-componentWillUpdate')
            }
            // 组件更新完毕的钩子
            componentDidUpdate() {
                console.log('Count-componentDidUpdate')
            }
            render() {
                console.log('render')
                const { count } = this.state
                return (
                    <div>
                        <h2>当前求和为{count}</h2>
                        <button onClick={this.add}>点我+1</button>
                        <button onClick={this.death}>点我卸载</button>
                        <button onClick={this.force}>不修改任何状态中的数据,强制更新一下</button>
                    </div>
                )
            }
        }
        // 父组件A
        class A extends React.Component {
            // 初始化状态
            state = { carName: '奔驰' }
            changeCar = () => {
                this.setState({ carName: '宝马' })
            }
            render() {
                return (
                    <div>
                        <div>我是A组件</div>
                        <button onClick={this.changeCar}>换车</button>
                        <B carName={this.state.carName} />
                    </div>
                )
            }
        }
        // 子组件B
        class B extends React.Component {
            componentDidMount() {
                console.log("B---componentDidMount")
            }
            // 组件将要接收props调用,(第一次不调用,之后调用)
            componentWillReceiveProps() {
                console.log("B---componentWillReceiveProps", this.props)
            }
            // 控制组件更新的阀门
            shouldComponentUpdate() {
                console.log('B-shouldComponentUpdate')
                
                return true
            }
            // 组件将要更新的钩子
            componentWillUpdate() {
                console.log('B-componentWillUpdate')
            }
            // 组件更新完毕的钩子
            componentDidUpdate() {
                console.log('B-componentDidUpdate')
            }
            render() {
                console.log('B-render')
                return (
                    <div>我是B组件,接收到的车是:{this.props.carName}</div>
                )
            }
        }
        // 渲染组件,实际上就是挂载(mount),
        ReactDOM.render(<Count />, document.getElementById('test'))
    </script>
</body>
</html>
生命周期的三个阶段(旧)
初始化阶段: 由ReactDOM.render()触发—初次渲染
1.constructor()
2.componentWillMount()
3.render()
4.componentDidMount().  =====》常用
一般在这个钩子中做一些初始化的事,例如:开启定时器,发送网络请求,订阅消息
更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.shouldComponentUpdate()
2.componentWillUpdate()
3.render()
4.componentDidUpdate()
卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount() ====》常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
生命周期流程图(新)

 新版本和旧版本相比,废弃了componentWillMount、componentWillReceiveProps、componentWillUpdate,提出了两个新的getDerivedStateFromProps、getSnapshotBeforeUpdate方法
初始化阶段: 由ReactDOM.render()触发—初次渲染
1.constructor()
2.getDerivedStateFromProps
3.render()
4.componentDidMount()
更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.getDerivedStateFromProps
2.shouldComponentUpdate()
3.render()
4.getSnapshotBeforeUpdate
5.componentDidUpdate()
卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount()
重要的勾子
1.render:初始化渲染或更新渲染调用
 2.componentDidMount:开启监听, 发送ajax请求
 3.componentWillUnmount:做一些收尾工作, 如: 清理定时器
即将废弃的勾子
1.componentWillMount
 2.componentWillReceiveProps
 3.componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。


















