1、组件与模块
模块
- 理解:一个
js
文件就是一个模块 - 拆成模块的原因:随着业务逻辑的增加,代码越来越多并且复杂
- 模块的作用:复用
js
,简写js
的编写,提高js
的运行效率
组件
- 理解:用来实现局部功能效果的代码和资源的集合
{html/css/image等等}
- 原因:一个界面的功能更复杂
- 作用:复用代码,简化项目编码,提高运行效率
模块化
当应用的js
都以模块来编写的,这个应用就是一个模块化的应用。
组件化
当应用是以多组件的方式实现,这个应用就是一个组件化的应用
2、React面向组件编程
函数式组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>函数式组件</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/JavaScript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/JavaScript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将JSX转为JS -->
<script type="text/JavaScript" src="../js/babel.min.js"></script>
<script type="text/babel">
//1.创建函数式组件
function Demo(){
return <h1>我是函数式组件</h1>
}
//2.渲染到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
此处的Demo
就是一个组件,并且首字母必须大写。大写字母开头,React
就去渲染对应的组件,若组件没有定义,则报错
此处类式组件的执行过程
- 执行了
ReactDOM.render(<Demo/>,document.getElementById('test')
发生了什么 React
解析组件标签,找到Demo
组件- 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟
DOM
转为真实的DOM
,随后呈现在页面中
注意
- 函数式组建的首字母必须大写
- 函数必须有返回值
ReactDOM.render()
中必须写组件标签,并且必须是闭合的
类式组件
类的总结
- 类中的构造器不是必须写的,如果要对实例进行一些初始化的操作,如添加指定属性时才写。
- 如果A类继承了B类,且A类中写了构造器,那么A类构造器中
super
是必须要调用的,并且super
必须放在第一行 - 类中所定义的方法,都是放在了类的原型对象上,供实例去使用。
代码展示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>类式组件</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/JavaScript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/JavaScript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将JSX转为JS -->
<script type="text/JavaScript" src="../js/babel.min.js"></script>
<script type="text/babel">
//1.创建类式组件
class Demo extends React.Component {
render() {
return <h1>我是类组件</h1>
}
}
//2.渲染到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
执行了ReactDOM.render()
之后,发生了什么
React
解析组件标签,找到Demo
组件。- 发现组件是使用类定义的,随后
new
出来该类的实例, 并通过该实例调用到原型上的render
方法。 - 将
render
返回的虚拟DOM
转为真实DOM
随后呈现在页面中。
三大核心
- 必须继承
React.Component
父类 - 必须要写
render()
render()
必须有返回值。
3、组件实例的三大核心属性
state
效果(动态的切换炎热和凉爽)
代码展示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>类式组件</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/JavaScript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/JavaScript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将JSX转为JS -->
<script type="text/JavaScript" src="../js/babel.min.js"></script>
<script type="text/babel">
//1.创建类式组件
class Weather extends React.Component{
constructor(props){
super(props)
//初始化状态
this.state = {isHot:true,wind:'微风'}
//解决changeWeather中this的指向问题
this.changeWeather = this.changeWeather.bind(this)
}
render(){
//读取状态
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'},{wind}</h1>
}
changeWeather(){
//changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
//由于changeweather是作为onClick的回调,所以不是通过实例调用的,是直接调用
//类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
//获取原来的isHot值
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
//2.渲染到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>
</body>
</html>
state
的简化写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>类式组件</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/JavaScript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/JavaScript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将JSX转为JS -->
<script type="text/JavaScript" src="../js/babel.min.js"></script>
<script type="text/babel">
//1.创建类式组件
class Weather extends React.Component{
//初始化状态
state = {isHot:true,wind:'微风'}
render(){
//读取状态
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'},{wind}</h1>
}
//自定义方法 ———— 要用赋值语句+箭头函数
changeWeather = ()=> {
//获取原来的isHot值
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
//2.渲染到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>
</body>
</html>
state
总结
state
是组件对象最重要的属性, 值是对象(可以包含多个key-value
的组合)- 组件中
render
方法中的this
为组件实例对象 - 组件自定义的方法中
this
为undefined
- 强制绑定
this
: 通过函数对象的bind()
:this.changeWeather = this.changeWeather.bind(this)
- 箭头函数:要用赋值语句+箭头函数
- 强制绑定
- 状态数据,不能直接修改或更新,需使用
setState()函数
props
入门使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>props基本使用</title>
</head>
<body>
<!-- 创建容器 -->
<div id='test'></div>
<div id='test1'></div>
<!-- 引入react核心库 -->
<script type="text/JavaScript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/JavaScript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将JSX转为js-->
<script type="text/JavaScript" src="../js/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='张三' age='20' sex='男'/>,document.getElementById('test'))
ReactDOM.render(<Person name='李四' age='10' sex='女'/>,document.getElementById('test1'))
</script>
</body>
</html>
上面是属性少,那么属性多的时候怎么办呢?
我们可以使用三点运算符
,那么什么是三点运算符
呢?
三点运算符
为对象的扩展运算符,用于取出参数对象中的所有可遍历属性,拷贝到当前的对象之中,拓展运算符拷贝的是对象中的基本数据类型,并不是对原有对象的引用,因此修改当前对象中的值不会影响原有对象中的值。
- 数组的复制
//创建一个数组
var number = ['吃饭','睡觉','喝酒']
//展开一个数组
console.log(...number) // 吃饭,睡觉,喝酒
//复制
var arr = [...number]
console.log(arr)// [0:'吃饭',1:'睡觉',2:'喝酒']
- 对象的复制
var people= { name: '张三', age: 20 }
//展开运算符不能展开对象
console.log(...obj); //不能直接打印对象,会报错Uncaught TypeError: Found non-callable @@iterator at 三目运算符.html:12
var p= { ...people}
console.log(p) //{ name: '张三', age: 20 }
//不会影响原来对象的值
console.log(people)//{ name: '张三', age: 20 }
- 对象的合并
var people= { name: '张三'}
var people1= { age: 20 }
var p2 = {...people,...people1}
console.log(p2)//{ name: '张三'}
- 字符串转化为数组
var str = '我爱计算机'
var arr = [...str]
console.log(arr)//["我", "爱", "计", "算", "机"]
简写方式(批量传递 )
<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>
)
}
}
//渲染组件到页面
const p = {name:'王五',age:30,sex:'男'}
ReactDOM.render(<Person {...p}/>,document.getElementById('test2'))
</script>
限制props
使用prop-types
库进限制(需要引入prop-types
库)
- 姓名必须指定,且为字符串类型;
- 性别为字符串类型,如果性别没有指定,默认为男
- 年龄为字符串类型,且为数字类型,默认值为18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>props基本使用</title>
</head>
<body>
<!-- 创建容器 -->
<div id='test'></div>
<div id='test1'></div>
<div id='test2'></div>
<!-- 引入react核心库 -->
<script type="text/JavaScript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/JavaScript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将JSX转为js-->
<script type="text/JavaScript" src="../js/babel.min.js"></script>
<!-- 引入prop-types,用于对组件标签属性进行限制-->
<script type="text/JavaScript" src="../js/prop-types.js"></script>
<script type="text/babel">
//创建组件
class Person extends React.Component{
render(){
const {name,age,sex} = this.props
//注意:props是只读的
return(
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
//对标签属性进行类型和必要性的限制
Person.propTypes = {
name:PropTypes.string.isRequired,//名字是字符串类型且为必填
sex:PropTypes.string,//性别为字符串类型
age:PropTypes.number//年龄为数字类型
}
//指定默认标签属性值
Person.defaultProps = {
sex:'男', //性别默认值
age:22 //年龄默认值
}
//渲染组件到页面
ReactDOM.render(<Person name='张三' age={20} sex='男'/>,document.getElementById('test'))
ReactDOM.render(<Person name='李四' />,document.getElementById('test1'))
const p = {name:'王五',age:18,sex:'男'}
ReactDOM.render(<Person {...p}/>,document.getElementById('test2'))
</script>
</body>
</html>
注意
- 组件自身添加属性
Person.propTypes
,这块的名propTypes
是固定的 React
的内置属性为PropTypes
,注意区分二者,后边跟限制类型
简写方式
将定义的属性直接放在类中
<script type="text/babel">
//创建组件
class Person extends React.Component{
//对标签属性进行类型和必要性的限制
static propTypes = {
name:PropTypes.string.isRequired,//名字是字符串类型且为必填
sex:PropTypes.string,//性别为字符串类型
age:PropTypes.number//年龄为数字类型
}
//指定默认标签属性值
static defaultProps = {
sex:'男', //性别默认值
age:22 //年龄默认值
}
render(){
const {name,age,sex} = this.props
//注意:props是只读的
return(
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
//渲染组件到页面
ReactDOM.render(<Person name='张三' age={20} sex='男'/>,document.getElementById('test'))
ReactDOM.render(<Person name='李四' />,document.getElementById('test1'))
const p = {name:'王五',age:18,sex:'男'}
ReactDOM.render(<Person {...p}/>,document.getElementById('test2'))
</script>
这里的构造器可省略,也可不省略,如果不省略,那么必须写super()
,此时构造器是否接收props
,是否传递给super
,取决于是否希望在构造器中通过this
访问props
refs
String
形式的ref <input ref="input1"/> 已过时
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ref</title>
</head>
<body>
<!-- 创建容器 -->
<div id='test'></div>
<!-- 引入react核心库 -->
<script type="text/JavaScript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/JavaScript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将JSX转为js-->
<script type="text/JavaScript" src="../js/babel.min.js"></script>
<script type="text/babel">
//创建组件
class Demo extends React.Component{
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this.refs
alert(input1.value)
}
//展示右侧输入框的数据
showData1 = ()=>{
const {input2} = this.refs
alert(input2.value)
}
render(){
return(
<div>
//点击事件
<input ref="input1" type="text" placeholder='点击按钮提示数据'/>
<button onClick={this.showData}>点我提示左侧的数据</button>
//失去焦点
<input ref="input2" onBlur={this.showData1} type="text" placeholder='失去焦点提示数据'/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
回调形式的ref<input ref={(c)=>{this.input1 = c}}
React
也支持另一种设置refs
的方式,称为回调 refs
。它能助你更精细地控制何时refs
被设置和解除
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ref</title>
</head>
<body>
<!-- 创建容器 -->
<div id='test'></div>
<!-- 引入react核心库 -->
<script type="text/JavaScript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/JavaScript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将JSX转为js-->
<script type="text/JavaScript" src="../js/babel.min.js"></script>
<script type="text/babel">
//创建组件
class Demo extends React.Component{
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this
alert(input1.value)
}
//展示右侧输入框的数据
showData1 = ()=>{
const {input2} = this
alert(input2.value)
}
render(){
return(
<div>
<input ref={ c => this.input1 = c } type="text" placeholder='点击按钮提示数据'/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref={ c => this.input2 = c} onBlur={this.showData1} type="text" placeholder='失去焦点提示数据'/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
关于回调 refs 的说明
如果ref
回调函数是以内联函数的方式定义的,在更新过程中它会被 执行两次
,第一次传入参数 null,然后第二次会传入参数 DOM
元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React
清空旧的ref
并且设置新的。通过将 ref
的回调函数定义成class
的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的
createRef
创建ref
容器 ==> 推荐的方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ref</title>
</head>
<body>
<!-- 创建容器 -->
<div id='test'></div>
<!-- 引入react核心库 -->
<script type="text/JavaScript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/JavaScript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将JSX转为js-->
<script type="text/JavaScript" src="../js/babel.min.js"></script>
<script type="text/babel">
//创建组件
class Demo extends React.Component{
//React.createRef()调用后可以返回一个容器,该容器可以存储被ref所标识的节点
//注意:该容器是专人专用的
myRef = React.createRef()
myRef2 = React.createRef()
//展示左侧输入框的数据
showData = ()=>{
alert(this.myRef.current.value)
}
//展示右侧输入框的数据
showData1 = ()=>{
alert(this.myRef2.current.value)
}
render(){
return(
<div>
<input ref={this.myRef} type="text" placeholder='点击按钮提示数据'/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref={this.myRef2} onBlur={this.showData1} type="text" placeholder='失去焦点提示数据'/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
事件处理
- 通过
onXxx(onBlur,onClick)
属性指定事件处理函数(注意大小写)
React.createRef()
调用后可以返回一个容器,该容器可以存储被ref
所标识的节点,该容器是专人专用的