React 组件浅析

React 组件

React书写的一切用户界面都是基于组件的,将界面分割成一些独立的,可复用的部件.

一.声明组件

1.ES5写法:React.createClass()

最老版本的用法,不建议使用,已经废弃了

实际上,createClass() 本质是一个工厂函数

虽然这种方法已经废弃了,但是Facebook出了一个能代替React.createClass() 的替代品,需要使用create-react-class模块,先安装

$ npm install create-react-class -S
import React from 'react';
var createReactClass = require('create-react-class');
var Greeting = createReactClass({
    render: function () {
        return <h1>使用create-react-class模块创建react组件</h1>;
    }
});

export default Greeting;

2.ES6写法:React.Component

目前官方极为推荐的创建有状态组件的方式

支持 class语法 (状态 属性 生命周期) [email protected] =>class生命周期写法升级

import React from 'react';
import ReactDOM from 'react-dom';

class Animal extends React.Component {
   render() {
       return (
           <div>Animal组件</div>
       )
   }
}

ReactDOM.render(
   <div>
       <Animal></Animal>
   </div>,
   document.getElementById('root')
);

类定义的组件

  • 定义一个类,类名就是组件名
  • 这个类需要继承React.Component基础组件
  • 类中的render函数是必须的,render函数中的return出来的是一段JSX语法,这一段JSX语法就是这个组件的模版的内容

3.函数式组件

无状态函数式写法

不支持状态 属性 生命周期

[email protected] => 支持状态 属性 “生命周期” React Hooks

import React from 'react';
import ReactDOM from 'react-dom';

const Cat = () => {
    return (
        <div>
            Cat组件
        </div>
    );
}

ReactDOM.render(
    <div>
        <Cat></Cat>
    </div>,
    document.getElementById('root')
);

函数式的定义组件

  • 定义一个函数,然后return出来一段JSX语法,这一段JSX语法就是这个组件的模版的内容
  • 函数名称就是组件的名字,[所以首字母需要大写()

PS:组件模版内容,如果需要换行的话,return后面需要写小括号()括起来,因为原生js,是会返回undefind

2.模版内容只能有一个跟元素

二.组件注意事项

1.组件名首字母大写

组件名,在被引用时 首字母必须大写 编译时会被认为是自定义组件,如果写成了小写,会被认为是原生js的dom节点

2.一个根元素

组件的最外层必须有一个标签包裹,不能有兄弟元素

3.return需要加上小括号

return加上小括号,可以回车,如果内容多了,肯定不会写成一行,原生中return后面没有跟内容,会返回undefind

4.组件是可以嵌套的

组件是可以嵌套的,如父子组件

5.JSX语法糖问题

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
    <div>
        jsx语法糖问题
    </div>
    ,
    document.getElementById("root")
);

以上代码发现一个问题,就是我们没有调用React,为什么必须要引用,而且如果删掉的话,ReactDOM.render中第一个参数会报语法错误!原因呢就是:这种JSX语法是一个语法糖,它经过babel转义之后会变成如下代码

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
    React.createElement('div', {className: 'teststyle'}, React.createElement('h1', {}, 'hello world')),
    document.getElementById("root")
);

React.createElement(),接收3三个参数

  • 第一个是节点,一般是标签名称
  • 第二个是属性,比如className,id之类
  • 第三个是内容,就是标签中的内容,可以包含文本,或者节点之类

`正是因为解析之后的代码中运用到了React.createElement()函数,所以React必须引用!’

三.状态(state)

每个React组件都有自己的状态(除了函数式组件外),state只存在于组件自身内部,用来存储自身的数据,相当于Vue中的data选项

1.定义state

在组件内部的构造器中,直接定义this.state

constructor(props) {
    super(props);
    this.state = {
        data: {
            name: 'liuqiao',
            age: 18
        }
    };
}

在ES7中可以直接省略构造器,直接在当前Class中定义也可以,但是为了规范,建议还是不要省略

 // 注意,这里是es7的写法
 //不是状态,单纯只是变量
 a=10;
 //状态
 state = {
     name: 'liuqiao',
     age: 18
 }

2.访问state

同样,在需要使用当前状态的地方,通过this.state访问状态

render() {
    return (
        <div>
            我的名字叫{this.state.data.name}
        </div>
    );
}

3.改变state

改变state时,使用React内置的setState()方法修改state,每当使用setState时,React会将需要更新的state合并后放入状态队列,触发调和过程,而不是立即更新state.所以,setState是异步的

this.setState({
    username:'xiaohong'
});

在需要修改的地方触发此方法即可!另外,如果要修改setState里面的对象的某一属性,但不会修改其他的属性呢?

this.state = {
    username:'xiaoming',
    data: {
        name: 'liuqiao',
        age: 18,
        address:'深圳',
        like:['爬山','跑步']
    }
};

比如我要当前状态中的data对象中的name属性,但是其他属性不修改,我不可能,为了修改一个name属性,把其他的都在setState里都带上吧,那样肯定是不行的,所以我提供了如下方法:

1.方法一
let newData = { ...this.state.data };
newData.name=30;
this.setState({
    data: newData
});

采用展开运算符,全部展开,然后只赋值需要修改的属性即可.其实原理呢,就是将原来的对象浅拷贝一个新对象,然后新对象改变属性之后,再浅拷贝回来

2.方法二
let newData = Object.assign(this.state.data, {
    ['age']: 30
});
this.setState({
    data: newData
});

采用Object.assign方法功能,其原理就是此方法的特性可以合并具有相同属性的对象

3.方法三
let data=this.state.data;
data.age=30;
this.setState({
    data
});

4.多个改变satae操作情况

culeCount = () => {
    this.setState({
        count: this.state.count + 1
    });
    this.setState({
        count: this.state.count + 1
    });
    this.setState({
        count: this.state.count + 10
    });
    // 这里多个setState操作,会被合并,只会执行最后一次的setState,进行了批量更新优化
}

如果遇到某个函数内进行了多个setSatae的情况,会被合并,整合上面的setState,如果有重复的,只会执行最后一次的setState,进行了批量更新优化

5.setState异步

setState()方法接收2个参数,后面参数可以选

this.setState({
    myname: 'xiaoming'
}, () => {
    //2.再打印
    console.log("我是异步的" + this.state.myname);
    // 
});
//1.先打印
console.log(this.state.myname);

原理:状态更新是异步的,这里的回调函数,会等待状态更新完,并且dom更新完之后,才会被调用,原因是创建虚拟dom,diff算法对比旧的dom节点,和文档碎片,进行最小化重新渲染

6.setState同步

setState在ajax,原生事件,setTimeout是同步的


handleClick2 = () => {
    setTimeout(() => {
        this.setState({
            myname: 'xiaohong'
        });
        console.log(this.state.myname);
        this.setState({
            myname: 'xiaofang'
        });
        console.log(this.state.myname);
    }, 0);
}

官网的一句话:setState 并不保证是同步的!所以,得出结论:setState是否同步异步,是得看情况的,当setState在ajax,原生事件,setTimeout是同步的,其他情况是异步的

四.事件绑定

1.匿名箭头函数写法

比较简洁,适合处理代码少,简洁的逻辑

<button onClick={() => {
    console.log(this.refs.username.value)
}}>提交(匿名箭头函数写法)</button>

2.自定义函数写法

会存在this指向问题,需要使用bind强制改变this指向 注意,必须使用bind才可以,call和 apply不行

原因是call和apply改变this指向后,会立即执行函数

<button onClick={this.submitLogin.bind(this)}>提交(自定义函数写法)</button>

submitLogin(e) {
    //e是事件源
    console.log('自定义函数写法',e);
}

3.自定义声明式箭头函数写法

一般推荐这种方式

<button onClick={this.submitOK}>提交(自定义声明式箭头函数写法)</button>

submitOK=()=>{
    console.log('外部自定义函数', this.refs.username.value);
}

4.改编第一种写法,推崇这种

原理是使用箭头函数做中转,就不会有this指向问题了

<button onClick={(e)=>this.submitLogin('liuqiao','123')}>改编第一种  箭头函数做中转</button>

submitLogin(username,pwd,e) {
    //e是事件源
    console.log('外部自定义函数', username,pwd,e);
}

五.样式写法

1. 嵌套一个变量写法

// style改造对象写法
let styleObj = {
background: ‘red’,
fontSize: ‘30px’
}

嵌套变量

需要注意fontSize 写法

2.嵌套一个类写法

<div style={{ background: 'yellow',fontSize: '30px'}}></div>

3.使用className

<div className='box'>推荐这种方式写样式</div>

4.变量形式抽离

一般工程化环境可以使用此种方式,把css代码进行抽离,通过变量形式抛出来

//将css行内样式以变量的形式抽离出去

export default{
  title:{color:"red",fontSize:'30px',fontWeight:'400',textAlign:'center'},
  itembox:{border:'1px dashed #ccc',margin: '10px',padding:'10px',boxShadow:'0px 10px 10px #ccc'},
  user:{fontSize:'14px'},
  content:{fontSize:'12px'}
}

import styles from '@/components/css.js'
//使用
<h1 style={styles.title}>你好</h1>

六.Props

在React中数据流是单向流动的,从父组件向子组件自上而下传递,子组件可以通过属性Props接收来自父组件的状态.这个props是个集合对象

props是不可以直接改变的,props的值只能从默认属性和父组件中传递过来.

1.函数式组件接收props

实际上就是接收参数,此参数是个对象

import React from 'react';
// 函数式组件,props就是从外部传入的参数
function Hello(props) {
    return <p>hello {props.name}</p>;
}

export default Hello;
2.class类组件接收props

类中的构造器接收

class World extends React.Component {
    //构造器接收
    constructor(props) {
        //继承属性
        super(props);
    }

    render() {
        return (
            <div>
                类定义组件:{this.props.name}
            </div>
        );
    }
};
3.props只读

props是只读的,不可以修改,否则报错

4.props的校验

React中的props校验不同于Vue,需要引入模块prop-types

1.引入prop-types
cnpm install prop-types --save-dev
2.使用关键字static

关键字static 进行属性验证

import myPropTypes from 'prop-types' // 包含数据类型验证方法

// 关键字static 进行属性验证
static propTypes={
    myModule:myPropTypes.string,//必须是字符串才可以传
    isShow:myPropTypes.bool//必须是bool类型
}
3.设置默认属性

当组件不传属性时,设置默认属性

class Navbar extends Component {
    // 还可以设置默认属性(当组件不传属性时,设置默认属性)
    static defaultProps={
        isShow:true
    }
}
4.不使用static,另一种方法

直接在声明组件的类外面,给组件动态添加propTypes校验

 Navbar.propTypes = {
     name: PropTypes.string
 };
5.设置默认属性,另一种方法
// 指定 props 的默认值:
Navbar.defaultProps = {
   isShow:true
};

props校验更多API

七.列表

1.map循环列表

在React中,官网推荐使用map() 函数来遍历数据,最后渲染列表

//初始化数据
initList() {
    //解构获取数据
    let { userinfo } = this.state;
    //返回结果
    let res = [];
    userinfo.map(ele => {
        ele.id*=2;
        res.push(
            <div key={ele.id}>
                <span>id:{ele.id}</span>
                <span>name:{ele.name}</span>
                <span>age:{ele.age}</span>
                <br/>
            </div>
        );
    });
    return res;
}

遍历数组的方法有很多,看个人习惯使用哪种,都可以,但是不要忘记我们需要在列表的节点,设置唯一key,因为这个相关于diff运算,不设置会报错!

八,表单

1.非受控组件

没有使用state数据去控制的表单元素就是非受控组件

{ /*非受控表单*/ }
<input id='mobile' type="text"/>
<input ref='address' type="text"/>
<input type="file"/>
<button onClick={this.submit.bind(this)}>提交数据</button>

在React中,非受控组件不能写value值,否则会报错!如果想给当前input框的value值,需要使用defaultValuedefaultChecked 我们一般都是使用受控组件来控制表单

2.受控组件

需要使用state数据去控制的表单元素就是受控组件

{ /*受控表单*/ }
<input value={username} onChange={this.userHandle.bind(this)} type="text"/>
    
<select name="" id="" value={this.state.sltNameValue} name="sltNameValue" onChange={this.changeValue}>
    {this.initSltUserInfo()}     
</select>    

受控组件有个很明显特点,必须要写valueonChange事件,并且value必须使用state赋值,但是还有个比较特殊的就是checkbox框和radio,他们俩都是checked

<input type="radio" value="0" checked={this.state.sex === '0'} onChange={this.chg} />
     
<input type="checkbox" checked={this.state.isOk} onChange={this.chg} />

3.处理多个表单

遇到一个比较恶心的问题,比如我们要处理多个表单的时候,在开发中,表单元素的十几个都是经常的事,难道我们也要每个表单元素给个onChange事件吗?这样写太恶心了,我们可以提取一个公共方法来进行处理

  • 1.将onChange事件设置为一个
  • 2.在input表单上面设置不同的name属性(必须设置跟state中定义的变量一样)
  • 3.在事件处理函数中通过事件源对象,根据name属性来修改state的数据
<input type="text" name="username" value={this.state.username} onChange={this.changeValue} />
<input type="text" name="age" value={this.state.age} onChange={this.changeValue} />
<input type="checkbox" name="isOk" checked={this.state.isOk} onChange={this.changeValue} />
    
changeValue = (e) => {
    let name = e.target.name;
    this.setState({
        [name]: e.target.value;
    });
};

4.ref获取表单

ref这种方式权限比较大,能获取到整个dom元素中的任何对象,一般不建议使用ref,如果页面一堆的ref,看着非常恶心,一般解决一些通过state获取不到的问题

<input type="text" placeholder="请输入用户名" ref='username' />
<input type="password" placeholder="请输入密码" ref='pwd' />

//获取ref标记的表单元素
this.refs.username.value

给元素设置ref属性,可以通过this.refs.username.value获取dom的值

猜你喜欢

转载自blog.csdn.net/liuqiao0327/article/details/107194817