(1)用于构建用户界面的 JavaScript
库,只提供了 UI
层面的解决方案
(2)遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效
(3)使用虚拟 DOM
来有效地操作 DOM
,遵循从高阶组件到低阶组件的单向数据流
(4)帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面
(5)react
类组件使用一个名为 render()
的方法或者函数组件return
,接收输入的数据并返回需要展示的内容
特性:
JSX 语法
单向数据绑定
虚拟 DOM
声明式编程
Component
区别:
(1)虚拟 DOM
不会进行排版与重绘操作,而真实 DOM
会频繁重排与重绘
(2)虚拟 DOM
的总损耗是“虚拟 DOM
增删改+真实 DOM
差异增删改+排版与重绘”,真实 DOM
的总损耗是“真实 DOM
完全增删改+排版与重绘”
优缺点:
真实dom:
优点: 易用
缺点:
真实 DOM
,易于导致重绘与回流虚拟dom:
优点:
缺点:
创建阶段:
constructor
getDerivedStateFromProps
render
componentDidMount
更新阶段
getDerivedStateFromProps
shouldComponentUpdate
render
getSnapshotBeforeUpdate
componentDidUpdate
销毁阶段
componentWillUnmount
在 React
中,如果是由 React
引发的事件处理(比如通过 onClick
引发的事件处理),调用 setState
不会同步更新 this.state
,除此之外的 setState
调用会同步更新 this.state
所谓的除此之外,指的是绕过 React
,通过 addEventListener
直接添加的事件处理函数,还有通过 setTimeout
/setInterval
产生的异步任务
React
基于浏览器的事件机制自身实现了一套事件机制,包括事件注册
、事件的合成
、事件冒泡
、事件派发
等,在React
中这套事件机制被称之为合成事件
它所有的对象最终会被挂载到顶级对象上,其他任何dom
节点上绑定的事件均无效
父组件向子组件传递:
父组件在调用子组件的时候,在子组件标签内传递参数,子组件通过props
属性就能接收父组件传递过来的参数
子组件向父组件传递:
父组件向子组件传一个函数,然后通过这个函数的回调,拿到子组件传过来的值
兄弟组件之间的通信:
父组件作为中间层来实现数据的互通,通过使用父组件传递
父组件向后代组件传递:
使用context
提供了组件之间通讯的一种方式,可以共享数据,其他数据都能读取对应的数据
通过使用React.createContext
创建一个context
;
context
创建成功后,其下存在Provider组件用于创建数据源,Consumer
组件用于接收数据
Provider
组件通过value
属性用于给后代组件传递数据
如果想要获取Provider
传递的数据,可以通过Consumer
组件或者或者使用contextType
属性接收
非关系组件传递:
将数据进行一个全局资源管理,从而实现通信
受控组件:即通过
setState
的形式控制输入的值及更新,
非受控组件:即通过dom
的形式更新值,要获取其值可以通过ref
的形式去获取。
受控组件:
简单来讲,就是受我们控制的组件,组件的状态全程响应外部数据
class TestComponent extends React.Component {
constructor (props) {
super(props);
this.state = { username: 'lindaidai' };
}
render () {
return <input name="username" value={this.state.username} />
}
}
当我们在输入框输入内容的时候,会发现输入的内容并无法显示出来,也就是input
标签是一个可读的状态
这是因为value
被this.state.username
所控制住。当用户输入新的内容时,this.state.username
并不会自动更新,这样的话input
内的内容也就不会变了
如果想要解除被控制,可以为input
标签设置onChange
事件,输入的时候触发事件函数,在函数内部实现state
的更新,从而导致input
框的内容页发现改变
非受控组件:
简单来讲,就是不受我们控制的组件
一般情况是在初始化的时候接受外部数据,然后自己在内部存储其自身状态
当需要时,可以使用ref
查询 DOM
并查找其当前值
解决的问题:
JavaScript
引擎和页面渲染引擎两个线程是互斥的,当其中一个线程执行时,另一个线程只能挂起等待;如果 JavaScript
线程长时间地占用了主线程,那么渲染层面的更新就不得不长时间地等待,界面长时间不更新,会导致页面响应度变差,用户可能会感觉到卡顿
理解:
React Fiber
是对 React
做出的一个重大改变与优化,是对 React
核心算法的一次重新实现
主要做了:
requestIdleCallback api
,浏览器空闲的时候执行dom diff
树变成了链表,一个dom
对应两个fiber
(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行react
中diff
算法主要遵循三个层级的策略:
tree层
不会做任何修改,如果有不一样,直接删除创建
component层
从父级往子集查找,如果发现不一致,直接删除创建
element层
有key
值做比较,如果发现key
值可以复用的话,就会将位置进行移动,如果没有,则执行删除创建
理解:
中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的
Redux
中,中间件就是放在就是在dispatch
过程,在分发action
进行拦截处理
本质上一个函数,对store.dispatch
方法进行了改造,在发出 Action
和执行 Reducer
这两步之间,添加了其他功能
常用中间件:
redux-thunk
:用于异步操作
redux-logger
:用于日志记录
实现原理:
所有中间件被放进了一个数组chain
,然后嵌套执行,最后执行store.dispatch
。可以看到,中间件内部(middlewareAPI
)可以拿到getState
和dispatch
这两个方法
内部会将dispatch
进行一个判断,然后执行对应操作
只需要设置上、左、右三条边即可,并且要上三角,就把左右border
设为透明
.a{
Width:0px
Height:0px
Border-top:100px solid ‘red’
Border-left:100px solid ‘red’
Border-right:100px solid ‘red’
}
强缓存是利用http
请求头中的Expires
和Cache-Control
两个字段来进行控制,用来表示资源的缓存时间;浏览器不会像服务器发送任何请求,而是直接从本地缓存中读取文件并返回Status Code
: 200 OK;
协商缓存是服务器用来确定缓存资源是否可用过期,向服务器发送请求,服务器会根据这个请求的request header
的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header
通知浏览器从缓存中读取资源
使用React.createElement
或JSX
编写React
组件,实际上所有的 JSX
代码最后都会转换成React.createElement(...)
,Babel
帮助我们完成了这个转换的过程。
createElement
函数对key
和ref
等特殊的props
进行处理,并获取defaultProps
对默认props
进行赋值,并且对传入的孩子节点进行处理,最终构造成一个虚拟DOM对象
ReactDOM.render
将生成好的虚拟DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM
react-redux:
react
官方推出的redux
绑定库,react-redux
将所有组件分为两大类:UI组件和容器组件,其中所有容器组件包裹着UI组件,构成父子关系。容器组件负责和redux
交互,里面使用redux API
函数,UI组件负责页面渲染,不使用任何redux API
。容器组件会给UI组件传递redux中保存对的状态和操作状态的方法
reduxjs/toolkit:
Redux
官方强烈推荐,开箱即用的一个高效的 Redux
开发工具集。它旨在成为标准的 Redux
逻辑开发模式,使用 Redux Toolkit
都可以优化你的代码,使其更可维护
原理:
在类组件中render
函数指的就是render
方法;而在函数组件中,指的就是整个函数组件
render
函数中的jsx
语句会被编译成我们熟悉的js代码,在render
过程中,react
将新调用的render
函数返回的树与旧版本的树进行比较,这一步是决定如何更新 DOM 的必要步骤,然后进行 diff
比较,更新dom树
触发时机:
类组件调用 setState
修改状态
函数组件通过useState hook
修改状态
一旦执行了setState
就会执行render
方法,useState
会判断当前值有无发生改变确定是否执行render
方法,一旦父组件发生渲染,子组件也会渲染
1.避免使用内联函数
2.使用react fragement
避免额外标记
3.使用immutable
,减少渲染的次数,为了避免重复渲染,会在shouldComponentUpdate()
中做对比,当返回true
,执行render
方法。immutable
通过is方法完成对比
4.懒加载组件
5.事件绑定方式(在constructor
中使用bind
绑定性能更高)
6.服务端渲染
7.组件拆分,合理使用hooks
防抖:
const debounce = (func, wait = 50) => {
let timer = 0
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
节流:
export const throttle = function (fn, wait = 500) {
let flg = true
return function () {
if (!flg) return;
flg = false
setTimeout(() => {
fn.apply(this, arguments)
flg = true
}, wait);
}
}
常见loader
:
css-loader
:分析css
模块之间的关系,并合成⼀个css
style-loader
:把css-loader
生成的内容,用style
标签挂载到页面的head
中
less-loader
:开发中,我们也常常会使用less
、sass
、stylus
预处理器编写css
样式,使开发效率提高,这里需要使用less-loader
raw-loader
:在webpack
中通过import
方式导入文件内容,该loader
并不是内置的,所以首先要安装,然后在 webpack.config.js
中进行配置
file-loader
:把识别出的资源模块,移动到指定的输出⽬目录,并且返回这个资源在输出目录的地址(字符串)
url-loader
:可以处理理file-loader
中的所有事情,但是遇到图片格式的模块,可以选择性的把图片转成base64
格式的字符串,并打包到js
中,对小体积的图片比较合适,大图片不合适
devtool
属性值ParallelUglifyPlugin
代替自带的 UglifyJsPlugin
插件fast-sass-loader
代替sass-loader
babel-loader
开启缓存
标签引入的方式DllPlugin
和 DllReferencePlugin
HappyPack
来加速构建JSON
文件)ModuleConcatenationPlugin
插件来加快JS
执行速度noParse
闭包、定时器、函数内部的this调用污染、全局变量污染
call
、apply
、bind
都是用于实现this
指向改变的方式
区别:
call
(参数1,参数2,参数3,…) :参数1代表this
指向改变的方向,后面的参数代表传递的参数
apply
(参数1,[a,b,c]):参数1代表this
指向改变的方向,第二个参数是一个数组,数组中的内容代表要传递的参数
bind
(参数1,…args):参数1代表this
指向改变的方向,第二个参数代表传递的参数,可以分批传入,有函数作为返回值,需要调用后才会生效
实现一个bind
:
//this指向window
function fn() {
console.log(this);
}
let a = 123
// this指向预改变
const changeFn = fn.bind(a);
changeFn() // 调用返回函数,this指向a
fn()