react V15在渲染时,会递归比对VirtualDOM树,找出需要变动的节点,然后同步更新他们,一气呵成.这个过程期间,react会间距浏览器资源,这回导致用户触发的时间得不到响应,并且会导致掉帧,导致用户感觉卡顿。
为了给用户制造一种应用很快的“假象”,不能让一个任务长期霸占着资源。可以将浏览器的渲染、布局、绘制、资源加载(liru HTML解析)、时间相应、脚本执行视作操作题痛的“进程”,需要通过某些调度策略合理地分配CPU资源 从而提高浏览器的用户相应速率,同时兼顾任务执行效率。
所以React通过Fiber架构,让这个执行过程变成可被中断。“适时”地让出CPU执行权,除了可以让浏览器及时的响应用户的交互,还有其他好处:
2.1什么是WebSocket?
** WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)
它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的Websocket是一个持久化的协议
WebSocket原理
客户端向 WebSocket 服务器通知一个带有所有接收者ID的事件,服务器接收后立即通知所有活跃的客户端,只有ID在接收者ID序列中的客户端才会处理这个事件。
WebSocket特点
支持双向通信,实时性更强
可以发送文本,也可以发送二进制数据‘’
建立在TCP协议之上,服务端的实现比较容易
数据格式比较轻量,性能开销小,通信高效
没有同源限制,客户端可以与任意服务器通信
协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且 握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
应用场景
基于websocket的事实通信的特点,其存在的应用场景大概有:
弹幕
媒体聊天
协同编辑
基于位置的应用
体育实况更新
股票基金报价实时更新
4.1什么是洋葱模型。
Koa的洋葱模型是以next()函数为分割点,先由外到内执行Request的逻辑,然后再由内到外执行Response的逻辑,这里的request的逻辑,我们可以理解为是next之前的内容,response的逻辑是next函数之后的内容,也可以说每一个中间件都有两次处理时机。洋葱模型的核心原理主要是借助compose方法
major、minor、patch
主版本号、次版本号、修补版本号
patch:修复bug,兼容老版本
minor:新增功能,兼容老版本
major:新的架构调整,不兼容老版本
这三个依赖分别使用了三个符号来表明依赖的版本范围。
*:升级到最新版本
^:升级次版本号和修订号
~:只升级修订号
getDerivedStateFromProps
容易编写反模式代码,使受控组件和非受控组件区分模糊componentWillMount
在 React
中已被标记弃用,不推荐使用,主要的原因是因为新的异步架构会导致它被多次调用,所以网络请求以及事件绑定应该放到 componentDidMount
中componentWillReceiveProps
同样也被标记弃用,被 getDerivedStateFromProps
所取代,主要原因是性能问题。shouldComponentUpdate
通过返回 true
或者 false
来确定是否需要触发新的渲染。主要用于性能优化。componentWillUpdate
同样是由于新的异步渲染机制,而被标记废弃,不推荐使用,原先的逻辑可结合 getSnapshotBeforeUpdate
与 componentDidUpdate
改造使用。componentWillUnmount
函数中忘记解除事件绑定,取消定时器等清理操作,容易引发 bug
。Diff
算法是虚拟DOM
的一个必然结果,它是通过新旧DOM
的对比,将在不更新页面的情况下,将需要内容局部更新Diff
算法遵循深度优先,同层比较的原则key
值,可以更加准确的找到DOM
节点react
中diff
算法主要遵循三个层级的策略:tree
层不会做任何修改,如果有不一样,直接删除创建component
层从父级往子集查找,如果发现不一致,直接删除创建element
层有key值做比较,如果发现key
值可以复用的话,就会将位置进行移动,如果没有,则执行删除创建(1)代码中调用 setState
函数之后,React
会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation
)。
(2)经过调和过程,React
会以相对高效的方式根据新的状态构建 React
元素树并且着手重新渲染整个 UI 界面;
(3)在 React
得到元素树之后,React
会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染;
(4)在差异计算算法中,React
能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
原理
1. 将应用的状态统一放到state
中,由store
来管理state
。
2. reducer
的作用是 返回一个新的state
去更新store
中对用的state
。
3. 按redux
的原则,UI层
每一次状态的改变都应通过action
去触发,action
传入对应的reducer
中,reducer
返回一个新的state
更新store
中存放的state
,这样就完成了一次状态的更新
4. subscribe
是为store
订阅监听函数,这些订阅后的监听函数是在每一次dipatch
发起后依次执行
5. 可以添加中间件对提交的dispatch
进行重写
核心API
6. createStore
创建仓库,接受reducer
作为参数
7. bindActionCreator
绑定store.dispatch
和action
的关系
8. combineReducers
合并多个reducers
9. applyMiddleware
洋葱模型的中间件,介于dispatch
和action
之间,重写dispatch
10. compose
整合多个中间件
React
并不是将click事件绑在该div
的真实DOM
上,而是在document
处监听所有支持的事件,当事件发生并冒泡至document
处时,React
将事件内容封装并交由真正的处理函数运行。
收集的事件放在dispatchQueue
数组中,而冒泡和捕获的区别在于执行时机和顺序,那么我们只需要对数组按照不同顺序循环执行即可
首先会在fiber
节点进入render
阶段的complete
阶段时,将事件监听绑定在root
上。然后调用ensureListeningTo
进行事件绑定,生成事件合成对象、收集事件、触发真正的事件。
React 合成事件(SyntheticEvent)是 React 模拟原生 DOM 事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器。它根据 W3C 规范 来定义合成事件,兼容所有浏览器,拥有与浏览器原生事件相同的接口。
将事件绑定在document - v16/容器元素 - v17统一管理,防止很多事件直接绑定在原生的dom元素上。造成一些不可控的情况
React 想实现一个全浏览器的框架, 为了实现这种目标就需要提供全浏览器一致性的事件系统,以此抹平不同浏览器的差异。
props
的方式,向子组件进行通讯。props
+回调的方式,父组件向子组件传递props
进行通讯,此props
为作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中。Context
设计⽬的是为了共享那些对于⼀个context
提供了组件之间通讯的一种方式,可以共享数据,其他数据都能读取对应的数据Context
通信再适合不过。event
模块进⾏通信。Redux
或者Mobx
等全局状态管理⼯具进⾏通信,这种⼯具会维护⼀个全局状态中⼼Store
,并根据不同的事件产⽣新的状态 目的是为了防止 XSS 攻击
。因为 Synbol
无法被序列化,所以 React
可以通过有没有 $$typeof
属性来断出当前的 element
对象是从数据库来的还是自己生成的。如果没有 $$typeof
这个属性,react
会拒绝处理该元素。
connect
是一个高阶函数,它真正连接 Redux
和 React
,包在我们的容器组件的外一层,接收上面 Provider
提供的 store
里面的 state
和 dispatch
,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
原理:
首先传入mapStateToProps
、mapDispatchToProps
,然后返回一个生产Component
的函数(wrapWithConnect
),然后再将真正的Component
作为参数传入wrapWithConnect
,这样就生产出一个经过包裹的Connect
组件,该组件具有:
(1)通过props.store
获取祖先Component
的store
(2)props
包括stateProps
、dispatchProps
、parentProps
,合并在一起得到nextState
,作为props
传给真正的Component
(3)componentDidMount
时,添加事件this.store.subscribe(this.handleChange)
,实现页面交互
(4)shouldComponentUpdate
时判断是否有避免进行渲染,提升页面性能,并得到nextState
(5)componentWillUnmount
时移除注册的事件this.handleChange
首先connect之所以会成功,是因为Provider组件:
理解:
中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的
Redux
中,中间件就是放在就是在dispatch
过程,在分发action
进行拦截处理
本质上一个函数,对store.dispatch
方法进行了改造,在发出 Action
和执行 Reducer
这两步之间,添加了其他功能
常用中间件:
redux-thunk
:用于异步操作
redux-logger
:用于日志记录
实现原理:
所有中间件被放进了一个数组chain
,然后嵌套执行,最后执行store.dispatch
。可以看到,中间件内部(middlewareAPI
)可以拿到getState
和dispatch
这两个方法内部会将dispatch
进行一个判断,然后执行对应操作
React.memo
进行组件记忆(React.memo
是一个高阶组件),对 于相同的输入,不重复执行;shouldComponentUpdate
(这是在重新渲染组件之前触发的其中一个生命周期事件)生命周期事件,可以利用此事件来决定何时需要重新渲染组件;React Fragments
避免额外标记;render
”函数时都会创建一个新的函数实例);Willxxx
系列的生命周期中进行异步请求,操作dom
等;Constructor
中绑定bind
改变this
指向;React
中的条件渲染;render
方法中导出数据;key
;useCallback
和useMemo
来进行组件优化,依赖没有变化的话,不重复执行;immutable
对象; 任务在主线程不断进栈出栈的一个循环过程。任务会在将要执行时进入主线程,在执行完毕后会退出主线程。
JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,
但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环
在JavaScript中,所有的任务都可以分为
同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等
异步任务还可以细分为微任务与宏任务
微任务一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前
宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合
一、什么是跨域?
在前端领域中,跨域是指浏览器允许向服务器发送跨域请求,从而克服Ajax只能同源使用的限制。
二、什么是同源策略?
同源策略是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
同源策略限制以下几种行为:
Cookie、LocalStorage 和 IndexDB 无法读取
DOM和JS对象无法获得
AJAX 请求不能发送
三、9种跨域解决方案
1、JSONP跨域
2、跨域资源共享(CORS)
3、nginx代理跨域
4、nodejs中间件代理跨域
5、document.domain+ iframe跨域
6、location.hash + iframe跨域
7、window.name + iframe跨域
8、postMessage跨域
9、WebSocket协议跨域
小结
以上就是9种常见的跨域解决方案,jsonp(只支持get请求,支持老的IE浏览器)适合加载不同域名的js、css,img等静态资源;CORS(支持所有类型的HTTP请求,但浏览器IE10以下不支持)适合做ajax各种跨域请求;Nginx代理跨域和nodejs中间件跨域原理都相似,都是搭建一个服务器,直接在服务器端请求HTTP接口,这适合前后端分离的前端项目调后端接口。document.domain+iframe适合主域名相同,子域名不同的跨域请求。postMessage、websocket都是HTML5新特性,兼容性不是很好,只适用于主流浏览器和IE10+。
Array.length:
返回或设置一个数组中的元素个数Array.from() :
对伪数组或可迭代对象(包括arguments
,Array
,Map
,Set
,String
…)转换成数组对象Array.isArray():
用于确定传递的值是否是一个 Array
concat():
方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。every(callback):
方法测试数组的所有元素是否都通过了指定函数的测试filter():
创建一个新数组, 其包含通过所提供函数实现的测试的所有元素find():
返回数组中满足提供的测试函数的第一个元素的值forEach():
方法对数组的每个元素执行一次提供的函数includes():
用来判断一个数组是否包含一个指定的值,如果是,酌情返回 true
或 false
indexOf():
返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1join():
将数组(或一个类数组对象)的所有元素连接到一个字符串中lastIndexOf():
返回指定元素(也即有效的 JavaScript
值或变量)在数组中的最后一个的索引,如果 不存在则返回 -1。从数组的后面向前查找map():
创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果pop():
从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度push():
将一个或多个元素添加到数组的末尾reduce():
累加器和数组中的每个元素(从左到右)应用一个函数shift():
从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度slice():
返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象some():
测试数组中的某些元素是否通过由提供的函数实现的测试。sort():
当的位置对数组的元素进行排序,并返回数组。splice():
通过删除现有元素和/或添加新元素来更改一个数组的内容toString():
返回一个字符串,表示指定的数组及其元素unshift():
将一个或多个元素添加到数组的开头,并返回新数组的长度toLocaleString():
返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString
方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 “,”)隔开原理:
在类组件中render
函数指的就是render
方法;而在函数组件中,指的就是整个函数组件
render
函数中的jsx
语句会被编译成我们熟悉的js代码,在render
过程中,react
将新调用的render
函数返回的树与旧版本的树进行比较,这一步是决定如何更新 DOM 的必要步骤,然后进行 diff
比较,更新dom树
触发机:
类组件调用 setState
修改状态
函数组件通过useState hook
修改状态
一旦执行了setState
就会执行render
方法,useState
会判断当前值有无发生改变确定是否执行render
方法,一旦父组件发生渲染,子组件也会渲染
mixin
是一种类,在vue
中就是js
文件,主要的作用是作为功能模块引用。因为在项目中,可能不同组件会有相同的功能,比如控制元素的显示和隐藏,如果他们的变量和规则也完全相同的话,就可以把这个功能单独提取出来,放在mixin.js
中,再引入,就可以实现一样的功能了。引入的方法也分为全局混入和局部混入,局部混入就是在每个组件中引入,全局混入就是在main.js中通过Vue.mixin()
引入。
for...in
循环:只能获得对象的键名,不能获得键值
for…in
循环有几个缺点
①数组的键名是数字,但是for…in
循环是以字符串作为键名“0”、“1”、“2”等等。
②for…in
循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
③某些情况下,for…in
循环会以任意顺序遍历键名。
for…in
循环主要是为遍历对象而设计的,不适用于遍历数组。
for...of
循环:允许遍历获得键值
for…of
循环
①有着同for…in
一样的简洁语法,但是没有for…in
那些缺点。
②不同于forEach
方法,它可以与break
、continue
和return
配合使用。
③提供了遍历所有数据结构的统一操作接口
1. typeof
判断
typeof
返回的类型都是字符串形式
2. Constructor
实例constructor
属性指向构造函数本身
constructor
判断方法跟instanceof
相似,但是constructor
检测Object
与instanceof
不一样,constructor
还可以处理基本数据类型的检测,不仅仅是对象类型
3. Instanceof
instanceof
可以判类型是否是实例的构造函数
instanceof
后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。
4. Object.prototype.toString.call()
判断类型的原型对象是否在某个对象的原型链上
5. 通过object
原型上的方法判断
比如array.isArray()
来判断是不是一个数组
6. ===(严格运算符)
通常出现在我们的条件判断中,用来判断数据类型的话就会非常的有局限性,比如判断一个变量是否为空,变量是否为数据等
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
该方法接受三个参数
obj
:要定义属性的对象,prop
:要定义或修改的属性的名称或 Symbol
,descriptor
:要定义或修改的属性描述符descriptor
所表示的属性描述符有两种形式:数据描述符和存取描述符。getter
函数和 setter
函数所描述的属性。configurable
: 是否可以删除目标属性或是否可以再次修改属性的特性(writable
, configurable
, enumerable
)。设置为true
可以被删除或可以重新设置特性;设置为false
,不能被可以被删除或不可以重新设置特性。默认为false
。enumerable
: 当且仅当该属性的 enumerable
键值为 true
时,该属性才会出现在对象的枚举属性中。默认为 false
。不同点:
1.props不可以在组件内部修改,但state可以在组件内部修改
2.可以从父组件修改自组件的props,而不能从父组件修改自组件的state
相同点:
1.props和state都是导出HTML的原始数据。
2.props和state都是确定性的,如果我们写的组件为同一props和state的组合生成了不同的输出,那木我们肯定在哪里做错了
4.props和state都会触发渲染更新
5.props和state都是纯JS对象(用typeof来判断,结果都是object)
6.可以从父组件得到初始值props和state的初始值
shouldComponentUpdate () 的返回值用于判断 React 组件的输出是否受当前 state 或 props 更改的影响,当 props 或 state 发生变化时,shouldComponentUpdate () 会在渲染执行之前被调用。
(1)挂载阶段:
constructor() 在 React 组件挂载之前,会调用它的构造函数。
componentWillMount: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用
(2)更新运行阶段:
componentWillReceiveProps: 在接受父组件改变后的props需要重新渲染组件时用到的比较多,外部组件传递频繁的时候会导致效率比较低
shouldComponentUpdate():用于控制组件重新渲染的生命周期,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
render(): render() 方法是 class 组件中唯一必须实现的方法。
componentWillUpdate()*: shouldComponentUpdate返回true以后,组件进入重新渲染完成之前进入这个函数。
componentDidUpdate(): 每次state改变并重新渲染页面后都会进入这个生命周期
(3)卸载或销毁阶段:
componentWillUnmount (): 在此处完成组件的卸载和数据的销毁。
是浏览器对之前请求过的文件进行缓存,以便下一次访问时重复使用,节省带宽,提高访问速度,降低服务器压力 HTTP 1.0协议中的。简而言之,就 是告诉浏览器在约定的这个时间前,可以直接从缓存中获取资源而无需跑到服务器去获取。 http缓存机制主要在http响应头中设定,响应头中相关字段为Expires、Cache-Control、Last-Modified、Etag
浏览器不会像服务器发送任何请求,直接从本地缓存中读取文件并返回Status Code: 200 OK
jsx是JavaScript的一种语法扩展,它跟模板语言很接近,但是它充分具备JavaScript的能力
JSX就是用来声明React当中的元素,React使用JSX来描述用户界面
JSX语法糖允许前端开发者使用我们最熟悉的类HTML标签语法来创建虚拟DOM在降低学习成本
CommonJS用同步的方式加载模块。在服务端,模块文件都存放在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。
AMD规范采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行
CMD是另一种js模块化方案,它与AMD很类似,不同点在于:AMD推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。
指令系统是计算机硬件的语言系统,也叫机器语言,它是系统程序员看到的计算机的主要属性。因此指令系统表征了计算机的基本功能决定了机器所要求的能力
注册一个自定义指令有全局注册与局部注册
全局注册主要是通过Vue.directive
方法进行注册
Vue.directive
第一个参数是指令的名字(不需要写上v-
前缀),第二个参数可以是对象数据,也可以是一个指令函数
局注册通过在组件options
选项中设置directive
属性
然后你可以在模板中任何元素上使用新的 v-focus
property,如下:
分片传
Koa
的洋葱模型指的是以 next()
函数为分割点,先由外到内执行 Request
的逻辑,再由内到外执行 Response
的逻辑。通过洋葱模型,将多个中间件之间通信等变得更加可行和简单
在洋葱模型中,每一层相当于一个中间件,用来处理特定的功能, 也就是说每一个中间件都有两次处理时机
新生命周期中新增了两个钩子,分别为getDerivedStateFromProps(从props中得到衍生的state)和getSnapshotBeforeUpdate。
区别
1、componentWillMount中可能需要做的事(一些数据初始化的操作就应该放在这个钩子中处理),constructor与componentDidMount也能做,甚至做的更好,此方法被废弃。
2、componentWillReceiveProps实际行为与命名并不相符,由于不稳定性已由getDerivedStateFromProps代替;
3、而componentWillUpdate同等理由被getSnapshotBeforeUpdate代替
key 当同一层级的某个节点添加了对于其他同级节点唯一的key属性,当它在当前层级的位置发生了变化后。react diff算法通过新旧节点比较后,如果发现了key值相同的新旧节点,就会执行移动操作(然后依然按原策略深入节点内部的差异对比更新),而不会执行原策略的删除旧节点,创建新节点的操作。
把不是通过路由切换过来的组件中,将react-router 的 history、location、match 三个对象传入props对象上
不是所有组件都直接与路由相连(比如拆分的子组件)的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,将react-router的history、location、match三个对象传入props对象上,此时就可以使用this.props.history跳转页面了或者接受参数了
在reactJS中props.children不一定是数组
有三种可能 :
1当前组件没有子节点数据类型就是undefined,
2有一个子节点数据类型就是object 。
3 有多个子节点的时候才会是array ,只有在多个节点的时候才可以直接调用map方法,react资深提供了一个react.children.map()方法,可以安全遍历子节点对象。
Immutable.js采用了 持久化数据结构 ,保证每一个对象都是不可变的,任何添加、修改、删除等操作都会生成一个新的对象,且通过 结构共享 等方式大幅提高性能
同步: Redux的教程中反复使用todo列表的例子,那就是个典型的同步action,每当disptach action时,state就会被立即更新[当然setState是异步的]
异步: 一般异步处理都会使用中间件,比如redux-thunk或者redux-saga,他们做的事情是包装dispatch,request action由view触发,receive action由这些中间件触发
redux-thunk:通过执行action中的函数实现业务逻辑,没有拓展API
redux-saga:通过定义saga函数进行监控,同时提供一些常用的API
redux-thunk将部分异步处理业务逻辑写在action中,redux-sagasaga则是放在监控的函数中。
DN的功能特点:
(1)节省骨干网带宽,减少带宽需求量;
(2)提供服务器端加速,解决由于用户访问量大造成的服务器过载问题;
(3)服务商能使用Web
Cache技术在本地缓存用户访问过的Web页面和对象,实现相同对象的访问无须占用主干的出口带宽,并提高用户访问因特网页面的相应时间的需求;
(4)能克服网站分布不均的问题,并且能降低网站自身建设和维护成本;
(5)降低“通信风暴”的影响,提高网络访问的稳定性
意义
使用CDN可以获取一些好处,无论它们是公有CDN还是提供静态内容的私有CDN,你的里程可能会有所不同,具体取决于通过CDN传递的流量以及你产生的流量。
React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案
遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效
使用虚拟DOM来有效地操作DOM,遵循从高阶组件到低阶组件的单向数据流
帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面
react 类组件使用一个名为 render() 的方法或者函数组件return,接收输入的数据并返回需要展示的内容
特性
React特性有很多,如:
JSX语法
单向数据绑定
虚拟DOM
声明式编程
Component
1.父组件通过 props 向子组件传递数据,子组件通过 e m i t 和父组件通信 2. r e f :这个属性用在子组件上,指向子组件的实例,可以通过实例来访问组件的数据和方法 3. emit 和父组件通信 2.ref:这个属性用在子组件上,指向子组件的实例,可以通过实例来访问组件的数据和方法 3. emit和父组件通信2.ref:这个属性用在子组件上,指向子组件的实例,可以通过实例来访问组件的数据和方法3.emit 绑定一个自定义事件,当这个事件被执行的时候就会将参数传递给父组件,而父组件通过v-on监听并接收参数
4.eventBus事件总线($emit / $on)
Vuex采用MVC模式中的Model层,规定所有的数据必须通过action—>mutaion—>state这个流程进行来改变状态的。再结合Vue的数据视图双向绑定实现页面的更新。统一页面状态管理,可以让复杂的组件交互变的简单清晰,同时在调试时也可以通过DEVtools去查看状态
创建初始值的状态initialState
创建所有对状态的操作reducer(state,action)
传给useReducer,得到读和写的接口
调用写({‘type’:‘操作类型’})
useReducer
接受的第一个参数是一个函数,我们可以认为它就是一个 reducer
, reducer
的参数就是常规 reducer
里面的 state
和 action
,返回改变后的 state
, useReducer
第二个参数为 state
的初始值 返回一个数组,数组的第一项就是更新之后 state
的值 ,第二个参数是派发更新的 dispatch
函数。
如果一个函数 接受一个或多个函数作为参数或者返回一个函数 就可称之为 高阶函数。
什么是React高阶组件:一个组件的参数是组件,并且返回值是一个组件,我们称这类组件为高阶组件
withRouter() memo() react-redux中connect方法是高阶组件
防抖:
1.当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间(非常短的时间);
2.当事件密集触发时,函数的触发会被频繁的推迟;
3.只有等待了一段时间也没有事件触发,才会真正的执行响应函数;
function debounce(func, wait) {
let timeout;
return function () {
let context = this; // 保存this指向
let args = arguments; // 拿到event对象
clearTimeout(timeout)
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
节流:
1.当事件触发时,会执行这个事件的响应函数;
2.如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函数
3.不管在这个中间有多少次触发这个事件,执行函数的频繁总是固定的
function throttled1(fn, delay = 500) {
let oldtime = Date.now()
return function (...args) {
let newtime = Date.now()
if (newtime - oldtime >= delay) {
fn.apply(null, args)
oldtime = Date.now()
}
}
}
创建ref的形式有三种:
1.传入字符串,使用时通过 this.refs.传入的字符串的格式获取对应的元素
2.传入对象,对象是通过 React.createRef() 方式创建出来,使用时获取到创建的对象中存在 current 属性就是对应的元素
3.传入hook,hook是通过 useRef() 方式创建,使用时通过生成hook对象的 current 属性就是对应的元素
在某些情况下,我们会通过使用refs来更新组件,但这种方式并不推荐,更多情况我们是通过props与state的方式进行去重新渲染子元素
但下面的场景使用refs非常有用:
1.对Dom元素的焦点控制、内容选择、控制
2.对Dom元素的内容设置及媒体播放
3.对Dom元素的操作和对组件实例的操作
4.集成第三方 DOM 库
1.中间件都需要通过applyMiddlewares进行注册,
2.将所有的中间件组成一个数组,依次执行然后作为第二个参数传入到createStore中
3.applyMiddlewares调用实例:返回store对象
我们用 applyMiddleware 是为了改造 dispatch 的,所以 applyMiddleware 执行完后,dispatch 是变化了的,而 middlewareAPI 是 applyMiddleware 执行中分发到各个 middleware,所以必须用匿名函数包裹 dispatch, 这样只要 dispatch 更新了, middlewareAPI 中的 dispatch 应用也会发生变化
\1. 深拷贝或者改变引用类型数据的地址
\2. Key最好不用index
\3. 在this.setState中使用一个回调来获取一个新的值
\4. 不要直接修改store中的值
\5. 在render中判断state.value值不等于空,再加载子组件
一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state
当需要修改里面的值的状态需要通过调用setState
来改变,从而达到更新组件内部数据的作用 setState
第一个参数可以是一个对象,或者是一个函数,而第二个参数是一个回调函数,用于可以实时的获取到更新之后的数据 在使用setState
更新数据的时候,setState
的更新类型分成: 同步和异步,在组件生命周期或React合成事件中,setState是异步在setTimeout或者原生dom事件中,setState是同步
受控组件,简单来讲,就是受我们控制的组件,组件的状态全程响应外部数据 非受控组件,简单来讲,就是不受我们控制的组件一般情况是在初始化的时候接受外部数据,然后自己在内部存储其自身状态大部分时候推荐使用受控组件来实现表单,因为在受控组件中,表单数据由React
组件负责处理如果选择非受控组件的话,控制能力较弱,表单数据就由DOM
本身处理,但更加方便快捷,代码量少
react中的jsx语法会通过babel转化为 js代码,以React.createElement函数形式存在,createElement函数返回一个ReactElement函数,ReactElement函数返回一个的虚拟节点,虚拟节点中嵌套虚拟节点,就形成了虚拟DOM,最后通过ReactDOM.render方法转化为真实DOM。babel在转化jsx过程中,会判断首字母的大小写当首字母为小写的时候,会被认为是原生DOM标签, 那么createElement中的第一个参数就是一个字符串,表示标签的名称当首字母为大写的时候,会被认为是组件,那么createElement中的第一个参数就是组件的名称,
react-redux 是的官方 React UI 绑定层,允许您的 React 组件从 Redux 存储中读取数据,并将操作分派到存储以更新状态。
@reduxjs/toolkit 是对 Redux 的二次封装,开箱即用可的一个高效的 Redux 开发工具集,使得创建store、更新store更加方便
Image-loader 加载并且压缩图片文件
Babel-loader 将es6转换为es5
Css-loader 加载css 支持模块化 压缩 文件导入
Style-loader 把css代码注入到js中 通过dom操作去加载css
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中,对小体积的图片比较合适,大图片不合适
Portal 将提供一种将子节点渲染到 DOM 节点中的方式,该节点存在于 DOM 组件的层次结构之外。
因此 Portals 适合脱离文档流(out of flow) 的组件,特别是 position: absolute 与 position: fixed的组件。比如模态框,通知,警告,goTop 等
①、根据运算符优先级 ,! 的优先级是大于 == 的,所以先会执行 ![]
②、根据上面提到的规则(如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1),则需要把 false 转成 0
③、根据上面提到的规则(如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较,如果对象没有valueOf()方法,则调用 toString())
④、根据上面提到的规则(如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值)
闭包就是能够读取其他函数内部变量的函数,说白了闭包就是个函数,只不过是处于其他函数内部而已。
1.访问函数内部的变量
2.防止函数内部的变量执行完城后,被销毁,使其一直保存在内存中。
[flex弹性布局]
[百分比]
[用框架搭建页面]
[viewport适配]
[媒体查询media]
[rem+viewport缩放(也成为1px适配)]
弹性盒中的项目设置flex-grow属性定义项目的放大比例,默认值为0,值越大,放大越厉害,且不支持负值; 而flex-shrink属性定义项目的缩小比例,默认值为1,数值越大,缩小越厉害,同样不支持负值;
泛型的本质是参数化类型,通俗的将就是所操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法的创建中,分别成为泛型类,泛型接口、泛型方法。
TypeScript 中不建议使用 any 类型,不能保证类型安全,调试时缺乏完整的信息。
TypeScript可以使用泛型来创建可重用的组件。支持当前数据类型,同时也能支持未来的数据类型。扩展灵活。可以在编译时发现你的类型错误,从而保证了类型安全。
使用泛型可以创建泛型函数、泛型接口,泛型类
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上,是一种在不改变原类和使用继承的情况下,动态地扩展对象功能同样的,本质也不是什么高大上的结构,就是一个普通的函数,@expression 的形式其实是Object.defineProperty的语法糖,expression 求值后必须也是一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入
场景 1.逻辑层消除繁琐的try/catch块,装饰器内统一输出函数日志 2.校验参数或返回值类型
React,用于构建用户界面的 JavaScript 库,只提供了 UI 层面的解决方案
遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效
使用虚拟 DOM 来有效地操作 DOM,遵循从高阶组件到低阶组件的单向数据流
帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面
react 类组件使用一个名为 render() 的方法或者函数组件return,接收输入的数据并返回需要展示的内容
React 特性有很多,如:
JSX 语法
单向数据绑定
虚拟 DOM
声明式编程
Component
React 存在的优势:
高效灵活
声明式的设计,简单使用
组件式开发,提高代码复用率
单向响应的数据流会比双向绑定的更安全,速度更快
两者的区别如下:
虚拟 DOM 不会进行排版与重绘操作,而真实 DOM 会频繁重排与重绘
虚拟 DOM 的总损耗是“虚拟 DOM 增删改+真实 DOM 差异增删改+排版与重绘”,真实 DOM 的总损耗是“真实 DOM 完全增删改+排版与重绘”
真实 DOM 的优势:
易用
缺点:
效率低,解析速度慢,内存占用量过高
性能差:频繁操作真实 DOM,易于导致重绘与回流
使用虚拟 DOM 的优势如下:
简单方便:如果使用手动操作真实 DOM 来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难
性能方面:使用 Virtual DOM,能够有效避免真实 DOM 数频繁更新,减少多次引起重绘与回流,提高性能
跨平台:React 借助虚拟 DOM,带来了跨平台的能力,一套代码多端运行
缺点:
在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化
首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,速度比正常稍慢
jsx是JavaScript的一种语法扩展,它跟模板语言很接近,但是它充分具备JavaScript的能力
JSX就是用来声明React当中的元素,React使用JSX来描述用户界面
JSX语法糖允许前端开发者使用我们最熟悉的类HTML标签语法来创建虚拟DOM在降低学习成本
React.createElement()
参考答案:
https://vue3js.cn/interview/React/Building%20components.html
class组件是有状态的组件,可以定义state状态,函数组件无状态
class组件有生命周期的,函数组件无生命周期
class组件是由this对象,函数组件没有this对象
组件调用: class组件实例化后调用render方法调用,函数组件直接调用的。
class组件内部的话,render方法return返回渲染jsx模板,函数组件直接返回即可
ref获取子组件的对象,class组件可以直接获取到的,函数组件无法直接获取到。
绑定bind改变this指向,只适用于class组件
React 事件绑定属性的命名采用驼峰式写法, 采用 JSX 的语法传入一个函数作为事件处理函数
事件绑定函数的方式
直接写函数名字{callback},
可以使用bind方法绑定调用 {callback.bind(this)}
当我们把事件函数写成普通函数的形式时 , 调用函数使用state变量会报错,提示state变量不存在,
是因为
事件处理程序的函数式函数调用模式,在严格模式下,this指向undefined
render函数是被组件实例调用的,因此render函数中的this指向当前组件
解决方法: 1. 把普通函数改成箭头函数 2. 调用函数的时候使用bind方法改变this指向
调用的时候定义一个箭头函数 函数中调用方法传递参数据
给文本框绑定value属性,value属性绑定state中定义的变量
给表单绑定onChange事件,调用定义的方法
在方法中我们获取e.target.value属性,赋给value属性绑定的变量
if-else的条件渲染方法
三元运算符进行条件渲染,可以缩短代码量
switch的多条件渲染效果
HOC条件渲染
react中可以使用map方法渲染列表,return对应的页面结构即可, React 在渲染列表时,会要求开发者为每一个列表元素指定唯一的 key ,我们尽量不要使用index索引值作为key,如果对数据进行:逆序添加、逆序删除等破坏顺序操作:可能会引起页面更新错误问题。
key是虚拟DOM对象的唯一标识,在更新显示时key起到极其重要的作用 ,简单的来说就是为了提高diff的同级比较的效率,避免原地复用带来的副作用
react采用的是自顶而下的更新策略,每次小的改动都会生成一个全新的的vdom,从而进行diff,如果不写key,就会发生本来应该更新却没有更新
参考答案: https://vue3js.cn/interview/React/key.html
外联样式
定义css文件,在组件中通过import导入css样式,
import “App.css”
内联样式
React推崇的是内联的方式定义样式。这样做的目的就在于让你的组件更加的容易复用
定义一个style属性,属性中定义对应的css样式即可,比如style={{fontSize:‘15px’}}
外层花括号是语法,内层花括号是对象边界符
array(数组)、bool(布尔值)、func(函数number(数字)、object(对象)、string(字符串)
受控组件
由React控制的输入表单元素而改变其值的方式,称为受控组件。
比如,给表单元素input绑定一个onChange事件,当input状态发生变化时就会触发onChange事件,从而更新组件的state。
非受控组件
非受控组件指的是,表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值。
在非受控组件中,可以使用一个ref来从DOM获得表单值。
props是指组件间传递的一种方式,props自然也可以传递state。由于React的数据流是自上而下的,所以是从父组件向子组件进行传递;另外组件内部的this.props属性是只读的不可修改!
state是组件内部的状态(数据),不能够直接修改,必须要通过setState来改变值的状态,从而达到更新组件内部数据的作用。
参考链接: https://www.bilibili.com/read/cv15666960
V5中Switch换成Routes标签,
V5中exact属性没有了,V6默认是精准匹配
V5中的component属性,V6用的element,element属性是组件标签
V6中重定向导航组件换成Navigate
V6中路由嵌套直接采用组件包裹的方式,可以不适用path绝对路径,
V6中的 相当于vue中router-view
获取参数和Hooks方法的变化
props获取V6中props值没有参数数据,必须采用Hooks的路由获取数据。
withRouter在V6版本中也被去掉了。
在父组件中我们通过createContext() 创建一个空对象,在父组件的最外层我们使用Provider包裹数据,通过value绑定要传递的对象数据。
在嵌套的子组件中,我们有两种方式获取数据:
(1) 我们可以使用Customer标签,在标签中绑定一个箭头函数,函数的形参context就是value传递的数据
(2). class组件中我们可以定义static contextType=context对象,组件中直接使用this.context获取数据。
Mounting(挂载阶段):已插入真实 DOM
Updating(更新阶段):正在被重新渲染
Unmounting(卸载阶段):已移出真实 DOM
(1) componentWillMount ,在ssr中 这个方法将会被多次调用, 所以会重复触发多遍,同时在这里如果绑定事件,
将无法解绑,导致内存泄漏 , 变得不够安全高效逐步废弃。
(2) componentWillReceiveProps 外部组件多次频繁更新传入多次不同的 props,会导致不必要的异步请求
(3) componetWillupdate, 更新前记录 DOM 状态, 可能会做一些处理,与componentDidUpdate相隔时间如果过长, 会导致 状态不太信
用 getDerivedStateFromProps替换了 compoentWillMount和compontWillReceiveProps生命周期函数
用getSnapshotBeforeUpdate函数替换componetWillUpdate方法,避免和CompoentDidUpdate函数中获取数据不一致的问题
HashRouter或BrowserRouter配置路由模式
Route 定义路由组件映射关系
Redirect 设置路由重定向
NavLink 或者Link 页面路由跳转
Switch 路由匹配,当path匹配到一个component之后,将不会再想下继续匹配,提高了程序效率
//隐士参数传递
(1) this.props.history.push({ pathname : ‘/user’ ,query : {id:100}})
this.props.location.query.id 获取query传递的参数据,刷新数据不在
(2) this.props.history.push({ pathname:‘/user’,state:{id: 1000 } }) this.props.location.state.id 获取state的数据,刷新数据还在
3. url传参方式 () history.location.search获取数据比较麻烦,得自己解析
4. 动态路由定义 /detail/:id => /detail/100 => location.match.params中接受的参数是 {id:100}
声明式导航:
使用NavLink或者Link跳转, to属性后面跟字符串或者跟对象
编程式导航跳转:
props.history.push(url) 跳转页面可以返回上一页,保留历史记录
props.history.replace(url) 跳转页面,清空历史记录
props.history.go(num) 返回第几个页面
31. withRouter是干什么的?
不是所有组件都直接与路由相连(比如拆分的子组件)的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,将react-router的history、location、match三个对象传入props对象上,此时就可以使用this.props.history跳转页面了或者接受参数了
在react中每个组件的state是由自身进行管理,包括组件定义自身的state、组件之间的通信通过props传递、使用Context实现数据共享等,如果让每个组件都存储自身相关的状态,理论上来讲不会影响应用的运行,但在开发及后期我们将比较难以维护,所以我们可以把数据进行集中式的管理,redux就是一个实现上述集中管理的容器的工具,redux并不是只应用在react中,还与其他界面库一起使用,如Vue
state数据必须是单一数据源
redux中的state数据必须 是只读的,只能通过dispatch调用actions修改
Reducer必须使用纯函数来执行修改
React的组件需要获取或者修改页面的数据,通过dispatch方法调用actions进入到Reducer函数中修改state的数据内容,state更新后,通知组件更新页面即可。
创建一个store文件夹,新建一个index.js文件
文件中导入redux的createStore方法,用于创建公共数据区域
创建一个reducer纯函数,接受两个参数state,actions分别表示分别表示数据和操作state的方法,返回state数据给组件页面
把reducer作为createStore的参数抛出
在需要使用的页面导入store文件,通过store.getState获取数据,通过store.dispatch触发action修改state数据
store.subscrible 方法监听 store 的改变,避免数据不更新
在 React 中,类组件基于 ES6,所以在 constructor 中必须使用 super
在调用 super 过程,无论是否传入 props,React 内部都会将 porps 赋值给组件实例 porps 属性中
如果只调用了 super(),那么 this.props 在 super() 和构造函数结束之间仍是 undefined
React事件机制总结如下:
创建ref的形式有三种:
在某些情况下,我们会通过使用refs来更新组件,但这种方式并不推荐,更多情况我们是通过props与state的方式进行去重新渲染子元素
但下面的场景使用refs非常有用:
一个函数的参数是一个函数,或者 函数的返回值是一个函数,我们称这类函数是高阶函数。
什么是React高阶组件:一个组件的参数是组件,并且返回值是一个组件,我们称这类组件为高阶组件
withRouter() memo() react-redux中connect方法是高阶组件
React 中的高阶组件主要有两种形式:属性代理 和 反向继承。
属性代理: 是 一个函数接受一个 WrappedComponent 组件作为参数传入,并返回一个继承了 React.Component 组件的类,且在该类的 render() 方法中返回被传入的 WrappedComponent 组件
反向继承:是 一个函数接受一个 WrappedComponent 组件作为参数传入,并返回一个继承了该传入 WrappedComponent 组件的类,且在该类的 render() 方法中返回 super.render() 方法。
Redux中,中间件就是放在就是在dispatch过程,在分发action进行拦截处理
前面我们了解到了Redux整个工作流程,当action发出之后,reducer立即算出state,整个过程是一个同步的操作
那么如果需要支持异步操作,或者支持错误处理、日志监控,这个过程就可以用上中间件,其本质上一个函数,对store.dispatch方法进行了改造,在发出 Action和执行 Reducer这两步之间,添加了其他功能
常用的redux中间件,如:
redux-thunk:用于异步操作
redux-logger:用于日志记录
中间件都需要通过applyMiddlewares进行注册,作用是将所有的中间件组成一个数组,依次执行然后作为第二个参数传入到createStore中
const store = createStore(
reducer,
applyMiddleware(thunk, logger)
);
useState
useState()用于为函数组件引入状态。在useState()中,数组第一项为一个变量,指向状态的当前值。类似this.state,第二项是一个函数,用来更新状态,类似setState
useEffect
useEffect()接受两个参数,第一个参数是你要进行的异步操作,第二个参数是一个数组,用来给出Effect的依赖项。只要这个数组发生变化,useEffect()就会执行
useRef
相当于class组件中的createRef的作用,ref.current获取绑定的对象
useContext
接受context状态树传递的数据内容
useReducer
接受reducer函数和状态的初始值作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数
userMemo useCallback
useMemo 和 useCallback接收的参数都是一样,第一个参数为回调,第二个参数为要依赖的数据
共同作用:仅仅依赖数据发生变化, 才会调用,也就是起到缓存的作用。useCallback缓存函数,useMemo 缓存返回值。
useMemo 主要缓存复杂运算的数据的结果,第二个参数,定义监听的变量,需要返回一个结果。
当父组件的组件更新的时候会导致子组件的重新渲染,但是如果父组件的更新的数据没有传递给子组件的话,这个时候如果还让子组件重新渲染的化,就会导致组件的更新的性能消耗比较大。
所以说这个时候我们可以使用useMemo, 或者React中内置的memo方法对子组件进行缓存,这样的话只有父组件更新跟子组件相关联的数据的时候才会导致子组件的重新渲染,从而提高组件的渲染性能。
但是如果我们给子组件传递方法的时候,上面memo方法的缓存就不起作用了,原因是父组件没更新一次会导致方法的重新调用,进而导致子组件的重新更新,所以这个时候我们可以使用useCallback对传递的方法进行缓存,监听数据更新后才会重新调用方法,从而提高组件的渲染性能。