React
是一个用于构建用户界面
的JavaScript库
。
用户界面: HTML页面(前端)
React主要用来写HTML页面, 或构建Web应用
如果从MVC的角度来看,React仅仅是视图层(V),也就是只负责视图的渲染,而并非提供了完整的M和C的功能。
React 有如下几个特点:
最重要
的内容;组件是页面中的部分内容;组合、复用多个组件,可以实现完整的页面功能在我们安装React之前, 可以先安装yarn的安装参考链接
React的安装命令:npm install react react-dom
使用React脚手架初始化项目
npx命令介绍:
无需安装脚手架包
,就可以直接使用这个包提供的命令使用React脚手架初始化项目
npx create-react-app xxx(项目名称)
yarn init
在脚手架中使用React
import React from "react";
import { ReactDOM } from "react";
React.createElement()
方法创建react元素ReactDOM.render()
方法渲染react元素到页面中// // 导入react
import React from "react";
import ReactDOM from 'react-dom';
// 创建react元素
const title = React.createElement('h1', null, 'Hello React')
// 渲染react元素到页面中
ReactDOM.render(title, document.getElementById('root'))
目前创建项目是下面新的写法:
import React from "react";
import ReactDOM from 'react-dom/client';
// 创建react元素
const title = React.createElement('h1', null, 'Hello React')
// 渲染react元素到页面中
// 创建root对象
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(title)
React.createElement()
有如下问题:繁琐不简洁、不直观,无法一眼看出所描述的结构、不优雅,用户体验不爽 问题JSX是JavaScript XML
的描写,表示在JavaScript代码中写XML(HTML)格式的代码。
优势:声明式语法更加直观、与HTML结构相同,降低了学习成本、提升开发效率
为什么脚手架中可以使用JSX语法?
JSX不是标准的ECMAScript语法,它是ECMAScript的语法扩展
需要使用babel编译处理后,才能再浏览器环境中使用
creat-react-app脚手架中以默认有该配置,无需手动配置
编译JSX语法的包为@babel/preset-react
JSX的使用注意点:
className
、for -htmlFor
、tabindex- tabIndex
/>
结束小括号包括JSX
,从而避免JS中的自动插入分号陷阱数据都存储在JS中,语法:{ JavaScript表达式 }
, 注意: 语法中是单大括号
,不是双大括号
单大括号
中可以使用任意的JavaScript表达式不能在{}中出现语句
(比如:if/for 等)if/else
或三元运算符
或逻辑运算符
来实现map()
方法key属性的值要保证唯一
map()
便利谁,就给谁添加key属性尽量避免使用索引号作为key
一等共民
,使用React就是在用组价大写字母开头
必须有返回值
,表示该组件的结构用函数名作为组件标签名
函数必须有返回值
组件名称必须大写字母开头
,React据此区分 组件和 普通的React元素class
创建的组件大写字母开头
ReactComponet
父类,从而可以使用父类中提供的方法或属性render()
方法render()
方法必须有返回值
,表示该组件的结构放到一个单独的JS文件中
抽离为独立JS文件:
on + 事件名称 = (事件处理 程序)
,比如: onClick = { () => () }React事件采集用驼峰命名法
,比如:onMouseEnter、onFocus无状态组件
, 类组件又叫做有状态组件
只负责数据展示
(静)负责更新UI
,让页面动
起来比如接下来我们想做一个计数器的案例,点击按钮然数值加1。0和1就是不同的状态,而由0变为1就表示状态发生了变化,状态变化后,UI也相应的更新。React想要实现该功能,就要使用状态组件来完成。
state的基本使用:
私有
问题,只能在组件内部使用state的值是对象
,表示一个组件中可以有多个数据this.state
不要直接修改state的值,这是错误的!!!
修改state
2更新UI
思想: 数据驱动视图
通过上面代码可知,JSX中掺杂过多的JS逻辑代码,会显得非常混乱, 推荐:将逻辑抽离到单独的方法中,
保证JSX结构清晰
this
值为undefined
如果解决处理上述程序中this的指向问题,有如下三种方法:
箭头函数不会产生this,它会从作用域链的上一层继承this
)bind()
方法,将事件处理程序中的this与组件实例绑定到一起class的实例方法
: 利用箭头函数形式的class实例方法, 注意:该语法是实验性语法,但是,由于babel的存在可以直接使用总结: 推荐使用class的实例方法
下载安装。react-devtools
下载完了之后将crx后缀改为zip,然后将这个zip拖到谷歌浏览器扩展程序那里就行了
由state的值来控制表单元素的值
组件
是独立且封闭的单元,默认情况下,只能使用组件自己的数据,在组件化过程中,我们将一个完整的功能拆分成多个组件,以更好的完成整个应用的功能。而这个过程中,多个组件之间不可避免的要共享某些数据。为了实现这些功能,就需要打破组件的独立封闭性,让其与外界沟通。这个过程就是组件通讯
props的作用: 接收传递给组件的数据
参数props
接受数据,类组件通过this.props
接受数据props
是只读的对象,只能读取属性的值,无法修改对象应该将props传递给super()
,否则,无法在构造中获取到props!思路: 利用回调函数,父组件提供回调函数,子组件调用,将要传递的数据作为回调函数的参数。
共享状态
提升到最近的公共组件中,由公共父组件
管理这个状态props
接受状态或操作状态的方法思考: App组件要传递数据给Child组件,该如何处理?
原始方法: 可以通过props属性,一层一层的往下传递,这种方式比较繁琐。
推荐: 使用Context, 其作用:跨组件传递数据
(比如:主题、语言等)
总结:
注意: 只有组件标签有子节点的时候,才会有children属性
对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据
如果传入的数据格式不对,可能会导致组件内部报错
props校验:允许在创建组件的时候,就指定props的类型、格式等
props校验的使用步骤:
yarn add prop-types/npm i porp-types
)组件名.propTypes={}
来组件的props添加校验规则校验规则
通过PropTypes对象来指定约束规则:
相关规则参考地址:https://zh-hans.react.dev/reference/react/Component#static-proptypes
props的默认值:
注意:props的值,如果有默认值,在使用组件时没有传值时使用默认值,有传值则使用传递的值
render props
模式不是新的API
,而是利用React自身特点的编码技巧,演化而成的固定模式(写法)
函数的prop
,通过函数参数
来获取(需要组件内部实现)该函数的返回值
作为要渲染的UI内容(需要组件内部实现)使用步骤:
状态逻辑
代码(1.状态 2.操作状态的方法)复用的状态
作为props.render(state)方法的参数,暴露到组件的外部返回值
作为要渲染的内容render的prop
,实际 商行可以使用任意的prop名称render props模式
children
代替render属性包装(装饰)模式
,高阶组件就是通过包装组件,增强组件功能高阶组件
(HOC, Hight-Order Component)是一个函数
,接受要包装的组件,返回增强后的组件创建一个类组件
,在这个类组件中提供复用的状态逻辑
代码,通过prop将复用的状态传递给被包装组件WrappedComponentwith开头
组件名称
作为displayNamedisplayName
便于调试时区分不同的组件
拿不到传递过来的props
当我们给组件传递props时, 会发现一个问题, 发现组件并没有获得我们传递的props。
原因: 因为当我们传递props时,首先是传递给高阶组件的,但是高阶组件并没有往下传递props,所以导致我们的组件并没有获取到props。
解决方式: 渲染WrappedComponent时,将state
和this.props
一起传递给组件
setState()
是异步
更新数据的由上图可以看到, setState()方法是异步执行的。我们打印结果都显示1, 但是实际上count的值已经是2。
如果我们有需要连续调用setState()的需求, 并且调用存在依赖关系, 那么我们如何实现了?
setState((state, props) => {} )
语法, 这种语法 也是异步更新state的
第二次调用setState,拿到第一次调用setState的结果, 参数state和props 都是最新状态的
减轻state
: 只存储组件渲染相关的数据(比如:count/列表数据/loading等)需求:随机生成数字,显示在页面,如果生成的数字与当前显示的数字相同,那么就不需要更新UI,反之更新UI。
class App extends React.Component {
state = {
number: 0
}
// 点击事件,每次点击生成一个随机数
hanldeBtn = () => {
this.setState({
number: Math.floor(Math.random() * 3)
})
}
// 将要更新UI的时候会执行这个钩子函数
shouldComponentUpdate(nextProps,nextState) {
// 判断一下当前生成的 值是否与页面的值相等
if(nextState.number !== this.state.number){
return true
}
return false
}
render() {
return (
<div>
随机数:{this.state.number} <br />
<button onClick={this.hanldeBtn}>生成随机数</button>
</div>
)
}
}
class App extends React.Component {
state = {
number: 0
}
// 点击事件,每次点击生成一个随机数
hanldeBtn = () => {
this.setState({
number: Math.floor(Math.random() * 3)
})
}
render() {
return (
<div>
<NumberBox number={this.state.number} />
<button onClick={this.hanldeBtn}>生成随机数</button>
</div>
)
}
}
class NumberBox extends React.Component {
// 将要更新UI的时候会执行这个钩子函数
shouldComponentUpdate(nextProps, nextState) {
// 判断一下当前生成的 值是否与页面的值相等
if (nextProps.number !== this.props.number) {
return true
}
return false
}
render() {
return (
<h1>随机数:{this.props.number} </h1>
)
}
}
部分更新
,只更新变化的地方虚拟DOM 配合 Diff 算法
react-router-dom最新版本官方文档 建议参照最新文档编程
现代的前端应用大多数是SPA(单页应用程序),也就是只有一个HTML页面的应用程序。因为它的用户体验更好、对服务器压力更小,所以更受欢迎。为了有效的使用单个页面来管理多页面的功能,前端路由应运而生。
yarn add react-router-dom
Router / Route / Link
Router
组件包括整个应用(重要)Link
组件作为导航菜单(路由入口)
这里在使用react-router-dom
时有个注意点, 6.0以下版本和6.0以上版本 route的使用方式不一样,具体使用差异如下:
push(path):
跳转到某个页面,参数path表示要跳转的路径'/'
以上是v6.0版本以下的精准匹配,V6版本内部算法改变,它默认就是匹配完整路径,先后顺序不再重要,它能够自动找出最优匹配路径