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
};
七.列表
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值,需要使用defaultValue
或defaultChecked
我们一般都是使用受控组件来控制表单
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>
受控组件有个很明显特点,必须要写value
和onChange
事件,并且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的值