文章目录
模块化与组件化
模块化
- 模块是向外提供
特定功能
的js程序, 一般一个js文件就是一个模块 - 当应用的js都以模块来编写的, 这个应用就是一个模块化的应用
组件化
- 组件就是用来实现
局部功能效果
的代码和资源的集合(html/css/js/image等等) - 当应用是以多组件的方式实现, 这个应用就是一个组件化的应用
react定义组件
函数式组件
组件以函数的形式进行编写,函数的返回值是dom结构。
<!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>Document</title>
<style>
.header{
background-color: orange;
}
</style>
</head>
<body>
<!-- 容器 -->
<div id="test"></div>
<!-- REACT ,react核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- REACTDOM ,支持react操作dom-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 将jsx转换成js文件 -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!--text/babel表明是jsx -->
<script type="text/babel">
// 1.创建函数式组件
function Demo(){
console.log(this)//注意this是undefined,因为babel翻译后开启了严格模式,严格模式下自定义的this是不允许指向window的
return <h2>我是函数式组件(适用于【简单组件】的定义)</h2>
}
// 2.渲染虚拟DOM到页面
// 如果渲染的是组件需要使用标签,并且首字母要大写,并且需要进行闭合
ReactDOM.render(<Demo/>,document.getElementById("test"))
</script>
<!-- 总结:
执行了ReactDOM.render(<Demo/>.......之后,发生了什么?
1.React解析组件标签,找到了Demo组件。
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。 -->
</body>
</html>
类式组件
类补充
- 类的属性存放在实例对象上,类上的方法存放在类的原型对象上。
- 类中的方法默认开启了局部严格模式,this是undefined
<!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>Document</title>
</head>
<body>
<script>
class Person {
// 属性放在了类的实例对象上
sex = 'f';
constructor(name, age) {
// this指向的是实例对象
this.name = name
this.age = age
}
// 方法放在了类的原型对象上,供实例使用
speak() {
// 通过person实例调用speck时,this是对应的实例对象
console.log(`我叫${
this.name}, 我今年 ${
this.age}岁了`);
}
}
const p1 = new Person('yang', 20)
const p2 = new Person('cheng', 21)
console.log('p1:', p1);
console.log('p2:', p2);
p1.speak()
p2.speak()
</script>
</body>
</html>
输出:
如果子类中使用构造器,则必须使用调用super()
类式组件
- 定义的类需要继承React.Component
- 必须写render
- render必须有返回值
- 写了几个组件标签就会new几个组件实例
eg:
<!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>Document</title>
<style>
.header{
background-color: orange;
}
</style>
</head>
<body>
<!-- 容器 -->
<div id="test"></div>
<!-- REACT ,react核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- REACTDOM ,支持react操作dom-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 将jsx转换成js文件 -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!--text/babel表明是jsx -->
<script type="text/babel">
// 1.创建类式组件(需要继承React.Component;必须写render;render必须有返回值)
class Demo extends React.Component{
// render放在Demo的原型对象上,供实例使用
// render中的this是谁?—Demo的实例对象,即Demo的组件实例对象。
render(){
return <h2>我是类式组件(适用于【复杂组件】的定义)</h2>
}
}
// 2.渲染虚拟DOM到页面()
// Demo实例由React创建
ReactDOM.render(<Demo/>,document.getElementById("test"))
</script>
<!-- 总结:
执行了ReactDOM.render(<Demo/>.......之后,发生了什么?
1.React解析组件标签,找到了Demo组件。
2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。 -->
</body>
</html>
在render中输出组件实例对象
render(){
console.log('组件实例对象', this);
return <h2>我是类式组件(适用于【复杂组件】的定义)</h2>
}
简单组件和复杂组件
- 拥有状态的组件叫复杂组件
- 状态存储在组件实例对象(使用类定义的组件)的state中
所以具有state属性的组件是复杂组件。
组件的三大属性
组件的三大属性实际上说的是组件实例的三大属性,而只有类式组件才会有实例,所以组件的三大属性实际上是针对类式组件而言的
组件实例对象有三大属性:state、props、refs
state
state一般用来存储一些变量的值,我们知道state属性是在组件实例对象上的,如果想要获取state中的值就需要获取组件实例对象,而在类的构造函数中的this就是组件实例对象,所以我们可以通过类的构造函数
来实现对state的操作。
构造函数是有参数的,可以参照官方文档以props为参数
constructor(props) {
super(props);
this.state = {
seconds: 0 };
}
可以在构造函数或render中通过 this.state.seconds
(确保this是组件实例对象) 来获取state数据
补充:react的事件绑定
格式:点击事件onClick={clickEvent}
- 事件使用驼峰命名法
- 方法名后面无需添加()
- 方法名使用{}进行包裹
eg:
// 1.创建类式组件
class Weather extends React.Component{
render(){
// 原生的事件绑定<h1 οnclick="clickEvent()">
// {}中放的是js表达式,如果函数加()就会自动执行,onClick的值就是一个undefined
return <h1 onClick={
clickEvent}>今天天气真{
this.state.isHot?"炎热":"凉爽"}</h1>
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
function clickEvent(){
console.log('被点击了');
}
state使用案例
实现效果:点击切换天气
- this的获取问题
<!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>Document</title>
<style>
.header{
background-color: orange;
}
</style>
</head>
<body>
<!-- 容器 -->
<div id="test"></div>
<!-- REACT ,react核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- REACTDOM ,支持react操作dom-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 将jsx转换成js文件 -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!--text/babel表明是jsx -->
<script type="text/babel">
// 1.创建类式组件
class Weather extends React.Component{
constructor(props){
super(props)
// 组件实例对象上存储值(写成对象)
this.state = {
isHot:false
}
// 利用bind修改changeWeather的this并且将返回的新函数赋值给定义的changeWeather2(bind返回一个新的函数)
// changeWeather2是在实例对象上的
this.changeWeather2 = this.changeWeather.bind(this)
}
render(){
return <h1 onClick={
this.changeWeather2}>今天天气真{
this.state.isHot?"炎热":"凉爽"}</h1>
}
changeWeather(){
// 类的方法放在类的原型对象上,所以changeWeather放在Weather的原型对象上
// 所以如果通过Weather的实例对象调用changeWeather时,changeWeather的实例对象就是Weather的实例对象
// 由于类中的方法默认开启严格模式,所以如果是直接调用函数(即不使用实例对象调用)那么this就是undefine
// 按<h1 onClick={this.changeWeather}>的写法,是将实例对象的方法赋值给onClick,所以如果点击后再调用的话就是直接调用,this就是undefined
// 并且类中的方法默认开启了局部严格模式,所以changeWeather中的this是undefined
// 所以进行修改,在构造函数中进行设置this.changeWeather2 = this.changeWeather.bind(this)
console.log(this);
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
// function changeWeather(){
// console.log('修改isHot')
// // babel开启严格模式,this指向是undefined,所以放在外面不可取,放在类中作为方法使用
// }
</script>
</body>
</html>
- 实现响应式数据
<!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>Document</title>
<style>
.header{
background-color: orange;
}
</style>
</head>
<body>
<!-- 容器 -->
<div id="test"></div>
<!-- REACT ,react核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- REACTDOM ,支持react操作dom-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 将jsx转换成js文件 -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!--text/babel表明是jsx -->
<script type="text/babel">
// 1.创建类式组件
class Weather extends React.Component{
constructor(props){
super(props)
// 组件实例对象上存储值(写成对象)
this.state = {
isHot:false,
wind:'微风'
}
// 修改changeWeather的this并且赋值给定义的changeWeather(bind返回一个新的函数)
// 这里定义的 this.changeWeather是在实例对象上的
this.changeWeather = this.changeWeather.bind(this)
}
render(){
return <h1 onClick={
this.changeWeather}>今天天气真{
this.state.isHot?"炎热":"凉爽"},{
this.state.wind}</h1>
}
// 这里定义的 changeWeather是在类的原型对象上的
changeWeather(){
// 如果直接修改数据实现不了响应式数据
// this.state.isHot = !this.state.isHot错误写法
// 使用setState()才能实现数据的响应式,该方法是React.Component原型对象上的方法
// setState()的参数是对象
// setState()只是修改某个属性值, 不会修改其他属性值
const isHot = this.state.isHot
this.setState({
isHot:!isHot})
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>
<!-- setState总结:
1.构造器调用1次
2.render调用n+1次(n代表状态更新的次数,1是第一次渲染的时候进行调用)
3.数据是进行修改,而不是覆盖 -->
</body>
</html>
- 简写方式
<!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>Document</title>
<style>
.header{
background-color: orange;
}
</style>
</head>
<body>
<!-- 容器 -->
<div id="test"></div>
<!-- REACT ,react核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- REACTDOM ,支持react操作dom-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 将jsx转换成js文件 -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!--text/babel表明是jsx -->
<script type="text/babel">
// 1.创建类式组件
class Weather extends React.Component{
// 可以将组件实例对象的属性直接在类中定义
state = {
isHot:false,
wind:'微风'
}
render(){
return <h1 onClick={
this.changeWeather}>今天天气真{
this.state.isHot?"炎热":"凉爽"},{
this.state.wind}</h1>
}
// 直接将方法写成属性赋值的样式,那么方法就放在组件实例对象上了
// 使用箭头函数,this就会向外找就是组件实例对象(结构上的向外查找,class中的this就是实例对象)
changeWeather = ()=>{
const isHot = this.state.isHot
this.setState({
isHot:!isHot})
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>
</body>
</html>
- 组件类中的方法我们一般做
绑定事件
使用,要用赋值语句+箭头函数
的方式进行定义 - 组件类中的属性直接在类中定义即可
state小结
- 理解
- state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
- 组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
- 强烈注意
- 组件中render方法中的this为组件实例对象
- 组件自定义的方法中this为undefined,如何解决?
a)强制绑定this: 通过函数对象的bind()
b)箭头函数----------推荐 - 状态数据,
不能直接修改或更新,必须调用setState()
调用setState,react就会更新数据状态并且重新调用一次render()
使页面的重新渲染,数据进行刷新。
props
props的基本使用
props的使用方法:prop是在调用组件的时候为通过属性名=属性
值为其传值,之后就可以在组件实例对象的props
中访问到传过来的值。
eg:
<!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>Document</title>
<style>
.header{
background-color: orange;
}
</style>
</head>
<body>
<!-- 容器 -->
<div id="test"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 1.创建类式组件
class Person extends React.Component{
render(){
return(
<ul>
<li>姓名:{
this.props.name}</li>
<li>年龄:{
this.props.age}</li>
<li>性别:{
this.props.sex}</li>
</ul>
)
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Person name='yang' age='20' sex='nv'/>,document.getElementById('test'))
ReactDOM.render(<Person name='zhe' age='21' sex='nan'/>,document.getElementById('test2'))
ReactDOM.render(<Person name='ming' age='22' sex='nv'/>,document.getElementById('test3'))
</script>
</body>
</html>
批量传递props
当传递多个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>Document</title>
<style>
.header{
background-color: orange;
}
</style>
</head>
<body>
<!-- 容器 -->
<div id="test"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 1.创建类式组件
class Person extends React.Component{
render(){
const{
name,age,sex} = this.props
return(
<ul>
<li>姓名:{
name}</li>
<li>年龄:{
age}</li>
<li>性别:{
sex}</li>
</ul>
)
}
}
// 2.渲染虚拟DOM到页面
const p = {
name:'yang',age:20,sex:'nv'}
// 注意扩展运算符...不能直接给对象使用(可以直接给数组使用),即在js中...p语法是不对的,但是可以这样使用{...p}(这里的{}表示的是对象, 浅克隆复制对象)
// 但是在react+jsx中可以直接使用...p(注意这里的{}表示的是使用js表达式),但也仅限于标签传递属性时可以使用
ReactDOM.render(<Person {
...p}/>,document.getElementById('test3'))
</script>
<!-- 总结:
1. props批量传递属性是通过{...p}实现的 -->
</body>
</html>
props的限制
给组件传递的属性,对某些特殊的属性一般会有格式上的限制,如必须是字符串类型,我们可以在组件上进行限制,react为我们提供了propTypes属性进行限制:
需要引入prop-types依赖包才会有PropTypes属性。
// Person是一个组件名
Person.propTypes={
// 名字必须指定而且是字符串
// 直接使用PropTypes需要引入prop-types依赖包(用于对组件标签属性进行限制)
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func
}
也可以为属性设置默认值
// 属性默认值
Person.defaultProps={
sex:'男',
age:18
}
eg:案例使用
<!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>Document</title>
<style>
.header{
background-color: orange;
}
</style>
</head>
<body>
<!-- 容器 -->
<div id="test"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入propTypes用于对组件标签属性进行限制,引入该模块会有PropTypes属性 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// 1.创建类式组件
class Person extends React.Component{
render(){
const{
name,age,sex} = this.props
return(
<ul>
<li>姓名:{
name}</li>
<li>年龄:{
age+1}</li>
<li>性别:{
sex}</li>
</ul>
)
}
}
// 对属性进行限制
Person.propTypes={
// 名字必须指定而且是字符串
// 直接使用PropTypes需要引入prop-types依赖包(用于对组件标签属性进行限制)
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
// 限制speak是一个函数
speak: PropTypes.func
}
// 属性默认值
Person.defaultProps={
sex:'男',
age:18
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Person name='yang' age={
20} sex='nv' speak={
speak}/>,document.getElementById('test'))
ReactDOM.render(<Person name='zhe' age={
21} sex='nan'/>,document.getElementById('test2'))
const p = {
name:'yang',age:20,sex:'nv'}
ReactDOM.render(<Person {
...p}/>,document.getElementById('test3'))
function speak(){
console.log('说话:balabalabala');
}
</script>
<!-- 总结:
1. props批量传递属性是通过{...p}实现的 -->
</body>
</html>
注意:props是只读的
props的简写方式
propTypes
和 defaultProps
都是为组件添加的属性,类式组件的本质又是一个类,所以想要给类式组件添加属性可以直接在类中使用static
标识符进行添加。
所以上述代码可以写成如下代码
<!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>Document</title>
<style>
.header{
background-color: orange;
}
</style>
</head>
<body>
<!-- 容器 -->
<div id="test"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入propTypes用于对组件标签书机型进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// 1.创建类式组件
class Person extends React.Component{
render(){
const{
name,age,sex} = this.props
return(
<ul>
<li>姓名:{
name}</li>
<li>年龄:{
age+1}</li>
<li>性别:{
sex}</li>
</ul>
)
}
// 对属性的限制可以直接利用static放在class上
// 对属性进行限制
static propTypes={
// 名字必须指定而且是字符串
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func
}
// 属性默认值
static defaultProps={
sex:'男',
age:18
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Person name='yang' age={
20} sex='nv' speak={
speak}/>,document.getElementById('test'))
ReactDOM.render(<Person name='zhe' age={
21} sex='nan'/>,document.getElementById('test2'))
const p = {
name:'yang',age:20,sex:'nv'}
// 注意扩展运算符...不能直接给对象使用,即在js中...p语法是不对的,但是可以这样使用{...p}
// 但是在react+jsx中可以直接使用...p(注意这里的{}表示的是使用js表达式)
ReactDOM.render(<Person {
...p}/>,document.getElementById('test3'))
function speak(){
console.log('说话:balabalabala');
}
</script>
<!-- 总结:
1. props是只读的
2. 对props属性的限制可以直接利用static放在class上-->
</body>
</html>
类中构造器的的props参数
之前在类中使用构造器啊的时候我们一般会传递一个props属性。如下:
constructor(props){
super(props)
}
类中的构造器一般有两个作用:
- 通过给 this.state 赋值对象来初始化内部 state。—— 可以直接在类中定义state属性
- 为事件处理函数绑定实例。——可以直接在类中定义成箭头函数
因为上述两个内容都可以在类中直接定义解决,不是必须在箭头函数中执行,所以类的构造器一般可以省略不写。
如果写了构造函数可以传递参数也可以不传递参数(props),但是如果写了构造函数一定要写super。传不传props参数的区别是:如果不传递props参数就不能在构造器中通过this.props
访问props属性。
函数式组件使用props
与组件实例的其他两个属性不同,函数时组件也可以使用props属性,利用的是函数可以传递参数的特性。
只不过此时要对参数进行限制时,只能在函数外进行限制
eg:
<!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>Document</title>
<style>
.header{
background-color: orange;
}
</style>
</head>
<body>
<!-- 容器 -->
<div id="test"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// 1.创建函数式组件
function Person(props){
return(
<ul>
<li>姓名:{
props.name}</li>
<li>年龄:{
props.age}</li>
<li>性别:{
props.sex}</li>
</ul>
)
}
// 对属性进行限制
Person.propTypes={
// 名字必须指定而且是字符串
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func
}
// 属性默认值
Person.defaultProps={
sex:'男',
age:18
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Person name='yang' age={
20} sex='nv'/>,document.getElementById('test'))
ReactDOM.render(<Person name='zhe' age={
21} sex='nan'/>,document.getElementById('test2'))
ReactDOM.render(<Person name='ming' age={
22} sex='nv'/>,document.getElementById('test3'))
</script>
<!-- 总结:
1. 函数式组件只能使用props,直接通过参数进行使用
2. 也可以进行限制 -->
</body>
</html>
refs
可以使用ref
唯一的表示组件内的标签,标识完之后就可以在组件实例对象的refs
属性上读取读取标识的标签。
eg:
字符串ref
标签使用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>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// <!-- 1.创建类式组件 -->
class Demo extends React.Component{
render(){
return (
<div>
<input ref="input1" type="text" id="input1" placeholder="点击按钮提示数据"/>
<button onClick={
this.showDataClick}>点我提示左侧数据</button>
<input ref="input2" onBlur={
this.showDataBlur} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
showDataClick = ()=>{
//读取ref标识的标签
const {
input1} = this.refs
alert(input1.value)
}
showDataBlur = ()=>{
const {
input2} = this.refs
alert(input2.value)
}
}
// <!--2.渲染虚拟DOM到页面 -->
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
<!-- 小结:
1. ref用于标识存储dom元素
2. 在组件实例对象的refs中总重可以获取对应的dom元素进行参送
缺点:效率低-->
</body>
</html>
字符串ref的缺点:效率低
回调函数形式的ref
标签使用ref标识时需要使用回调函数进行标识。
回调函数有一个参数,该参数就是所在的标签节点。
回调函数是一个箭头函数的形式,所以this由上一级决定,上一级就是render()
函数,则this就是指向组件实例对象,所以可以直接将回调函数的参数(该节点)放在this上,这样在组件实例对象上就可以直接读取该节点了。
<!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>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// <!-- 1.创建类式组件 -->
class Demo extends React.Component{
showDataClick = ()=>{
const {
input1} = this
alert(input1.value)
}
showDataBlur = ()=>{
const {
input2} = this
alert(input2.value)
}
render(){
return (
<div>
<input ref={
(currentNode)=>{
this.input1=currentNode}} type="text" id="input1" placeholder="点击按钮提示数据"/>
<button onClick={
this.showDataClick}>点我提示左侧数据</button>
<input ref={
(currentNode)=>{
this.input2=currentNode}} onBlur={
this.showDataBlur} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
// <!--2.渲染虚拟DOM到页面 -->
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
<!-- 小结:
ref={(currentNode)=>{this.input1=currentNode}}:将当前节点挂载到组件实例对象上
-->
</body>
</html>
回调函数的 ref 比较常用。
回调ref的回调执行次数问题
如果 ref 回调函数
是以内联函数
(即在一行内定义的代码)的方式定义的,在更新过程
中它会被执行两次
,第一次传入参数 null
,然后第二次会传入参数 DOM 元素
。
注意是更新过程,即render()
被重新执行渲染
eg:
<!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>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// <!-- 1.创建类式组件 -->
class Demo extends React.Component{
state ={
isHot:true
}
showDataClick = ()=>{
const {
input1} = this
alert(input1.value)
}
changeWeather=()=>{
const {
isHot} = this.state
this.setState({
isHot : !isHot})
}
saveInput=(currentNode)=>{
this.input1 = currentNode;
console.log('@',currentNode);
}
render(){
const {
isHot} = this.state
return (
<div>
<h1 onClick = {
this.changeWeather}>今天天气很{
isHot?'炎热':'凉爽'}</h1>
<input ref={
(currentNode)=>{
this.input1=currentNode ; console.log('@',currentNode);}} type="text" id="input1" placeholder="点击按钮提示数据"/>
<button onClick={
this.showDataClick}>点我提示左侧数据</button>
</div>
)
}
}
// <!--2.渲染虚拟DOM到页面 -->
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
输出:
这是因为,当数据更改重新调用render()的时候,当读取到ref定义的函数,因为不确定前一次调用的参数是否被清空,为了保证参数的干净,ref会先传入一个 null 参数,确保参数被清空 ,之后再传入当前节点进行执行,所以数据更新之后再次渲染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>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// <!-- 1.创建类式组件 -->
class Demo extends React.Component{
state ={
isHot:true
}
showDataClick = ()=>{
const {
input1} = this
alert(input1.value)
}
changeWeather=()=>{
const {
isHot} = this.state
this.setState({
isHot : !isHot})
}
saveInput=(currentNode)=>{
this.input1 = currentNode;
console.log('@',currentNode);
}
render(){
const {
isHot} = this.state
return (
<div>
<h1 onClick = {
this.changeWeather}>今天天气很{
isHot?'炎热':'凉爽'}</h1>
{
/*<input ref={(currentNode)=>{this.input1=currentNode ; console.log('@',currentNode);}} type="text" id="input1" placeholder="点击按钮提示数据"/> */}
<input ref={
this.saveInput} type="text" id="input1" placeholder="点击按钮提示数据"/>
<button onClick={
this.showDataClick}>点我提示左侧数据</button>
</div>
)
}
}
// <!--2.渲染虚拟DOM到页面 -->
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
<!-- 小结:
ref={(currentNode)=>{this.input1=currentNode}}:将当前节点挂载到组件实例对象上
当使用内联的回调函数的ref,在第一次加载的时候会执行一次;当数据修改的时候diaoyongrender会执行两次,第一次是为了清空dom元素,第二次返回标记的ref
通过将ref的回调函数定义成class的绑定函数可以解决此问题
-->
</body>
</html>
createRef
React.createRef
调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的(即一个createRef只能存储一个dom元素)
之后可以再标签中使用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>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// <!-- 1.创建类式组件 -->
class Demo extends React.Component{
// React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的(即一个createRef只能存储一个dom元素)
myRef = React.createRef()
myRef2 = React.createRef()
showDataClick = ()=>{
console.log(this.myRef);
alert(this.myRef.current.value)
}
showDataBlur = ()=>{
const {
input2} = this
alert(this.myRef2.current.value)
}
render(){
return (
<div>
{
/*this.myRef就会将该DOM元素放在上面创建的ref中*/}
<input ref={
this.myRef} type="text" id="input1" placeholder="点击按钮提示数据"/>
<button onClick={
this.showDataClick}>点我提示左侧数据</button>
<input ref={
this.myRef2} onBlur={
this.showDataBlur} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
// <!--2.渲染虚拟DOM到页面 -->
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
<!-- 小结:
myRef = React.createRef()
React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专川用”的(即一个createRef只能存储一个dom元素)
-->
</body>
</html>