什么是React
一个用于构建用户界面的javaScript库
一个将数据渲染为HTML视图的开源Javascript库
特点
1.采用组件化模式,声明式编码,提高开发效率,组件复用率
2.使用虚拟DOM,尽量减少与真实DOM交互
第一个代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>react</title>
<!-- 核心库 全局多了React -->
<script type="text/javascript" src="./react.development.js"></script>
<!-- 支持react操作dom 全局多了ReactDOM -->
<script type="text/javascript" src="./react-dom.development.js"></script>
<!-- babel转为js -->
<script type="text/javascript" src="./babel.min.js"></script>
<!-- babel -->
<script type="text/babel">
// 不要加引号
const VDOM = <h1 id='hid'>hello</h1>
ReactDOM.render(VDOM, document.getElementById("idiv"))
</script>
</head>
<body>
<div id="idiv">
</body>
</html>
虚拟dom创建的两种方式
// 注意 只要创建了虚拟dom,就必须导入前两个库
/*
方法一:
不使用babel,第三个导入的可以删掉
只引入前两个
*/
<script type="text/javascript">
const VDOM = React.createElement('h1', { id: 'hid' }, 'hello')
ReactDOM.render(VDOM, document.getElementById("idiv"))
</script>
/*
方法二:
使用babel
灵活,多层嵌套
*/
<script type="text/babel">
// 括号可以不加
const VDOM = (<h1>
<span>
<p>
你好
</p>
</span>
</h1>);
ReactDOM.render(VDOM,document.getElementById('idiv'))
</script>
语法规则
js语句和js表达式
/*
会产生返回值的都算表达式,返回为空也算
eg:
a
a+b
不产生值的都算语句
eg:
if(){}
for(){}
*/
/*
jsx语法规则
1. 定义虚拟dom,不要写引号
2. 标签中混入js表达式(注意注意注意是表达式) 加{}
eg:
const myid = 'gcid'
const mydata = 'gcdata'
const VDOM = (<h1><span><p id= {myid}>{mydata}</p></span></h1>);
3. 混入css,不需要{},直接用
eg:
类名需要写className
const VDOM = (<h1 className='myclass'><span>{mydata}</span></h1>);
4. 内联样式使用,使用json格式,再嵌套{}
eg:
const VDOM = (<h1 style={{background:'aqua'}}>{mydata}</h1>);
// 外层括号表示jsx中引用js表达式,内层括号表示使用json格式传递
// style ={{key:value,key:value}}
5. 虚拟dom只能有一个根标签,所有标签必须闭合
6. 标签首字母
若以小写开头,则直接将标签转为html同名元素,没有的话报错(不要写h5中没有的标签)
若以大小开头,react就去渲染对应的组件,若没有定义该组件,则报错
*/
组件
组件实例的三大属性
state,props,refs
简单组件:无状态
复杂组件:有状态
函数式组件
/*
1.首字母大写
2.渲染时要使用闭合标签
*/
function Demo(){
return <h2>demo组件的标签</h2>
}
ReactDOM.render(<Demo/>,document.getElementById('idiv'))
类式组件
/*
1. 首字母大写
2. 必须继承React.Component
*/
class MyComponent extends React.Component{
render() {
return (<div>mycomponent组件的标签</div>);
}
}
ReactDOM.render(<MyComponent/>,document.getElementById('idiv'))
state
// state 设置
constructor(props){
super(props)
this.state={ishot:true,wind:'大'}
}
// state 获取
// 一般用法
render(){
return <h1>今天天气很{this.state.ishot ? '炎热':'凉爽'}</h1>
}
// 进阶用法
const {wind} = this.state
/*
1. 传入格式为dict,可以多个属性
2. 使用进阶用法时,{}里面的key必须在status已经存在,可以多个同时获取
*/
事件绑定
一般用法
/*
1. onClick c要大写
2. 调用时 {}
3. function 不能加(),否则是函数返回值进行赋值(未执行操作已经出结果)
*/
function demo(){
console.log('点击')
}
return <h1 onClick={demo}>今天天气很{this.state.ishot ? '炎热':'凉爽'}</h1>
事件修改state
一般形式
/*
注意1 :
为什么要有这句???
因为在 onClick={this.changeweather} 中,并未调用changeweather方法,只是将其绑定给了onclick,当在页面中点击调用时,该方法已经脱离了weather类,即无获取到类中的state属性
1. 等号右边先在下方查找changeweather()方法,因为是在类中定义的方法,需要this调用
2. 为1中的方法绑定this,该this是指weather类中的this,并将其赋给一个新的function,名称为this.changeweather
3. 2中的this.changeweather 便可获取到weather中的state,onclick中调用的方法也是2中等号左边的方法
*/
class Weather extends React.Component {
constructor(props){
super(props)
this.state={ishot:true,wind:'大'}
//注意1
this.changeweather = this.changeweather.bind(this)
}
render(){
const {ishot,wind} = this.state
return <h1 onClick={this.changeweather}>今天天气很{this.state.ishot ? '炎热':'凉爽'}</h1>
}
changeweather(){
const ishot = this.state.ishot
// 不能够直接修改变量,即 ishot = !ishot 是错误的,必须使用setState该方法
// 修改格式是更新,而不是全部覆盖
this.setState({ ishot : !ishot})
console.log(this.state.ishot)
}
}
/*
constructor 调用 1次
render 调用 1+n(状态更新次数)次
changeweather 调用 n(点击几次,调用几次)次
*/
精简模式
/*
=> 箭头函数,没有自己的this,它会在当前所在地方的父级指定为this
eg:
下面箭头函数的父级为weather类,所以this为weather这个类
在类中直接写个赋值语句,相当于在类之后创建的所有的对象中,都会有此属性
*/
class Weather extends React.Component {
state = { ishot: true, wind: '大' }
render() {
const { ishot, wind } = this.state
return <h1 onClick={this.changeweather}>今天天气很{this.state.ishot ? '炎热' : '凉爽'}</h1>
}
// 箭头函数的父级
changeweather = () => {
const ishot = this.state.ishot
this.setState({ ishot: !ishot })
}
}
props
一般用法
/*
1. 渲染时可以直接设置属性, 多传不报错,少传不报错
2. const {name,age,sex} = this.props 获取到不显示不报错
3. 没有获取,显示,会报错
*/
class Person extends React.Component {
render() {
const {name,age,sex} = this.props
return (
<ul>
<li>{name}</li>
<li>{age}</li>
<li>{sex}</li>
</ul>
)
}
}
ReactDOM.render(<Person name='gc' age='18' sex='男' />, document.getElementById('idiv1'))
ReactDOM.render(<Person name='gc' age='18' sex='男' />, document.getElementById('idiv2'))
ReactDOM.render(<Person name='gc' age='18' sex='男' />, document.getElementById('idiv3'))
props限制
// 错误示范
Person.propTypes = {
name:React.PropTypes.string
}
/*
直接如上写法报错,在react15以及15以前,都在维护PropTypes,react16以后,因为怕react过于庞大,所以删减了,将其放在prop-types.js文件中,想要使用,导入即可
*/
// 正确示范
<script type="text/javascript" src="./prop-types.js"></script>
// 为属性添加设置
Person.propTypes = {
name:PropTypes.string
}
常见的属性限制
/*
PropTypes.string 必须为字符串
PropTypes.string.isRequired 必填
PropTypes.number 必须为数字
PropTypes.func 必须为函数
*/
// 为属性加默认值,传递的时候没有传递,自动获取
Person.defaultProps = {
sex :'不男不女'
}
props值只可以读,不可以修改,this.props.name = 'gc' 是错误的
精简模式
/*
Person.propTypes等一系列相当于类方法,将其放在类内部,并加上static
*/
class Person extends React.Component {
static propTypes = {
name:PropTypes.string.isRequired,
}
static defaultProps = {
sex :'不男不女'
}
render() {
const {name,age,sex} = this.props
return (
<ul>
<li>{name}</li>
<li>{age}</li>
<li>{sex}</li>
</ul>
)
}
}
ReactDOM.render(<Person name={100} age='18' />, document.getElementById('idiv1'))
函数式组件使用props
/*
函数也可以使用props,此时类似于传参,props是形参名,可以随意
*/
function Person(a) {
const { name, age, sex } = a
return (
<ul>
<li>{name}</li>
<li>{age}</li>
<li>{sex}</li>
</ul>
)
}
小扩展
构造器中是否接受props,是否传递给super,取决于是否希望在构造器中通过this访问props,但一般不用
refs
字符串型(过时了)
/*
1. 个人感觉ref就像id
2. 多用箭头函数
*/
class Demo extends React.Component {
showData1 =()=>{
const {input1} = this.refs;
alert(input1.value)
}
showData2 =()=>{
const {input2} = this.refs;
alert(input2.value)
}
render() {
return (
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
<button ref="btn" onClick = {this.showData1}>点我提示左边数据</button>
<input ref="input2" onBlur = {this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
回调形式的ref(推荐使用)
/*
1. a代表的是当前标签
2. (a)=>this.input2 = a 箭头函数只有一句,可以省略括号
3. this 指的是这个类
*/
class Demo extends React.Component {
showData1 =()=>{
const {input1} = this;
alert(input1.value)
}
showData2 =()=>{
const {input2} = this;
alert(input2.value)
}
changeweather = () => {
const { ishot } = this.state;
this.setState({ ishot: !ishot })
}
render() {
return (
<div>
<input ref={ a =>{this.input1 = a;console.log(this)}} type="text" placeholder="点击按钮提示数据"/>
<button ref="btn" onClick = {this.showData1}>点我提示左边数据</button>
<input ref={(a)=>this.input2 = a} onBlur = {this.showData2} type="text" placeholder="失去焦点提示数据"/>
<br/><button ref="btn1" onClick={this.changeweather}>更改天气</button>
</div>
)
}
}
/*
内联方式的ref调用次数 1+2*n
每次render都会清空上一次,所以每次都是新的function,并且赋null
*/
createRef
/*
创建的myRef只能供一个使用,多次赋值会被覆盖
所以想要多个使用,创建myref1,myref2……
*/
class Demo extends React.Component {
myRef = React.createRef()
howData1 = () => {
console.log(this.myRef.current.value)
}
render() {
return (
<div>
<input ref={this.myRef} type = "text" />
<button ref="btn" onClick={this.showData1}>点我提示数据</button>
</div>
)
}
}
事件处理
/*
1. 通过onXxx属性指定事件处理函数(注意大小写)
1). 使用的是合成事件,不是原生dom事件(大写了字母,react重新封装的)
2). 事件委托给最外层元素
2. event.target 得到发生事件的dom元素对象
3. 不要过度的使用ref
4. ref 避免:
发生事件的元素正好是你操作的元素,可以避免(事件你自己调用,并且自己出结果,不经过其他的元素)
*/
// 可以避免
<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
// 不可以避免(button不是操作自身,而是操作input)
<input ref={a => { this.input1 = a;console.log("@",a) }} type="text" placeholder="点击按钮提示数据" />
<button ref="btn" onClick={this.showData1}>点我提示左边数据</button>
非受控组件
/*
1. `${}`,直接可以填入字符串,类似于py的format格式
2. event.preventDefault() 阻止页面跳转
*/
class Login extends React.Component{
mysubmit=(event)=>{
event.preventDefault()
const {username,password} = this;
alert(`用户名是:${username.value},密码是:${password.value}`)
}
render(){
return(
<form onSubmit={this.mysubmit}>
用户名:<input ref ={c=>this.username = c} type='text'/>
密码:<input ref ={c=>this.password= c} type='password'/>
<button>登录</button>
</form>
)
}
}
受控组件
/*
输入类的组件再输入中将value维护到state中,在使用的时候直接从state中取
没有用 ref
*/
class Login extends React.Component {
state ={
username:'',
password:''
}
saveusername = (event) => {
this.setState({ username: event.target.value })
}
savepassword = (event) => {
this.setState({ password: event.target.value })
}
mysubmit = (event) => {
event.preventDefault()
const { username, password } = this.state;
alert(`用户名是:${username},密码是:${password}`)
}
render() {
return (
<form onSubmit={this.mysubmit}>
用户名:<input onBlur={this.saveusername} type='text' />
密码:<input onChange={this.savepassword} type='password' />
<button>登录</button>
</form>
)
}
}
高阶函数
/*
高阶函数:若一个函数符合下面2个规范的任何一个,都算
1. 接受的参数是一个function
2. 返回值是一个 function
函数的柯里化: 通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
注意:
1. onChange={this.savedata('username')} onchege接受的是一个函数,而等式右边的返回值也是一个function
2. {[key]:event.target.value} 可以获取多个参数 类似于mydic[key]的形式
*/
class Login extends React.Component {
state = {
username: '',
password: ''
}
savedata = (key) => {
return (event)=>{
this.setState({[key]:event.target.value})
}
}
mysubmit = (event) => {
event.preventDefault()
const { username, password } = this.state;
alert(`用户名是:${username},密码是:${password}`)
}
render() {
return (
<form onSubmit={this.mysubmit}>
用户名:<input onChange={this.savedata('username')} type='text' />
密码:<input onChange={this.savedata('password')} type='password' />
<button>登录</button>
</form>
)
}
}
/*
内联式写法
*/
savedata = (key,event) => {
this.setState({[key]:event.target.value})
}
用户名:<input onChange={this.savedata('username')} type='text' />
密码:<input onChange={this.savedata('password')} type='password' />