本文旨在记录react的基础内容,帮助有需要的同学快速上手,需要进一步了解描述更加稳妥和全面的信息,请查阅官方文档
官方文档点击这里进行跳转
vue,react,angular这几种主流前端框架使用频率较高…本质还是js库。
React.js是一个用于构建用户界面的JavaScript库。它由Facebook开发并开源,被广泛应用于单页应用程序和移动应用开发中。
React采用组件化的开发方式,将用户界面拆分成独立的可重用组件。每个组件都有自己的状态(state)和属性(props),可以根据这些状态和属性来渲染出相应的UI。React使用虚拟DOM(Virtual DOM)来高效地更新和渲染界面,通过对比前后两个虚拟DOM的差异,只更新需要改变的部分,提高了性能。
React的核心思想是声明式编程,开发者只需关注界面应该是什么样子的,而不需要关心具体的更新过程。React还提供了丰富的生命周期方法和钩子函数,可以在组件不同的生命周期阶段执行相应的操作,方便开发者进行状态管理、数据处理和交互逻辑的处理。
React还具有良好的可扩展性和可组合性,可以与其他库和框架(如Redux、React Router等)无缝集成,方便构建复杂的应用程序。
使用脚手架创建(create react app),打开某个你能记得住具体位置的文件夹,然后在这个文件夹下面使用指令创建react项目
npx create-react-app react-basic(项目名称)
(注意一个问题,使用脚手架创建react项目的时候,不允许使用驼峰命名法,只允许使用小写字母+连字符链接)
创建成功后则会提示,并且产生一个命名好的文件夹,这就是所有的react文件的所在地点
接下来启动可以使用指令:
yarn start 或者 npm start
等待大约30秒以后,出现这个web界面即为成功,这个时刻react服务会占据你的3000端口(默认)
其中,入口文件为index.js,内部结构如下,和vue中挂载的原理很接近
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
//获取跟组件dom元素,并且根据这个元素创建节点
const root = ReactDOM.createRoot(document.getElementById('root'));
//然后把渲染挂载到这个dom节点上
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
jsx就是JavaScriptHTML,在js中去书写html标签,文件名后缀可以是js文件,也可以是jsx文件
//举个例子
function App() {
return (
);
}
优势:语法类似html,可以使用js的可编程性创建html,综合了以上两种的优点
注意:jsx是js语法的扩展,本身浏览器是不理解jsx语法的,在底层中react中有一个jsx的包帮助转化成js语法
注意这些案例只是一些实际应用到jsx的情况,不是什么官方语法!可以简单学着写一写,理解为主
和模板语法很像 大致写法就是 {js的表达式} ,注意这里是表达式!
表达式:说白了表达式的意思就是数值,最简单的判断方法就是看能不能输出到控制台
比如我们想要实现一个数组的时候,可以通过map在遍历过程中创建标签,举个例子差不多是这样的
总结起来就是在js中写html自由度较高,但是想在html标签中写js表达式,要在中括号里写
并且这个规律是可以嵌套的
如何判断是不是表达式:能return,或者说能打印的
另外在jsx语法中,可以作为数值的东西,包括html标签捏
或者说选择性渲染,可以使用三元运算符和逻辑运算符,或者像这样手写if
例如这个列表中,我们对于id大于15的人进行显示
总之就是在html标签内部写js表达式的时候要加上{}
或者说满足另一个需求,符合条件才渲染(这个时候想要显示的html标签可以使用null来代替)
对于一些比较复杂,而且方向比较多的分支,建议将其抽象出来作为一个方法
在实际应用中,为了防止某些标签发生响应不及时,或者无法动态更新自己的属性的时候,我们还有一种肥肠丑陋但是有效的写法
{ isShow && 利用前面的布尔数值来判断这个是否显示}
使用jsp语法可以对style实现样式的处理…不过这个玩意好像在js里也是有的
样式一共有两种控制方法:内联控制,以及第二种使用类名控制
很简单,并且第一种的第一个{}内部可以换成一个符合style规范的对象
另外类名可以动态控制,也可以有多个
是不被允许的,必须有一个总领的标签元素
组件化开发在vue中也是经常使用的,每个组件都是独立的,并且组件之间也可以进行通信,有利于复用和维护.
函数组件的创建和渲染
函数类组件的创建其实就是一个函数,返回值为一个div标签
function Hello(){
return hello
}
函数式组件的使用也是直接在父组件中使用
function App() {
return (
);}
类组件的创建和渲染
类组件的创建比较模板化,首先是继承react.component,然后是render必须有返回值
class HelloComponent extends React.Component{
render(){
return <div>hello this is a classComponent</div>
}
}
on+(对应的事件) = {触发的函数}
以下是一个比较标准的类组件中绑定事件的写法
class HelloComponent extends React.Component{
f1=()=>{
console.log("回调函数已经启动")
}
render(){
return <div onClick={this.f1}>hello this is a classComponent</div>
}
}
想要获取事件对象本身,例如在上面这个地方获取到"点击事件"的具体信息
只需要在事件绑定的回调函数里面,加上参数e
f1=( e )=>{
console.log("事件的具体信息为",e)
}
render(){
return hello this is a classComponent
}
按照上面的例子修改,则事件具体信息如下
得到事件以后,我们可以进行一些操作**:比如阻止默认跳转,阻止冒泡,懒加载等等**
有些时候想要传入的是参数,或者说想要传入事件和参数两种argument,就用回调函数先把参数是一个事件这种事情,确定下来
f1=(e,num)=>{
console.log("事件为:",e,"传入的参数为",num)
}
render(){
return this.f1(e,12)}>hello this is a classComponent
}
/*解释一下为什么这样子写
首先是使用回调函数的形式,是为了第一步先行接收事件对象,后面即使在传入参数的时候,也不会把这个当成普通参数看待
如果是一开始就一同接收,只会当成一个普通的参数*/
状态的定义其实很简单,就是一个在类组件中,名为state的对象
在vue里面,我们可以对某个固定属性绑定v-model标签,这个标签可以让data和数据产生双向的响应的动态绑定.并且vue本身主打就是响应式.但是react有所不同,react的响应式效果没有vue那么强大,但是更加专一.
写在状态中的数据,是可以被响应式检测和监听的
在这个state中定义的所有属性,都是一个状态,其实这部分很类似vue中的data
组件内部数据的修改用到的方法比较粗暴,不允许我们直接去this.state.name=''xxxxx’这样修改
但是我们可以修改整个state(锁喉)
使用整个setState(对象)函数,在参数中填写一个新的state对象
(不然你以为它为什么叫setState…)
* 一些需要注意的问题
1.编写组件其实就是编写一个原生类/函数,定义状态必须使用state整个固定名称
2.修改state中的任何属性都不能通过直接赋值,只能通过setState方法,这个方法来自于继承
3.以及一些关于this的问题
//对象调用自己的方法,this就是当前的对象
//单独的全局函数调用,指向的是undefined(前提是严格模式,非严格模式(sloppy)下为自身)
//箭头函数没有自己的this,指向的是父上下文的this
//作为监听器的回调函数,指向的是监听的dom元素
4.一个老版本问题
以前的话,如果调用函数的时候不加上括号(),会让函数无法获取绑定到this属性
但是es6版本后更新了这个毛病,使用箭头函数可以自动绑定this
一些修改的小技巧:解构语法
目标:使用受控组件获取表单的数值
方法:受控组件,非受控组件
手动实现类似v-model双向数据绑定的操作
{//react中没有类似的语法糖,只能手动实现
const str=document.querySelector('.inputLog').value
this.setState({
name:this.state.name,
valueOfBlock:str
})
} }/>
这种是稍微原始一点的操作(一看就是刚学完es6)
首先获取到当前dom元素的value数值,然后根据这个value数值修改状态(这也是v-model的使用方法)
则是数值不被state所控制,直接通过dom元素进行控制
组件之间的通信可以分为以下情况和处理方法,由于函数和类组件的不同,可能还有不同的处理方式
父亲向子元素传递数据的方法和vue有些相似,但是原理8一样
class Father extends React.Component{
state={ name:'this is father`s data' } 父组件数据
render() {
return ( 父组件中调用子组件
)
}
}
class SonOfClass extends React.Component{ 类子组件
render() {
return (
类组件接收数据的方法为{this.props.msg} 类子组件接收
)
}
}
function SonOfFunction(props) { 函数子组件
return (
函数组件接收数据的方法为{props.msg} 函数子组件接收(注意上面还有个函数)
)
}
子传父通信的原理为:子组件调用父组件传递过来的函数,并且把想要传递的数据当成函数的实参传入
父组件仍然是通过给子组件上标签的方法,给子组件传递一个函数对象
//父组件中的情况
met1=(num)=>{
console.log('子组件传递过来的消息为',num)//打印所有的方法
}
render() {
return (
//传入方法仍然是可以自定义名称的
)
}
子组件可能会遇到一些问题,下面直接给出两种常用写法,再说为什么要这样
子组件调用的时候传入一些数据,然后父组件中就可以利用`这些数据修改点东西了
这里解释这两个button为什么要这样子写,因为我们要传入参数,对一个函数传入参数这种表达已经不符合"js表达式"
我们只有让返回值是一个传入参数的函数
兄弟组件的通信,综合了以上两种方式,使用一个通过的父组件
A传递到Father组件,然后再由Father组件传递给B
这里不加解释了
不是兄弟也不是父子的时候,通信方式就很抽象了
其实也不是很抽象,用到Provider和Consumer两个对象,这两个对象是在Context对象下的
第一步,先创建Context,然后从里面得到两个对象Provider和Consumer
(为了省点力气,这里我们直接使用解构语法)
(注意这里的defaultValue是默认数值,没啥影响)
通过解构语法直接得到传输对象
(注意Consumer不用包裹任何东西, 按照这个,里面写个jsx语法包裹回调函数就行力
这种传递数据的方法其实是父传子的延申,爷传孙罢
首先注意,只有类组件由生命周期这个东西,虽然函数组件也存在更新和副作用的机制,但是本质上还是不一样的
组件的声明周期一共包括几个概念:
有一张图画的挺清晰明白的(模仿着画的)
每个阶段都是由上至下进行经历的,在此期间会触发钩子函数方便访问
挂载阶段:
constructor:创建组件的时候最先执行,初始化state,创建ref,使用bind绑定什么的
(但是前两种是过时写法了,现在不怎么用)
render:每次组件渲染(包括初次创建和每次更新)都会触发,负责渲染ui(这里不能用this)
componentDidMount:组件挂载并且完成渲染以后执行一次,这时候可以进行一些网络请求和dom操作
更新阶段
render:同上
componentDidUpdate:更新结束触发,可以直接获取dom元素
卸载阶段
componentWillUnMount:8用解释了吧
注意两点:
钩子函数的使用方法举例:
class Life extends React.Component{
constructor() {
super();
console.log("construct")
}
componentDidMount() {
console.log("挂载结束")
}
render() {
return (
<div>
</div>
)
}
}
hook的本质也是一系列钩子函数,这些工资函数让函数组件也拥有了state
hook这个i能在函数组件中使用,解决了复用和class过于笨重的问题
该钩子函数的返回值为一个数组,数组的第一项和第二项,分别为数据和修改数据的方法
该函数输入参数为这个数据的初始数值
每次使用这个函数,都可以得到一个数据和对应的方法,允许多次调用获得多个state属性
const [count,setCount]=useState(0) //直接使用解构语法获得前面两项
//count:数据状态
//setCount(新数据):整体修改数据
这其中,setCount的本质和前面类组件的setState原理是一样的,都是用新数值替换旧数值,所以在需要一些复杂逻辑的时候,可以这样子写
//对于setCount(姑且这么称呼)这种函数,有一种情况为用回调函数作为参数
setCount( ()=>{
//编写运算逻辑.........
return //但是最后还是要返回东西
})
//这样子帮助我们解决了一些复杂问题,其实本质还没变....
关于具体的使用
function TestHook_useStated(){
//每一个state属性都要重新创建
const [count,setCount]=useState(0)
const [count2,setCount2]=useState(100)
const [count3,setCount3]=useState({age:12,name:'张三'})
//得到的修改数值的函数,参数就是新的state,本质和setState一样,用新数替换旧数值
return (
<>
<div onClick={()=>setCount(count+1)}>{count}</div>
<div onClick={()=>setCount2(count2-1)}>{count2}</div>
<div onClick={()=>setCount3({age:count3.age+1,name:'李四'})}>{count3.age},{count3.name}</div>
</>
)
}
首先,有必要说明一下,什么是副作用
副作用:对于react来说,主作用就是根据内部数据渲染ui,除了主作用,剩下都是副作用,
比如dom操作,发送ajax请求,以及一些webapi的调用
useEffect就是干这个用的,把副作用放进这里面,维护函数的纯净
useEffect函数的执行时机是可以控制的,在默认条件下,执行是发生在任何一次更新(包括构建)
//但是可以通过依赖项目,控制时机
//useEffect有两个参数,一个参数为回调函数,另一个参数为监听数组
//1.默认情况下不加整个参数,则每次更新/重构都会执行 useEffect(()=>{})
//2.空数组,代表只执行一次 useEffect(()=>{},[])
//3,数组中的监听元素如果发生变化,则执行一次+初始化一次 useEffect(()=>{},[A,B,C])
useEffect对于一些副作用来说,是要进行消除的,函数内部提供了析构的方法,就是返回一个回调函数
useEffect(()=>{
return ()=>{
//关于清理的逻辑
}
},[]) //这种就是只有初始化的时候执行一次的情况
关于具体的使用案例如下
function TestHook_useEffect(){
useEffect(()=>{
console.log("修改")
document.querySelector('title').innerHTML=1;
return ()=>{
//关于清理的逻辑
}
},[]) //这种就是只有初始化的时候执行一次的情况
return(
console.log("修改")}>
点我测试
)
}
首先路由不是原生react中的内容,和vuex,redux一样,需要另外单独安装
(另外注意,v6版本是router的一个分水岭,我这里写的都是基于router6的内容)
下载指令为
yarn add react-router-dom@6
然后导入几个最重要的包
import {BrowserRouter, Link, Routes, Route} from 'react-router-dom'
就可以放心使用了
路由最重要的组件为四个
Router:最根本的展示区域,所有和路由相关的东西都应该包裹在内部
Link: 跳转路由的位置,需要在内部的to属性中写明将要跳转的地址,会在最后的h5界面中被变化为a标签
Routes:这个名字怪怪的,叫RouteContainer更好一些…
Route:路由组件,里面的path属性为开放端口,element属性则为具体的组件
路由会根据条件,从中跳转到合适的路由组件并且显示
举个例子:
首页
关于
}/>
}/>
路由传递参数有两种格式
第一种,使用Params传递
在父组件中,url中给传递数据的格式为如下格式
to中直接传递参数,path中使用占位符的方式获取参数
首页
}/>
子组件中,使用useParams获取一个对象
```
const params=useParams()
return(
输出结果为:{params.id}
)
```
第二种,使用SearchParams传递
在父组件中,url中给传递数据的格式为如下格式
to中通过url的get格式传递参数
首页
子组件中,使用useParams获取一个数组,数组的第一个是一个解构类似map的对象
const [params]=useSearchParams()
return(
输出结果为:{params.get("id")}
)
至于嵌套路由,这里直接举个例子
import { BrowserRouter as Router, Link, Route } from 'react-router-dom';
function ParentComponent() {
return (
父级组件内容
子路由1
子路由2
{/* 子级路由规则 */}
);
}
function ChildComponent1() {
return 子组件1内容
;
}
function ChildComponent2() {
return 子组件2内容
;
}
function App() {
return (
{/* 路由规则和组件的对应关系 */}
);
}
编程式导航,其实就是我们尝试使用代码逻辑去控制路由的跳转,而不是link
使用编程导航,主要一下两步骤
举个例子
const navigage=useNavigate()
const goPage=function (){
navigage('/page',{replace:true})
}
return(
)
//跳转函数里面自带两个参数
//里面两个参数,第一个参数要跳转到的路由地址
//第二个参数为一些配置信息的对象,比如replace:true 为不加入历史路径
//注意以上实现只能在组件中使用,换句话说就是在这个标签里面(就是哪两种核心组件)
//所以一般引入一个新的组件,比如routerLink(自己随便去起个名字吧)用来管理固有的信息和逻辑
//{最好在根组件外部创建}
#补充:关于状态管理工具
redux:集中状态管理,类似Java中的vuex
redux的本体其实是一个状态管理工具,js的状态容器,在react中,通常用来存储一些通用的,需要响应化的属性
npm install --save redux
绑定react
npm install --save react-redux
创建开发者工具
store是根据reducer方法创建的
集中状态管理最终抽象一下可以理解为这样子
redux本身支持很多东西,包括原生js,redux官方出品,专门用于react的绑定库,方便一些操作
包裹在根组件上(注意一定是根组件上),并且在这里往下传递一个store属性,让下面都能访问到store
const store=react.createStore()
root.render(
);
这样传递一个很显著的特点是,后面的组件都不需要再手动调用store来进行dispatch或者和getState
并且结合后面的两个方法,可以让发送action的方法,和state都整合进props里面
mapStateToProps(state,ownProps),监听store是否被修改,并且把store中的state绑定到props中
mapDispatchToProps(dispatch,ownProps),把action绑定到props中
(上面两个函数的第二个参数可以不用写,因为把你需要的东西加入返回值里面,就会自动绑定到你的props中)
mapDispatchToProps:作用是将Action的构建方法以返回值的形式封装进props中,举个例子,主要用在发出修改的主页上面
//使用了这个绑定该方法以后,就会把你需要的东西绑定进props中
const misDispatchToProps=(dispatch)=>{
return{
sentAct:()=>{ 发送action对象的方法
dispatch({ 由于在根组件上已经提供了一个store,所以这里直接可以使用dispatch
type:'send',
value:a
})
a++
}
}
}
//首页组件
class ComA extends React.Component{
handleClick=()=>{
//发送action
this.props.sentAct();
}
render(){
return(
)
}
}
//增强这个对象
export default connect(null,misDispatchToProps)(ComA)
mapStateToProps:**作用是将Store中更新的state接受下来,**并且以返回值的形式保存在props中,主要用在接收组件上面
//会把接收到的,被修改以后的state保存在props中
const mapStateToProps=(state)=>{
console.log('comb',state)
return state
}
//首页组件
class Comb extends React.Component{
sta=this.props
render() {
return(
显示数据{this.props.value}
)
}
}
//增强这个对象
export default connect(mapStateToProps,null)(Comb)
大致的逻辑结构可以为:
某种角度上来说,可以认为props托管了一部分本该有state发出的动作