前言
react是一种声明式、用于构建用户界面的javascript库。
features
- 组件化 React.Component
class ShoppingList exteds React.Component {
render() {
//jsx语法(对渲染内容的轻量级描述) react元素 js对象,可以用于参数传递或者变量使用 === React.createElement(); ReactDOM.render(jsx,dom)
return (
//xxx
//{this.props.xxx}
//onClick={function() {
xxx
}
)
}
}
- props属性传值
- 交互 onClick
- 状态 state
构造函数中初始化state
constructor(props) {
super(props);
this.state = {
//私有属性
}
}
更改state
this.setState({//});
- 状态提升
将自组价中的状态提升到共同的父组件中进行保存,实现组件的状态数据共享。子组件中不在持有state,从父组件中接收值并且通知父组件更新state,称之为受控组件。
- 事件监听属性on[Event],事件处理handle[Event]
- 不可变性
不直接修改或者改变底层数据,简化复杂的功能。
跟踪数据的变化,直接修改数据会使得跟踪数据的变化变得非常的困难。shouldComponentUpdate
创建 pure组件,可以确实不可变属性何时发生改变,组件何时渲染。
- 函数式组件
只有render,没有state
function Square(props) {
return (
//
);
}
- 练习demo
井字棋、时间旅行
- react元素是javascript一等公民中的对象(first-class javascript objects)
- 动态更新列表时指定一个合适的key,在当前的同一级元素中保持唯一。
- 不能通过this.props.key获取
jsx
标记和组件放在组建中
关注点分离
1.使用字符串标识字符串字面量propName="propValue"
2.可以防止注入攻击xss(cross-site-scripting),渲染内容前会先进行转义,变成字符串
3.babel会将jsx转成一个React.createElement()函数调用
{
type:xx,
props: {
xx:xx
}
}
4.元素是构成react应用的最小砖块
将react元素渲染到根节点DOM中 使用ReactDOM.render(element,document.getELementById("root"))
5.React元素是不可变对象 只会更新实际变化的内容
组件与属性
独立可复用的代码片段
函数组件和class组件
function Xxx(props) {
return xxx;
}
//本质上是一个javascript函数
//es6的class定义组件
class Xxx extends React.Component {
render() {
retuern xxx;
}
}
<Xxx /> //大写字母开头
组合组件 function APP (props) { return (
); } 拆分组件 拆分成更小的组价
Props只读性,不可以更改自身的属性 React的组件都是类似于纯函数不能更改props
state与生命周期
state是组件的私有属性 不能直接修改 构造函数中赋值初始化 state的更新是异步的 浅合并 this.setState((state,props) => ({ xx:state.xx + props.xx }))
componentDidMount 组件挂载 componentWillUnmount 组件卸载
数据是向下流动的 单向 瀑布
事件处理
- 命名是小驼峰
- {this.fn}
- 显示调用e.preventDefault()阻止默认行为
- 回调函数绑定this this.fn = this.fn.bind(this)
1.
fn = () => {
//xxx
}
{this.fn}
2.
fn() {
//
}
{() => this.fn()}
3.传参
{(e) => this.fn(xx,e)}
{this.fn.bind(this,xx)}
条件渲染
- 根据应用的状态渲染对应状态下的不同的内容 if
- 使用元素变量存储元素
- 与运算符 &&
在{}中使用实现条件渲染
- 使用三目运算符实现条件渲染
- 阻止组件渲染
render方法返回null 不影响组件的生命周期
列表和key
key用于告诉react哪些元素改变了
key的信息传给react 组件无法使用
使用map生成列表
表单
受控组件
- 自己维护state
input textarea select
- form
onSubmit
onchange
状态提升
将反应相同变化的共享的状态提升到最近的共同父组件中
组合与继承
推荐使用组合实现代码的复用
使用{props.children}将子组件传递到渲染结果中
自定义 {props.left} left= {} 使用props传递 类似于“槽”的概念
props可以接收任意类型
react的设计哲学
- 组件
- 单一功能
- 先实现一个静态版本,不考虑太多的交互
- ui state的最小表示(完整)、
- 确定state放置的位置
- 添加反向数据流
无障碍
aria-*
语义化的html
<Fragment>
//xxx
</Fragment>
段语法:
<>
//xxx
</>
无障碍的表单
htmlFor
控制焦点
React.createRef()
使用ref控制组件
使用forwardRef向子组件转发ref
代码分割
- 打包
将文件引入并合并成一个单独的文件,形成一个bundle
- 创建多个包,运行时动态加载
使用动态import
import(xx).thne(xx => {
//xx
})
babel-plugin-sync-dynamic-import
- 懒加载
React.lazy(() => import())
<Suspense fallback={xx}></Suspense>
- 异常捕获边界error boundary
- 基于路由的代码分割
1.使用react-router-dom
context
无需添加props,实现组件树直接的数据传递 应用中共享的全局的数据
const ThemeContext = React.creatContext('xxx')
<ThemeContext.provider value="xxx"></ThemeContext.provider> //provider组件 下面的组件都可以使用 从最近匹配到的provider去获取数据 设置xxx.contextType = ThemeContext this.context
不使用context的方式:将使用数据的整个组件作为props传递下去,减少props传递的数量,如果传递多个组件下去,使用多个属性进行传递,类似于slots的东西。
<ThemeContext.consumer>{ value => xx }</ThemeContext.consumer>
使用displayName设置在devTool中显示的名字
* 使用多个context的情况
<ThemeContext1.consumer>{ value1 => (
<ThemeContext2.consumer>{ value2 => (
//使用值的地方
) }</ThemeContext2.consumer>
) }</ThemeContext1.consumer>
错误边界组件
避免部分ui的js错误导致整个应用的崩溃
捕获构造 渲染 生命周期期间发生的错误,使用备用的组件
1.捕获错误信息
componentDidCatch(error) {
//
}
2.渲染错诶的组件
static gerDerivedStateFromError() {
return {} //更新state
}
使用ref转发
1.创建ref
const ref = React.createRef()
2.自定义组件
const Xxx = React.forwardRef((props,ref) => (
<div ref={ref}></div>
{ props.children }
))
3.获取ref指向的dom元素
ref.current
高阶组建中转发ref
属性可以层层透传下去
ref或者key不会传下去
使用forwardRef传到指定的组件
使用Fragment
<React.Fragment>
//可以不用有父标签
</React.Fragment>
or
<></>
循环使用时传递key
高阶组件(HOC)
参数是组件,返回值是组件的函数
使用组合的方式,避免直接修改传入的组件的原型
类似于容器的组件模式
function xx(WrapperComponent) {
return class extends React.Component {
conponentDidMount() {
}
render() {
return (
<WrapperComponent {...this.props}>
</WrapperComponent>
)
}
}
}
//将原组件的静态方法拷贝到新的组件上面
与第三方库一起使用
React一般自己维护dom的更新,如果第三方更新相同的dom,会出现问题。
通过禁止组件的更新来避免冲突
深入jsx的高级用法
<Xxx.Xx>
<xx[xx]>
使用展开运算符赋值整个props
{ ...this.props }
设置只传递部分属性
{ xx,...other } = props
开始和结束标签之间的jsx内容可以通过props.children传递给外层的组件
{}用于js表达式
{ true && <Xxx/> }
性能优化
减少dom的操作
部署时使用生产版本
shouldComponentUpdate
当props或者state发生变化时,比较虚拟dom的变化
diff算法
使用React.PureComponent 实现对属性和状态的浅比较 代替去自行处理shouldComponentUpdate
改变数据时不改变原数据
this.setState(state => (
{
xx: state.xx.concat(['xx'])
}
))
Object.assign({},xx,{xx:xx})
portal
将子节点渲染到父组件之外的节点中
React.createPortal(child,container)
Profiler
可以用于测量渲染的频率和渲染使用的时间 开销
识别渲染比较慢的部分
<Profiler id="xxx" onRender={callback}> </Profiler>
创建React组件的几种方式
1. function Xxx(props) {
return (
//
)
}
2.class Xxx extends React.Component {
constructor(props) {
super(props)
this.state = {
xx:xx
}
//es6的方法需要绑定this
this.xx = this.xx.bind(this)
}
render() {
return (
xxx
)
}
}
//前两种实现方式设置默认props的方式
Xxx.defaultProps = {
xx:xx
}
3.不使用es6的方式
安装create-react-class插件
createReactClass({
render: function() {
return xxx
}
//设置默认属性的方式
getDefaultProps: function() {
return {
xx:xx
}
}
//初始化state
getInitialState: function() {
return {
xx:xx
}
}
//方法会自动绑定到this中不需要绑定
})
使用mixins
相同的功能,生命周期方法
mixins: [xx] //常用的设置定时器和清除定时器
diffing算法
复杂度 O(n^3)
1.根节点类型不同,拆卸原树,重新建树
2.元素相同时候,比较元素的属性,更新改变的属性
3.子组件节点
4.子节点进行递归
refs和dom
1.焦点 文本选择
2.触发强制动画
3.使用第三方的dom库
在html元素 class组件上使用ref,函数组件不能使用ref
回调ref
ref={el => this.xx = el}
渲染属性 render props
<Xxx render={data => (
//返回自定义的渲染函数
// 不渲染自身
//告知组件需要渲染的内容
)}></Xxx>
组件中部分内容的渲染使用回调函数
{ this.props.render(this.state) }
children={ data => (
//
) }
静态类型检查
编译时检测错误或者bug信息
flow typescript
1.安装 npx create-react-app xx --template typescript
2.配置
sctript: {
"buid":"tsc"
}
3.npx tsc --init 生成tsconfig.json文件
{
"compilerOptions": {
"rootDir": xx,
"outDir": xxx
}
}
4.编译器依赖于声明文件
类型声明
declarations.d.ts
严格模式
<React.StrictMode>
//开启严格模式
</React.StrictMode>
1.识别不安全的声明周期方法
2.过时的api
3.错误的findDom方法
react的工作过程:
1.提交 componentDidMount componentDidUpdate;
2.渲染
//使用concurrent模式将渲染拆解成多个部分,实现不阻塞的渲染
react内置的类型检查 propType
声明组件的propsType,
Xxx.propType = {
"xx":PropType.array | bool | object | number | element.isRequired
}
设置默认的属性
XXx.defaultProps = {
xx:xx
}
使用非受控组件
一般表单提交是使用受控组件,也就是将控制权交给react组件,
非受控组件是将控制权交给dom节点
webComponent
react组件是声明式解决方案 ,用户可以自定义一些组件使用
webComponent组件是一些内嵌的组件,这种组件可以像使用标签一样去使用
1.react组件中使用webComponent
render() {
retutn (
<x-search class="xx">
</x-search>
)
}
2.webComponent组件中使用react组件
class Xxx extends HTMLElement {
connectedCallback() {
//创建dom
//将创建的reactDom放到dom中
ReactDom.render()
}
}
核心api讲解
1.顶层对象React 入口
2.ReactDOMServer 将组件渲染成静态标记
hook特性
函数组件没有state也可以实现类似于state的功能
import { useState } from 'react' //钩子 钩住state或者生命周期的函数
const [xx,setXxx] = useState(0);//通过以上的方式初始化使用的状态值以及改变状态值的方法
//类似于state的使用 但是数据格式更加的灵活
EffectHook
增加额外能力 组件挂载 更新
import { useEffect } from 'react';
useEffect(() => {
//类似于componentDidMount componentDidUpdate
//初次和每次渲染后调用一次 使用props state
})
hook使用规则:
1.最外层函数使用
2.react的函数组件中使用