react基础
一.react简介
React 是一个流行的JavaScript库,用于构建用户界面。以下是React框架的主要特点简介:
1.组件化开发:React鼓励将应用程序拆分为小组件,每个组件管理自己的状态和渲染。这种组件化开发方式使得代码更容易理解、维护和重用。
2.虚拟DOM:React引入了虚拟DOM的概念,它是一个轻量级的内存中表示实际DOM的树结构。React使用虚拟DOM来减少对实际DOM的直接操作,提高性能和渲染效率。
3.JSX语法:React使用JSX(JavaScript XML)语法,允许开发者在JavaScript中编写类似HTML的代码,这使得编写组件的界面更直观和易读。
4.跨平台开发:React可以用于构建Web应用、移动应用(使用React Native)以及桌面应用(使用Electron等),这使得开发者可以共享代码和技能。
这些特点使React成为构建现代、高性能用户界面的强大工具之一。
二.react使用
1.react的基本使用
安装react
在终端输入npm i react react-dom
这里安装了两个包,
一个是react,一个是react-dom
react包是核心,提供创建元素,组件等功能。
react-dom包提供DOM相关功能。
引入react和react-dom文件
在html中输入以下代码:
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
创建react元素
使用 React.createElement() 方法
该函数有多个参数,第一个是元素名称,第二个是元素属性,第三个及之后是元素的子节点,该方法可以嵌套。
示例:
const title=React.createElement('h1',null,'hello')
渲染react元素
使用 ReactDOM.render() 方法
该函数有两个参数,第一个是要渲染的react元素,第二个是一个DOM对象,用于指定渲染位置。
示例:
ReactDOM.render(title,document.getElementById('root'));
2.通过脚手架使用react(推荐)
使用React脚手架初始化项目
在终端输入 npx create-react-app my-app (my-app可替换为不含大写字母的其它项目名)。
显示 Happy hacking! 时成功。
ps:用npx不用提前全局安装脚手架。
启动项目
在根目录终端输入 npm start。
导入react和react-dom两个包
在src下的index.js并输入:
import React from 'react';
import ReactDOM from 'react-dom';
启动项目
在根目录终端输入 npm start。
创建react元素
使用 React.createElement() 方法
该函数有多个参数,第一个是元素名称,第二个是元素属性,第三个及之后是元素的子节点,该方法可以嵌套。
示例:
const title=React.createElement('h1',null,'hello')
渲染react元素
使用 ReactDOM.render() 方法
该函数有两个参数,第一个是要渲染的react元素,第二个是一个DOM对象,用于指定渲染位置。
示例:
ReactDOM.render(title,document.getElementById('root'));
三.jsx
1.jsx的基本使用与要点
基本使用
jsx与html语法极其相似,主要用于替代前面的createElement()方法,但建议用括号包裹住jsx代码。
const title= (<h1>hello</h1>) 等同于 const title=React.createElement('h1',null,'hello')
注意点
1.React元素的属性名使用驼峰命名法。
2.特殊属性名:class -> className , for -> htmlFor , tabindex -> tabIndex.
3.没有子节点的React元素可以直接用 ‘/>’ 结束
eg:
<span/>
2.jsx中插入js表达式
原始jsx:
const dv=(
<div>我叫小明</div>
)
嵌入:
const name='小明'
const dv=(
<div>我叫{name}</div>
)
注意点
1.单大括号中可以嵌入几乎任意js表达式,如:三元表达式,函数调用表达式,jsx自身等等。
2.js中的对象是一个例外,一般只会出现在style属性中。
3.单大括号中不可以使用语句,如for,if等。
3.jsx的条件与列表渲染
(1)jsx的条件渲染
根据条件渲染特定的jsx代码。
eg:
let isLoading=true
const loadData=()=>{
if(isLoading){
return (<div>loading...</div>)
}
else
return (<div>加载完成</div>)
}//还可以使用三元运算符实现
(2)jsx的列表渲染
根据数组的map()方法渲染jsx代码。
eg:
const songs =[
{id:1, name:'abc'},
{id:2, name:'def'},
{id:3, name:'ghi'}
]
const list = (
<ul>
{songs.map(item => <li key={item.id}>{item.name}</li>)}//key属性要加给遍历的元素
</ul>
)
4.jsx的样式处理
(1)通过style处理样式
<h1 style={{color: 'red', backgroundColor: 'skyblue' }} >
JSX的样式处理
</h1>
(1)通过css处理样式
css:
.title{
color: 'red'
}
javascript:
import './css/index.css'
<h1 className="title" >
JSX的样式处理
</h1>
四.react组件基础
1.组件的概念
组件
组件表示页面中的部分功能,组合多个组件实现完整的页面功能。
状态
状态(state)即数据。函数组件又叫无状态组件,类组件又叫有状态组件,
2.组件的创建与种类
(1)使用函数创建组件(无状态)
创建
- 约定1:函数名称必须由大写字母开头。
- 约定2:函数组件必须有返回值,若不渲染任何东西也得返回null。
如下:
function Hello(){
return (
<div>Hello</div>
)
}
//箭头函数也可创建组件:
const Hello = () => <div>Hello</div>
渲染
- 用函数名作为标签名
- 组件标签可为单标签也可为双标签
React.DOM.render(<Hello/>,root)
无状态组件
函数组件没有自己的状态,只负责数据展示(静)。
(2)使用类创建组件(有状态)
创建
- 约定1:类名称必须由大写字母开头。
- 约定2:类组件必须继承React.Component父类,从而可以使用父类中提供的方法或属性。
- 约定3:类组件必须提供render()方法。
- 约定4:render方法必须有返回值,若不渲染任何东西也得返回null。
如下:
class Hello extends React.Component{
render(){
return <div>Hello</div>
}
}
渲染
与函数组件一致
有状态组件
类组件有自己的状态,负责更新UI,使页面动起来(动)。
(3)抽离为独立JS文件
【1】创建js文件
【2】在js文件中导入React
import React form 'react'
【3】创建组件
class Hello extends React.Component{
render(){
return <div>Hello</div>
}
}
【4】导出该组件
export default Hello
【5】调用该组件文件
import Hello from './Hello'
整体如下:
Hello.js:
import React form 'react'
class Hello extends React.Component{
render(){
return <div>Hello</div>
}
}
export default Hello
index.js:
import Hello from './Hello'
3.组件中的事件
(1)事件绑定
- 语法与DOM事件语法相似
- 语法:on+事件名称={事件处理程序},比如:onClick={()=>{}}(驼峰命名)。
类组件:
class Hello extends React.Component{
handleClick(){
console.log('单击事件触发了')
}
render(){
return <button onClick={this.handClick} >click</button>
}
}
函数组件:
function Hello(){
function handleClick(){
console.log('单击事件触发了')
}
return <button onClick={handClick} >click</button>//没有this即可调用函数
}
(2)事件对象
- 通过事件处理函数的参数获取到事件对象(与dom一样)
(3)事件绑定this指向(从JSX中抽离事件处理程序)
【1】箭头函数
class Hello extends React.Component{
state = {
count:0
}
add(){
this.setState({//这里的this指向调用函数的this
count:this.state.count+1//点击使count加一
})
}
render(){
return <button onClick={()=>this.add()} >click</button>//箭头函数中的this指向的是外部
}
}
【2】bind方法
使用Function.prototype.bind() ,bind()方法会创建一个新函数,称为绑定函数.当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind()方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.
class Hello extends React.Component{
constructor(){
super()
this.add=this.add.bind(this)//constructor里的this指向的是组件实例,这里将绑定函数中的'this'与组件实例绑定在一起了
this.state = {
count:0
}
}
add(){
this.setState({//这里的this指向的是组件实例
count:this.state.count+1//点击使count加一
})
}
render(){
return <button onClick={this.add()} >click</button>
}
}
【3】class的实例方法(黑马推荐)
该语法是实验性语法,由于脚手架中babel的存在可以直接使用。
class Hello extends React.Component{
state = {
count:0
}
add = () => {
this.setState({//箭头函数中的this指向的是外部
count:this.state.count+1//点击使count加一
})
}
render(){
return <button onClick={this.add()} >click</button>//箭头函数中的this指向的是外部
}
}
4.组件中的state(类组件才有)
(1)state的基本使用
- state是组件内部的私有数据,只能在组件内部使用。
- state的值是对象,表示一个组件可以有多个数据。
class Hello extends React.Component{
/*原语法constructor(){
super()
this.state = {
count: 0
}
}*/
//简化语法
state = {
count: 0
}
render(){
return <div>Hello</div>
}
}
(2)state的获取
使用this.state.
class Hello extends React.Component{
state = {
sentence: 'Hello'
}
render(){
return <div>{this.state.sentence}</div>
}
}
(3)setState()修改state
- 语法:this.setState({要修改的state})。
- setState作用:1.修改state 2.更新UI
- 不能直接修改state中的值。
class Hello extends React.Component{
state = {
count:0
}
render(){
return <button onClick={()=>{
this.setState({
count:this.state.count+1//点击使count加一
})
}} >click</button>
}
}
5.组件中的表单处理
(1)受控组件概念
受控组件
- HTML中的表单元素是可输入的,有自己的可变状态(输入的内容),相当于自己有个独立的state部分存储状态。
- 但,React中的状态都是保存在state中,只能由setState()修改,与前者矛盾。
- 解决方法:React将state与表单元素值value绑定到了一起,由state的值来控制表单元素的值。
- 受控组件就是状态全交给React中的state控制的表单元素。
(2)受控组件使用
步骤
- 在state中添加一个状态,作为表单元素的value值或checked值(控制表单元素值的来源)。
- 给表单元素绑定change事件,将表单元素的值设置为state的值(控制表单元素值的变化)。
class Hello extends React.Component{
state = {
count:0
isChecked:false
}
inputChange = e => this.setState({count:e.target.value}
checkChange = e => this.setState({count:e.target.checked}
render(){
return <input type="text" value={this.state.count} //除复选框以外的其它表单元素也是这样做的
onChange={this.inputChange}/>
<input type="checkbox" checked={this.state.isChecked} onChange={this.checkChange}/>
}
}
多表单元素优化
【1】给表单元素添加name属性,名称与state相同。
【2】根据表单元素类型获取对应值。
【3】在change事件处理程序中通过[name]来修改对应的state。
class Hello extends React.Component{
state = {
txt:0
isChecked:false
}
formChange = e => {
//获取当前DOM对象
const target=e.target
//根据类型获取值
const value=target.type === 'checkbox'?
target.checked
:target.value
this.setState({[name]:value}
}
render(){
return <input type="text" name="txt" value={this.state.txt}
onChange={this.formChange}/>
<input type="checkbox" name="isChecked" checked={this.state.isChecked} onChange={this.formChange}/>
}
}
(3)非受控组件使用
class Hello extends React.Component{
constructor(){
super()
//1.创建ref对象
this.txtRef=React.createRef()
this.state = {
count:0
}
}
render(){
return (<input type="text" ref={this.txtRef}/>//2.将创建的ref对象添加到文本框中
<button onClick{this.getTxt} >获取文本框的值<button>
)
}
//3.获取文本框的值
getTxt=()=>{
console.log('文本框为:',this.txtRef.current.value)
}
}
五.组件通讯
1.概念
组件通讯是指在一个应用程序中不同组件之间共享数据、信息或状态的过程。在前端开发中,通常使用组件通讯来实现不同部分之间的协作和数据传递。
2.组件的props使用
- 组件是封闭的,要接收外部数据应该通过props来实现。
- props的作用:props是一个接受传递给组件数据的对象。
- 传递数据:给组件标签添加属性。
- 接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据。
函数组件:
//1.接收数据
function Hello(props){
return (
<div>props:{props.name}</div>
)
}
//1.传递数据
ReactDOM.render(<Hello name="jack" age={19} />,document.getElementById('root'))
类组件:
class Hello extends React.Component{
render(){
return (
<div>props:{this.props.name}</div>
)
}
}
//1.传递数据
ReactDOM.render(<Hello name="jack" age={19} />,document.getElementById('root'))
3.props的特点
- props可以传递任意类型的数据,如数组,函数,JSX等。
- props是只读的对象,只能读取属性的值,无法修改。
- 使用类组件时,如果写了构造函数,应该将props传递给super(),否则无法在构造函数(constructor(){})中获取到props。
class Hello extends React.Component{
constructor(props){//传递props
super(props)//传递props
this.txtRef=React.createRef()
this.state = {
count:0
}
}
render(){
return (
<div>props:{this.props.name}</div>
)
}
}
ReactDOM.render(<Hello name="jack" age={19} />,document.getElementById('root'))
3.简单关系组件间通讯的三种方式
1.父传子方式
通过 props 将父组件的数据传递给子组件
大致步骤:
- 父组件提供要传递的 state 数据
- 给子组件标签添加属性,值为 state 中的数据
- 子组件中通过 props 接收父组件中传递的数据
具体代码:
(1).父组件提供要传递的 state 数据
class Parent extends React.Component {
state = {
money: 10000,
};
render() {
return (
<div>
<h1>父组件:{this.state.money}</h1>
</div>
);
}
}
(2).给子组件标签添加属性,值为 state 中的数据
class Parent extends React.Component {
state = {
money: 10000
}
render() {
return (
<div>
<h1>父组件:{this.state.money}</h1>
+ <Child money={this.state.money} />
</div>
)
}
}
(3).子组件中通过 props 接收父组件中传递的数据
function Child(props) {
return (
<div>
<h3>子组件:{props.money}</h3>
</div>
);
}
2.子传父方式
通过 props 将子组件的数据传递给父组件
大致步骤:
- 父组件提供回调函数,通过 props 传递给子组件
- 子组件调用 props 中的回调函数,函数可传参
- 父组件函数的参数就是子组件传递的数据
具体代码:
1.父组件
class Parent extends React.Component {
state = {
money: 10000,
};
// 回调函数
buyPhone = (price) => {
this.setState({
money: this.state.money - price,
});
};
render() {
const { money } = this.state;
return (
<div>
<h1>父组件:{money}</h1>
<Child money={money} buyPhone={this.buyPhone} />
</div>
);
}
}
2.子组件
const Child = (props) => {
const handleClick = () => {
// 子组件调用父组件传递过来的回调函数
props.buyPhone(5000);
};
return (
<div>
<h3>子组件:{props.money}</h3>
<button onClick={handleClick}>买手机</button>
</div>
);
};
2.兄弟组件通讯
通过状态提升思想完成兄弟组件数据通讯
状态提升思想(先子传父,再父传子):
将共享状态 提升到最近的公共父组件 (子传父)中,由公共父组件管理这个状态和修改状态的方法
需要通讯的组件通过 props 接收状态和函数(父传子)即可。
2.参考代码
index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
// 导入两个子组件
import Jack from './Jack';
import Rose from './Rose';
// App 是父组件
class App extends Component {
// 1. 状态提升到父组件
state = {
msg: '',
};
changeMsg = (msg) => {
this.setState({ msg });
};
render() {
return (
<div>
<h1>我是App组件</h1>
{/* 兄弟组件 1 */}
<Jack changeMsg={this.changeMsg}></Jack>//(子传父)
{/* 兄弟组件 2 */}
<Rose msg={this.state.msg}></Rose>//(父传子)
</div>
);
}
}
// 渲染组件
ReactDOM.render(<App />, document.getElementById('root'));
Jack.js
import React, { Component } from 'react';
export default class Jack extends Component {
say = () => {
// 修改数据
this.props.changeMsg('you jump i look');
};
render() {
return (
<div>
<h3>我是Jack组件</h3>
<button onClick={this.say}>说</button>
</div>
);
}
}
Rose.jsx
import React, { Component } from 'react';
export default class Rose extends Component {
render() {
return (
<div>
<h3>我是Rose组件-{this.props.msg}</h3>
</div>
);
}
}
4.跨组件的组件间通讯(Context)
跨组件概念
组件间相隔多层,理解成叔侄,甚至更远的亲戚。
Context使用
调用creatContext()创建一个上下文对象,包含:Provider,Consumer 两个组件。
Provider包裹确定上下文生效范围,通过Consumer来消费数据。
大致步骤:
- 使用creatContext()创建一个上下文对象,包含:Provider Consumer 组件。
- 使用 Provider 包裹组件,value 属性注入状态,函数,被包裹组件下的任何组件可以使用。
- 使用 Consumer 消费 Provider 提供的数据和函数,语法{value=>使用数据和函数}
代码示范:
index.jsx
import React, { Component, createContext } from 'react'
import Parent from './Parent'
// 1. 创建上下文对象
export const {Provider,Consumer} = createContext()
export default class App extends Component {
state = {
money: 10000
}
updateMoney = newMoney => {
this.setState({
money: newMoney
})
}
render() {
return (
// 2. Provider包裹确定上下文生效范围,value注入范围内可用的数据
<Provider value={{
money: this.state.money,
updateMoney: this.updateMoney
}}>
<div className="app">
<h1>根组件:{this.state.money}</h1>
<hr />
<Parent />
</div>
<Provider>
)
}
}
Parent.jsx
import Child from './Child';
const Parent = () => {
return (
<div className="parent">
<h3>父组件:</h3>
<hr />
<Child />
</div>
);
};
export default Parent;
Child.jsx
import { MyContext } from './App'
const Child = () => {
return (
// 3. 通过Consumer来消费数据,(value)=>{ 这里使用数据 }
<Consumer>
{(value) => (//参数接收传值
<div className="child">
<h5>子组件:{value.money} <button onClick={()=>value.updateMoney(5000)}>修改money</button></h5>
</div>
)}
</Consumer>
);
};
export default Child;
5.props深入
children属性
- children属性:表示组件标签的子节点,当组件标签有子节点时,props就会有该属性。
- children属性和props一样,值可以是任意值(文本,React元素,组件,甚至是函数)。
function Hello(props){
return (
<div>
子节点:{props.children}
</div>
)
}
ReactDOM.render(<Hello>
<p>achdcbjhadsbc</p>
</Hello>,document.getElementById('root'))
props校验
-
调用组件用props传值时,可能因为传值方和组件内部变量类型不一致而报错,且不知原因。
-
props校验:允许在创建组件的时候,就指定props的类型,格式等,于是在使用组件时能捕获因为props导致的错误,给出明确的错误提示。
使用步骤: -
安装包prop-types(指令: npm i props-types)
-
导入pro-types包
-
使用组件名.propTypes={}来给组件的props添加校验规则。
-
校验规则通过PropTypes对象来指定。
import PropTypes from 'prop-types'
function App(props){
return (
<h1>Hi,{props.name}</h1>
)
}
App.propTypes={
name:PropTypes.array
}
ReactDOM.render(<App name={['jack','bob']}/>,document.getElementById('root'))
约束规则
所有约束规则
props默认值
- 应用场景:分页组件->每页显示条数(在未输入某个数据时采用默认值)
function App(props){
return (
<h1>默认值:{props.pagesize}</h1>
)
}
App.defaultprops={//设定默认值
pagesize:10
}
ReactDOM.render(<App name={['jack','bob']}/>,document.getElementById('root'))