react组件化

模块化与组件化

模块化

  • 模块是向外提供特定功能的js程序, 一般一个js文件就是一个模块
  • 当应用的js都以模块来编写的, 这个应用就是一个模块化的应用

组件化

  • 组件就是用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)
  • 当应用是以多组件的方式实现, 这个应用就是一个组件化的应用

react定义组件

函数式组件

组件以函数的形式进行编写,函数的返回值是dom结构。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .header{
      
      
            background-color: orange;
        }
    </style>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>

    <!-- REACT ,react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- REACTDOM ,支持react操作dom-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 将jsx转换成js文件 -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!--text/babel表明是jsx  -->
    <script type="text/babel">
        // 1.创建函数式组件
        function Demo(){
      
      
            console.log(this)//注意this是undefined,因为babel翻译后开启了严格模式,严格模式下自定义的this是不允许指向window的
            return <h2>我是函数式组件(适用于【简单组件】的定义)</h2>
        }
        // 2.渲染虚拟DOM到页面
        // 如果渲染的是组件需要使用标签,并且首字母要大写,并且需要进行闭合
        ReactDOM.render(<Demo/>,document.getElementById("test"))
    </script> 


        <!-- 总结:
                执行了ReactDOM.render(<Demo/>.......之后,发生了什么?
                     1.React解析组件标签,找到了Demo组件。
                     2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。 -->
</body>
</html>

类式组件

类补充

  • 类的属性存放在实例对象上,类上的方法存放在类的原型对象上。
  • 类中的方法默认开启了局部严格模式,this是undefined
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        class Person {
      
      
            // 属性放在了类的实例对象上
            sex = 'f';
            constructor(name, age) {
      
      
                // this指向的是实例对象
                this.name = name
                this.age = age
            }

            // 方法放在了类的原型对象上,供实例使用 
            speak() {
      
      
                // 通过person实例调用speck时,this是对应的实例对象
                console.log(`我叫${ 
        this.name}, 我今年 ${ 
        this.age}岁了`);
            }
        }
        const p1 = new Person('yang', 20)
        const p2 = new Person('cheng', 21)
        
        console.log('p1:', p1);
        console.log('p2:', p2);

        p1.speak()
        p2.speak()
    </script>    
</body>
</html>

输出:
在这里插入图片描述
如果子类中使用构造器,则必须使用调用super()

类式组件

  • 定义的类需要继承React.Component
  • 必须写render
  • render必须有返回值
  • 写了几个组件标签就会new几个组件实例
    eg:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .header{
      
      
            background-color: orange;
        }
    </style>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>

    <!-- REACT ,react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- REACTDOM ,支持react操作dom-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 将jsx转换成js文件 -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!--text/babel表明是jsx  -->
    <script type="text/babel">
        // 1.创建类式组件(需要继承React.Component;必须写render;render必须有返回值)
        class Demo extends React.Component{
      
      
            // render放在Demo的原型对象上,供实例使用
            // render中的this是谁?—Demo的实例对象,即Demo的组件实例对象。
            render(){
      
      
                return  <h2>我是类式组件(适用于【复杂组件】的定义)</h2>
            }
        }
        // 2.渲染虚拟DOM到页面()
        // Demo实例由React创建
        ReactDOM.render(<Demo/>,document.getElementById("test"))

    </script> 


        <!-- 总结:
                执行了ReactDOM.render(<Demo/>.......之后,发生了什么?
                    1.React解析组件标签,找到了Demo组件。
                    2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
                    3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。 -->
</body>
</html>

在render中输出组件实例对象

render(){
    
    
    console.log('组件实例对象', this);
          return  <h2>我是类式组件(适用于【复杂组件】的定义)</h2>
      }

在这里插入图片描述

简单组件和复杂组件

  • 拥有状态的组件叫复杂组件
  • 状态存储在组件实例对象(使用类定义的组件)的state中
    所以具有state属性的组件是复杂组件。

组件的三大属性

组件的三大属性实际上说的是组件实例的三大属性,而只有类式组件才会有实例,所以组件的三大属性实际上是针对类式组件而言的

组件实例对象有三大属性state、props、refs

state

state一般用来存储一些变量的值,我们知道state属性是在组件实例对象上的,如果想要获取state中的值就需要获取组件实例对象,而在类的构造函数中的this就是组件实例对象,所以我们可以通过类的构造函数来实现对state的操作。

构造函数是有参数的,可以参照官方文档以props为参数

  constructor(props) {
    
    
    super(props);
    this.state = {
    
     seconds: 0 };
  }

可以在构造函数或render中通过 this.state.seconds(确保this是组件实例对象) 来获取state数据

扫描二维码关注公众号,回复: 15098984 查看本文章

补充:react的事件绑定

格式:点击事件onClick={clickEvent}

  • 事件使用驼峰命名法
  • 方法名后面无需添加()
  • 方法名使用{}进行包裹

eg:

        // 1.创建类式组件
        class Weather extends React.Component{
    
    
            render(){
    
    
                // 原生的事件绑定<h1 οnclick="clickEvent()">
                // {}中放的是js表达式,如果函数加()就会自动执行,onClick的值就是一个undefined
                return <h1 onClick={
    
    clickEvent}>今天天气真{
    
    this.state.isHot?"炎热":"凉爽"}</h1>
            }
        }
        // 2.渲染虚拟DOM到页面
        ReactDOM.render(<Weather/>,document.getElementById('test'))
        function clickEvent(){
    
    
            console.log('被点击了');
        }

state使用案例

实现效果:点击切换天气
在这里插入图片描述
在这里插入图片描述

  • this的获取问题
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .header{
      
      
            background-color: orange;
        }
    </style>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>

    <!-- REACT ,react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- REACTDOM ,支持react操作dom-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 将jsx转换成js文件 -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!--text/babel表明是jsx  -->
    <script type="text/babel">
        // 1.创建类式组件
        class Weather extends React.Component{
      
      
            constructor(props){
      
      
                super(props)
                // 组件实例对象上存储值(写成对象)
                this.state = {
      
      
                    isHot:false
                }

                // 利用bind修改changeWeather的this并且将返回的新函数赋值给定义的changeWeather2(bind返回一个新的函数)
                // changeWeather2是在实例对象上的
                this.changeWeather2 = this.changeWeather.bind(this)

            }
            render(){
      
      
                return <h1 onClick={
      
      this.changeWeather2}>今天天气真{
      
      this.state.isHot?"炎热":"凉爽"}</h1>
            }
            changeWeather(){
      
      
                // 类的方法放在类的原型对象上,所以changeWeather放在Weather的原型对象上
                // 所以如果通过Weather的实例对象调用changeWeather时,changeWeather的实例对象就是Weather的实例对象
                // 由于类中的方法默认开启严格模式,所以如果是直接调用函数(即不使用实例对象调用)那么this就是undefine
                // 按<h1 onClick={this.changeWeather}>的写法,是将实例对象的方法赋值给onClick,所以如果点击后再调用的话就是直接调用,this就是undefined
                    // 并且类中的方法默认开启了局部严格模式,所以changeWeather中的this是undefined
                // 所以进行修改,在构造函数中进行设置this.changeWeather2 = this.changeWeather.bind(this)
                console.log(this);
            }

        }
        // 2.渲染虚拟DOM到页面
        ReactDOM.render(<Weather/>,document.getElementById('test'))

        // function changeWeather(){
      
      
        //     console.log('修改isHot')
        //     // babel开启严格模式,this指向是undefined,所以放在外面不可取,放在类中作为方法使用
        // }

    </script> 
</body>
</html>
  • 实现响应式数据
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .header{
      
      
            background-color: orange;
        }
    </style>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>

    <!-- REACT ,react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- REACTDOM ,支持react操作dom-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 将jsx转换成js文件 -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!--text/babel表明是jsx  -->
    <script type="text/babel">
        // 1.创建类式组件
        class Weather extends React.Component{
      
      
            constructor(props){
      
      
                super(props)
                // 组件实例对象上存储值(写成对象)
                this.state = {
      
      
                    isHot:false,
                    wind:'微风'
                }

                // 修改changeWeather的this并且赋值给定义的changeWeather(bind返回一个新的函数)
                // 这里定义的 this.changeWeather是在实例对象上的
                this.changeWeather = this.changeWeather.bind(this)

            }
            render(){
      
      
                return <h1 onClick={
      
      this.changeWeather}>今天天气真{
      
      this.state.isHot?"炎热":"凉爽"},{
      
      this.state.wind}</h1>
            }
            // 这里定义的 changeWeather是在类的原型对象上的
            changeWeather(){
      
      

                // 如果直接修改数据实现不了响应式数据
                // this.state.isHot = !this.state.isHot错误写法

                //  使用setState()才能实现数据的响应式,该方法是React.Component原型对象上的方法
                // setState()的参数是对象
                // setState()只是修改某个属性值, 不会修改其他属性值
                const isHot = this.state.isHot
                this.setState({
      
      isHot:!isHot})

            }

        }
        // 2.渲染虚拟DOM到页面
        ReactDOM.render(<Weather/>,document.getElementById('test'))

    </script> 
    <!-- setState总结:
        1.构造器调用1次
        2.render调用n+1次(n代表状态更新的次数,1是第一次渲染的时候进行调用)
        3.数据是进行修改,而不是覆盖 -->
</body>
</html>
  • 简写方式
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .header{
      
      
            background-color: orange;
        }
    </style>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>

    <!-- REACT ,react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- REACTDOM ,支持react操作dom-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 将jsx转换成js文件 -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!--text/babel表明是jsx  -->
    <script type="text/babel">
        // 1.创建类式组件
        class Weather extends React.Component{
      
      
            // 可以将组件实例对象的属性直接在类中定义
            state = {
      
      
                    isHot:false,
                    wind:'微风'
                }
            render(){
      
      
                return <h1 onClick={
      
      this.changeWeather}>今天天气真{
      
      this.state.isHot?"炎热":"凉爽"},{
      
      this.state.wind}</h1>
            }
            // 直接将方法写成属性赋值的样式,那么方法就放在组件实例对象上了
            // 使用箭头函数,this就会向外找就是组件实例对象(结构上的向外查找,class中的this就是实例对象)
            changeWeather = ()=>{
      
      
                const isHot = this.state.isHot
                this.setState({
      
      isHot:!isHot})
            }

        }
        // 2.渲染虚拟DOM到页面
        ReactDOM.render(<Weather/>,document.getElementById('test'))

    </script> 
</body>
</html>
  • 组件类中的方法我们一般做绑定事件使用,要用赋值语句+箭头函数的方式进行定义
  • 组件类中的属性直接在类中定义即可

state小结

  • 理解
  1. state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
  2. 组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
  • 强烈注意
  1. 组件中render方法中的this为组件实例对象
  2. 组件自定义的方法中this为undefined,如何解决?
    a)强制绑定this: 通过函数对象的bind()
    b)箭头函数----------推荐
  3. 状态数据,不能直接修改或更新,必须调用setState()
    调用setState,react就会更新数据状态并且重新调用一次render()使页面的重新渲染,数据进行刷新。

props

props的基本使用

props的使用方法:prop是在调用组件的时候为通过属性名=属性值为其传值,之后就可以在组件实例对象的props中访问到传过来的值。
eg:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .header{
      
      
            background-color: orange;
        }
    </style>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>
    <div id="test2"></div>
    <div id="test3"></div>

    <script type="text/javascript" src="../js/react.development.js"></script>
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/babel">
        // 1.创建类式组件
        class Person extends React.Component{
      
      
            render(){
      
      
                return(
                    <ul>
                        <li>姓名:{
      
      this.props.name}</li>
                        <li>年龄:{
      
      this.props.age}</li>
                        <li>性别:{
      
      this.props.sex}</li>
                    </ul>
                )
            }
        }
        // 2.渲染虚拟DOM到页面
        ReactDOM.render(<Person name='yang' age='20' sex='nv'/>,document.getElementById('test'))
        ReactDOM.render(<Person name='zhe' age='21' sex='nan'/>,document.getElementById('test2'))
        ReactDOM.render(<Person name='ming' age='22' sex='nv'/>,document.getElementById('test3'))
    </script> 
</body>
</html>

在这里插入图片描述

批量传递props

当传递多个props时直接在组件上进行传递就会显得有点乱,所以可以使用批量传递的方式进行值传递。
利用扩展运算符(…)进行毗连数据传递

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .header{
      
      
            background-color: orange;
        }
    </style>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>
    <div id="test2"></div>
    <div id="test3"></div>

    <script type="text/javascript" src="../js/react.development.js"></script>
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/babel">
        // 1.创建类式组件
        class Person extends React.Component{
      
      
            render(){
      
      
                const{
      
      name,age,sex} = this.props
                return(
                    <ul>
                        <li>姓名:{
      
      name}</li>
                        <li>年龄:{
      
      age}</li>
                        <li>性别:{
      
      sex}</li>
                    </ul>
                )
            }
        }
        // 2.渲染虚拟DOM到页面
        const p = {
      
      name:'yang',age:20,sex:'nv'}
        // 注意扩展运算符...不能直接给对象使用(可以直接给数组使用),即在js中...p语法是不对的,但是可以这样使用{...p}(这里的{}表示的是对象, 浅克隆复制对象)
        // 但是在react+jsx中可以直接使用...p(注意这里的{}表示的是使用js表达式),但也仅限于标签传递属性时可以使用
        ReactDOM.render(<Person {
      
      ...p}/>,document.getElementById('test3'))

    </script> 
    <!-- 总结:
    1. props批量传递属性是通过{...p}实现的 -->
</body>
</html>

props的限制

给组件传递的属性,对某些特殊的属性一般会有格式上的限制,如必须是字符串类型,我们可以在组件上进行限制,react为我们提供了propTypes属性进行限制:

需要引入prop-types依赖包才会有PropTypes属性。

// Person是一个组件名
        Person.propTypes={
    
    
            // 名字必须指定而且是字符串
            // 直接使用PropTypes需要引入prop-types依赖包(用于对组件标签属性进行限制)
            name: PropTypes.string.isRequired,
            sex: PropTypes.string,
            age: PropTypes.number,
            speak: PropTypes.func
        }

也可以为属性设置默认值

 // 属性默认值
        Person.defaultProps={
    
    
            sex:'男',
            age:18
        }

eg:案例使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .header{
      
      
            background-color: orange;
        }
    </style>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>
    <div id="test2"></div>
    <div id="test3"></div>

    <script type="text/javascript" src="../js/react.development.js"></script>
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- 引入propTypes用于对组件标签属性进行限制,引入该模块会有PropTypes属性 -->
    <script type="text/javascript" src="../js/prop-types.js"></script>
    <script type="text/babel">
        // 1.创建类式组件
        class Person extends React.Component{
      
      
            render(){
      
      
                const{
      
      name,age,sex} = this.props
                return(
                    <ul>
                        <li>姓名:{
      
      name}</li>
                        <li>年龄:{
      
      age+1}</li>
                        <li>性别:{
      
      sex}</li>
                    </ul>
                )
            }
        }
        // 对属性进行限制
        Person.propTypes={
      
      
            // 名字必须指定而且是字符串
            // 直接使用PropTypes需要引入prop-types依赖包(用于对组件标签属性进行限制)
            name: PropTypes.string.isRequired,
            sex: PropTypes.string,
            age: PropTypes.number,
            // 限制speak是一个函数
            speak: PropTypes.func
        }
        // 属性默认值
        Person.defaultProps={
      
      
            sex:'男',
            age:18
        }
        

        // 2.渲染虚拟DOM到页面
        ReactDOM.render(<Person name='yang' age={
      
      20} sex='nv' speak={
      
      speak}/>,document.getElementById('test'))
        ReactDOM.render(<Person name='zhe' age={
      
      21} sex='nan'/>,document.getElementById('test2'))

        const p = {
      
      name:'yang',age:20,sex:'nv'}
        ReactDOM.render(<Person {
      
      ...p}/>,document.getElementById('test3'))

        function speak(){
      
      
            console.log('说话:balabalabala');
        }

    </script> 
    <!-- 总结:
    1. props批量传递属性是通过{...p}实现的 -->
</body>
</html>

注意:props是只读的

props的简写方式

propTypesdefaultProps都是为组件添加的属性,类式组件的本质又是一个类,所以想要给类式组件添加属性可以直接在类中使用static标识符进行添加。

所以上述代码可以写成如下代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .header{
      
      
            background-color: orange;
        }
    </style>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>
    <div id="test2"></div>
    <div id="test3"></div>

    <script type="text/javascript" src="../js/react.development.js"></script>
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- 引入propTypes用于对组件标签书机型进行限制 -->
    <script type="text/javascript" src="../js/prop-types.js"></script>
    <script type="text/babel">
        // 1.创建类式组件
        class Person extends React.Component{
      
      
            render(){
      
      
                const{
      
      name,age,sex} = this.props
                return(
                    <ul>
                        <li>姓名:{
      
      name}</li>
                        <li>年龄:{
      
      age+1}</li>
                        <li>性别:{
      
      sex}</li>
                    </ul>
                )
            }
            // 对属性的限制可以直接利用static放在class上
            // 对属性进行限制
            static propTypes={
      
      
                // 名字必须指定而且是字符串
                name: PropTypes.string.isRequired,
                sex: PropTypes.string,
                age: PropTypes.number,
                speak: PropTypes.func
            }
            // 属性默认值
            static defaultProps={
      
      
                sex:'男',
                age:18
            }
        }
        
        // 2.渲染虚拟DOM到页面
        ReactDOM.render(<Person name='yang' age={
      
      20} sex='nv' speak={
      
      speak}/>,document.getElementById('test'))
        ReactDOM.render(<Person name='zhe' age={
      
      21} sex='nan'/>,document.getElementById('test2'))

        const p = {
      
      name:'yang',age:20,sex:'nv'}
        // 注意扩展运算符...不能直接给对象使用,即在js中...p语法是不对的,但是可以这样使用{...p}
        // 但是在react+jsx中可以直接使用...p(注意这里的{}表示的是使用js表达式)
        ReactDOM.render(<Person {
      
      ...p}/>,document.getElementById('test3'))

        function speak(){
      
      
            console.log('说话:balabalabala');
        }

    </script> 
    <!-- 总结:
    1. props是只读的
    2. 对props属性的限制可以直接利用static放在class上-->
</body>
</html>

类中构造器的的props参数

之前在类中使用构造器啊的时候我们一般会传递一个props属性。如下:

constructor(props){
    
    
       super(props)
   }

类中的构造器一般有两个作用:

  • 通过给 this.state 赋值对象来初始化内部 state。—— 可以直接在类中定义state属性
  • 为事件处理函数绑定实例。——可以直接在类中定义成箭头函数

因为上述两个内容都可以在类中直接定义解决,不是必须在箭头函数中执行,所以类的构造器一般可以省略不写。
如果写了构造函数可以传递参数也可以不传递参数(props),但是如果写了构造函数一定要写super。传不传props参数的区别是:如果不传递props参数就不能在构造器中通过this.props访问props属性。

函数式组件使用props

与组件实例的其他两个属性不同,函数时组件也可以使用props属性,利用的是函数可以传递参数的特性。
只不过此时要对参数进行限制时,只能在函数外进行限制
eg:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .header{
      
      
            background-color: orange;
        }
    </style>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>
    <div id="test2"></div>
    <div id="test3"></div>

    <script type="text/javascript" src="../js/react.development.js"></script>
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/javascript" src="../js/prop-types.js"></script>
    <script type="text/babel">
        // 1.创建函数式组件
        function Person(props){
      
      
            return(
                    <ul>
                        <li>姓名:{
      
      props.name}</li>
                        <li>年龄:{
      
      props.age}</li>
                        <li>性别:{
      
      props.sex}</li>
                    </ul>
                )
        }
        // 对属性进行限制
        Person.propTypes={
      
      
            // 名字必须指定而且是字符串
            name: PropTypes.string.isRequired,
            sex: PropTypes.string,
            age: PropTypes.number,
            speak: PropTypes.func
        }
        // 属性默认值
        Person.defaultProps={
      
      
            sex:'男',
            age:18
        }

        // 2.渲染虚拟DOM到页面
        ReactDOM.render(<Person name='yang' age={
      
      20} sex='nv'/>,document.getElementById('test'))
        ReactDOM.render(<Person name='zhe' age={
      
      21} sex='nan'/>,document.getElementById('test2'))
        ReactDOM.render(<Person name='ming' age={
      
      22} sex='nv'/>,document.getElementById('test3'))



    </script> 
    <!-- 总结:
    1. 函数式组件只能使用props,直接通过参数进行使用
    2. 也可以进行限制 -->
</body>
</html>

refs

可以使用ref唯一的表示组件内的标签,标识完之后就可以在组件实例对象的refs属性上读取读取标识的标签。
eg:

字符串ref

标签使用ref标识时直接使用字符串进行标识即可。

案例:

  1. 点击按钮,弹窗提示数据
  2. 失去焦点,弹窗提示数据
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>字符串ref</title>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>

    <script type="text/javascript" src="../js/react.development.js"></script>
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <script type="text/babel">
        //  <!-- 1.创建类式组件 -->
        class Demo extends React.Component{
      
      
            render(){
      
      
                return (
                    <div>
                        <input ref="input1" type="text" id="input1" placeholder="点击按钮提示数据"/>
                        <button onClick={
      
      this.showDataClick}>点我提示左侧数据</button>&nbsp;
                        <input ref="input2" onBlur={
      
      this.showDataBlur} type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
            showDataClick = ()=>{
      
      
            //读取ref标识的标签
                const {
      
      input1} = this.refs
                alert(input1.value)
            }
            showDataBlur = ()=>{
      
      
                const {
      
      input2} = this.refs
                alert(input2.value)
            }
        }

        // <!--2.渲染虚拟DOM到页面 -->
        ReactDOM.render(<Demo/>,document.getElementById('test'))
        
    </script>


<!-- 小结:
        1. ref用于标识存储dom元素
        2. 在组件实例对象的refs中总重可以获取对应的dom元素进行参送
    
    缺点:效率低-->
</body>
</html> 

字符串ref的缺点:效率低

回调函数形式的ref

标签使用ref标识时需要使用回调函数进行标识。
回调函数有一个参数,该参数就是所在的标签节点。
回调函数是一个箭头函数的形式,所以this由上一级决定,上一级就是render()函数,则this就是指向组件实例对象,所以可以直接将回调函数的参数(该节点)放在this上,这样在组件实例对象上就可以直接读取该节点了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>字符串ref</title>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>

    <script type="text/javascript" src="../js/react.development.js"></script>
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <script type="text/babel">
        //  <!-- 1.创建类式组件 -->
        class Demo extends React.Component{
      
      
            showDataClick = ()=>{
      
      
                const {
      
      input1} = this
                alert(input1.value)
            }
            showDataBlur = ()=>{
      
      
                const {
      
      input2} = this
                alert(input2.value)
            }
            render(){
      
      
                return (
                    <div>
                        <input ref={
      
      (currentNode)=>{
      
      this.input1=currentNode}} type="text" id="input1" placeholder="点击按钮提示数据"/>
                        <button onClick={
      
      this.showDataClick}>点我提示左侧数据</button>&nbsp;
                        <input ref={
      
      (currentNode)=>{
      
      this.input2=currentNode}} onBlur={
      
      this.showDataBlur} type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
        }

        // <!--2.渲染虚拟DOM到页面 -->
        ReactDOM.render(<Demo/>,document.getElementById('test'))
        
    </script>


<!-- 小结:
        ref={(currentNode)=>{this.input1=currentNode}}:将当前节点挂载到组件实例对象上
-->
</body>
</html> 

回调函数的 ref 比较常用。

回调ref的回调执行次数问题
如果 ref 回调函数是以内联函数(即在一行内定义的代码)的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素
注意是更新过程,即render()被重新执行渲染
eg:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>字符串ref</title>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>

    <script type="text/javascript" src="../js/react.development.js"></script>
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <script type="text/babel">
        //  <!-- 1.创建类式组件 -->
        class Demo extends React.Component{
      
      
            state ={
      
      
                isHot:true
            }
            showDataClick = ()=>{
      
      
                const {
      
      input1} = this
                alert(input1.value)
            }
            changeWeather=()=>{
      
      
                const {
      
      isHot} = this.state
                this.setState({
      
      isHot : !isHot})
            }
            saveInput=(currentNode)=>{
      
      
                this.input1 = currentNode;
                console.log('@',currentNode);
            }
            render(){
      
      
                const {
      
      isHot} = this.state
                return (
                    <div>
                        <h1 onClick = {
      
      this.changeWeather}>今天天气很{
      
      isHot?'炎热':'凉爽'}</h1>
                        <input ref={
      
      (currentNode)=>{
      
      this.input1=currentNode ; console.log('@',currentNode);}} type="text" id="input1" placeholder="点击按钮提示数据"/> 
                        <button onClick={
      
      this.showDataClick}>点我提示左侧数据</button>
                    </div>
                )
            }
        }

        // <!--2.渲染虚拟DOM到页面 -->
        ReactDOM.render(<Demo/>,document.getElementById('test'))  
    </script>
</body>
</html> 

输出:

在这里插入图片描述
这是因为,当数据更改重新调用render()的时候,当读取到ref定义的函数,因为不确定前一次调用的参数是否被清空,为了保证参数的干净,ref会先传入一个 null 参数,确保参数被清空 ,之后再传入当前节点进行执行,所以数据更新之后再次渲染ref的回调函数就会被执行两次。
所以为避免该问题可以不将函数写成回调函数,写成类绑定的回调函数
即:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>字符串ref</title>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>

    <script type="text/javascript" src="../js/react.development.js"></script>
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <script type="text/babel">
        //  <!-- 1.创建类式组件 -->
        class Demo extends React.Component{
      
      
            state ={
      
      
                isHot:true
            }
            showDataClick = ()=>{
      
      
                const {
      
      input1} = this
                alert(input1.value)
            }
            changeWeather=()=>{
      
      
                const {
      
      isHot} = this.state
                this.setState({
      
      isHot : !isHot})
            }
            saveInput=(currentNode)=>{
      
      
                this.input1 = currentNode;
                console.log('@',currentNode);
            }
            render(){
      
      
                const {
      
      isHot} = this.state
                return (
                    <div>
                        <h1 onClick = {
      
      this.changeWeather}>今天天气很{
      
      isHot?'炎热':'凉爽'}</h1>
                        {
      
      /*<input ref={(currentNode)=>{this.input1=currentNode ; console.log('@',currentNode);}} type="text" id="input1" placeholder="点击按钮提示数据"/> */}
                        <input ref={
      
      this.saveInput} type="text" id="input1" placeholder="点击按钮提示数据"/>
                        <button onClick={
      
      this.showDataClick}>点我提示左侧数据</button>
                    </div>
                )
            }
        }

        // <!--2.渲染虚拟DOM到页面 -->
        ReactDOM.render(<Demo/>,document.getElementById('test'))  
    </script>
<!-- 小结:
        ref={(currentNode)=>{this.input1=currentNode}}:将当前节点挂载到组件实例对象上
        当使用内联的回调函数的ref,在第一次加载的时候会执行一次;当数据修改的时候diaoyongrender会执行两次,第一次是为了清空dom元素,第二次返回标记的ref
        通过将ref的回调函数定义成class的绑定函数可以解决此问题
-->
</body>
</html> 

createRef

React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的(即一个createRef只能存储一个dom元素)
之后可以再标签中使用ref标识该容器,则该标签就会存储在该容器中。
使用:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>字符串ref</title>
</head>
<body>
    <!-- 容器 -->
    <div id="test"></div>

    <script type="text/javascript" src="../js/react.development.js"></script>
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <script type="text/babel">
        //  <!-- 1.创建类式组件 -->
        class Demo extends React.Component{
      
      
            // React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的(即一个createRef只能存储一个dom元素)
            myRef = React.createRef()
            myRef2 = React.createRef()
            showDataClick = ()=>{
      
      
                console.log(this.myRef);
                alert(this.myRef.current.value)
            }
            showDataBlur = ()=>{
      
      
                const {
      
      input2} = this
                alert(this.myRef2.current.value)
            }
            render(){
      
      
                return (
                    <div>
                        {
      
      /*this.myRef就会将该DOM元素放在上面创建的ref中*/}
                        <input ref={
      
      this.myRef} type="text" id="input1" placeholder="点击按钮提示数据"/>
                        <button onClick={
      
      this.showDataClick}>点我提示左侧数据</button>&nbsp;
                        <input ref={
      
      this.myRef2} onBlur={
      
      this.showDataBlur} type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
        }

        // <!--2.渲染虚拟DOM到页面 -->
        ReactDOM.render(<Demo/>,document.getElementById('test'))
        
    </script>
<!-- 小结:
    myRef = React.createRef()
    React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专川用”的(即一个createRef只能存储一个dom元素)
-->
</body>
</html> 

猜你喜欢

转载自blog.csdn.net/mantou_riji/article/details/127213995