3、React:React面向组件编程

1、组件与模块

模块

  • 理解:一个js文件就是一个模块
  • 拆成模块的原因:随着业务逻辑的增加,代码越来越多并且复杂
  • 模块的作用:复用js,简写js的编写,提高js的运行效率

组件

  • 理解:用来实现局部功能效果的代码和资源的集合{html/css/image等等}
  • 原因:一个界面的功能更复杂
  • 作用:复用代码,简化项目编码,提高运行效率

模块化

当应用的js都以模块来编写的,这个应用就是一个模块化的应用。

组件化

当应用是以多组件的方式实现,这个应用就是一个组件化的应用

2、React面向组件编程

函数式组件

<!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>函数式组件</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.创建函数式组件
        function Demo(){
            return <h1>我是函数式组件</h1>
        }
        //2.渲染到页面 
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>
</body>
</html>

此处的Demo就是一个组件,并且首字母必须大写。大写字母开头,React就去渲染对应的组件,若组件没有定义,则报错


此处类式组件的执行过程

  • 执行了ReactDOM.render(<Demo/>,document.getElementById('test')发生了什么
  • React解析组件标签,找到Demo组件
  • 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实的DOM,随后呈现在页面中

注意

  • 函数式组建的首字母必须大写
  • 函数必须有返回值
  • ReactDOM.render()中必须写组件标签,并且必须是闭合的

类式组件


类的总结

  • 类中的构造器不是必须写的,如果要对实例进行一些初始化的操作,如添加指定属性时才写。
  • 如果A类继承了B类,且A类中写了构造器,那么A类构造器中super是必须要调用的,并且super必须放在第一行
  • 类中所定义的方法,都是放在了类的原型对象上,供实例去使用。

代码展示

<!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>类式组件</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 Demo extends React.Component {
            render() {
                return <h1>我是类组件</h1>
            }
        }
        //2.渲染到页面 
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>
</body>
</html>

执行了ReactDOM.render()之后,发生了什么

  • React解析组件标签,找到Demo组件。
  • 发现组件是使用类定义的,随后new出来该类的实例, 并通过该实例调用到原型上的render方法。
  • render返回的虚拟DOM转为真实DOM随后呈现在页面中。

三大核心

  1. 必须继承React.Component父类
  2. 必须要写render()
  3. render()必须有返回值。

3、组件实例的三大核心属性

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>类式组件</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{
            constructor(props){
                super(props)
                //初始化状态
                this.state = {isHot:true,wind:'微风'}
                //解决changeWeather中this的指向问题
                this.changeWeather = this.changeWeather.bind(this)
            }
            render(){
                //读取状态
                const {isHot,wind} = this.state
                return <h1 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}{wind}</h1>
            }
            changeWeather(){
                //changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
                //由于changeweather是作为onClick的回调,所以不是通过实例调用的,是直接调用
                //类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined

                //获取原来的isHot值
                const isHot = this.state.isHot
                this.setState({isHot:!isHot})
            }
        }
        //2.渲染到页面 
        ReactDOM.render(<Weather/>,document.getElementById('test'))
    </script>
</body>
</html>

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>类式组件</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:true,wind:'微风'}

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

            //自定义方法  ———— 要用赋值语句+箭头函数
            changeWeather = ()=> {
                //获取原来的isHot值
                const isHot = this.state.isHot
                this.setState({isHot:!isHot})
            }
        }
        //2.渲染到页面 
        ReactDOM.render(<Weather/>,document.getElementById('test'))
    </script>
</body>
</html>

state总结

  • state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
  • 组件中render方法中的this为组件实例对象
  • 组件自定义的方法中thisundefined
    • 强制绑定this: 通过函数对象的bind()this.changeWeather = this.changeWeather.bind(this)
    • 箭头函数:要用赋值语句+箭头函数
  • 状态数据,不能直接修改或更新,需使用setState()函数

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>props基本使用</title>
</head>
<body>
    <!-- 创建容器 -->
    <div id='test'></div>
    <div id='test1'></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(){
                const {name,age,sex} = this.props
                return(
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age}</li>
                    </ul>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Person name='张三' age='20' sex='男'/>,document.getElementById('test'))
        ReactDOM.render(<Person name='李四' age='10' sex='女'/>,document.getElementById('test1'))
    </script>
</body>
</html>

上面是属性少,那么属性多的时候怎么办呢?

我们可以使用三点运算符,那么什么是三点运算符呢?

三点运算符为对象的扩展运算符,用于取出参数对象中的所有可遍历属性,拷贝到当前的对象之中,拓展运算符拷贝的是对象中的基本数据类型,并不是对原有对象的引用,因此修改当前对象中的值不会影响原有对象中的值。

  • 数组的复制
	//创建一个数组
	var number = ['吃饭','睡觉','喝酒']
	//展开一个数组
    console.log(...number) // 吃饭,睡觉,喝酒
 	//复制
 	var arr = [...number]
    console.log(arr)// [0:'吃饭',1:'睡觉',2:'喝酒']
  • 对象的复制
	var people= { name: '张三', age: 20 }
	//展开运算符不能展开对象
	console.log(...obj); //不能直接打印对象,会报错Uncaught TypeError: Found non-callable @@iterator at 三目运算符.html:12
	var p= { ...people}
	console.log(p) //{ name: '张三', age: 20 }
	//不会影响原来对象的值
	console.log(people)//{ name: '张三', age: 20 }
  • 对象的合并
	var people= { name: '张三'}
	var people1= { age: 20 }
	var p2 = {...people,...people1}
	console.log(p2)//{ name: '张三'}
  • 字符串转化为数组
	var str = '我爱计算机'
	var arr = [...str]
	console.log(arr)//["我", "爱", "计", "算", "机"]

简写方式(批量传递 )

<script type="text/babel">
        //创建组件
        class Person extends React.Component{
            render(){
                const {name,age,sex} = this.props
                return(
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age}</li>
                    </ul>
                )
            }
        }
        //渲染组件到页面
        const p = {name:'王五',age:30,sex:'男'}
        ReactDOM.render(<Person {...p}/>,document.getElementById('test2'))
    </script>

限制props

使用prop-types库进限制(需要引入prop-types库)

  • 姓名必须指定,且为字符串类型;
  • 性别为字符串类型,如果性别没有指定,默认为男
  • 年龄为字符串类型,且为数字类型,默认值为18
<!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>props基本使用</title>
</head>
<body>
    <!-- 创建容器 -->
    <div id='test'></div>
    <div id='test1'></div>
    <div id='test2'></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(){
                const {name,age,sex} = this.props
                //注意:props是只读的
                return(
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                    </ul>
                )
            }
        }
        //对标签属性进行类型和必要性的限制
        Person.propTypes = {
            name:PropTypes.string.isRequired,//名字是字符串类型且为必填
            sex:PropTypes.string,//性别为字符串类型
            age:PropTypes.number//年龄为数字类型
        }
        //指定默认标签属性值
        Person.defaultProps = {
            sex:'男', //性别默认值
            age:22 //年龄默认值
        }
        //渲染组件到页面
        ReactDOM.render(<Person name='张三' age={20} sex='男'/>,document.getElementById('test'))
        ReactDOM.render(<Person name='李四' />,document.getElementById('test1'))

        const p = {name:'王五',age:18,sex:'男'}
        ReactDOM.render(<Person {...p}/>,document.getElementById('test2'))
    </script>
</body>
</html>

注意

  • 组件自身添加属性Person.propTypes,这块的名propTypes是固定的
  • React的内置属性为PropTypes注意区分二者,后边跟限制类型

简写方式

将定义的属性直接放在类中

   <script type="text/babel">
        //创建组件
        class Person extends React.Component{
            //对标签属性进行类型和必要性的限制
            static propTypes = {
                name:PropTypes.string.isRequired,//名字是字符串类型且为必填
                sex:PropTypes.string,//性别为字符串类型
                age:PropTypes.number//年龄为数字类型
        }
            //指定默认标签属性值
            static defaultProps = {
                sex:'男', //性别默认值
                age:22 //年龄默认值
        }
            render(){
                const {name,age,sex} = this.props
                //注意:props是只读的
                return(
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                    </ul>
                )
            }
        }
        
        //渲染组件到页面
        ReactDOM.render(<Person name='张三' age={20} sex='男'/>,document.getElementById('test'))
        ReactDOM.render(<Person name='李四' />,document.getElementById('test1'))

        const p = {name:'王五',age:18,sex:'男'}
        ReactDOM.render(<Person {...p}/>,document.getElementById('test2'))
    </script>

这里的构造器可省略,也可不省略,如果不省略,那么必须写super(),此时构造器是否接收props,是否传递给super,取决于是否希望在构造器中通过this访问props

refs

String形式的ref <input ref="input1"/> 已过时

<!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>

    <!-- 引入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 {input1} = this.refs
                alert(input1.value)
            }
            //展示右侧输入框的数据
            showData1 = ()=>{
                const {input2} = this.refs
                alert(input2.value)
            }
            render(){
                return(
                    <div>
                    	//点击事件
                        <input ref="input1" type="text" placeholder='点击按钮提示数据'/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                        //失去焦点
                        <input ref="input2" onBlur={this.showData1} type="text" placeholder='失去焦点提示数据'/>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>
</body>
</html>

回调形式的ref<input ref={(c)=>{this.input1 = c}}

React 也支持另一种设置refs的方式,称为回调 refs。它能助你更精细地控制何时refs 被设置和解除

<!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>

    <!-- 引入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 {input1} = this
                alert(input1.value)
            }
            //展示右侧输入框的数据
            showData1 = ()=>{
                const {input2} = this
                alert(input2.value)
            }
            render(){
                return(
                    <div>
                        <input ref={ c => this.input1 = c } type="text" placeholder='点击按钮提示数据'/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                        <input ref={ c => this.input2 = c} onBlur={this.showData1} type="text" placeholder='失去焦点提示数据'/>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>
</body>
</html>

关于回调 refs 的说明

如果ref回调函数是以内联函数的方式定义的,在更新过程中它会被 执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的ref 并且设置新的。通过将 ref的回调函数定义成class的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的

createRef创建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>

    <!-- 引入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)
            }
            //展示右侧输入框的数据
            showData1 = ()=>{
                alert(this.myRef2.current.value)
            }
            render(){
                return(
                    <div>
                        <input ref={this.myRef} type="text" placeholder='点击按钮提示数据'/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                        <input ref={this.myRef2} onBlur={this.showData1} type="text" placeholder='失去焦点提示数据'/>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>
</body>
</html>

事件处理

  • 通过onXxx(onBlur,onClick)属性指定事件处理函数 (注意大小写)
  • React.createRef()调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是专人专用的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值