React是一个前端js框架
React高效灵活
声明式设计,使用简单
组件式开发,提高代码的复用率
单向的数据绑定比双向的数据绑定更加安全
1)虚拟dom不会进行重绘和回流,而真实dom会频繁重排与重绘
2)虚拟dom的总损耗是”虚拟dom的增删改+真实dom的差异增删改+重排“;真实dom的消耗是”真实dom全部增删改+重排“
真实dom
优点:直接操作HTML,易用
缺点:
1)解析速度慢,效率低,内存占用量高
2)性能差:频繁操作真实DOM,导致重绘、回流
虚拟dom
优点:
1)减少真实dom的频繁更新,减少重绘回流、占用内存少
2)跨平台:一套react代码可以多端运行
缺点:页面首次渲染时,由于多一层虚拟dom的计算,速度比正常慢些
挂载卸载过程
constructor,完成了React数据的初始化;
componentWillMount,组件初始化数据后,未渲染DOM前;
componentDidMount,组件第一次渲染完成,dom节点已经生成;
componentWillUnmount,组件的卸载和数据的销毁。
更新过程
componentWillReceiveProps (nextProps),父组件改变后的props需要重新渲染组件时;
shouldComponentUpdate(nextProps,nextState),主要用于性能优化(部分更新),
因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候不需要所有子组件都跟着重新渲染,
然后return false就可以阻止组件的更新;
componentWillUpdate (nextProps,nextState),shouldComponentUpdate返回true后,
组件进入重新渲染的流程;
componentDidUpdate(prevProps,prevState),组件更新完毕后触发;
render(),渲染时触发
简单版本
React 利用状态队列机制实现了 setState 的“异步”更新,避免频繁的重复更新 state。
首先将新的 state 合并到状态更新队列中,然后根据更新队列和 shouldComponentUpdate 的状态来判断是否需要更新组件
复杂版本
点我
React并不是将click事件绑定到了div的真实DOM上,而是在document处监听了所有的事件,当事件发生并且冒泡到document处的时候,React将事件内容封装并交由真正的处理函数运行。这样的方式不仅减少了内存的消耗,还能在组件挂在销毁时统一订阅和移除事件。
JSX 上写的事件并没有绑定在对应的真实 DOM 上,而是通过事件代理的方式,将所有的事件都统一绑定在了 document 上。这样的方式不仅减少了内存消耗,还能在组件挂载销毁时统一订阅和移除事件。
另外冒泡到 document 上的事件也不是原生浏览器事件,而是 React 自己实现的合成事件(SyntheticEvent)。因此我们如果不想要事件冒泡的话,调用 event.stopPropagation 是无效的,而应该调用 event.preventDefault。
实现合成事件的目的如下:
合成事件首先抹平了浏览器之间的兼容问题,另外这是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力;
对于原生浏览器事件来说,浏览器会给监听器创建一个事件对象。
如果有很多的事件监听,那么就需要分配很多的事件对象,造成高额的内存分配问题。
但是对于合成事件来说,有一个事件池专门来管理它们的创建和销毁,
当事件需要被使用时,就会从池子中复用对象,
事件回调结束后,就会销毁事件对象上的属性,从而便于下次复用事件对象
1)父组件向子组件传递:
父组件在调用子组件的时候,在子组件标签内传递参数,子组件通过props属性就能接收父组件传递过来的参数
2)子组件向父组件传递:
父组件向子组件传一个函数,然后通过这个函数的回调,拿到子组件传过来的值
3)兄弟组件之间的通信:
父组件作为中间层来实现数据的互通,通过使用父组件传递
4)父组件向后代组件传递:
使用context提供了组件之间通讯的一种方式,可以共享数据,其他数据都能读取对应的数据
通过使用React.createContext创建一个context;
context创建成功后,其下存在Provider组件用于创建数据源,Consumer组件用于接收数据
Provider组件通过value属性用于给后代组件传递数据
如果想要获取Provider传递的数据,可以通过Consumer组件或者或者使用contextType属性接收
5)非关系组件传递:
将数据进行一个全局资源管理,从而实现通信
1)受控组件:指的是这个值受到setState()的影响,当页面刷新时,它会根据是值是否变化进行刷新
2)非受控组件:指的是不受setState()的影响,像input框中的值,只取决于本身DOM节点的值,从新属性是值不发生变化
Fiber 可以理解为一个执行单元,每次执行完一个执行单元,react 就会检查现在还剩多少时间,如果没有时间则将控制权让出去。
首先 React 向浏览器请求调度,浏览器在一帧中如果还有空闲时间,会去判断是否存在待执行任务,不存在就直接将控制权交给浏览器,如果存在就会执行对应的任务,执行完成后会判断是否还有时间,有时间且有待执行任务则会继续执行下一个任务,否则就会将控制权交给浏览器。
1)Diff算法是虚拟DOM的一个必然结果,它是通过新旧DOM的对比,
将在不更新页面的情况下,将需要内容局部更新
2)Diff算法遵循深度优先,同层比较的原则
3)可以使用key值,可以更加准确的找到DOM节点
react中diff算法主要遵循三个层级的策略:
tree层级
conponent 层级
element 层级
tree层不会做任何修改,如果有不一样,直接删除创建
component层从父级往子集查找,如果发现不一致,直接删除创建
element层有key值做比较,如果发现key值可以复用的话,就会将位置进行移动,如果没有,则执行删除创建
1)中间件(Middleware)在计算机中,是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的
2)redux-thunk:用于异步操作
3)redux-logger:用于日志记录
4)redux中间件就是辅助redux不能完成的功能
5)applayMiddleWare()
6)将它包裹在要使用的插件外部
.box {/* 内部大小 */width: 0px;height: 0px;/* 边框大小 只设置两条边*/border-top: #4285f4 solid;border-right: transparent solid;border-width: 85px; /* 其他设置 */margin: 50px;
}
浏览器首次请求资源后,需要再次请求时,浏览器会首先获取该资源缓存的header信息,
然后根据Cache-Control和expires来判断该资源在本地缓存否过期
没过期:直接从本地缓存中获取资源信息,浏览器就不再向服务器重新请求资源,
过期:需重新发送请求,重新缓存资源,更新缓存时间
强缓存是利用http请求头中的Expires和Cache-Control两个字段来进行控制,用来表示资源的缓存时间
协商缓存是服务器用来确定缓存资源是否可用过期
因为服务器需要向浏览器确认缓存资源是否可用,二者要进行通信,
而通信的过程就是发送请求,所以在header中就需要有专门的标识来让服务器确认请求资源是否可以缓存访问,
所以就有了两组header字段
Etag和If-None-Match
Last-Modified和If-Modified-Since
使用React.createElement或JSX编写React组件,实际上所有的 JSX 代码最后都会转换成React.createElement(…) ,Babel帮助我们完成了这个转换的过程。
createElement函数对key和ref等特殊的props进行处理,并获取defaultProps对默认props进行赋值,并且对传入的孩子节点进行处理,最终构造成一个虚拟DOM对象
ReactDOM.render将生成好的虚拟DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM
reduxjs/toolkit:
Redux 官方强烈推荐,开箱即用的一个高效的 Redux 开发工具集。它旨在成为标准的 Redux 逻辑开发模式,使用 Redux Toolkit 都可以优化你的代码,使其更可维护
react-redux:
react官方推出的redux绑定库,react-redux将所有组件分为两大类:UI组件和容器组件,其中所有容器组件包裹着UI组件,构成父子关系。容器组件负责和redux交互,里面使用redux API函数,UI组件负责页面渲染,不使用任何redux API。容器组件会给UI组件传递redux中保存对的状态和操作状态的方法 #
原理:
在类组件中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 = 0return function(...args) {if (timer) clearTimeout(timer)timer = setTimeout(() => {func.apply(this, args)}, wait)}
}
节流:
export const throttle = function (fn, wait = 500) {let flg = truereturn function () {if (!flg) return;flg = falsesetTimeout(() => {fn.apply(this, arguments)flg = true}, wait);}
}
style-loader: 将css添加到DOM的内联样式标签style里
css-loader :允许将css文件通过require的方式引入,并返回css代码
less-loader: 处理less
sass-loader: 处理sass
postcss-loader: 用postcss来处理CSS
autoprefixer-loader: 处理CSS3属性前缀,已被弃用,建议直接使用postcss
file-loader: 分发文件到output目录并返回相对路径
url-loader: 和file-loader类似,但是当文件小于设定的limit时可以返回一个Data Url
html-minify-loader: 压缩HTML
babel-loader :用babel来转换ES6文件到ES
JS代码压缩
CSS代码压缩
Html文件代码压缩
文件大小压缩
图片压缩
Tree Shaking
代码分离
内联 chunk
意外使用全局变量(比如函数内部使用全局变量)
定时器不清除,一直占用内存,得不到释放
或者在定时器内调用外部函数,得不到释放
闭包,内部函数引用外部函数变量,得不到释放
不清理dom元素的引用
监听事件的解除,监听的时候addEventListener,在不监听的时候要使用