目录
1.什么是虚拟DOM?
2.类组件和函数组件之间的区别是什么?
3.什么是React?
4. 说说 Real DOM 和 Virtual DOM 的区别?优缺点?
5.在react中如何处理事件?
6.class组件和函数组件区别
7.state和props区别是什么?
8.React 事件绑定的方式
9. 事件处理方法this指向改变
10.React事件处理方法传值
11. React如何获取表单数据?
12.为什么不直接更新state?
13.React条件渲染方法有哪些?
14.React怎么实现列表渲染?
15. React中key的作用是什么?
16. React组件样式的定义方式?
17.Props校验数据类型
18.受控组件和非受控组件的区别
19.props和state的区别
20.react-roter-dom中V5和V6区别?
21.父传子通信流程
22.子传父通信的流程
23.非父子组件通信
24.context状态树是怎么运行的?
25.React生命周期分为几个阶段?
26.简述React的生命周期函数?
27.React旧生命周期有哪些问题?
28. React新生命周期有哪些改变?
29. react的路由几种模式,是什么?
30.react路由常用的组件有哪些?
31.react路由传参的方式有哪些?
32.react路由跳转的方式有哪些?
33 .react路由嵌套如何配置?
34. withRouter是干什么的?
35. 什么是Redux?
36.Redux的三大原则
37.redux的执行原理
38.redux的使用步骤
39.React Router与常规路由有何不同?
40.state和props有什么区别
41.super() 和super(props)有什么区别?
42.说说 React中的setState执行机制
43.React的事件机制总结
44.说说对React refs 的理解?应用场景?
45.说说对高阶组件的理解?应用场景?
46.说说对Redux中间件的理解?常用的中间件有哪些?
47.React中常见的Hooks方法有哪些?
48.useMemo, useCallback做了那些性能优化?
49.react中ref的使用方式?
50.你对“单一事实来源”有什么理解?
51.SPA(单页应用)首屏加载速度慢怎么解决?
52.BFC的理解
53.说说javascript内存泄漏的几种情况?
54.说说你对webSocket的理解?
55.说说package.json中版本号的规则?
56.说说你对koa中洋葱模型的理解?
57.说说Connect组件的原理是什么?
虚拟DOM是真实DOM在内存中的表示,ul的表示形式保存在内存中,并且与实际的DOM同步,这是一个发生在渲染函数被调用和元素在屏幕上显示的步骤,整个过程被称为调和
类组件可以使用其他特性,如状态和生命周期钩子,并且他有this
函数组件只能接收props渲染到页面,无状态组件,没有this,不能使用生命周期钩子
函数组件性能要高于类组件,因为类组件使用要实例化,而函数组件直接执行取返回结果即可,为了提高性能,尽量使用函数组件
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 的计算,速度比正常稍慢
为了解决跨浏览器的兼容性问题,SyntheticEvent 实例将被传递给你的事件处理函数,SyntheticEvent是 React 跨浏览器的浏览器原生事件包装器,它还拥有和浏览器原生事件相同的接口,包括 stopPropagation() 和 preventDefault()。
比较有趣的是,React 实际上并不将事件附加到子节点本身。React 使用单个事件侦听器侦听顶层的所有事件。这对性能有好处,也意味着 React 在更新 DOM 时不需要跟踪事件监听器。
相同点:都是普通的js对象,他们包含着影响渲染输出的信息
不同点:state是组件自己管理数据,控制自己的状态,可变
props是外部传入的数据参数,不可变
没有state的叫做无状态组件,有state的叫有状态组件
多用props,少用state
React 事件绑定属性的命名采用驼峰式写法, 采用 JSX 的语法传入一个函数作为事件处理函数
事件绑定函数的方式
1.直接写函数名字{callback},
2.可以使用bind方法绑定调用{callback.bind(this)}
当我们把事件函数写成普通函数的形式时 , 调用函数使用state变量会报错,提示state变量不存在,
是因为
事件处理程序的函数式函数调用模式,在严格模式下,this指向undefined
render函数是被组件实例调用的,因此render函数中的this指向当前组件
解决方法: 1. 把普通函数改成箭头函数 2. 调用函数的时候使用bind方法改变this指向
1.调用的时候定义一个箭头函数 函数中调用方法传递参数据
2. bind方法传递参数
如果试图直接更新state,就不会重新渲染组件
需要使用setState()方法更新state,它对state对象进行更新,当state改变时,组件通过重新渲染来响应
react中可以使用map方法渲染列表,return对应的页面结构即可, React 在渲染列表时,会要求开发者为每一个列表元素指定唯一的 key
,我们尽量不要使用index索引值作为key,如果对数据进行:逆序添加、逆序删除等破坏顺序操作:可能会引起页面更新错误问题。
key是虚拟DOM对象的唯一标识,在更新显示时key起到极其重要的作用 ,简单的来说就是为了提高diff的同级比较的效率,避免原地复用带来的副作用
react采用的是自顶而下的更新策略,每次小的改动都会生成一个全新的的vdom,从而进行diff,如果不写key,就会发生本来应该更新却没有更新
外联样式
定义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来改变值的状态,从而达到更新组件内部数据的作用。
Mounting(挂载阶段):已插入真实 DOM
Updating(更新阶段):正在被重新渲染
Unmounting(卸载阶段):已移出真实 DOM
挂载阶段:
- constructor() 在 React 组件挂载之前,会调用它的构造函数。
- componentWillMount: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
- componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用
更新运行阶段:
* componentWillReceiveProps: 在接受父组件改变后的props需要重新渲染组件时用到的比较多,外部组件传递频繁的时候会导致效率比较低
* shouldComponentUpdate():用于控制组件重新渲染的生命周期,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
* render(): render() 方法是 class 组件中唯一必须实现的方法。
* *componentWillUpdate(): shouldComponentUpdate返回true以后,组件进入重新渲染完成之前进入这个函数。
* **componentDidUpdate(): 每次state改变并重新渲染页面后都会进入这个生命周期
卸载或销毁阶段
componentWillUnmount (): 在此处完成组件的卸载和数据的销毁。
(1) componentWillMount ,在ssr中 这个方法将会被多次调用, 所以会重复触发多遍,同时在这里如果绑定事件,
将无法解绑,导致内存泄漏 , 变得不够安全高效逐步废弃。
(2) componentWillReceiveProps 外部组件多次频繁更新传入多次不同的 props,会导致不必要的异步请求
(3) componetWillupdate, 更新前记录 DOM 状态, 可能会做一些处理,与componentDidUpdate相隔时间如果过长, 会导致 状态不太信
两种路由模式:
一种是Hash路由模式,用的是HashRouter组件
一种是历史路由模式,用的是BrowserRouter组件绑定
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) 返回第几个页面
是所有组件都直接与路由相连(比如拆分的子组件)的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,将react-router的history、location、match三个对象传入props对象上,此时就可以使用this.props.history跳转页面了或者接受参数了
在react中每个组件的state是由自身进行管理,包括组件定义自身的state、组件之间的通信通过props传递、使用Context实现数据共享等,如果让每个组件都存储自身相关的状态,理论上来讲不会影响应用的运行,但在开发及后期我们将比较难以维护,所以我们可以把数据进行集中式的管理,redux就是一个实现上述集中管理的容器的工具,
redux
并不是只应用在react
中,还与其他界面库一起使用,如Vue
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 路由 |
---|---|---|
参与的页面 | 每个视图对应一个新文件 | 只涉及单个HTML页面 |
URL 更改 | HTTP 请求被发送到服务器并且接收相应的 HTML 页面 | 仅更改历史记录属性 |
体验 | 用户实际在每个视图的不同页面切换 | 用户认为自己正在不同的页面间切换 |
相同点:
两者都是 JavaScript 对象
两者都是用于保存信息
props 和 state 都能触发渲染更新
区别:
props 是外部传递给组件的,而 state 是在组件内被组件自己管理的,一般在 constructor 中初始化
props 在组件内部是不可修改的,但 state 在组件内部可以进行修改
state 是多变的、可以修改
在 React 中,类组件基于 ES6,所以在 constructor 中必须使用 super
在调用 super 过程,无论是否传入 props,React 内部都会将 porps 赋值给组件实例 porps 属性中
如果只调用了 super(),那么 this.props 在 super() 和构造函数结束之间仍是 undefined
一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state, 当需要修改里面的值的状态需要通过调用setState来改变,从而达到更新组件内部数据的作用
setState第一个参数可以是一个对象,或者是一个函数,而第二个参数是一个回调函数,用于可以实时的获取到更新之后的数据
在使用setState更新数据的时候,setState的更新类型分成:同步更新,异步更新
在组件生命周期或React合成事件中,setState是异步
在setTimeout或者原生dom事件中,setState是同步
对同一个值进行多次 setState, setState 的批量更新策略会对其进行覆盖,取最后一次的执行结果
React事件机制总结如下:
- React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
- React 自身实现了一套事件冒泡机制,所以这也就是为什么我们 event.stopPropagation()无效的原因。
- React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
- React 有一套自己的合成事件 SyntheticEvent
创建ref的形式有三种:
- 传入字符串,使用时通过 this.refs.传入的字符串的格式获取对应的元素
- 传入对象,对象是通过 React.createRef() 方式创建出来,使用时获取到创建的对象中存在 current 属性就是对应的元素
- 传入hook,hook是通过 useRef() 方式创建,使用时通过生成hook对象的 current 属性就是对应的元素在某些情况下,我们会通过使用refs来更新组件,但这种方式并不推荐,更多情况我们是通过props与state的方式进行去重新渲染子元素
但下面的场景使用refs非常有用:
- 对Dom元素的焦点控制、内容选择、控制
- 对Dom元素的内容设置及媒体播放
- 对Dom元素的操作和对组件实例的操作
- 集成第三方 DOM 库
一个函数的参数是一个函数,或者 函数的返回值是一个函数,我们称这类函数是高阶函数。
什么是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对传递的方法进行缓存,监听数据更新后才会重新调用方法,从而提高组件的渲染性能。
1.可以直接写一个字符串,不过它只适用于类组件
2.createRef和useRef来定义ref变量,ref.current获取数据
3.可以使用箭头函数的方式,提前定义一个变量,箭头函数的形参就是当前对象
const App = ()=>{
let h = useRef();
let inp = null;//定义一个空变量
const setVal = ()=>{
console.log(inp.value);
}
return (
ceshi
{ inp = el; }} onChange={setVal}/>
)
}
ref属性不能直接绑定到函数子组件的,函数组件没有this对象,无法获取函数子组件的对象内容,最后函数组件forwardRef高阶组件组件,可以把ref属性通过参数的参数传递到函数子组件的内部,对ref属性进行转发的操作
Redux 使用 “Store” 将程序的整个状态存储在同一个地方。因此所有组件的状态都存储在 Store 中,并且它们从 Store 本身接收更新。单一状态树可以更容易地跟踪随时间的变化,并调试或检查程序
1,减少入口文件体积:常用的手段是路由懒加载,只有在解析路由时才会加载组件;
2,静态资源本地缓存: 后端返回资源: 采用HTTP缓存;前端合理利用:localStorage;
3,UI框架按需加载
4,避免组件重复打包
5,图片资源压缩
6,开启GZip压缩,拆完包后,我们再用gzip做一下压缩,安装compression-webpack-plugin
webpack中配置安装
BFC(Block Formatting Context)块格式化上下文,简单的理解:BFC就是一个块级容器,它会隔离外部,让盒子里面的元素不影响外面的元素,也就是在搭建页面的时候,不影响外面的布局。
不使用BFC:内部元素使用浮动的时候,外面的盒子计算不到高度,就会导致高度坍塌
如果两个块级盒子都设置了margin值,两个盒子相邻的那一边的margin就会重叠
触发条件:最常见的是通过 overflow: hidden 来构建 BFC。一般情况下,它的副作用最小。但如果元素下有超出盒子范围的内容,会被截掉,
解决问题:
1.解决边距重叠问题:我们只需要在其中一个块级元素外面包裹一层容器,使两个块级元素不属于同一层级,并且触发BFC
2.解决高度塌陷问题:如果盒子里面的元素加了浮动,计算外面盒子的高度不会计算浮动的盒子高度,因为浮动不占空间。这个时候我们就需要在外面的盒子触发BFC,这样使他即使内部元素加了浮动也计算浮动元素的高度,不会导致高度坍塌
3.解决相邻盒子浮动区域重叠问题:给相邻的盒子也加上浮动,给相邻的盒子设置overflow:hidden
1.意外的全局变量
一个未声明变量的引用会在全局对象中创建一个新的变量。
2.闭包引起的内存泄漏
闭包可以使变量常驻内存,但如果使用不当就会在成内存泄漏
3.DOM之外的引用
4.被遗漏的定时器和回调函数
怎样避免内存泄漏
1)减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;
2)注意程序逻辑,避免“死循环”之类的 ;
3)避免创建过多的对象 原则:不用了的东西要及时归还。
WebSocket是一种基于TCP的全双工通信协议,在应用层。
WebSocket的优缺点
优点:建立WebSocket连接之后,客户端与服务端交流更方便
客户端只需要向服务端发送一次请求,服务端主动向客户端发送消息
缺点:在服务端的状态不会频繁变化的时候,就不需要使用WebSocket连接了,浪费性能
为什么需要WebSocket
1、传统上的HTTP协议它是无状态的,服务器不能够识别是哪个客户端发送的请求,不能够保存状态。
2、WebSocket弥补了这一问题,在客户端向服务端发送请求之后,服务器处理请求并返回到客户端,使用WebSocket可以使得服务器主动向浏览器推送消息
WebSocket协议的原理
与服务器进行三次握手,建立TCP连接
向服务器发送HTTP请求,请求中包含WebSocket的版本信息:包括upgrade、connection等等。
服务器处理请求并返回客户端,此时可以进行WebSocket请求了
服务端也可以主动向客户端推送消息了。
第一部分为主版本号,变化了表示有了一个不兼容上个版本的大更改
第二部分为次版本号,变化了表示增加了新功能,并且可以向后兼容
第三部分为修订版本号,变化了表示 有bug修复,并且可以向后兼容
第四部分为日期版本号加希腊字母版本号,希腊字母版本号共有五种,分别为base、alpha、 bela 、RCrelease
Koa的洋葱模型是以next()函数为分割点,先由外到内执行Request的逻辑,然后再由内到外执行Response的逻辑,这里的request的逻辑,我们可以理解为是next之前的内容,response的逻辑是next函数之后的内容,也可以说每一个中间件都有两次处理时机。洋葱模型的核心原理主要是借助compose方法。
react-redux用于连接react组件及redux,方便开发者使用redux
管理状态。其中connect方法是关键,用法如下:
connect([mapStateToProps], [mapDispatchToProps])(component)
看connect使用方法就知道是高阶组件,接收参数为mapStateToProps和mapDispatchToProps俩个方法,返回的函数接收参数是组件,从而返回一个新的组件。
首先connect之所以会成功得原因?
是因为Provider组件,在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
接收Redux的store作为props,通过context对象传递给子孙组件上的connect
那connect做了些什么呢?
它真正连接 Redux 和 React,它包在我们的容器组件的外一层,它接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
概括:react-redux 库提供的一个 API,connect 的作用是让你把组件和store连接起来,产生一个新的组件(connect 是高阶组件)