React面向组件编程(理解与使用+state+props+refs与事件处理)

1 基本理解与使用
函数式组件
<!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>1_函数式组件</title>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-from,用于支持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. 创建函数式组件
    function MyComponent() {
      
      
        console.log(this);   // 此处的this是undefined,因为babel编译后开启了严格模式
        return <h2>我使用函数定义的组件(适用于【简单组件】的定义)</h2>;
    }

    // 2. 渲染组件到页面
    ReactDOM.render(<MyComponent/>, document.getElementById('test'));
    /*
    执行了React.render(<MyComponent/>.......之后发生了什么)
    1. react会解析组件标签,找到了Mycomponent组件。发现组件是使用函数定义的,随后调用该函数,将返回的虚拟Dom转为真实Dom,随后呈现在页面中
    */
    </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>2_类式组件</title>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-from,用于支持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 MyComponent extends React.Component{
      
      
        render() {
      
      
            // render方法放在了哪里? --- MyComponent的原型对象上,供实例使用
            // render中的this是谁? --- MyComponent的实例对象 MyComponent组件的实例对象
            console.log('render中的this', this);
            return <h2>我使用类定义的组件(适用于【复杂组件】的定义)</h2>;
        }
    }
    // 2.渲染组件到页面
    ReactDOM.render(<MyComponent/>, document.getElementById('test'));
    /*
    执行了React.render(<MyComponent/>.......之后发生了什么)
    1. react会解析组件标签,找到了Mycomponent组件。
    2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法
    3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中
    */
    </script>
</body>
</html>
注意
  1. 组件名必须首字母大写

  2. 虚拟DOM元素只能有一个根元素

  3. 虚拟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>1_类的基本知识</title>
</head>
<body>
    <script type="text/javascript">
        /*
        总结:
        1.类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时,才写
        2.如果A类继承了B类,且A类写了构造器,那么A类构造器中super是必须要调用的。
        3.类中所定义的方法,都是放在了类的原型对象上,供实例使用
        */

        // 创建一个Person类
        class Person {
      
      
            // 构造器方法
            constructor(name, age) {
      
      
                // 构造器中的this是谁?--类的实例对象
                this.name = name;
                this.age = age;
            }
            // 一般方法
            speak() {
      
      
                // speak方法放在了哪里? --- 类的原型对象上,供实例使用
                // 通过Person实例调用speak时,speak中的this就是Person实例
                console.log(`我叫${ 
        this.name},我年龄是${ 
        this.age}`);
            }
        }

        // 创建一个student类,继承于Person类
        class Student extends Person {
      
      
            constructor(name, age, grade) {
      
      
                super(name, age);
                this.grade = grade;
            }
            // 重写从父类继承过来的方法
            speak() {
      
      
                console.log(`我叫${ 
        this.name},我年龄时${ 
        this.age},我读的是${ 
        this.grade}年级`);
            }
            study() {
      
      
                // study方法放在了哪里? --- 类的原型对象上,供实例使用
                // 通过Student实例调用study时,study中的this就是Student实例
                console.log('我很努力的学习');
            }
        }
        // 创建一个Person的实例对象
        // const p1 = new Person('tom', 18);
        // const p2 = new Person('jerry', 19);
        // console.log(p1);
        // console.log(p2);
        // p1.speak();
        // p2.speak();
        // p1.speak.call({a: 1, b: 2});

        const s1 = new Student('小张', 15, '高一');
        console.log(s1);
        s1.speak();
        s1.study();
    </script>

</body>
</html>
2 组件实例的三大核心属性1.state
<!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>state.html</title>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-from,用于支持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 {
      
      
        constructor(props) {
      
      
            super(props);
            this.state = {
      
      isHot: true}
        }
        render() {
      
      
            // 读取状态
            const {
      
      isHot} = this.state;
            return <h1 onClick={
      
      demo}>今天天气很{
      
      isHot ? '炎热' : '凉爽'}</h1>
        }
    }
    // 2.渲染组件到页面
    ReactDOM.render(<Weather/>, document.getElementById('test'));

    function demo() {
      
      
        alert('按钮3被点击了');
    }
    </script>
</body>
</html>
2.1 练习

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

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>state.html</title>
  </head>
  <body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-from,用于支持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) {
      
      
          super(props);
          // 初始化状态
          this.state = {
      
       isHot: true, wind: '微风' };
          // 解决changeWeacther中this指向问题
          this.changeWeather = this.changeWeather.bind(this);
        }

        // render调用几次?----- 点几次调用几次
        render() {
      
      
          // 读取状态
          const {
      
       isHot, wind } = this.state;
          return (
            <h1 onClick={
      
      this.changeWeather}>今天天气很{
      
      isHot ? "炎热" : "凉爽"}, {
      
      wind}</h1>
          );
        }

        // changeWeather调用几次?----- 点几次调用几次
        changeWeather() {
      
      
          // changeWeather方法放在了哪里? --- Weather的原型对象上,供实例使用
          // 由于changeWeather是作为onClick的回调,所以不是通过实例调用,是直接调用
          // 类中的方法默认开启了局部的严格模式,所以changeWeather中的this位undefined
          
          // 获取原来的isHot值
          const isHot = this.state.isHot;
          // 严重注意:状态必须通过setState进行更新,更新是一种合并,不是替换
          this.setState({
      
      isHot: !isHot})


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

          
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<Weather />, document.getElementById("test"));
    </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>state.html</title>
  </head>
  <body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-from,用于支持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 Weather extends React.Component {
      
      
        state = {
      
       isHot: true, 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})
        }
      }
      ReactDOM.render(<Weather />, document.getElementById("test"));
    </script>
  </body>
</html>
2.2 理解
  1. state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)

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

2.3 强烈注意
  1. 组件中render方法中的this为组件实例对象

  2. 组件自定义的方法中this为undefined,如何解决?

    a) 强制绑定this: 通过函数对象的bind()

    b) 箭头函数

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

3 组件三大核心属性2: props

函数式组件能用props,但是不能用state,refs

3.1 效果

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

1. 姓名必须指定,且为字符串类型;

2. 性别为字符串类型,如果性别没有指定,默认为男

3. 年龄为字符串类型,且为数字类型,默认值为18

在这里插入图片描述

3.2 理解
  1. 每个组件对象都会有props(properties的简写)属性

  2. 组件标签的所有属性都保存在props中

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

  2. 注意: 组件内部不要修改props数据

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)//打印所有属性
    }
    

props是只读的,不能进行修改

eg:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello_react</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-from,用于支持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">  /* 此处一定要写babel */
    // 创建组件
    class Person extends React.Component {
      
      
        state = {
      
      name: 'tom', age: 18, sex: '女'};
        
        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, // 限制name为字符串
        age: PropTypes.number, // 限制age为数值
        speak: PropTypes.func, // 限制speak为函数
    }
    // 只当默认标签属性值
    Person.defaultProps = {
      
      
        sex: '不男不女', // sex默认值为男
        age: 18 // sex默认值为男
    }
    // 渲染组件到页面
    ReactDOM.render(<Person name="jerry" speak="1"/>, 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>
4 组件三大核心属性3: refs与事件处理
练习

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

  1. 点击按钮, 提示第一个输入框中的值

  2. 当第2个输入框失去焦点时, 提示这个输入框中的值

    <!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-from,用于支持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="失去焦点提示数据" />
                </div>
              );
            }
          }
          // 渲染组件到页面
          ReactDOM.render(<Demo />, document.getElementById("test"));
        </script>
      </body>
    </html>
    
理解

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

编码
  1. 字符串形式的ref
<input ref="input1"/>
  1. 回调形式的ref
<input ref={(c)=>{this.input1 = c}}
  1. createRef创建ref容器
myRef = React.createRef() 
<input ref={this.myRef}/>
事件处理
  1. 通过onXxx属性指定事件处理函数(注意大小写)

    1. React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 ---- 为了更好的兼容性

    2. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ----- 为了高效

  2. 通过event.target得到发生事件的DOM元素对象 ----- 不要过度使用ref

猜你喜欢

转载自blog.csdn.net/m0_57809042/article/details/129578253