React学习01 jsx、组件与组件的三大属性

news/2024/10/7 23:28:51/

文章目录

  • jsx的介绍与语法
      • 1.真实DOM和虚拟DOM
      • 2.jsx语法
  • 模块与模块化,组件与组件化
      • 模块与模块化
      • 组件与组件化
  • React组件
      • React事件绑定
      • 函数式组件
      • 类式组件
      • 组件属性state
      • 组件属性props
      • 组件属性ref

尚硅谷react教程+官方文档学习记录笔记01

jsx的介绍与语法

1.真实DOM和虚拟DOM

虚拟DOM本质是一般对象(Object对象),虚拟DOM的附带属性比真实DOM少,更轻量,虚拟DOM最终会被React转换成真实DOM显示在页面上。

<body><div id="h1">真实DOM</div><!-- ....省略引入react有关js文件,示范虚拟DOM渲染过程--><script type="text/babel">const vdom = <h1> Hello JS <h1/>;  // 虚拟DOMReactDOM.render(vdom,document.getElementById('h1'));  // 将虚拟DOM渲染到页面</script>
</body>

2.jsx语法

jsx语法是react定义的js+XML的语法,本质是React.creatElement(component,props,…children)的语法糖,用来简化和创建虚拟DOM。

// babel转译jsx成React.createElement()函数调用
const element = (<h1 className="greeting">Hello, world!</h1>
);
const element = React.createElement('h1',{className: 'greeting'},'Hello, world!'
);   // 两段代码等效

语法规则一:定义虚拟DOM变量,赋值不写引号

// 例子 创建一个h1标签的虚拟DOM
var element = <h1> Hello JS </h1>    // 最终产生一个JS对象// 创建嵌套标签
var element = (<h1><span> Hello JS </span></h1>)   // ()表示这一段为一个整体

语法规则二:标签中需要混入js表达式时使用{},表达式为可以接在=后的(let x = 表达式)

const data = "Hello JS";
var element = (<h1><span> {data} </span>  <span> {data.toLowerCase()} </span>  </h1>) 
// 使用jsx动态显示列表,页面显示一个列表,有a,b,c三个项,每个li标签必须有唯一的key值,这里为数组的下标
const data = [ 'a','b','c'];
const element = (<div><ul>{data.map((item,index)=>{return <li key ={index}< {item} </li>})}  // 这里的花括号里面是一个表达式</ul></div>
)

语法规则三:样式的类名使用className,内联样式使用style={{key:value}}。

var element1 = <h1 className="title"> Hello JS </h1> 
var element2 = <h1 style={{color:'white', fontSize:'20px'}}> Hello JS </h1>   // 类似font-size由多个词连接的属性改为小驼峰写法

语法规则四:虚拟DOM只能有一个根标签

var h1 = (<h1><span> {data} </span>  <span> {data.toLowerCase()} </span>  </h1>)    // h1为根标签var span = (<span> {data} </span>  <span> {data.toLowerCase()} </span>  )   // 错误,有两个根标签span

语法规则五:标签必须闭合,单标签以“/”结尾或者使用闭合标签

<input />
<input ></input>

语法规则六:标签的首字母规范:
1)以小写字母开头,则转换为html同名标签,若html无对应标签,报错;
2)以大写字母开头,react渲染该组件,若组件未定义,报错;
语法规则七:使用{/* 内容 */}在jsx内写注释。应避免在jsx结构内写注释

var element = (<div> {/* <h1>这是注释掉的标签</h1> */}<h1>这是未注释标签</h1></div> )

模块与模块化,组件与组件化

模块与模块化

模块:提供特定js功能的程序,一般指一个js文件;将代码拆成模块可以简化js,提高js运行效率;
模块化:应用的js都以模块编写

组件与组件化

组件:实现局部功能效果代码的资源集合,包含html,css,js,image,video等,考虑复用性,简化项目编码,提高运行效率;
组件化:应用以多组件的方式实现

React组件

在Chorme里导入ReactDevTool插件;

React事件绑定

react把js原生事件onXXX方法重写了,变成小驼峰写法,比如onclick方法变为onClick方法,使用时需要注意。函数调用时使用{}包含函数名。

<button onclick="activateLasers()"></button>  // 原生写法
<button onClick={activateLasers}></button>  // react写法

函数式组件

定义一个函数,函数名首字母大写,返回虚拟DOM,调用react的render方法时,使用闭合标签<函数名/>作为第一个参数

// 函数组件
<body><div id="test"></div><!--引入react有关js文件--><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">// 创建函数式组件function Demo(){return <h1> Hello JS <h1/>;}// 渲染页面。过程:React解析组件标签,寻找Demo组件,发现组件是函数Demo定义的,调用Demo函数,返回虚拟DOMReactDOM.render(<Demo/>,document.getElementById('test'));  </script>
</body>

类式组件

定义一个类,继承React.Component类,无变量需要传递获取,不写supper方法,必须写render方法,返回虚拟DOM

// 类组件
<body><div id="test"></div><!--....省略引入react有关js文件--><script  type="text/babel">
// 创建类式组件
class Welcome extends React.Component {render() {return <h1>Hello</h1>;}
}// 渲染页面。过程:React解析组件标签,寻找Welcome组件,发现组件是类Welcome定义的,然后new一个Welcome实例对象,通过Welcome实例对象调用Welcome类里的render方法,返回虚拟DOMReactDOM.render(<Welcome/>,document.getElementById('test'));  </script>
</body>

简单组件(h函数组件)与复杂组件(类组件)区别在于有无状态State

组件属性state

状态(state)驱动页面。
类组件对象实例自带state属性,值为null。当需要使用state取值时,借助构造器初始化state为对象,将需要的变量挂在state对象上。

// 初始化并获取state
<body><div id="test"></div><!--....省略引入react有关js文件--><script  type="text/babel">
class Weather extends React.Component {// 定义构造器constructor(props){super(props);this.state = {isHot:false}  // 借助构造器初始化state}render() {return <h1>天气{this.state.isHot ? '热' : '凉'}</h1>;  // 获取state}
}ReactDOM.render(<Weather/>,document.getElementById('test'));  </script>
</body>
// 实现点击事件
class Weather extends React.Component {constructor(props){super(props);this.state = {isHot:false};this.changeWeather = this.changeWeather.bind(this); // 3. bind方法返回一个新函数,绑定在参数this上;这里bind(this)绑定了类的实例对象,这一行右边表示将类的changeWeather方法绑定在实例对象上,左边将绑定的函数取名为changeWeather}render() {return <h1 onClick={this.changeWeather}>天气{this.state.isHot ? '热' : '凉'}</h1>;   // 2.添加点击事件,这里相当于把类方法changeWeather作为onClick的回调,所以当用户点击时,this不是实例对象,且严格模式不能指向window,所以为undefined}// 点击事件changeWeather(){console.log(this);  // 1.类中的方法默认开启局部严格模式,非对象实例调用时,this指向undefined}
}
// 实现点击事件,理解以上注释部分
class Weather extends React.Component {constructor(props){super(props);this.state = {isHot:false};this.change = this.changeWeather.bind(this); }render() {return <h1 onClick={this.change}>天气{this.state.isHot ? '热' : '凉'}</h1>;  }// 点击事件changeWeather(){console.log(this);  }
}

state不能直接更改,更新要借助api setState()方法。

 // 点击事件里更改state里的变量 -- 直接更改changeWeather(){const isHot = this.state.isHot;   // 获取原来的isHot值this.state.isHot = !isHot;   // 点击后,借助开发插件可以看到this.state.isHot值没有变化,页面也无变化,react不允许直接改变状态}// 点击事件里更改state里的变量 -- 借助setState()方法changeWeather(){const i = this.state.isHot;  // 获取原来的isHot值this.setState({isHot:!i}); // 点击后,借助开发插件可以看到this.state.isHot值发生变化,页面也随state改变而改变}

setState方法起到的是一个合并的动作。

// 原state
state = {a:1,b:2,c:0}
// 使用setState()更新
this.setState({a:99});
// 更新后的state为
state = {a:99,b:2,c:0}

精简state写法,避免有多属性和多方法需要绑定。在类组件的类里使用属性名+赋值语句+值/函数。赋值的函数定义必须使用箭头函数。

class Weather extends React.Component {state = {isHot:false} // 往实例对象上挂一个state属性,值为一个对象,里面有isHot属性render() {return <h1 onClick={this.changeWeather}>天气{this.state.isHot ? '热' : '凉'}</h1>;  }changeWeather = ()=>{const i = this.state.isHot;   this.setState({isHot:!i});  } // 往实例对象上挂一个changeWeather属性,值为一个方法,方法里更新state。为什么使用箭头函数不使用function定义?因为箭头函数没有自己的this,箭头函数的this指向上下文的this,也就是实例对象
}

总结
1.state是对象,包含一个或多个key-value的组合,不能直接修改更新。
2.通过setState()方法更新state,从而从新渲染页面。
3.render()方法中的this指向的也是类组件的实例对象。
4.组件自定义方法的this为undefined时,在构造器里使用bind方法或使用赋值语句箭头函数改变this。
5.适用于用户数据交互渲染页面。

组件属性props

类组件对象实例自带props属性,值为{}。在ReactDOM.render方法内使用key="value"的方式传参,jsx内使用this.props.属性的方式接收。

// props的基本使用
class Person extends React.Component{     render(){return (<ul><li>name:{this.props.name}</li><li>sex:{this.props.sex}</li><li>age{this.props.age}</li></ul>)}}
ReactDOM.render(<Person name="tom" age="18" sex="男"/>,document.getElementById('test')); // 这里传递的age为字符串
ReactDOM.render(<Person name="tom" age={18} sex="男"/>,document.getElementById('test')); // 这里传递的age为Number

传递多个props属性时,使用{}+扩展运算符“…”

// 传递多个props属性
class Person extends React.Component{     render(){return (<ul><li>name:{this.props.name}</li><li>sex:{this.props.sex}</li><li>age{this.props.age}</li></ul>)}}// 定义变量,包含需要的属性
const p = {name:"tom",age:"18",sex:"男"};
// 传递时使用{}
ReactDOM.render(<Person {...p}/>,document.getElementById('test'));

对props属性做限制,如参数类型,默认值,必要性

// 写法1--在类的外部
// 对类组件定义一个propTypes属性,属性内规定了props各个属性的类型
类名.propTypes = {属性1: PropTypes.string.isRequired,  // 使用isRequired标定为必要字段属性2: PropTypes.number
}
// 对类组件定义一个defaultProps属性,属性内规定了props内各个属性的默认值
类名.defaultProps = {属性1:'默认值',属性2:'默认值'
}// 写法2--在类的内部
class 类名{static propTypes = {属性1: PropTypes.string.isRequired,  属性2: PropTypes.number}static defaultProps = {属性1:'默认值',属性2:'默认值'}// ...state,render等
}
// ... 增加一个引入文件,以使用对标签属性增加限制的方法
<script type="text/javascript" src="prop-types.js"></script>
<script type="text/babel">
class Person extends React.Component{     render(){return (<ul><li>name:{this.props.name}</li><li>sex:{this.props.sex}</li><li>age{this.props.age}</li></ul>)}}
// 对标签属性进行类型和必要性限制
Person.propTypes = {name: PropTypes.string.isRequired,age:PropTypes.number
}
// 对标签属性指定默认值
Person.defaultProps = {sex:'未知'
}
const p = {name:"tom",age:"18",sex:"男"};
ReactDOM.render(<Person {...p}/>,document.getElementById('test'));
</script>

对类组件传递方法

// 写法--在类的外部
Person.propTypes = {speak: PropTypes.func
}
function speak(){console.log('say something')
}
ReactDOM.render(<Person speak={speak}/>,document.getElementById('test'))

函数式组件使用props,组件三大属性中值由props可以在函数式组件使用。

function Person(props){return (<ul><li>name:{props.name}</li><li>sex:{props.sex}</li><li>age{props.age}</li></ul>)
}
Person.propTypes = {name: PropTypes.string.isRequired,age:PropTypes.number
}
Person.defaultProps = {sex:'未知'
}
ReactDOM.render(<Person  name="tom" age={18} sex="男"/>,document.getElementById('test'))

总结
1.props属性只读,使用this.props.属性=值会报错。
2.组件标签的所有属性都保存在props中,通过标签属性从组件外向组件内传递变化的数据,组件内不要修改props数据。
3.适用于父传子,组件间通信。

组件属性ref

类组件对象实例自带refs属性,值为{}。类似原生html标签的id属性。

<标签 ref="属性名"></标签>  // 在标签内定义
this.refs.属性名   // 使用
// refs基本使用
class Demo extends react.Component{showData = ()=>{console.log(this.refs); // {input1:input,button1:button,input2:input} input1是input标签的ref值,冒号后的input表示的是ref为input1的这一个input标签节点console.log(this.refs.input1); // <input type="text" placeholder=""/>}render(){return (<div><input ref="input1" type="text" placeholder="点击按钮"/><button ref="button1" onClick="{this.showData}">点击提示左侧数据</button><input ref="input2" type="text" placeholder=""/></div>)}
}
ReactDOM.render(<Demo/>,document.getElementById('test'));

以上为refs的字符串写法,ref属性都为字符串类型,会有效率问题。因此有了回调函数形式的ref。

<标签 ref={箭头函数}></标签>  // 在标签内定义,在创建节点时自动调用箭头函数
<标签 ref={(a)=>{this.属性名=a}}></标签>  // 在标签内定义,指定ref属性名
this.属性名   // 使用,因为直接挂在了实例对象上,所以可以直接使用this
// refs回调函数形式,使用内联函数
class Demo extends react.Component{showData = ()=>{console.log(this.input2.value)}render(){return (<div><input ref={(a)=>{console.log(a)}} type="text" placeholder="点击按钮"/>  // 打印<input type="text" placeholder="点击按钮"/><input ref={(a)=>{this.input2=a}} type="text" placeholder=""/> // 获取当前所在的节点(a,也就是input标签),将这个节点挂在组件实例对象上,并命名为input2<button ref="button1" onClick="{this.showData}">点击提示左侧数据</button></div> )}
}
ReactDOM.render(<Demo/>,document.getElementById('test'));

以上是使用内联函数的形式定义ref回调函数,内联函数在组件更新的时候会被执行两次,第一次传入参数null,第二次才传入DOM元素。因为每次调用render方法进行渲染时,都会创建一个新的函数实例,React会清空旧的ref然后设置一个新的,当旧的被清空,无法找到当前节点,所以第一次传入null。

// refs回调函数形式,使用内联函数,组件更新二次执行回调函数问题
class Demo extends react.Component{state = {isHot:'hot'}showData = ()=>{alert(this.input2.value)}showWeather = ()->{const isHot = this.state.isHot;this.setState({isHot:!isHot});}render(){return (<div><input ref={(a)=>{this.input2=a;console.log(a)}} type="text" placeholder=""/> <button ref="button1" onClick="{this.showData}">点击提示左侧数据</button>// 点击提示数据按钮,内联函数调用一次,打印<input  type="text" placeholder=""/><button  onClick="{this.showWeather}">点击提示天气</button>// 点击显示天气按钮,内联函数调用两次,第一次打印null第二次打印<input  type="text" placeholder=""/></div> )}
}
ReactDOM.render(<Demo/>,document.getElementById('test'));

下面类绑定式的ref写法可以避免调用两次的情况,但使用内联函数调用两次的影响无关紧要,可继续使用。

函数名=(a)=>{this.属性名=a}  // 在类组件内定义函数
<标签 ref=this.函数名></标签>  // 在标签内定义
// refs回调函数形式,使用类绑定的形式,解决组件更新时二次执行回调函数问题
class Demo extends react.Component{state = {isHot:'hot'}showData = ()=>{alert(this.input2.value)}showWeather = ()->{const isHot = this.state.isHot;this.setState({isHot:!isHot});}showInput = (a)=>{this.input2=a;console.log(a)}render(){return (<div><input ref={this.showInput} type="text" placeholder=""/> <button ref="button1" onClick="{this.showData}">点击提示左侧数据</button>// 点击提示数据按钮,内联函数调用一次,打印<input  type="text" placeholder=""/><button  onClick="{this.showWeather}">点击提示天气</button>// 点击显示天气按钮,内联函数不会被频繁调用</div> )}
}
ReactDOM.render(<Demo/>,document.getElementById('test'));

React.createRef调用后返回一个容器,容器内存储被ref标识的一个节点,每个被ref标识的节点都有专属自己的容器。

// 在类组件内,挂在类组件的实例对象上
myRef = React.createRef();
// 在虚拟DOM中
<input ref={this.myRef} type="text" />
// React.createRef的基本使用
class Demo extends React.Component{myRef = React.createRef()myRef2 = React.createRef()showRef(){console.log(this.myRef); // {current:input}console.log(this.myRef.current); //<input type="text" />console.log(this.myRef.current.value); // 输入框输入的值}render(){return (<div><input ref={this.myRef} type="text" /><input ref={this.myRef2} type="text" /><button onClick={this.showRef}>点击</button></div>)}
}

http://www.ppmy.cn/news/1535905.html

相关文章

vue的el-button防止重复点击

这样效果仅生效在按钮上

鸿蒙harmonyos next flutter混合开发之开发plugin(获取操作系统版本号)

创建Plugin为my_plugin flutter create --org com.example --templateplugin --platformsandroid,ios,ohos my_plugin 创建Application为my_application flutter create --org com.example my_application flutter_application引用flutter_plugin&#xff0c;在pubspec.yam…

<数据集>工程机械识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;2644张 标注数量(xml文件个数)&#xff1a;2644 标注数量(txt文件个数)&#xff1a;2644 标注类别数&#xff1a;3 标注类别名称&#xff1a;[dump truck, wheel loader, excavators] 序号类别名称图片数框数1dum…

(done) go 语言 hello world

创建一个文件名为 hello.go&#xff0c;内容如下 package mainimport "fmt"func main() {fmt.Println("Hello, World!") }可以用这种方式运行&#xff08;不会生成可执行文件&#xff09;&#xff1a; go run hello.go也可以用先编译出可执行文件&#x…

基于STM32的超声波测距仪设计

引言 本项目将基于STM32微控制器设计一个超声波测距仪&#xff0c;通过超声波传感器实现距离测量&#xff0c;并将结果显示在液晶屏上。该项目展示了STM32微控制器与超声波传感器、LCD显示器的接口通信&#xff0c;以及信号处理和距离计算的过程。 环境准备 1. 硬件设备 ST…

Chromium 中JavaScript Fetch API接口c++代码实现(一)

Fetch API主要暴露了三个接口一个方法。 三个接口 Request(资源请求)Response(请求的响应)Headers(Request/Response头部信息)一个方法 fetch()(获取资源调用的方法更多介绍参考 Fetch API - Web API | MDN (mozilla.org) 一、 来看一段前端代码 <!DOCTYPE html> <h…

AI 大模型的核心能力与应用场景全解析

深入理解 AI 大模型&#xff1a;核心能力与应用场景全解析 AI大模型是什么 通过概念考察的方式了解AI大模型&#xff0c;拆开来看。 AI领域术语丰富&#xff0c;涵盖模式识别、自然语言处理、神经网络、机器学习、深度学习、强化学习及人类反馈强化学习。大模型&#xff1a;把…

昇思学习打卡营第31天|深度解密 CycleGAN 图像风格迁移:从草图到线稿的无缝转化

1. 简介 图像风格迁移是计算机视觉领域中的一个热门研究方向&#xff0c;其中 CycleGAN (循环对抗生成网络) 在无监督领域取得了显著的突破。与传统需要成对训练数据的模型如 Pix2Pix 不同&#xff0c;CycleGAN 不需要严格的成对数据&#xff0c;只需两类图片域数据&#xff0c…