2.1 基本理解和使用

2.1.1 使用 React 开发者工具调试

image.png

2.1.2 效果

image.png

函数组件

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>1_函数式组件</title>
  6. </head>
  7. <body>
  8. <!-- 准备好一个容器-->
  9. <div id="test"></div>
  10. <!-- 引入react核心库-->
  11. <script type="text/javascript" src="../js/react.development.js"></script>
  12. <!--引入react-dom 用于支持react操作dom-->
  13. <script type="text/javascript" src="../js/react-dom.development.js"></script>
  14. <!--引入babel,用于对jsx转为js-->
  15. <script type="text/javascript" src="../js/babel.min.js"></script>
  16. <script type="text/babel">
  17. //1.创建函数式组件
  18. function MyComponent(){
  19. console.log(this) //此处的this是undefined , 因为babel编译后开启了严格模式
  20. return <h2> 我是用函数定义的组件(适用于【简单组件】的定义)</h2>
  21. }
  22. //2.渲染组件到页面
  23. ReactDOM.render(<MyComponent/> ,document.getElementById('test'))
  24. /*
  25. // 执行了ReactDOM.render(<MyComponent/>) ....之后,发生了什么?
  26. 1. React解析组件标签,找到了MyComponent标签
  27. 2. 发现组件是使用函数定义定义的,随后调用该函数,将返回的虚拟DOM 转为真实的DOM,随后呈现在页面中。
  28. */
  29. </script>
  30. </body>
  31. </html>

类似组件:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>2_类似组件</title>
  6. </head>
  7. <body>
  8. <!-- 准备好一个容器-->
  9. <div id="test"></div>
  10. <!-- 引入react核心库-->
  11. <script type="text/javascript" src="../js/react.development.js"></script>
  12. <!--引入react-dom 用于支持react操作dom-->
  13. <script type="text/javascript" src="../js/react-dom.development.js"></script>
  14. <!--引入babel,用于对jsx转为js-->
  15. <script type="text/javascript" src="../js/babel.min.js"></script>
  16. <script type="text/babel">
  17. //1.创建类似组件
  18. class MyComponent extends React.Component{
  19. render() {
  20. //render是放在哪里的, - MyComponent的原型对象上,供实例使用
  21. //render中this是谁,—— MyComponent的实例对象 MyComponent组件实例对象
  22. console.log("render中的this:",this)
  23. return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
  24. }
  25. }
  26. //2.渲染组件到页面
  27. ReactDOM.render(<MyComponent/> ,document.getElementById('test'))
  28. /* 执行了ReactDOM.render(<MyComponent/> ,document.getElementById('')) 之后,发生了什么
  29. 1. react解析组件标签,找到了MyComponent组件
  30. 2. 发现组件是使用类定义的,随后new出来的该类的实例,并通过该实例调用到原型上的render方法。
  31. 3.将render返回的虚拟DOM转为真实DOM ,随后呈现在页面上。*/
  32. </script>
  33. </body>
  34. </html>

2.1.3 注意

  1. 组件名必须首字母大写
  2. 虚拟DOM元素只能有一个根元素
  3. 虚拟DOM元素必须有结束标签

2.1.4 渲染类组件标签的基本流程

  1. React 内部会创建组件实例对象
  2. 调用 render() 得到虚拟 DOM ,并解析为真实的DOM。
  3. 插入到指定的页面元素内部。

2.2. 组件三大核心属性 1: false

2.2.1 效果

需求: 定义一个展示天气信息的组件

  1. 默认展示天气炎热 或 凉爽
  2. 点击文字切换天气

image.png

第一种方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>State</title>
</head>
<body>
    <!--    准备好一个容器-->
    <div id="test"></div>

    <!--    引入react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--引入react-dom  用于支持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 Weather extends React.Component{
            //构造器调用几次  ——> 1 次

            constructor(props){
                console.log('constuctor')
                super(props)
                //初始化状态
                this.state = {isHot:false,wind: '微风'}

                //解决changeWeather中this指向问题
                this.changeWeather  = this.changeWeather.bind(this)
            }

            //render调用几次, ----1+n次  1是初始化的那次,n是状态更新的次数
            render(){
                console.log("render")
                //读取状态
                const {isHot,wind} = this.state
                return <h1 onClick={this.changeWeather}>  今天天气很{isHot?'炎热':'凉爽'},{wind}</h1>
            }

            //changeWeather 调用几次? 点几次调几次

            changeWeather(){
                //changeWeather放在哪里? ----Weather 的原型对象上, 供实例使用
                //由于changeWeather是作为onClick的调用,所以不是通过实例调用的,是直接调用

                //类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined

                console.log('changeWeather')

                //获取原来的isHot的值
                const isHot = this.state.isHot

                //严重注意: 状态必须通过setState进行更新,且更新是一种合法,不是替换
                this.setState({isHot:!isHot})

                console.log(this)

                //严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
                this.state.isHot = !isHot//这是错误的写法

            }
        }

        //2.渲染组件到页面
        ReactDOM.render(<Weather></Weather>,document.getElementById('test'))
    </script>


</body>
</html>

第二种方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>state简写方式</title>
</head>
<body>
    <!--    准备好一个容器-->
    <div id="test"></div>

    <!--    引入react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--引入react-dom  用于支持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 Weather extends React.Component {

            //初始化状态
            state = {isHot:false,wind:'微风'}
            render(){
                const {isHot,wind} = this. state
                return <h1 onClick={this.changeWeather}> 今天天气很{isHot?'炎热':'凉爽'},{wind}</h1>
            }


            //自定义方法 —— 要用赋值语句的形式 + 箭头函数
            changeWeather = () =>{
                const isHot = this.state.isHot

                this.setState({isHot:!isHot})
            }
        }

        //2.渲染组件到页面
        ReactDOM.render(<Weather></Weather>,document.getElementById('test'))
    </script>
</body>
</html>

2.2.2. 理解

  1. state 是组件对象最重要的属性,值是对象(可以包含多个 key - value 的组合)
  2. 组件被称为 “状态机”,通过更新组件的 state 来更新对应的页面显示(重新渲染组件)

2.2.3 强烈注意

  1. 组件中 render 方法中的 this 为组价实例对象。
  2. 组件自定义的方法中 this undefined ,如何解决?

a. 强制绑定 this , 通过函数对象的 bind()
b. 箭头函数

  1. 状态数据,不能直接修改 或 更新

2.3. 组件三大核心属性2: props

2.3.1 效果

需求: 自定义用来显示一个人信息的组件

  1. 姓名必须指定,且为字符串类型。
  2. 性别为字符串类型,如果性别没有指定,默认为男。
  3. 年龄为字符串类型,且为数字类型,默认值为18

image.png

props 的基本使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>props基本使用</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  用于支持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">
         //创建组件
         class Person extends React.Component{
             render(){
                  console.log(this)
                  const {name,age,sex} = this.props

                 return (
                     <ul>
                         <li>姓名:{name}</li>
                         <li>性别:{sex}</li>
                         <li>年龄:{age+1}</li>
                     </ul>
                 )
             }
         }

         ReactDOM.render(<Person name="jerry" age={19} sex="男"></Person>,document.getElementById('test1'))
         ReactDOM.render(<Person name="tom" age={19} sex="女"></Person>,document.getElementById('test2'))

         const person = {name:'老刘',age: 19,sex:'女' }

       //  ReactDOM.render(<Person name={person.name} age={person.age} sex={person.sex}></Person>,document.getElementById('test1'))

         ReactDOM.render(<Person {...person}></Person>,document.getElementById('test3'))
     </script>
</body>
</html>

props进行限制

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>对props进行限制</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,用于支持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>
    <!-- 引入prop-types,用于对组件标签属性进行限制 -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <script type="text/babel">
        //创建组件
        class Person extends React.Component{
            render(){
                // console.log(this);
                const {name,age,sex} = this.props
                //props是只读的
                //this.props.name = 'jack' //此行代码会报错,因为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={100} speak={speak}/>,document.getElementById('test1'))
        ReactDOM.render(<Person name="tom" age={18} sex="女"/>,document.getElementById('test2'))

        const p = {name:'老刘',age:18,sex:'女'}
        // console.log('@',...p);
        // ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>,document.getElementById('test3'))
        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>对props进行限制</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,用于支持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>
    <!-- 引入prop-types,用于对组件标签属性进行限制 -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <script type="text/babel">
        //创建组件
        class Person extends React.Component {
             constructor( props){
                 //构造器是否接收props,是否传递给super,取决于:是否希望在构造中通过this访问props
                 console.log(props)
                 super(props)
                 console.log('constructor',this.props)
             }

            //对标签属性进行类型、必要性的限制
            static propTypes = {
                name:PropTypes.string.isRequired, //限制name必传,且为字符串
                sex:PropTypes.string,//限制sex为字符串
                age:PropTypes.number,//限制age为数值
            }

            //指定默认标签属性值
            static defaultProps = {
                sex:'男',//sex默认值为男
                age:18 //age默认值为18
            }

            render(){
                // console.log(this);
                const {name,age,sex} = this.props
                //props是只读的
                //this.props.name = 'jack' //此行代码会报错,因为props是只读的
                return (
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                    </ul>
                )
            }
        }


        //渲染组件到页面
        ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
    </script>
</body>
</html>

函数组件使用 props

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>对props进行限制</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,用于支持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>
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>


<script type="text/babel">
    //创建组件
    function Person(props) {
        const {name, age, sex} = props
        return (
            <ul>
                <li>姓名:{name}</li>
                <li>性别:{sex}</li>
                <li>年龄:{age}</li>
            </ul>
        )
    }

    Person.propTypes = {
        name: PropTypes.string.isRequired, //限制name必传,且为字符串
        sex: PropTypes.string,//限制sex为字符串
        age: PropTypes.number,//限制age为数值
    }

    //指定默认标签属性值
    Person.defaultProps = {
        sex: '男',//sex默认值为男
        age: 18 //age默认值为18
    }
    //渲染组件到页面
    ReactDOM.render(<Person name="jerry"/>, document.getElementById('test1'))
</script>
</body>
</html>

2.3.2 理解

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

2.3.3 作用

  1. 通过标签属性从组件外向组件内传递变化的数据。
  2. 注意: 组件内部不要修改 props 数据。

2.3.4 编码操作

  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.
}
  1. 扩展属性: 将对象的所有属性通过 props 传递
<Person {...person}/>
  1. 默认属性值
Person。defaultProps = {
     age: 18,
   sex: ‘男’
}
  1. 组件类的构造函数
    constructor(props){
    super(props)
    console.log(props)//打印所有属性
    }
    

2.4. 组件三大核心属性3: refs 与 事件处理

2.4.1 效果

需求: 自定义组件,功能说明如下:

  1. 点击按钮,提示第一个输入框中的值。
  2. 当第2个输入框失去焦点时,提示这个输入框中的值。

image.png

字符串形式的Ref

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>1_字符串形式的Ref</title>
</head>
<body>

    <!--    准备好一个容器-->
    <div id="test"></div>

    <!--    引入react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--引入react-dom  用于支持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" >
        //创建组件

        class Demo extends React.Component {
            //展示左侧输入框的数据
            showData = () =>{
                const {input} = this.refs
                alert(input.value)
            }

            //展示右侧输入框的数据
            showData2 = () =>{
                const {input1} = this.refs
                alert(input1.value)
            }

            render() {
                return (
                    <div>
                        <input  ref="input" type="text" placeholder="点击按钮提示数据"/> &nbsp;
                        <button onClick={this.showData}> 点我提示左侧的数据</button>
                        <input ref = "input1" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
        }

        //渲染组件到页面
        ReactDOM.render(<Demo a="1" b='2'/> , document.getElementById('test'))
    </script>
</body>
</html>

回调函数形式的ref

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>2_回调函数形式的ref</title>
</head>
<body>

    <!--    准备好一个容器-->
    <div id="test"></div>

    <!--    引入react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--引入react-dom  用于支持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" >
        //创建组件

        class Demo extends React.Component {
            //展示左侧输入框的数据
            showData = () =>{
                const {input} = this
                alert(input.value)
            }

            //展示右侧输入框的数据
            showData2 = () =>{
                const {input1} = this
                alert(input1.value)
            }

            render() {
                return (
                    <div>
                        <input  ref={c => this.input = c} type="text" placeholder="点击按钮提示数据"/> &nbsp;
                        <button onClick={this.showData}> 点我提示左侧的数据</button>
                        <input onBlur={this.showData2} ref={c => this.input1 = c} type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
        }

        //渲染组件到页面
        ReactDOM.render(<Demo a="1" b='2'/> , document.getElementById('test'))
    </script>
</body>
</html>

回调ref中回调执行次数的问题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>2_回调函数形式的ref</title>
</head>
<body>

    <!--    准备好一个容器-->
    <div id="test"></div>

    <!--    引入react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--引入react-dom  用于支持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" >
        //创建组件

        class Demo extends React.Component {
            //展示左侧输入框的数据
            showData = () =>{
                const {input} = this
                alert(input.value)
            }

            //展示右侧输入框的数据
            showData2 = () =>{
                const {input1} = this
                alert(input1.value)
            }

            render() {
                return (
                    <div>
                        <input  ref={c => this.input = c} type="text" placeholder="点击按钮提示数据"/> &nbsp;
                        <button onClick={this.showData}> 点我提示左侧的数据</button>
                        <input onBlur={this.showData2} ref={c => this.input1 = c} type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
        }

        //渲染组件到页面
        ReactDOM.render(<Demo a="1" b='2'/> , document.getElementById('test'))
    </script>
</body>
</html>

createRef 的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>4_createRef</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持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">
        //创建组件
        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="点击按钮提示数据"/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                        <input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
    </script>
</body>
</html>

2.4.2 理解

组件内的标签可以定义ref属性来标识自己

2.4.3 编码

  1. 字符串形式的 ref

    <input ref="input1"/>
    
  2. 回调形式的 ref

<input ref = { (c) => {this.input1 = c}}
  1. createRef 创建 ref 容器
myRef = React.createRef()
<input ref={this.myRef} />

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>事件处理</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持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">
        //创建组件
        class Demo extends React.Component{
            /* 
                (1).通过onXxx属性指定事件处理函数(注意大小写)
                        a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
                        b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
                (2).通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref
             */
            //创建ref容器
            myRef = React.createRef()
            myRef2 = React.createRef()

            //展示左侧输入框的数据
            showData = (event)=>{
                console.log(event.target);
                alert(this.myRef.current.value);
            }

            //展示右侧输入框的数据
            showData2 = (event)=>{
                alert(event.target.value);
            }

            render(){
                return(
                    <div>
                        <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                        <input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
    </script>
</body>
</html>

2.5 收集表单数据

2.5.1 效果

需求: 定义一个包含表单的组件
输入用户名密码后,点击登录提示输入信息

image.png

2.5.2 理解

包含表单的组件分类

  1. 受控组件
  2. 非受控组件

非受控组件
**

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>1_非受控组件</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持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">
        //创建组件
        class Login extends React.Component {

            handleSubmit = (event) => {
                event.preventDefault()

                const {username, password} = this
                alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
            }
            render(){
                return (
                    <form 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>

受控组件
**

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>2_受控组件</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持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">
        //创建组件
        class Login extends React.Component{

            //初始化状态
            state = {
                username:'', //用户名
                password:'' //密码
            }

            //保存用户名到状态中
            saveUsername = (event)=>{
                this.setState({username:event.target.value})
            }

            //保存密码到状态中
            savePassword = (event)=>{
                this.setState({password:event.target.value})
            }

            //表单提交的回调
            handleSubmit = (event)=>{
                event.preventDefault() //阻止表单提交
                const {username,password} = this.state
                alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
            }

            render(){
                return(
                    <form 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.6. 组件的生命周期

2.6.1 效果

需求: 定义组件实现以下功能:

  1. 让指定的文本做显示 / 隐藏的渐变动画
  2. 从完全可见,到彻底消失,耗时2S。
  3. 点击 “不活了” 按钮从界面消失。

image.png

编码实现:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>1_引出生命周期</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持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">
        //创建组件
        //生命周期回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子
        class Life extends React.Component{

            state = {opacity:1}

            death = ()=>{
                //卸载组件
                ReactDOM.unmountComponentAtNode(document.getElementById('test'))
            }

            //组件挂完毕
            componentDidMount(){
                console.log('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(){
                console.log('render');
                return(
                    <div>
                        <h2 style={{opacity:this.state.opacity}}>React学不会怎么办?</h2>
                        <button onClick={this.death}>不活了</button>
                    </div>
                )
            }
        }
        //渲染组件
        ReactDOM.render(<Life/>,document.getElementById('test'))
    </script>
</body>
</html>

2.6.2 理解

  1. 组件从创建到死亡它会经历一些特定的阶段。
  2. React 组件中包含一系列勾子函数(生命周期函数),会在特定的时刻调用。
  3. 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。

2.6.3 生命周期流程图(旧)

image.png

  1. 初始化阶段: 由 ReactDOM.render() 触发 —- 初次渲染

    1. constructor
    2. componentWillMount()
    3. render()
    4. componentDidMount()
  2. 更新阶段: 由组件内部 this.setState()或 父组件重新 render 触发

    1. shouldComponentUpdate()
    2. componentWillUpdate()
    3. render()
    4. componentDidUpdate()
  3. 卸载组件: 由ReactDOM.unmountComponentAtNode() 触发

    1. componentWillUnMount()

2.6.4. 生命周期流程图(新)

image.png

生命周期的三个阶段(新)
  1. 初始化阶段 : 由 ReactDOM.render() 触发—-初次渲染
    1. constructor
    2. getDerivedStateFromProps
    3. render()
    4. componentDidMount()
  2. 更新阶段:由 组件内部 this.setState( ) 或 父组件重新 render 触发
    1. getDerivedStateFromProps
    2. shouldComponentUpdate()
    3. render()
    4. getSnapshotBeforeUpdate
    5. componentDidUpdate()
  3. 卸载组件: 由 ReactDOM.unmountComponentAtNode() 触发
    1. componentWillUNmount()

2.6.5. 重要的勾子

  1. render : 初始化渲染或更新渲染调用
  2. componentDidMount: 开启监听,发送 ajax 请求。
  3. compoentWillUnmount : 做一些收尾工作,如: 清理定时器

2.6.6 即将废弃的勾子

  1. componentWillMount
  2. componentWillReceivedProps
  3. componentWillUpdate

现在使用会出现警告,下一个大版本需要加上 UNSAFE_ 前缀才能使用,以后可能会被彻底废弃,不建议使用。

2.7 虚拟 DOM 与 DOM Diffing 算法

2.7.1 效果

需求: 验证虚拟 DOM Different 算法的存在

image.png

2.7.2 基本原理图

image.png

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>验证diff算法</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>

    <script type="text/babel">
        class Time extends React.Component {
            state = {date: new Date()}

            componentDidMount () {
                setInterval(() => {
                    this.setState({
                        date: new Date()
                    })
                }, 1000)
            }

            render () {
                return (
                    <div>
                        <h1>hello</h1>
                        <input type="text"/>
                        <span>
                            现在是:{this.state.date.toTimeString()}
                            <input type="text"/>
                        </span>
                    </div>
                )
            }
        }

        ReactDOM.render(<Time/>,document.getElementById('test'))
</script>
</body>
</html>