-
特性
-
声明式设计 React采用声明范式,可以轻松描述应用
-
高效 React通过对DOM的模拟(虚拟dom),最大限度地减少与DOM的交互
-
DOM操作频繁是影响前端性能的最大因素
-
Diff算法可以解决
-
-
灵活 React可以与已知的库或框架很好的配合
-
JSX JSX是Javascript语法的扩展
-
组件 通过React构建组件,是的代码更加容易得到复用,能够更好地应用在大项目的开发中
-
单项响应的数据流 React实现了单项响应的数据流,从而减少了重复代码,这又是他为什么比还童数据绑定更简单
-
-
虚拟DOM
-
传统DOM更新
真实页面对应一个DOM树,在传统页面的开发模式中,每次需要更新页面时,都要手动操作DOM来进行更新
-
基本使用(非脚手架)使用<script>标签
React的安装
-
安装命令
npm i react react-dom
-
react包是核心,提供创建元素,组件等功能
-
负责创建元素
-
-
react-dom 包提供DOM相关功能
-
只有web应用使用react-dom,若为手机应用或其他,将导入其他包
-
负责将创建好的元素渲染至dom中
-
React的使用
-
引入react和react-dom的两个js文件(类似引入'vue')
<script src="./node_modules/react/umd/react.development.js"></script> <script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
创建React元素
1.React.createEelement
名称,属性(对象),元素子节点
const title = React.createElement('p',{title:'我是标题',id:'p1'},'hello',React.create.Element('span',null,'我是span节点'))
并不方便,仅限于了解
2.ReactDOM.render()[已弃用并且修改]
-
渲染React元素到页面中
ReactDOM.render(title,root)
-
React18后该方法已弃用,改为
const root = ReactDOM.createRoot(document.getElementById('root'))//DOM对象,用于指定渲染到页面中的位置 root.render( el //要渲染的React元素 )
React组件
-
使用React就是在使用组件
两种创建方式
1.使用函数创建组件
-
函数名称必须以大写开头,React据此区分组件和普通的React元素
-
函数组件必须有返回值
-
如果返回值为null表示不渲染任何内容
function Hello(){ return( <div>这是我的第一个函数组件</<div> ) } //简写 const Hello = ()=> <div>这是我的第一个函数组件</div>
-
渲染:用函数名作为组件标签名
-
可以是单标签也可以是双标签
root.Render(<Hello/>)
2.使用类创建组件
-
使用ES6的class创建的组件
-
约定:
-
类名称也必须以大写字母开头
-
类组件应该继承React.Component父类,从而可以使用父类中提供的方法或属性
-
类组件必须有render()方法
-
render()方法必须有返回值,表示该组件的结构
class Hello extends React.Component{ render(){ return <div>hello</div> } } const root = ....getE() root.render(<Hello/>)
-
抽离为独立JS文件
-
组件作为一个独立的个体,一般都会放到一个单独的JS文件中
-
步骤
-
创建Hello.js
-
在Hello.js中导入React
import React from 'react'
-
创建组件(函数或类)
-
在Hello.js中导出该组件
export default Hello
-
在index.js中导入Hello组件
import Hello from 'hello.js'
-
渲染组件
-
有状态组件和无状态组件
-
函数组件又叫做无状态组件,类组件又叫做有状态组件
-
状态{state} 即数据
-
函数组件没有自己的状态,只负责数据显示(静)
-
类组件有自己的状态,负责更新UI,让页面动起来
组件中的state和setState()
-
state基本使用
-
setState()修改状态
1.state的基本使用
-
状态{state}即数据,是组件内部的私有数据,只能在组件内部使用
-
state的值是对象,表示一个组件中可以有多个数据
class Hello extends React.Component { constructor(){ super() //初始化state this.state = { count: 0 } } render(){ return{ <div>有状态组件{this.state.count}</div> } } }
2.setState()修改状态
-
状态是可变的
-
语法:this.setState({要修改的数据})
-
注意:不要直接修改state中的值,这是错误的
-
思想:数据驱动视图
export default class extends React.Component { // 第二种初始化方式 state = { count:1 } render(){ return ( <div> <div>计数器 :{this.state.count}</div> <button onClick={() => { this.setState({ count: this.state.count+1 }) }}>+1</button> </div> ) } }
注意异步
-
setState是异步更新的
-
所以使用该语法时,后面的setState()不要依赖于前面的setState()
-
可以多次调用setState(),只会触发一次重新渲染
推荐语法
-
setState((state,props)=>{})语法
-
state:表示最新的state
-
props:表示最新的props
setState((state,props)=>{ return{ } }) //也是异步更新 //但state是最新状态参数
-
第二个参数
-
场景:在状态更新(页面完成重新渲染)后立即执行某个操作
-
语法:setState(updater[,callback])
组件通讯(props)
组件的props
-
组件是封闭的,要接受外部数据应该通过props实现
-
props作用:接受传递给组件的数据
-
传递数据:给组件标签添加属性
-
接收数据:
-
函数组件通过参数props接收数据
<Hello name="jack" age={19}> function Hello(props){ console.log(props) return{ <div>接收到的数据:{props.name}</div> } }
-
类组件通过this.props接收数据
class Hello extends React.Component{ render(){ return{ <div>接收到的数据:{this.props.age}</div> } } } <Hello name="jack" age={19} /></Hello>
-
特点
-
可以给组件传递任意类型的数据
-
传非字符串{19}
-
传字符串"19"
-
也可以传函数 fn={()=>{}}
-
传JSX: tag={<p>这是一个p标签</p>}
-
-
props是只读的对象,只能读取属性值,不可修改
-
使用类组件时,如果写了构造函数,应该将props传递给super(),否则无法在构造函数中获取props
class Hello extends React.Component{ constructor(props) { super(props); this.state={ count:0 } } render(){ return{ <div>接收到的数据:{this.props.age}</div> } } } <Hello name="jack" age={19} /></Hello>
props深入
1.children属性
-
表示组件标签的子节点,当组件标签有子节点时,props就会有该属性
-
与普通的props一样,值可以是任意值(文本,React元素组件,甚至是函数)
function Hello(props){ return{ <div> 组件的子节点:{props.children} </div> } } <Hello>我是子节点</Hello> props.children即为"我是子节点"
2.props校验
-
对于组件来说props是外来的,无法保证使用者传入什么格式的数据
-
props校验:允许在创建组件的时候就制定props的类型,格式等
使用步骤
-
安装包prop-types
yarn add prop-types npm i props-types
-
导入prop-types包
import PropTypes from 'prop-types'
-
使用组件名.propTypes={}来给组件的props添加校验规则
Hello.propTypes = { //约定colors属性为Array类型 colors:PropsTypes.array //若类型不对,则报出明确错误,便于分析错误原因 }
-
校验规则通过PropTypes 对象来指定
约束规则
-
创建的类型:
array、bool、func、number、object、string
-
React元素类型:
element
-
必填项:
isRequired
-
特定结构的对象:
shape({})
-
更多的[约束规则
//常见类型 optionalFunc:PropTypes.func, //必选 requiredFunc:PropTypes.func.isRequired, //特定结构的对象 optionalObjectWithShape:PropTypes。shape({ color:PropTypes.string fontSize:PropTypes.number })
3.props默认值
-
场景:分页组件=>每页显示条数
Hello.defaultProps = { pageSize:10 } //若传入数据,则以传入的数据为主
父传子
-
父组件提供要传递的数据
class Fu extends React.Component{ state={ lastName:'王' } render(){ return( <Hello name={this.state.lastName}></Hello> ) } } class Hello extends React.Component{ render(){ return( <div>子组件接收到的数据:{props.name}</div> ) } }
子传父
-
利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的数据
-
父组件提供一个回调函数(用于接收数据)
class Fu extends React.Component{ state={ count:10 } getChildMsg=data=>{ this.setState( {count:data} ) } render(){ return( <Child getMsg={this.getChildMsg}></Child> ) } } class Child extends React.Component{ state={ myCount:20 } handleClick=()=>{ this.props.getMsg(this.state.myCount) } render(){ return( <button onClick={this.handleClick}>点击更改父组件数据</button> ) } }
-
兄弟组件
-
将共享状态提升到最近的公共组件中,由公共父组件管理这个状态
-
思想:状态提升
-
公共父组件职责:
-
提供共享状态
-
提供操作共享状态的方法
-
-
要通讯的子组件只需通过props接收状态或操作状态的方法
class Fu2 extends React.Component{ state={ count:0 } OnIncrement=()=>{ this.setState({count:this.state.count+1}) console.log('点击了') } render(){ return( <div> <Child1 data={this.state.count}/> <Child2 OnIn={this.OnIncrement}/> </div> ) } } class Child1 extends React.Component { render() { return( <div>我是子组件1,数据为{this.props.data}</div> ) } } class Child2 extends React.Component{ render(){ return( <button onClick={()=>this.props.OnIn()}>我是子组件2,点击我更改子组件1数据</button> ) } }
Context
-
祖级传递数据
-
eg:App组件要传递数据给Child组件,该如何处理?
-
使用Context
-
作用:跨组件传递数据(比如主题,语言)
-
-
使用步骤
-
调用React.createContext()创建Provider(提供数据)和Consumer(消费数据)两个组件
const {Provider,Consumer} = React.createContext()
-
使用Privider组件作为父节点,设置value属性表示要传递的数据
<Provider value="pink">//包裹父组件 <div className="App"> </div> </Provider>
-
调用Consumer组件接收数据
<Consumer> {data=><span>data参数表示接收到的数据</span>} </Consumer>
-
render-props和高阶组件
React组件复用
-
两个组件功能相同/相似->复用相似的功能(联想函数封装)
-
复用什么?
-
state
-
操作state的方法(组件状态逻辑)
-
-
两种方式:
-
render props模式
-
高阶组件(HOC)
-
注意:这两种方式不是新的API,而是利用React自身特点的编码技巧,演化而成的固定模式
-
1.render props模式
-
思路:将要复用的state和操作state的方法封装到一个组件中
-
在使用组件时,添加一个值为函数的prop,通过函数参数来获取(需要组件内部实现)
2.高阶组件
-
思路:高阶组件(HOC)是一个函数,接收要包装的组件,返回增强后的组件
组件生命周期
-
组件生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程
-
只有类组件才有生命周期(因为只有类组件能让页面动起来,函数组件不可以)
-
三个阶段
1.创建时(挂载阶段)
-
执行时期:组件创建时(页面加载时)
-
执行顺序
-
constructor()=>
-
创建组件时最先执行
-
初始化state
-
为事件处理程序绑定this
-
-
render()=>
-
每次组件渲染都会触发
-
渲染UI(注意不能调用setState())
-
-
componentDidMount
-
DOM已经完成渲染(组件挂载后)
-
可以发送网络请求(一进页面就发请求)
-
-
2.更新时(更新阶段)
-
三种导致组件更新的情况
-
New props
-
setState()
-
forceUpdate()
-
强制更新
this.forceUpdate()
-
-
-
执行顺序
-
render()=>
-
先执行渲染
-
每次组件渲染都会触发
-
-
componentDidUpdate()
-
在这里写函数,render也会重新执行
-
组件更新(完成DOM渲染后)
-
发送网络请求
-
DOM操作
-
注意:如果要setState()必须放在一个if条件中
-
否则导致递归更新::setState导致重新渲染,则会重新执行render,则会重新执行componentDidUpdate(),导致重新调用setState
-
-
正确做法:比较更新前后props是否相同
componentDidUpdate(prevProps){ // 上一次的props:prevProps, // 当前props:this.props if(prevProps.count!==this.props.count){ this.setState({}) //在此发送网络请求 } }
-
-
-
3.卸载时(卸载阶段)
-
执行时机:组件从页面中消失
-
钩子函数:ComponentWillUnmount
-
触发时机:组件卸载(从页面中消失)
-
执行清理工作:(比如:清理定时器等)
componentDidMount(){ //开启定时器 this.timerId = setInterval(()=>{ console.log('定时器正在执行') },500) } componentWillUnmount(){ //清理定时器 clearInterval(this.timerId) }
-
不常用的钩子函数
旧版的生命周期钩子函数
新版完整生命会走棋钩子函数
getDerivedStateFromProps()
-
getDerivedStateFromProps
会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容 -
不管原因是什么,都会在每次渲染前触发此方法
shouldComponentUpdate()
-
根据
shouldComponentUpdate()
的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。默认行为是 state 每次发生变化组件都会重新渲染 -
当 props 或 state 发生变化时,
shouldComponentUpdate()
会在渲染执行之前被调用。返回值默认为 true
getSnapshotBeforeUpdate()
-
getSnapshotBeforeUpdate()
在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给componentDidUpdate()
-
此用法并不常见,但它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等
组件性能优化
React事件处理
事件绑定
-
语法:on+事件名称={事件处理程序},比如 onClick={()=>{}}
-
注意:React事件采用驼峰命名法,比如onMouseEnter,onFocus
1.函数组件
function App(){ function handleClick(){ console.log('单击事件触发了') } return{ <button onClick={handleClick}>点我</button> } }
2.类组件
class Hello extends React.Component{ handleClick(){ console.log('点了点了') } render(){ return( <button οnclick={this.handleClick}>点我点我</button> ) } }
事件绑定中this指向
-
箭头函数没有this,所以箭头函数中的this指向render中实例
1.class实例方法(推荐)
-
将组件内部函数直接改为箭头函数
2. 箭头函数
3. bind
constructor(){ super() this.onIncrement = this. }
3. class实例方法(推荐)
-
将组件内部函数直接改为箭头函数
事件对象
-
可以通过事件处理函数的参数获取到事件对象
-
React中的事件对象叫做:合成事件
-
合成事件:兼容所有浏览器,无需担心跨浏览器兼容问题
export default class extends React.Component { clickHandle(e){ // 获取原生事件对象 console.log(e.nativeEvent) } render(){ return ( <div><button onClick = {this.clickHandle}>点我点我点我</button></div> ) } }
表单处理
1.受控组件
(v-model---->React)
-
React将state与表单元素值value绑定到一起,由state的值来控制表单元素的值
-
受控组件:其值收到React控制的表单元素
<input type="text" value={this.state.count}/>
class App extends React.Component { constructor(){ super() this.inputChange = this.inputChange.bind(this) } state = { txt : '' } inputChange(e){ this.setState({ txt: e.target.value }) } render(){ console.log(this.state); return ( <div> {/* 把state的值设置给输入框的value,绑定change事件,这样用户在输入内容的时候调用相应函数,在函数里面把当前设置的值赋值给state,从而达到数据的统一 */} <input type="text" value={this.state.txt} onChange={this.inputChange}/> </div> ) } } ReactDOM.render(<App />,document.getElementById('root'))
多表单元素优化
-
问题:每个表单元素都有一个单独的事件处理函数,这样太繁琐
-
优化:使用一个事件处理程序同时处理多个表单元素
步骤
-
给表单元素添加name属性(用来区分是哪一个表单),名称与state相同(用来更新数据的)
-
根据表单内容来获取对应值
-
在change事件处理程序中通过 [name] 来修改对应的state
示例demo
inputChange(e){ let target = e.target; let value = target.type == 'checkbox' ? target.checked : target.value; this.setState({ [e.target.name]: value }) } <input type="text" value={this.state.txt} name="txt" onChange={this.inputChange}/> <input type="checkbox" value={this.state.isChecked} name="isChecked" onChange={this.inputChange}/>
2.非受控组件 (了解)
-
说明:借助于ref,使用元素DOM方式获取表单元素值
-
ref的作用:获取DOM或者组件
使用步骤
-
调用
React.createRef()
方法创建ref对象 -
将创建好的 ref 对象添加到文本框中
-
通过ref对象获取到文本框的值
class App extends React.Component { constructor(){ super() //创建 ref this.txtRef = React.createRef() } // 获取文本框的值 getTxt =() => { console.log(this.txtRef.current.value) } render(){ return ( <div> <input type ="text" ref={this.txtRef} /> <button onClick ={this.getTxt}>获取值</button> </div> ) } }
路由
基本使用步骤
-
安装
yarn add react-router-dom
-
导入路由的三个核心组件Router/Router/Link
import {BrowserRouter as Router,Route,Link} from 'react-router-dom'
-
-
使用Router组件包裹整个应用
<Router> <div> 我是React路由器 </div> </Router>
-
使用Link组件作为导航菜单(路由入口)
<Router> <div> 我是React路由器 <Link to="/first"></Link> </div> </Router>
-
使用Route组件配置路由规则和要展示的组件(路由出口)
<Router> <div> 我是React路由器 <Link to="/first"></Link> <Route path="/first" component={First}></Route> </div> </Router>
常用组件说明
-
Router组件:包裹整个应用,一个React应用只需要使用一次
-
两种常用的Router: HashRouter和BrowserRouter
-
HashRouter: 使用URL的哈希值实现 (localhost:3000/#/first)
-
推荐 BrowserRouter:使用H5的history API实现(localhost3000/first)
-
-
Link组件:用于指定导航链接(a标签)
-
最终Link会编译成a标签,而to属性会被编译成 a标签的href属性
-
-
Route组件:指定路由展示组件相关信息
-
path属性:路由规则,这里需要跟Link组件里面to属性的值一致
-
component属性:展示的组件
-
Route写在哪,渲染出来的组件就在哪
-
路由的执行过程
-
当我们点击Link组件的时候,修改了浏览器地址栏中的url
-
React路由监听地址栏url的变化
-
React路由内部遍历所有的Route组件,拿着Route里面path规则与pathname进行匹配
-
当路由规则(path)能够匹配地址栏中的pathname时,就展示该Route组件的内容
编程式导航
-
场景:点击登陆按钮,登陆成功后,通过代码跳转到后台首页,如何实现?
-
编程式导航:通过JS代码来实现页面跳转
-
history是React路由提供的,用于获取浏览器历史记录的相关信息
-
push(path):跳转到某个页面,参数path表示要跳转的路径
-
go(n):前进或后退功能,参数n表示前进或后退页面数量
默认路由
-
现在的路由都是通过点击导航菜单后展示的,如果进入页面的时候就主动触发路由呢
-
默认路由:表示进入页面时就会匹配的路由
-
默认路由:只需要把path设置为
'/'
匹配模式
模糊匹配模式
-
当Link组件的to属性值为 '/login' 时候,为什么默认路由也被匹配成功?
-
默认情况下,React路由是模糊匹配模式
-
模糊匹配规则:只要pathname以path开头就会匹配成功
精准匹配
-
默认路由认可情况下都会展示,如果避免这种问题?
-
给Route组件添加exact属性,让其变为精准匹配模式
-
精确匹配:只有当path和pathname完全匹配时才会展示改路由
小结
-
React路由可以有效的管理多个视图实现 SPA
-
路由先需要通过安装
-
Router组件包裹整个应用,只需要使用一次
-
Link组件是入口,Route组件是出口
-
通过props.history实现编程式导航
-
默认是模糊匹配,添加exact编程精确匹配
-
React路由的一切都是组件,可以像思考组件一样思考路由
目录
README.md
使用方法的文档
node_modules
所有的依赖安装的目录
package-lock.json
npm install
-
npm install 会严格按照该锁定版本进行创建,安装和团队一致的项目所有文件版本
-
node_modules是根据该锁定的配置文件创建的,不用复制给他人
package.json
public
静态公共目录
index.html
项目唯一入口页面
src
开发用的源代码目录
index.js
import React from 'react' import REACTDOM from 'react-dom'
React原理-虚拟DOM和Diff算法
-
React更新视图的思想:只要state变化就重新渲染视图
虚拟DOM
-
本质上就是一个JS对象,用来描述你希望在屏幕上看到的内容(ui)
-
其实就是react元素
Diff算法
-
初次渲染时React会根据初始state(Model),创建一个虚拟DOM对象(树)
-
根据虚拟DOM生成真正的DOM,渲染到页面中
-
当数据变化后(SetState()),重新根据新的数据,创建新的虚拟DOM对象(树)
-
与上一次得到的虚拟DOM对象,使用Diff算法对比(找不同),得到需要更新的内容
第一个项目
从头创建
1.脚手架 create-react-app
npm install -g create-react-app create-react-app mydemo
npm和npx区别
-
npm 一直为创建时的版本
-
npx 不断更新,一直是最新工具
npx create-react-app mydemo
启动
npm start
初始化项目
1. npx create-react-app mydemo 2. npm init react-app mydemo yarn create react-app mydemo
2.导入react和react-dom两个包
-
删除src下所有文件
react开发需要引入多个依赖文件:react.js,react-dom.js,分别又有开发版本和生产版本,create-react-app里已经帮我们把这些东西都安装好了.把通过CRA创建的工程目录下的src目录清空,然后在里面重新创建一个index.js
-
新建index.js(必须叫这个名字)
3.调用React.createElement()方法创建react元素
略
4.调用ReactDOM.render()方法渲染react元素到页面中
//从react包当中引入了React,只要写React.js组件就必须引入React,因为React里有一种语法叫做JSX,要写JSX就必须引入React import React from 'react' //ReactDOM可以帮助我们把React组件渲染到页面上去,没有其他作用了.他是从react-dom中引入的,而不是从react引入 import ReactDOM from 'react-dom' //ReactDOM中有一个render方法,功能就是把组件渲染并且构造DOM树,然后插入到页面上某个特定元素上,React18后必须const root后才能render const root = ReactDOM.createRoot(document.getElementById('root'))//渲染到哪里 root.render( //JSX语法--在Javascript写标签---JavaScriptXML <h1>hello</h1>, )
路由
-
路由模式
-
开发,本地阶段:hash 不会从服务器请求资源
-
不带hash √ createBrowserRouter
-
-
命令
npm i reat-router-dom
-
通过配置文件
-
引入createBrowserRouter
-
Vite+TS+Hook
Hooks语法
1.useState:状态钩子
-
基本用法
useState,能让函数组件拥有自己的状态,因此,他是一个管理状态的hooksAPI,通过useState可以实现状态的初始化,读取,更新
-
语法
const [状态名,set函数] = useState(初始值)
-
eg
export const Count:React.FC = ()=>{ const [count,setCount] = useState(0) const add = ()=>{ setCount(count+1) } return ( <> <h1>我是COUNT{count}</h1> <button onClick={add}>点击</button> </> ) }
以函数形式为状态赋初值
import React, {useState} from "react"; export const DateCom:React.FC = ()=>{ const [date, setDate] = useState(()=>{ const dt = new Date() return { year:dt.getFullYear(), month:dt.getMonth()+1, day:dt.getDate() } }) return( <> <h1>当前年月日信息:</h1> <p>年:{date.year}</p> <p>月:{date.month}</p> <p>日:{date.day}</p> </> ) }
异步解决方案
setCount(count+1) setCount(count+1) //调两次count值不会变,因为setState函数是异步的 //可以写为prev setCoune((prev)=>{prev+1}) //count值改变
更新对象类型的值
import React, {useState} from "react"; import {render} from "react-dom"; export const DateCom:React.FC = ()=>{ const [date, setDate] = useState(()=>{ const dt = new Date() return { year:dt.getFullYear(), month:dt.getMonth()+1, day:dt.getDate() } }) return( <> <h1>当前年月日信息:</h1> <p>年:{date.year}</p> <p>月:{date.month}</p> <p>日:{date.day}</p> </> ) } export const UserInfo:React.FC = ()=>{ const [user,setUser] = useState({ name:'aaaa', age:21, gender:'男' }) const onChangeUser = ()=>{ user.name = 'wwwww' user.age = 20 setUser({...user}) //或者 setUser(Object.assign({},user)) } return( <> <button onClick={onChangeUser}>点击更改用户信息</button> <h1></h1> </> ) }
2.useContext():共享状态钩子
-
使用React.createContext()+useContext()轻松实现多层组件的数据传递
使用步骤
-
在全局创建Context对象
-
在父组件中使用Context.Provider 提供数据
-
在子组件中使用useContext 使用数据
3.useReducer():action钩子
-
当状态更新逻辑复杂时考虑使用useReducer
-
可以同步更新多个状态,而且能把对状态的修改从组件中独立出来
语法格式
const [state,dispatch] = useReducer(reducer,initState,initAction?)
-
. reducer 是一个函数,类似于 (prevState, action) => newState。形参 prevState 表示旧状态,形参 action 表示本次的行为,返回值 newState 表示处理完毕后的新状态。
-
initState 表示初始状态,也就是默认值。
-
initAction 是进行状态初始化时候的处理函数,它是可选的,如果提供了 initAction 函数,则会把 initState 传递给 initAction 函数进行处理,initAction 的返回值会被当做初始状态。
-
返回值 state 是状态值。dispatch 是更新 state 的方法,让他接收 action 作为参数,useReducer 只需要调用 dispatch(action) 方法传入的 action 即可更新 state。 作者:刘龙彬 8. react hooks - useReducer - 哔哩哔哩 出处:bilibili
4.useEffect():副作用钩子
语法格式
useEffect(fn,deps?)
.useRef
-
ref
项目
1.创建vite项目
npm create vite@latest ----选择----- npm i npm run dev
2.将路径模式改为@
npm i -D @types/node
-
配置 vite.config.ts 文件:
// 1. 以 ES6 模块化的方式,从 Node 的 path 模块中,导入 join 函数 import { join } from 'path' // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], // 2. 在 resolve.alias 对象下,配置 @ 的指向路径 resolve: { alias: { '@': join(__dirname, './src/') } } })
3.配置ts
-
配置 tsconfig.json 文件,在 compilerOptions 节点下,新增 "baseUrl": "." 和 "paths": { "@/": [ "src/" ] } 两项: 作者:刘龙彬 1. react hooks - 准备工作 - 哔哩哔哩 出处:bilibili
{ "compilerOptions": { /* 新增以下两个配置项,分别是 baseUrl 和 paths */ "baseUrl": ".", "paths": { "@/*": [ "src/*" ] }, "target": "ES2020", "useDefineForClassFields": true, "lib": [ "ES2020", "DOM", "DOM.Iterable" ], "module": "ESNext", "skipLibCheck": true, /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": [ "src" ], "references": [ { "path": "./tsconfig.node.json" } ] }
4.目录
main.tsx
-
删除React.StrictMode
-
只是为了确保组件由同样的输入输出
<React.StrictMode> <App /> </React.StrictMode>
-
App.jsx
-
创建函数式组件
import React from "react"; //React.FC<T> 表示这是一个函数式的react组件 const App:React.FC = ()=>{ console.log('渲染了App') return ( <> <h1>这是App组件</h1> </> ) } export default App
JSX
-
JSX表示在JS代码中写html结构,是react生命式的实现
-
JSX是将HTML语法直接加入到JavaScript代码中,在通过翻译器转化到纯JavaScript后由浏览器执行
-
编译过程由Babel的JSX编译器实现
const root = ReactDOM.createRoot(document.getElementById('root')) root.render( <h1>hello</h1>, ) //等价于(经过Babel的JSX编译后) root.render( React.createElement("div",{ id:"aaa", className:"bbb" //不能写成class },"111111") )
JSX语法转化过程
-
JSX仅仅是
createElement()
方法的语法糖(简化语法) -
JSX语法被 @babel/preset-react 插件编译为
createElement()
方法 -
React 元素: 是一个对象,用来描述你希望在屏幕上看到的内容
注意点
-
React元素的属性名使用驼峰命名法
-
特殊属性名: class->className,for->htmlFor,tabindex->tabIndex
-
没有子节点的React元素可以用/>结束(不能再html里这样!!)
-
推荐使用小括号包裹JSX,从而避免js中自动插入分号陷阱
嵌入JS表达式
语法:{JavaScritp表达式}
-
不要与vue插值表达式{{}}搞混
例子:
let content = '插入的内容' let h1 = <h1>我是通过JSX创建的元素+ {content}</h1>
注意点
-
只要是合法的js表达式都可以进行嵌入
-
JSX自身也是js表达式
-
注意:js中的对象是一个例外,一般只会出现在style属性中
-
注意:在{}中不能出现语句
JSX条件渲染
根据不同的条件来渲染不同的JSX结构
let isLoading = true let loading = ()=>{ if(isLoading){ return <div>Loading...</div> } return <div>加载完成</div> }
可以发现,写JSX的条件渲染与我们之前编写代码的逻辑是差不多的,根据不同的判断逻辑,返回不同的 JSX结构,然后渲染到页面中
JSX列表渲染
-
如果要渲染一组数据,应该使用数组的map()方法
const songs = [ {id:1,name:'痴心绝对'}, {id:2,name:'像我这样的人'}, {id:3,name:'南山南'} ] const title = ( <ul> {songs.map(item => <li key={item.id}>{item.name}</<li>)} </ul> )
-
如果需要渲染一组数据,我们应该使用数组的 map () 方法
-
注意:渲染列表的时候需要添加key属性,key属性的值要保证唯一
-
原则:map()遍历谁,就给谁添加key属性
-
注意:尽量避免使用索引号作为key
样式处理
1.行内样式--style
const list = ( <h1 style={ {color:'red',backgroundColor:'skyblue'} }> </h1> )
2.类名--className
const list = ( <h1 className = "title"> </h1> )
Redux
类似vuex
else
v-dom 虚拟dom
v-node 虚拟节点
nrm
npm的仓库(配置淘宝镜像)
npm i -g nrm nrm ls nrm use taobao
-
配置华为镜像比较好用
npm config set registry https://mirrors.huaweicloud.com/repository/npm/