首先,我们先大概了解一下什么是react以及react可以干什么。
React 是 Facebook 开源的一个用于构建用户界面的一款 JavaScript 库,主要用于构建 UI。
基于组件:页面中部分内容
学习一次 随处使用
react-native
以下是常见的React组件:
ReactDOM.render()
:用于将React组件渲染到DOM中。ReactDOM.createPortal()
:用于创建一个将子组件渲染到指定容器中的React组件。ReactDOM.Fragment()
:用于将多个React元素包装在一个不可展开的元素中。ReactDOM.StrictMode()
:用于启用React的严格模式,使React在渲染过程中更严格地检查组件的依赖关系。ReactDOM.createContext()
:用于创建一个上下文(Context)对象,可以在组件中使用该上下文对象来获取共享的状态和数据。ReactDOM.useContext()
:用于在组件中使用上下文对象来获取共享的状态和数据。ReactDOM.useState()
:用于在组件中定义状态(state)并更新状态。ReactDOM.useEffect()
:用于在组件中定义副作用(side effects)并更新状态。ReactDOM.useReducer()
:用于在组件中定义一个reducer函数,该函数将处理状态更新的逻辑。ReactDOM.useCallback()
:用于在组件中定义一个回调函数,该函数将在组件第一次渲染时被调用,并返回一个缓存的函数,以避免每次渲染时都重新计算。ReactDOM.useMemo()
:用于在组件中定义一个memoized函数,该函数将在第一次调用时计算结果,并在之后的调用中缓存结果,以避免重复计算。ReactDOM.useDebugValue()
:用于在调试时向React堆栈中添加调试信息。ReactDOM.createRef()
:用于创建一个引用(ref),可以在组件中使用该引用来访问其DOM元素或子组件。ReactDOM.forwardRef()
:用于将一个React组件转发给另一个React组件,以使其可以访问父组件的props和refs。ReactDOM.memo()
:用于创建一个memoized函数,该函数将在第一次调用时计算结果,并在之后的调用中缓存结果,以避免重复计算。在es6之后,出现了一种函数,叫箭头函数,它比普通函数更加简洁方便。我们来举一个简单的例子
//普通函数
const add=function (a,b){
return a+b
}
//箭头函数
const addNew=(a,b)=>{
return a+b
}
那么箭头函数和普通函数相比有哪些特性呢,我们来看看
在了解react之前,我们还需要了解一些基础工具,比如npm,npm 全称是 node package manager,其实就是一个管理js编写的软件包的管理工具,可以下载,安装,升级,上传js软件包。
node.js内置了npm,所以只要安装了node.js,就可以使用npm。node.js的安装非常简单,官方地址下载就可以安装了。安装完毕后,使用npm install npm@latest -g
命令将npm升级到最新版本。
为了在国内尽快的下载js库,我们需要配置一下npm的镜像源,即从哪个源下载,这里我指定了华为,其他的还有腾讯,淘宝等镜像源。
npm config set registry https://mirrors.huaweicloud.com/repository/npm/
我们可以通过npm config list
查看npm的相关配置项
$ npm config list
; "builtin" config from /usr/local/lib/node_modules/npm/npmrc
prefix = "/usr/local"
; "user" config from /Users/zhangguofu/.npmrc
registry = "https://mirrors.huaweicloud.com/repository/npm/"
; node bin location = /usr/local/Cellar/node/21.5.0/bin/node
; node version = v21.5.0
; npm local prefix = /Users/zhangguofu/website/react
; npm version = 10.2.4
; Run `npm config ls -l` to show all defaults.
prefix
:指定npm的安装路径。registry
:设置JavaScript库的镜像源。在这个例子中,设置为"https://mirrors.huaweicloud.com/repository/npm/"。node bin location
:指定Node.js二进制文件的位置。node version
:表示已安装的Node.js版本。npm local prefix
:指定npm的本地安装路径。即我下载的js组件的位置npm version
:表示已安装的npm版本。接下来我们就开始讲react的安装
我们前面安装了npm,下面我们通过npm来下载react,我执行命令npm i react react-dom
,那么在我的npm local prefix路径下,就会生成一个node_modules
的目录。注意这里面,react.js可以说是我们的核心文件,用来执行业务逻辑和数据,但是数据完成之后还是要需要渲染成dom才能被用户看到,那么这些数据怎么样才能转换成真正的domn呢,这就用到了react-dom
。
<body>
<div id="root">div>
body>
<script src="../node_modules/react/umd/react.development.js">script>
<script src="../node_modules/react-dom/umd/react-dom.development.js">script>
<script>
//这个方法需要三个参数 type prop children
const title= React.createElement('div',null,'hello world')
// 渲染到root里面
ReactDOM.render(title,document.getElementById("root"));
script>
我们知道,脚手架是开发现代web应用的必备,比如webpack babel eslint等工具辅助项目开发,为了让我们更专注的开发业务,减少配置项目,我们使用脚手架来安装和使用react
npx create-react-app my-app
即可创建一个my-app的目录,其中npx是npm5.2版本中的一个命令,create-react-app
是脚手架的名字。my-app 就是我们的项目了。$ ls
README.md node_modules package-lock.json package.json public src
npm start
即可启动项目,默认是3000端口,接下来我们只需使用三行代码就可以实现一个hello react//导入文件
import React from 'react'
import ReactDOM from 'react-dom'
ReactDOM.render(<div>hello react </div>,document.getElementById('root'))
我们在上面代码可以看到,在js里面我们竟然可以直接写html,而且还不报错,其实这个有趣的标签语法既不是字符串也不是 HTML,而是JSX,它是一个 JavaScript 的语法扩展。我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模板语言,但它具有 JavaScript 的全部功能。
那我们可能会很奇怪,为什么js里面的写JSX不会报错呢?其实这就是脚手架的功劳,脚手架已经帮我们下载安装了三个包
const h1 = this is a H1 Tag
经过编译后会变成const h1 = React.createElement("h1", null, "this is a H1 Tag");
,接下来我们看看JSX的功能。//导入文件
import React from 'react'
import ReactDOM from 'react-dom'
// 注意 dom的属性命名必须是小驼峰 整个jsx推荐用小括号包裹 dom如果没有子节点,可以省略闭合标签
const content=(<div className='content'>hello react <span/> </div>)
ReactDOM.render(content,document.getElementById('root'))
我们来对比一下渲染后的dom
<div id="root"><div class="content">hello react <span>span> div>div>
const myName='张三'
const content=(<div className='content'>{myName}</div>)
const age=19
const content=(
<div className='content'>
<li>是否成年:{age>18?'成年':'未成年'}</li>
</div>
)
const func=()=>{
return i am a function
}
const content={func()}
const age=20
const func=()=>{
//逻辑运算
return age >18 && <div>i am a function</div>
}
const func2=()=>{
if (age>18){
return <div>i am {age}</div>
}
}
const content=<h1>{func()}{func2()}</h1>
const heros=[
{
name:'batman',
id:20
},
{
name:'x-police',
id:21
},
]
const content=<h1>{heros.map(item=><li key={item.id}>{item.name}</li>)}</h1>
可以通过类名或者行内样式写样式
import './css/index.css'
const content=<div>
<h1 style={{color:"red"}}>i am h1</h1>
<h2 className='tag2'>i am h2</h2>
</div>
组件是react的一等公民,你的每一个业务,可能都是多个组件拼装起来的。每个组件可能代表一个功能。组件实现了代码之间的独立性,复用性和耦合性。
组件有两种实现方式,一种是以函数形式实现,一种是以class的形式实现。
接下来我们看一下函数实现的组件长什么样子,其中我们要注意,函数的名称必须大写,为什么?我们写普通的jsx,可能会有div ul 这些,那么babel如何知道你写的这个是普通的jsx还是组件?就是看第一个字母是否大写,另一方面,组件必须有返回值,也可以返回null
function GetName() {
return <div>i am x</div>
}
const content=<GetName/>
Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17
,这里我们一下改一下rendder的方式//导入文件
import React from 'react'
import ReactDOM from 'react-dom/client'
import './css/index.css'
const GetName=()=> {
return <div>i am x</div>
}
const content=<GetName/>
const root= ReactDOM.createRoot(document.getElementById('root'))
root.render(content)
其实对于一个后端人员来说,class对我们来说实在太熟西了。但是这里需要有两个地方注意,一个是class必须继承自父类React.Component
,第二是必须提供render方法,且该方法必须有返回值,我们举例看一下
class Hello extends React.Component{
render() {
return <div>i am class hello</div>
}
}
const content=<Hello/>
那么我们也看到了,既然可以使用class创建组件,那么我们不可能将很多个class堆在一个js文件中,下面我们看怎么组件抽离
组件抽离其实很简单,就是注意两点,一是js文件中导入react库,第二是导出即可export,我们新建一个hello.js的文件,
import React from "react";
class Hello extends React.Component {
render(){
return <div>i am hello </div>
}
}
//导出组件
export default Hello;
import Hello from './Hello'
const content=<Hello/>
我们知道,在js里面有很多事件
那么react里面是怎么绑定事件的呢?其实和js很像,就是这样的形式on+事件名称={()=>{}}
,但是注意一点,事件名称必须是驼峰命名,比如我们绑定一个点击事件
class Hello extends React.Component {
Haha(){
alert("haha");
}
render(){
return <button onClick={this.Haha}>i am hello </button>
}
}
事件可以通过合成事件进行实例传递,比如我们想调用浏览器原生事件,因为原生事件对所有浏览器都是兼容的。那么我们怎么使用这些原生事件呢,我们举个例子
class Hello extends React.Component {
Haha(e){
//禁止跳转
e.preventDefault();
}
render(){
return <a href='https://www.acurd.com' onClick={this.Haha}>i am hello </a>
}
}
我们前面说过,react是有状态管理的,通过state和setState来配合完成,但是函数组件是没有状态的。而类组件才有状态,为什么?很简单,因为class里面可以定义自己的属性,修改状态是通过this指向。函数明显不能实现这些功能。比如我们现在来定义一个
class Hello extends React.Component {
//利用构造函数完成state初始化
// constructor(props){
// super(props);
// this.state={
// name:'x',
// age:18
// }
// }
//简洁化state初始化
state={
name:'x',
age:18
}
render() {
return (
<ul>
<li>name:{this.state.name}</li>
<li>age:{this.state.age}</li>
</ul>
)
}
}
}
render() {
return (
<ul>
<li>name:{this.state.name}</li>
<li>age:{this.state.age}</li>
<button onClick={()=>{this.setState({age:this.state.age+1})}}>点我加一岁</button>
</ul>
)
}
ChangeAge(){
this.setState({age:this.state.age+1})
}
render() {
return (
<ul>
<li>name:{this.state.name}</li>
<li>age:{this.state.age}</li>
<button onClick={this.ChangeAge}>点我加一岁</button>
</ul>
)
}
Uncaught TypeError: Cannot read properties of undefined (reading 'setState')
,这个this可不是onClick的this,而是 this.setState的this,为啥函数内部的this是undefined
,其实这是babel编译后的结果,babel是在use strict
模式下编译的,不允许this指向函数以外的对象,那么this只能指向undefined。好了,我们说了函数内部的this是undefined,也解释了原因,那么我们怎么使用到外部的this呢。
render() {
return (
<ul>
<li>name:{this.state.name}</li>
<li>age:{this.state.age}</li>
<button onClick={()=>this.ChangeAge()}>点我加一岁</button>
</ul>
)
}
或者我们直接把函数定义为箭头函数
ChangeAge=()=>{
this.setState({age:this.state.age+1})
}
//利用构造函数完成state初始化
constructor(props){
super(props);
this.state={
name:'x',
age:18
}
this.ChangeAge=this.ChangeAge.bind(this)
}
props是什么东西?它类似于你在声明对象的时候,给对象添加的属性,它具有以下特性
我们举个例子看一下
const content=<Hello name='张三' age='18' />
<ul>
<li>name:{this.props.name}</li>
<li>age:{this.props.age}</li>
</ul>
render() {
const {name,age} = this.props
return (
<ul>
<li>name:{name}</li>
<li>age:{age}</li>
</ul>
)
}
//展开符
const arr=[1,2,3,4,5,6,7,8,9,10]
const arr2=[11,21,13]
console.log(...arr);
//合并数组
let arr3=[...arr,...arr2]
console.log(arr3);
//展开对象
let obj={
name:'张三',
age:18,
sex:'男'
}
//展开对象需要用花括号表示对象
console.log({...obj});
//给对象添加属性
let obj2={...obj,addr:"xxxx"}
console.log(obj2);
let p={
name:'张三1',
age:'182',
sex:'男'
}
const content=<Hello {...p} ></Hello>
//对类型进行限制
Hello.propTypes={
name:PropTypes.string.isRequired,
sex:PropTypes.string.isRequired,
age:PropTypes.number.isRequired,
speak:PropTypes.func.isRequired
}
//初始默认值
Hello.defaultProps={
name:'张三',
sex:'男',
age:18
}
static propTypes = {
name:PropTypes.string,
age:PropTypes.number,
sex:PropTypes.string
}
static defaultProps={
name:'李四',
sex:'女',
age:20
}
function Speak(props){
let {age, name, sex}=props
return (<div>
<ul>
<li>age:{age}</li>
<li>name:{name}</li>
<li>sex:{sex}</li>
</ul>
</div>)
}
Speak.propTypes={
age:PropTypes.number,
name:PropTypes.string,
sex:PropTypes.string
}
const content=<Speak {...p} ></Speak>
class Hello extends React.Component {
Msg=()=>{
//input1 就是ref=input1指定的那个dom
let msg=this.refs.input2.value;
alert(msg);
}
render() {
return (
<ul>
<li><input type="text" ref='input1' placeholder='输入数据'/></li>
<li><input type="text" ref='input2' placeholder='输入数据'/></li>
<button onClick={this.Msg} >点击提示数据</button>
</ul>
)
}
}
Msg=()=>{
alert(this.input1.value)
}
render() {
return (
<ul>
<li><input type="text" ref={(dom)=>{this.input1=dom}} placeholder='输入数据'/></li>
<button onClick={this.Msg} >点击提示数据</button>
</ul>
)
}
setInput=(e)=>{
this.input1=e
}
input1=React.createRef()
Msg=()=>{
alert(this.input1.current.value)
}
class Hello extends React.Component {
Msg = (e) => {
alert(e.target.value)
}
render() {
return (
<li><input onBlur={this.Msg} type="text" ref={this.input1} placeholder='输入数据'/></li>
)
}
}
用户名:
,其实就是onChange这个事件执行了setData()
的这个方法,返回了一个操作指令,所以,如果你传了参数,必须返回一个函数给onChange setData=(dataType)=>{
return (e)=>{
this.setState(
{
[dataType]:e.target.value
}
)
}
}
getData=(e)=>{
e.preventDefault()
let {name,pwd}=this.state
console.log(name,pwd)
}
render() {
return (
<form onSubmit={this.getData}>
用户名: <input onChange={this.setData("name")} type="text"/>
pwd: <input onChange={this.setData("pwd")} type="text"/>
<button>提交</button>
</form>
)
}
let sum = (a) => {
return (b) => {
return (c) => {
return a + b + c
}
}
}
console.log(sum(1)(2)(3));
官方文档
技术栈为:React + Hook + React-router-v6 + Mobx + AntD
尚硅谷react分章
尚硅谷React
源码地址:https://gitee.com/react-cp/react-pc-code
React基础讲义: https://www.yuque.com/fechaichai/qeamqf/xbai87
React和Mobx讲义: https://www.yuque.com/fechaichai/qeamqf/apomum
ReactPc项目讲义: https://www.yuque.com/fechaichai/tzzlh1