React学习笔记
1.React快速入门
主要分为3个步骤:
0.引入相关js文件
1.准备一个容器
2.创建虚拟DOM
3.将虚拟DOM渲染到容器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello_react</title>
</head>
<body>
<!-- 准备好“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../react.development.js"></script>
<!-- 引入react-DOM,用于支持react操作DOM -->
<script type="text/javascript" src="../react-DOM.development.js"></script>
<!-- 引入babel,将jsx转为js -->
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/babel">
//1.创建虚拟DOM
const VDOM = <h1>Hello,React</h1>
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
</html>
2.用jsx和js写虚拟DOM的区别
const JSXVDOM = <h1 id="title"><span>Hello,React</span></h1>
const JSVDOM = React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React'))
当出现标签嵌套的情况时,js的写法变得复杂起来,而jsx则是像写页面一样,故开发时选用jsx
3.虚拟DOM和真实DOM的区别
虚拟DOM的属性比真实DOM要少,在运行时更“轻便”。
4.jsx的语法规则
- 定义虚拟DOM时,不要写引号。
- 标签中混入js表达式时要用{}。
- 样式的类名指定不要用class,要用className。
- 内联样式,要用style={{key:value}}的形式写。
- 只有一个根标签。
- 标签必须闭合。
- 标签首字母
(1)若小写字母开头,则将该标签转为html中同名元素,若html中无对应的同名元素,则报错。
(2)若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
一定注意区分【js语句(代码)】与【js表达式】
- 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
下面这些都是表达式:
(1)a
(2)a+b
(3)demo(1)
(4)arr.map()
(5)function test () {} - 语句(代码):
下面这些都是语句(代码):
(1)if () {}
(2)for () {}
(3)switch () {case:xxxx}
5.React中定义组件
5.1函数式组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>函数式组件</title>
</head>
<body>
<!-- 准备好“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转成js -->
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/babel">
//1.创建函数式组件
function Demo(){
console.log(this)
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
//2.渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
函数式组件的返回值就是要渲染的内容。
函数式组件中的this为undefined,这是因为babel在翻译jsx时开启了严格模式,禁止自定义函数中的this指向window。
5.2类式组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>2_类式组件</title>
</head>
<body>
<!-- 准备好“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转成js -->
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/babel">
//1.创建函数式组件
class MyComponent extends React.Component{
render(){
console.log('render中的this:',this)
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>
</body>
</html>
定义类式组件需要继承React中的Component类,
并重写render()方法,React在渲染类式组件时会调用其中的render方法,其返回值就是渲染的内容。
render()和constructor()函数中的this就是实例对象,而其他的自定义函数中的this是undefine。
这是因为render()和constructor()函数是通过实例来调用,而其他自定义方法是直接调用,故取到的是window对象,而又因为babel翻译时开启了严格模式,取不到window,只能变成undefined。
解决方法是写成箭头函数,就能使用this取到实例对象。
6.组件实例的三大属性之state
标准写法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>state</title>
</head>
<body>
<div id="test"></div>
<script type="text/javascript" src="../react.development.js"></script>
<script type="text/javascript" src="../react-dom.development.js"></script>
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/babel">
class Weather extends React.Component{
constructor(props){
super(props)
this.state = {isHot:false}
this.changeWeather = this.changeWeather.bind(this)
}
render(){
const {isHot} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot?'炎热':'凉快'}</h1>
}
changeWeather(){
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>
</body>
</html>
state的特殊之处在于在React的Component对象中有setState对象可以用来更新state中属性的值。
简化写法:
简化的地方在于state可以直接属性赋值,而调用setState的方法可以通过箭头函数来触发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>2_state的简写方式</title>
</head>
<body>
<div id="test"></div>
<script type="text/javascript" src="../react.development.js"></script>
<script type="text/javascript" src="../react-dom.development.js"></script>
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/babel">
class Weather extends React.Component{
state = {isHot:false}
render(){
const {isHot} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot?'炎热':'凉快'}</h1>
}
changeWeather = ()=>{
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>
</body>
</html>
7.组件实例的三大属性之props
7.1props的基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1_props基本使用</title>
</head>
<body>
<div id="test1"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="../react.development.js"></script>
<script type="text/javascript" src="../react-dom.development.js"></script>
<script type="text/javascript" src="../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="小明" sex="男" age="20"/>,document.getElementById('test1'))
ReactDOM.render(<Person name="小红" sex="女" age="17"/>,document.getElementById('test2'))
ReactDOM.render(<Person name="小强" sex="男" age="18"/>,document.getElementById('test3'))
</script>
</body>
</html>
props属性的特殊之处在于:在React渲染虚拟DOM时,通过组件标签传入的属性,能够被props接收。
7.2对props属性进行限制
需要引入一个prop-types.js文件,因为比较大所以没有放入核心库,在需要的时候再引入。
使用类名调用propTypes方法,传入属性类型的限制规则
使用类名调用defaultProps方法,传入属性默认值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>2_对props进行限制</title>
</head>
<body>
<div id="test1"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="../react.development.js"></script>
<script type="text/javascript" src="../react-dom.development.js"></script>
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/javascript" src="../prop-types.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+1}</li>
</ul>
)
}
}
Person.propTypes = {
name:PropTypes.string.isRequired,//字符串类型,必要的属性(分别对应string,isRequired)
age:PropTypes.number,//数字类型
sex:PropTypes.string,
speak:PropTypes.func//函数类型
}
Person.defaultProps = {
sex:'男',
age:18
}
ReactDOM.render(<Person name="小明" speak={speak()} />,document.getElementById('test1'))
ReactDOM.render(<Person name="小红" sex="女" age={17}/>,document.getElementById('test2'))
ReactDOM.render(<Person name="小强" sex="男" age={18}/>,document.getElementById('test3'))
function speak(){
console.log('我说了一句话')
}
</script>
</body>
</html>
7.3props的简写方式
简化的地方在于:
将限制规则写在类中,将规则定义为类的属性,需要在propTypes、defaultProps 前面加static关键字,将其定义到类本身。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3_对props的简写方式</title>
</head>
<body>
<div id="test1"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="../react.development.js"></script>
<script type="text/javascript" src="../react-dom.development.js"></script>
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/javascript" src="../prop-types.js"></script>
<script type="text/babel">
class Person extends React.Component{
static propTypes = {
name:PropTypes.string.isRequired,
age:PropTypes.number,
sex:PropTypes.string,
speak:PropTypes.func
}
static defaultProps = {
sex:'男',
age:18
}
render(){
const {name,age,sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
ReactDOM.render(<Person name="小明" speak={speak()} />,document.getElementById('test1'))
ReactDOM.render(<Person name="小红" sex="女" age={17}/>,document.getElementById('test2'))
ReactDOM.render(<Person name="小强" sex="男" age={18}/>,document.getElementById('test3'))
function speak(){
console.log('我说了一句话')
}
</script>
</body>
</html>
7.4函数式组件使用props
函数式组件由于没有实例对象无法使用三大对象的state、refs,但可以通过传参的方式得到props。
而设置props规则只能使用组件名.propTypes (defaultProps )的方式实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>4_函数式组件使用props</title>
</head>
<body>
<div id="test1"></div>
<script type="text/javascript" src="../react.development.js"></script>
<script type="text/javascript" src="../react-dom.development.js"></script>
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/javascript" src="../prop-types.js"></script>
<script type="text/babel">
function Person (props){
const {name,age,sex} = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
Person.propTypes = {
name:PropTypes.string.isRequired,
age:PropTypes.number,
sex:PropTypes.string,
}
Person.defaultProps = {
sex:'男',
age:18
}
ReactDOM.render(<Person name="小明" />,document.getElementById('test1'))
</script>
</body>
</html>
8组件实例的三大属性之refs
8.1字符串形式的ref
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1_字符串形式的ref</title>
</head>
<body>
<div id="test"></div>
<script type="text/javascript" src="../react.development.js"></script>
<script type="text/javascript" src="../react-dom.development.js"></script>
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/javascript" src="../prop-types.js"></script>
<script type="text/babel">
class Demo extends React.Component{
render(){
return (
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
<input onClick={this.showData} type="button" value="点击显示左侧数据"/>
<input onBlur={this.showData2} ref="input2" type="text" placeholder="失去焦点显示数据"/>
</div>
)
}
showData = ()=>{
const {input1} = this.refs
// console.log(this)
alert(input1.value)
}
showData2 = ()=>{
const {input2} = this.refs
alert(input2.value)
}
}
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
refs的特殊之处在于:在render()函数中的标签可以设置一个ref属性,React在创建实例对象时会将有ref属性的标签会以{key:value}的方式存入对象上的refs属性上,其中key是ref属性名,value是标签本身。
举例:在上述代码中共设置了两个有ref属性的标签,他们在实例对象中大概是这样的:
demo实例对象{
...其他属性
refs{
input1: <input ref="input1" type="text" placeholder="点击按钮提示数据"/>
input2: <input onBlur={this.showData2} ref="input2" type="text" placeholder="失去焦点显示数据"/>
}
...其他属性
}
8.2回调函数形式的ref
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>2_回调函数形式的ref</title>
</head>
<body>
<div id="test"></div>
<script type="text/javascript" src="../react.development.js"></script>
<script type="text/javascript" src="../react-dom.development.js"></script>
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/javascript" src="../prop-types.js"></script>
<script type="text/babel">
class Demo extends React.Component{
render(){
return (
<div>
<input ref={(currentNode)=>{this.input1 = currentNode}} type="text" placeholder="点击按钮提示数据"/>
<input onClick={this.showData} type="button" value="点击显示左侧数据"/>
<input ref={c => this.input2 = c} onBlur={this.showData2} type="text" placeholder="失去焦点显示数据"/>
</div>
)
}
showData = ()=>{
const {input1} = this
// console.log(this)
alert(input1.value)
}
showData2 = ()=>{
const {input2} = this
alert(input2.value)
}
}
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
回调函数(currentNode)=>{this.input1 = currentNode}中的currentNode是自定义的参数名,它的含义是
当前的标签本身。这段代码的含义是将当前节点直接挂载到实例对象上的input1属性。
举例:在上述代码中共设置了两个有ref属性的标签,他们在实例对象中大概是这样的:
demo实例对象{
...其他属性
input1: <input ref="input1" type="text" placeholder="点击按钮提示数据"/>
input2: <input onBlur={this.showData2} ref="input2" type="text" placeholder="失去焦点显示数据"/>
...其他属性
}
8.3回调ref中回调执行次数的问题
组件在创建时会执行一次回调ref,在更新组件时,会执行2次回调ref。其中第一次执行是清空当前节点,第二次是赋值。以下是测试代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3_回调ref中回调执行次数的问题</title>
</head>
<body>
<div id="test"></div>
<script type="text/javascript" src="../react.development.js"></script>
<script type="text/javascript" src="../react-dom.development.js"></script>
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/javascript" src="../prop-types.js"></script>
<script type="text/babel">
class Demo extends React.Component{
render(){
const {isHot} = this.state
return (
<div>
<h1>今天天气真{isHot?'炎热':'凉快'}</h1>
<input ref={(currentNode)=>{this.input1 = currentNode,console.log('@',currentNode)}} type="text" placeholder="点击按钮提示数据"/>
<input onClick={this.showData} type="button" value="点击显示左侧数据"/><br/>
<input onClick={this.changeWeather} type="button" value="点击切换天气"/>
</div>
)
}
state = {isHot:true}
changeWeather = ()=>{
const {isHot} = this.state
this.setState({isHot:!isHot})
}
showData = ()=>{
const {input1} = this
alert(input1.value)
}
}
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
8.4createref
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>4_createRef</title>
</head>
<body>
<div id="test"></div>
<script type="text/javascript" src="../react.development.js"></script>
<script type="text/javascript" src="../react-dom.development.js"></script>
<script type="text/javascript" src="../babel.min.js"></script>
<script type="text/javascript" src="../prop-types.js"></script>
<script type="text/babel">
class Demo extends React.Component{
myRef = React.createRef()
myRef2 = React.createRef()
render(){
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
<input onClick={this.showData} type="button" value="点击显示左侧数据"/>
<input ref={this.myRef2} onBlur={this.showData2} type="text" placeholder="失去焦点显示数据"/>
</div>
)
}
showData = ()=>{
console.log(this.myRef)
alert(this.myRef.current.value)
}
showData2 = ()=>{
alert(this.myRef2.current.value)
}
}
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
该方式是通过React.createRef()来创建一个容器,然后在标签中的ref属性来绑定该容器来使用。
该容器是专人专用,节点在容器中的current上。
举例:在上述代码中共设置了两个有ref属性的标签,他们在实例对象中大概是这样的:
demo实例对象{
...其他属性
myRef: {current: <input ref="input1" type="text" placeholder="点击按钮提示数据"/>}
myRef2: {current: <input onBlur={this.showData2} ref="input2" type="text" placeholder="失去焦点显示数据"/>}
...其他属性
}