二、React 面向组件编程、React 组件三大核心属性、生命周期

一、React 面向组件编程

1.1、函数式组件(用函数定义的组件( 适用于 [ 简单组件 ] 的定义 ))

<!DOCTYPE html>
<html>
  <head>
    <title>函数式组件</title>
    <!-- 引入React核心库 -->
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <!-- 引入 react-dom: 用来支持 react 的dom操作(渲染) -->
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <!-- 引入 babel:用来将引入 babel:用来将jsx语法转换成js -->
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
  </head>
  <body>
    <!-- 准备好一个容器 -->
    <div id="app"></div>

    <!-- script的类型必须是text/babel -->
    <script type="text/babel">
        // 创建函数式组件
        function MyComponents(){
            console.log(this); // 此处的this是undefined,因为babe1编译后开启了严格模式
            return <h1>我是用函数定义的组件(适用于[简单组件]的定义)</h1>
        }
        // 渲染组件到页面
        ReactDOM.render(<MyComponents />,document.getElementById("app"));
        /*
            执行了ReactDOM. render( <MyComponent/>......之后,发生了什么?
                1. React解析组件标签,找到了MyComponent组件。
                2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
        */
    </script>
  </body>
</html>

1.2、类式组件(用类定义的组件( 适用于 [ 复杂组件 ] 的定义 ))

<!DOCTYPE html>
<html>
  <head>
    <title>函数式组件</title>
    <!-- 引入React核心库 -->
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <!-- 引入 react-dom: 用来支持 react 的dom操作(渲染) -->
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <!-- 引入 babel:用来将引入 babel:用来将jsx语法转换成js -->
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
  </head>
  <body>
    <!-- 准备好一个容器 -->
    <div id="app"></div>

    <!-- script的类型必须是text/babel -->
    <script type="text/babel">
        // 创建类式组件
        class MyComponents extends React.Component {
            //render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
            //render中的this是谁?一 MyComponent的实例对象。
            render(){
                console. log(' render中的this:' ,this);
                return <h1>我是用类定义的组件(适用于[复杂组件]的定义)</h1>
            }
        }
        
        // 渲染组件到页面
        ReactDOM.render(<MyComponents />,document.getElementById("app"));
        /*
            执行了ReactDOM. render( <MyComponent/>......之后,发生了什么?
                1. React解析组件标签,找到了MyComponent组件。
                2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
                3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
        */
    </script>
  </body>
</html>

简单组件和复杂组件的区别
简单组件:接收输入的数据并返回需要展示的内容(通过 this.props 访问)。
复杂组件:除了使用外部数据(通过 this.props 访问)以外,组件还可以维护其内部的状态数据(通过 this.state 访问)。当组件的状态数据改变时,组件会再次调用 render() 方法重新渲染对应的标记。

事件绑定

React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:

React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。

例如,传统的 HTML:

<button onclick="activateLasers()">
  Activate Lasers
</button>

在 React 中略微不同:

<button onClick={activateLasers}>
  Activate Lasers
</button>

注意一:React中的事件绑定只需要传参数名就行,一定不能加上(),加上()的函数相当于函数调用,在创建组件实例的时候就会执行,然后会默认返回一个undefined赋给onClick,当你点击标签的时候由于事件绑定的是undefined,会导致绑定的事件调用不成功

<!DOCTYPE html>
<html>
  <head>
    <title>react事件绑定</title>
    <!-- 引入React核心库 -->
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <!-- 引入 react-dom: 用来支持 react 的dom操作(渲染) -->
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <!-- 引入 babel:用来将引入 babel:用来将jsx语法转换成js -->
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
  </head>
  <body>
    <!-- 准备好一个容器 -->
    <div id="app"></div>

    <!-- script的类型必须是text/babel -->
    <script type="text/babel">
        // 创建类式组件
        class Weahter extends React.Component {
          constructor(props){
            super(props);
          }
          render(){
            // return <h1 onClick={this.changeWeahte()}>事件绑定</h1>//错误写法,不应该写(),写上()是调用函数,而不是绑定函数了
            return <h1 onClick={this.changeWeahter}>事件绑定</h1>//正确写法
          }
          changeWeahter(){
            console.log("事件调用成功");
          }
        }
        
        // 渲染组件到页面
        ReactDOM.render(<Weahter />,document.getElementById("app"));
    </script>
  </body>
</html>

注意二:事件绑定中函数 this 为 undefined问题
原因:
class 中的方法在 class 的原型对象上,供实例使用
而 class 中的方法是作为onclick的回调,所以不是通过实例调用的,是直接调用(直接调用this指向window)
因为 class 中的方法默认开启了局部的严格模式(严格模式禁止 this 指向 window ),所以 方法中的 this 为 undefined

解决方法:
一、强制绑定this: 通过函数对象的bind();
二、箭头函数(常用)

<script type="text/babel">
        // 创建类式组件
        class Weahter extends React.Component {
          constructor(props){
            super(props);
            //解决changewleather中this指向问题(解决方法 一)
            // this.changeWeahter = this.changeWeahter.bind(this);
          }
          render(){
            // return <h1 onClick={this.changeWeahter}>事件绑定</h1>//这样写 changeWeahter 中的this 为undefined
            return <h1 onClick={() => {this.changeWeahter()}}>事件绑定</h1>//(解决方法 二)
          }
          changeWeahter(){
            // changeweather放在哪里?—— Weather的原型对象上,供实例使用
            // 由于changeweather是作为onclick的回调,所以不是通过实例调用的,是直接调用(直接调用this指向window)
            // 类中的方法默认开启了局部的严格模式(严格模式禁止this指向window),所以changeleather中的this为undefined
            console.log("this",this);
          }
        }
        
        // 渲染组件到页面
        ReactDOM.render(<Weahter />,document.getElementById("app"));
    </script>

一、React 组件三大核心属性

1、组件三大核心属性 state

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。

React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。

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

需求:点击h1标签切换今天天气炎热凉爽

<script type="text/babel">
        // 创建类式组件
        class Weahter extends React.Component {
          constructor(props){
            super(props);
            //初始化状态
            this.state = {
              isHot: false,
            };
          }
          render(){
            return <h1 onClick={() => {this.changeWeahter}}>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h1>
          }
          changeWeahter(){
            const { isHot } = this.state;

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

            // 严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
            this.setState({
              isHot: !isHot
            })
          }
        }
        
        // 渲染组件到页面
        ReactDOM.render(<Weahter />,document.getElementById("app"));
    </script>

简写
<script type="text/babel">
        // 创建类式组件
        class Weahter extends React.Component {
          //初始化状态
          state = {
              isHot: false,
          };
          render(){
            return <h1 onClick={this.changeWeahter}>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h1>
          }
          //自定义方法——要用赋值语句的形式+箭头函数
          changeWeahter = ()=>{
            const { isHot } = this.state;
            this.setState({
              isHot: !isHot
            })
          }
        }
        
        // 渲染组件到页面
        ReactDOM.render(<Weahter />,document.getElementById("app"));
    </script>

2、组件三大核心属性 props

2.1、官网定义
组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。

state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据

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

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

<!DOCTYPE html>
<html>
  <head>
    <title>05_props简写方式</title>
    <!-- 引入React核心库 -->
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <!-- 引入 react-dom: 用来支持 react 的dom操作(渲染) -->
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <!-- 引入 babel:用来将引入 babel:用来将jsx语法转换成js -->
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.7.2/prop-types.js"></script>
  </head>
  <body>
    <!-- 准备好一个容器 -->
    <div id="app"></div>
    <div id="app2"></div>

    <!-- script的类型必须是text/babel -->
    <script type="text/babel">
        // 创建类式组件
        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: "人妖",
            age: 18
          }

          constructor(props){
            //构造器是否接受props,是否传递给super,取决于:是否希望在构造器中通过this访问props
            super(props);
            console.log("constructor",this.props);
          }
        
          render(){
            // props是只读的
            // this.props.name = "Jack";//慈航代码报错,因为props是只读的
            const {name, sex, age} = this.props;
            return (
                <ul>
                    <li>姓名:{ name }</li>
                    <li>性别:{ sex }</li>
                    <li>年龄:{ age + 1 }</li>
                </ul>
            )
          }
        }
        
        // 渲染组件到页面
        ReactDOM.render(<Person name="LBipanda" sex="男" />,document.getElementById("app"));
    </script>
  </body>
</html>

3、组件三大核心属性 refs 与事件处理

理解

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

3.2、编码**

  1. 字符串形式的ref
<input ref="input1" type="text"/>
  1. 回调形式的ref
 <input ref={c => {this.input1 = c}} type="text" />
  1. createRef创建ref容器·
myRef = React.createRef() 
<input ref={this.myRef} type="text"/>

ref常用方式

script type="text/babel">
        // 创建类式组件
        class Person extends React.Component {
          render(){
            return (
                <div>
                    <input ref={c => {this.input1 = c}} type="text" placehodel="点击展示左侧输入框的数据"/>&nbsp;
                    <button onClick={this.showData}>点击展示左侧输入框的数据</button>
                </div>
            )
          }
          showData = ()=>{
            console.log(this);
            let { input1 } = this;
            alert(input1.value)
          }
        }
        
        // 渲染组件到页面
        ReactDOM.render(<Person />,document.getElementById("app"));
    </script>
事件处理

1.通过onXxx属性指定事件处理函数(注意大小写)
  (1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件
  (2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
2.通过event.target得到发生事件的DOM元素对象

二、React 受控组件和非受控组件

受控组件就是可以被 react 状态(state)控制的组件
在 react 中,Input textarea 等组件默认是非受控组件(输入框内部的值是用户控制,和React无关,无法直接获取到值,需通过 ref 获取)。但是也可以转化成受控组件,就是通过 onChange 事件获取当前输入内容,将当前输入内容保存到组件的 state 属性中,此时就成为受控组件。

非受控组件:输入框内部的值是用户控制,和React无关,无法直接获取到值,需通过 ref 获取

<script type="text/babel">
        // 创建类式组件
        class Login extends React.Component {
          
          render(){
            return (
                <form action="http://www.atguigu.com" onSubmit={this.submit}>
                    用户名:<input type="text" ref={c => this.inputName = c} name="username" /><br/>
                    密&emsp;码:<input type="password" ref={c => this.inputPass = c} name="password" /><br/>
                    <button>登录</button>
                </form>
            )
          }
          submit = (event) => {
              event.preventDefault();//阻止表单提交
              //通过 refs 获取
              alert(`你输入的用户名是${this.inputName.value},密码是${this.inputPass.value}`)
          }
        }
        
        // 渲染组件到页面
        ReactDOM.render(<Login />,document.getElementById("app"));
    </script>

受控组件:通过 onChange 事件获取当前输入内容,将当前输入内容保存到组件的 state 属性中

<script type="text/babel">
        // 创建类式组件
        class Login extends React.Component {
        //   初始化状态
          state = {}
          render(){
            return (
                <form action="http://www.atguigu.com" onSubmit={this.submit}>
                    用户名:<input type="text" onChange={this.changeName} name="username" /><br/>
                    密&emsp;码:<input type="password" onChange={this.changePass} name="password" /><br/>
                    <button>登录</button>
                </form>
            )
          }
          submit = (event) => {
              console.log();
              event.preventDefault();//阻止表单提交
             //通过 state 获取
              alert(`你输入的用户名是${this.state.username},密码是${this.state.password}`)
          }
          //保存用户名到状态中
          changeName = (event) => {
            this.setState({username: event.target.value})
          }
          //保存密码到状态中
          changePass = (event) => {
            this.setState({password: event.target.value})
          }
        }
        
        // 渲染组件到页面
        ReactDOM.render(<Login />,document.getElementById("app"));
    </script>
像上面这种受控组件有点代码冗余,一个输入框我就要调一个方法,如果有多个输入框的值需要保存在state。我们需要通过高阶函数来解决代码冗余

高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
  1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
  2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、 setTimeout、arr.map()等等

函数的柯里化: -通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。

第一种方式(函数返回一个回调函数:个人建议使用第二种方式)
<script type="text/babel">
        // 创建类式组件
        class Login extends React.Component {
          render(){
            return (
                <form action="http://www.atguigu.com" onSubmit={this.submit}>
                    用户名:<input type="text" onChange={this.saveFormData("username")} name="username" /><br/>
                    密&emsp;码:<input type="password" onChange={this.saveFormData("password")} name="password" /><br/>
                    <button>登录</button>
                </form>
            )
          }
          submit = (event) => {
              event.preventDefault();//阻止表单提交
              alert(`你输入的用户名是${this.state.username},密码是${this.state.password}`)
          }
          //保存表单数据到状态中
          saveFormData = (dataType) =>{
            // console.log(dataType);
            return (event)=>{//返回一个回调函数
                console.log(event.target.value);
                this.setState({[dataType]: event.target.value})
            }
          }
        }
        
        // 渲染组件到页面
        ReactDOM.render(<Login />,document.getElementById("app"));
    </script>

第二种方式
<script type="text/babel">
        // 创建类式组件
        class Login extends React.Component {
          render(){
            return (
                <form action="http://www.atguigu.com" onSubmit={this.submit}>
                    用户名:<input type="text" onChange={(event) => {this.saveFormData("username",event)}} name="username" /><br/>
                    密&emsp;码:<input type="password" onChange={event => this.saveFormData("password",event)} name="password" /><br/>
                    <button>登录</button>
                </form>
            )
          }
          submit = (event) => {
              event.preventDefault();//阻止表单提交
              alert(`你输入的用户名是${this.state.username},密码是${this.state.password}`)
          }
          //保存表单数据到状态中
          saveFormData = (dataType,event) =>{
            // console.log(dataType);
            console.log(event.target.value);
            this.setState({[dataType]: event.target.value})
          }
        }
        
        // 渲染组件到页面
        ReactDOM.render(<Login />,document.getElementById("app"));
    </script>

三、React 生命周期

3.1、生命周期流程图(旧)


image.png

3.2、生命周期的三个阶段(旧)

  1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
      1.constructor()
      2.componentWillMount()
      3.render()====> 必须使用的一个
      4.componentDidMount() ====> 常用
componentDidMount:一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
  1. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
      1.shouldComponentUpdate()
      2.componentWillUpdate()
      3.render()
      4.componentDidUpdate()
  2. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
      1.componentWillUnmount() ====> 常用
componentWillUnmount:一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

3.3、生命周期流程图(新)

image.png

3.4、生命周期的三个阶段(新)

  1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
    1.constructor()
    2.getDerivedStateFromProps
    3.render()
    4.componentDidMount()

  2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
    1.getDerivedStateFromProps
    2.shouldComponentUpdate()
    3.render()
    4.getSnapshotBeforeUpdate
    5.componentDidUpdate()

  3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
    1.componentWillUnmount()

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

即将废弃的勾子
1.componentWillMount
2.componentWillReceiveProps
3.componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。

三、虚拟DOM与DOM Diffing算法

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容