likes
comments
collection
share

从零开始学习React---组件

作者站长头像
站长
· 阅读数 17

一,组件的类型

1.React的组件类型可以分为函数式组件和类式组件:

1.函数式组件是通过定义函数return对应dom建构并通过render函数进行渲染的组件,通常用于简单组件。其结构如下:

function MyComponent(){
            console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
            return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
        }
        //2.渲染组件到页面
        ReactDOM.render(<MyComponent/>,document.getElementById('test'))
        /* 
            执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
                    1.React解析组件标签,找到了MyComponent组件。
                    2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
        */

2.类式组件是通过继承React.Component并通过render函数进行渲染的组件,通常用于复杂组件。其结构如下:

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

以上两种类型是react组件的静态组件。如果需要实现通过外部数据来渲染的动态组件,还需要其他的内容来辅助实现。由此react引入组件实例的三大属性:statepropsrefs。但若在不引入react hooks的情况下,类式组件能拥有这三个属性,而函数式组件则只有props属性(因为props相当于函数的参数)。

2.受控组件和非受控组件:

主要是针对表单做的一次细分,定义是受控组件还是非受控组件主要看的是组件的表单元素(如<input><textarea><select>)是否实现了双向绑定,即不通过ref去实现输入与数据的实时变化,而是通过event.target去实现。

二,组件的属性

在开始学习三大属性前,先学习一下类式组件未简写时的结构,这里需要你懂得ES6中class,箭头函数,以及this指向等知识,若不清楚,请先去复习相关知识再继续阅读效果更佳。那么,这里用一段代码来表示未简写的类式组件:

//1.创建组件
    class Weather extends React.Component{
            
            //构造器调用几次? ———— 1次
            constructor(props){
                console.log('constructor');
                super(props)//写constructor时,需在定义其他属性前先super,不然可能会导致后面的属性未定义
                //初始化状态
                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/>,document.getElementById('test'))

接下来是简写形式的类式组件,即属性state的例子

//1.创建组件
        class Weather extends React.Component{
            //初始化状态
            state = {isHot:false,wind:'微风'}

            render(){
                //组件中render方法中的this为组件实例对象
                const {isHot,wind} = this.state
                return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
            }

            //自定义方法————要用赋值语句的形式+箭头函数
            changeWeather = ()=>{
                const isHot = this.state.isHot
                this.setState({isHot:!isHot})
                console.log(this);//不会输出内容
            }
        }
//2.渲染组件到页面
        ReactDOM.render(<Weather/>,document.getElementById('test'))

类式组件的写法类似于ES6中的class写法,render函数内的return是jsx的语法糖,使用箭头函数的特性,可以获取到当前定义位置的父作用域,因此可以通过this获取到当前类的state属性。

state在使用时的一些规则:1.状态(state)是不可直接更改,若要更改,需使用setState来更改,如this.setState({isHot:!isHot}),此时的set是合并而不是替换,wind:'微风'属性仍然存在。2.state的更新会导致页面相关内容的重新渲染。

属性props的类式组件例子(已简化):

<script type="text/javascript" src="../js/prop-types.js"></script>
//创建组件
        class Person extends React.Component{

            //构造器constructor是否接收props,是否传递给super,取决于:是否希望在构造器中通过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'))

属性props的函数式组件例子(已简化):

<script type="text/javascript" src="../js/prop-types.js"></script>
//创建组件
        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'))

props在使用时的一些规则:1.使用props时,若需要对prop做限制,需要引入prop-types.js,并使用如例子中的方式对属性做限制。2.以后学习到其他的了再补充,哈哈哈

对于refs来说,则有三种形式,分别是:1.字符串形式的ref<input ref="input1"/>

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

3.createRef创建ref容器

myRef = React.createRef() 
<input ref={this.myRef}/>

注:若ref使用回调函数形式且内联到jsx中,则每次更新页面时相关的ref回调函数也会被再执行一次(因为是内联回调,所以更新时会重新执行,而不是被缓存起来),但是在日常开发时是无关紧要的事情。

举个refs的例子:

//创建组件
        class Demo extends React.Component{
            /* 
                React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
            */
            myRef = React.createRef()
            //展示左侧输入框的数据
            showData = ()=>{
                const {input1} = this.refs
                alert(input1.value)
            }
            //展示右侧输入框的数据
            showData2 = ()=>{
                const {input2} = this.refs
                alert(input2.value)
            }
            render(){
                return(
                    <div>
                        <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
                        <input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                        <input ref="input2" 
                            onBlur={this.showData2} 
                            ref={c => this.input2 = c } 
                            type="text" 
                            placeholder="失去焦点提示数据"
                        />
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))

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

三,组件的生命周期

在React16之前的旧生命周期:

1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
    1.    constructor()//构造器
    2.    componentWillMount()//组件将要挂载的钩子
    3.    render() =====> 必须使用的一个
    4.    componentDidMount() =====> 常用//组件挂载完毕的钩子
          一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件render触发
  1.    shouldComponentUpdate()//控制组件更新的“阀门”,静态的,return true 或者false,默认返回true
  2.    componentWillUpdate()//组件将要更新的钩子
  3.    render() =====> 必须使用的一个
  4.    componentDidUpdate()//组件更新完毕的钩子
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
  1.    componentWillUnmount()  =====> 常用//组件将要卸载的钩子
        一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

其旧生命周期图示如下:从零开始学习React---组件

在React16之后的新生命周期:

1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
  1.    constructor()//构造器
  2.    static getDerivedStateFromProps()
          //得到一个state的衍生(派生)状态,state的值在任何时候都取决于props
        //且必须返回state object或者null
  3.    render()
  4.    componentDidMount() =====> 常用//组件挂载完毕的钩子
              一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
  1.    static getDerivedStateFromProps()
          //得到一个state的衍生(派生)状态,state的值在任何时候都取决于props
        //且必须返回state object或者null
  2.    shouldComponentUpdate()//控制组件更新的“阀门”,静态的,return true 或者false,默认返回true
  3.    render()
  4.    getSnapshotBeforeUpdate()//在更新之前获取快照
  5.    componentDidUpdate()//组件更新完毕的钩子
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
  1.    componentWillUnmount()  =====> 常用//组件将要卸载的钩子
  一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

其新生命周期图示如下:从零开始学习React---组件