目录
3. 说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?
10. 说说你对redux中间件的理解?常用的中间件有哪些?实现原理?
11. 说说AMD、CMD、commonJS模块化规范的区别?
15. 说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?
16. React render方法的原理,在什么时候会触发?
1. 说说你对react的理解?有哪些特性
理解:react是一个用于构建用户界面的JavaScript库,只提供了UI层面的解决方案,遵循组件设 计模式、声明式编程规范和函数式编程概念,使得前端应用程序更高效
使用虚拟 DOM 来有效地操作 DOM,遵循从高阶组件到低阶组件的单向数据流
react特性:
1.jsx语法
2.单向数据绑定
3.虚拟DOM
4.声明式编程
5.component
2. 说说real diff算法是怎么运作的
主要分为3层:tree层、component层、element层
(1)、tree层:
tree层对DOM节点的跨层级移动的操作忽略不计,只对相同层级的DOM节点进行比较(即同一个父节点下的所有子节点),一旦发现节点不存在,直接删除掉该节点以及之下的所有子节点
(2)、component层:
遇到同一类型的组件遵循 tree diff,进行层级对比
遇到不同类型的组件,直接将这个不同的组件判断为脏组件,并替换该组件和之下所有的子节点
在同一类型的两个组件中,当知道这个组件的虚拟dom没有任何变化,就可以手动使用shouldComponentUpdate()来判断组件是否需要进行diff,进一步的提升了diff效率和性能
(3)、element层:对同一层级的元素节点进行比较,有三种情况:
面对全新的节点时,执行插入操作
面对多余的节点时,执行删除操作
面对换位的节点时,执行移动操作
3. 说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?
挂载阶段:
- constructor() 在 React 组件挂载之前,会调用它的构造函数。
- componentWillMount: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
- componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用
更新运行阶段:
* componentWillReceiveProps: 在接受父组件改变后的props需要重新渲染组件时用到的比较多,外部组件传递频繁的时候会导致效率比较低
* shouldComponentUpdate():用于控制组件重新渲染的生命周期,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
* render(): render() 方法是 class 组件中唯一必须实现的方法。
* *componentWillUpdate(): shouldComponentUpdate返回true以后,组件进入重新渲染完成之前进入这个函数。
* **componentDidUpdate(): 每次state改变并重新渲染页面后都会进入这个生命周期
卸载或销毁阶段
componentWillUnmount (): 在此处完成组件的卸载和数据的销毁。
4. 说说你对React中虚拟dom的理解?
实际上它只是一层对真实DOM的抽象,以JavaScript 对象 (VNode 节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上
创建虚拟DOM就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟DOM对象的节点与真实DOM的属性一一照应
5. 说说你对react hook的理解?
Hook是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
函数组件也被称为无状态的组件,只负责渲染的一些工作。因此,现在的函数组件也可以是有状态的组件,内部也可以维护自身的状态以及做一些逻辑方面的处理,拥有了类组件相似的功能,Hook还拥有代码的复用机制,更加方便灵活
常见的Hook:useState()、useEffect()、useReducer()、useCallback()、useMemo()、useRef()
6. React组件之间如何通信?
父传子通信流程:
在父组件中的子组件标签上绑定自定义属性,挂载传递的数据
子组件中props接受传递的数据,直接使用即可
子传父通信的流程:
父组件中子组件标签上绑定一个属性,传递一个方法给子组件
子组件中通过props接受这个方法,直接调用,传递相应的参数即可
非父子组件通信:
状态提升(中间人模式):
React中的状态提升概括来说,就是将多个组件需要共享的状态提升到它们最近的父组件,在父组件上改变这个状态然后通过props分发给子组件
context状态树传参:
使用context提供了组件之间通讯的一种方式,可以共享数据,其他数据都能读取对应的数据
通过使用React.createContext创建一个context;
context创建成功后,其下存在Provider组件用于创建数据源,Consumer组件用于接收数据
Provider组件通过value属性用于给后代组件传递数据
如果想要获取Provider传递的数据,可以通过Consumer组件或者或者使用contextType属性接收
7. 说说你对受控组件和非受控组件的理解?应用场景?
受控组件:
由React控制的输入表单元素而改变其值的方式,称为受控组件。
比如,给表单元素input绑定一个onChange事件,当input状态发生变化时就会触发onChange事件,从而更新组件的state。
非受控组件:
非受控组件指的是,表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值。
在非受控组件中,可以使用一个ref来从DOM获得表单值。
应用场景:
(1) 大部分时候推荐使用受控组件来实现表单,因为在受控组件中,表单数据由React组件负责处理
(2) 如果选择非受控组件的话,控制能力较弱,表单数据就由DOM本身处理,但更加方便快捷,代码量少
8. 说说Connect组件的原理是什么?
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
9. 说说react 中jsx语法糖的本质?
jsx是JavaScript的一种语法扩展,它跟模板语言很接近,但是它充分具备JavaScript的能力
JSX就是用来声明React当中的元素,React使用JSX来描述用户界面
JSX语法糖允许前端开发者使用我们最熟悉的类HTML标签语法来创建虚拟DOM在降低学习成本
10. 说说你对redux中间件的理解?常用的中间件有哪些?实现原理?
中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的
Redux中,中间件就是放在就是在dispatch过程,在分发action进行拦截处理
本质上一个函数,对store.dispatch方法进行了改造,在发出 Action和执行 Reducer这两步之间,添加了其他功能
常用中间件:
redux-thunk:用于异步操作
redux-logger:用于日志记录
实现原理:
所有中间件被放进了一个数组chain,然后嵌套执行,最后执行store.dispatch。可以看到,中间件内部(middlewareAPI)可以拿到getState和dispatch这两个方法,内部会将dispatch进行一个判断,然后执行对应操作
11. 说说AMD、CMD、commonJS模块化规范的区别?
(1)AMD/CMD/CommonJs 是js模块化开发的规范,对应的实现是require.js/sea.js/Node.js
(2)CommonJs 主要针对服务端,AMD/CMD/ES Module主要针对浏览器端,容易混淆的是AMD/CMD
(3)AMD是预加载,在并行加载js文件同时,还会解析执行该模块;而CMD是懒加载,虽然会一开始就并行加载js文件,但是不会执行,而是在需要的时候才执行
(4)AMD优点:加载快速,尤其遇到多个大文件,因为并行解析,所以同一时间可以解析多个文件;AMD缺点:并行加载,异步处理,加载顺序不一定,可能会造成一些困扰,甚至为程序埋下大坑。
(5)CMD优点:因为只有在使用的时候才会解析执行js文件,因此,每个JS文件的执行顺序在代码中是有体现的,是可控的;CMD缺点:执行等待时间会叠加。因为每个文件执行时是同步执行(串行执行),因此时间是所有文件解析执行时间之和,尤其在文件较多较大时,这种缺点尤为明显。
(6)如何使用:CommonJs 的话,因为 NodeJS 就是它的实现,所以使用 node 就行,也不用引入其他包。AMD则是通过<script>标签引入require.js,CMD则是引入sea.js
12.说说package.json中版本号的规则?
version 指定版本号
>version 大于该版本号
>=version 大于等于该版本号
<version 小于该版本号
<=version 小于等于该版本号
~version 右侧任意
^version 从左向右,第一个非0号的右侧任意
x-version x位置任意
" "|| * version 表示版本任意
version1-version2 表示版本区间范围 包含首尾版本号
version1||version2||…version 表示或,或version1或version2,支持多个
14. 说说React jsx转换成真实DOM的过程?
使用React.createElement或JSX编写React组件,实际上所有的 JSX 代码最后都会转换成React.createElement(...) ,Babel帮助我们完成了这个转换的过程。
createElement函数对key和ref等特殊的props进行处理,并获取defaultProps对默认props进行赋值,并且对传入的孩子节点进行处理,最终构造成一个虚拟DOM对象
ReactDOM.render将生成好的虚拟DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM
15. 说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?
reduxjs/toolkit:
Redux 官方强烈推荐,开箱即用的一个高效的 Redux 开发工具集。它旨在成为标准的 Redux 逻辑开发模式,使用 Redux Toolkit 都可以优化你的代码,使其更可维护react-redux:
react官方推出的redux绑定库,react-redux将所有组件分为两大类:UI组件和容器组件,其中所有容器组件包裹着UI组件,构成父子关系。容器组件负责和redux交互,里面使用redux API函数,UI组件负责页面渲染,不使用任何redux API。容器组件会给UI组件传递redux中保存对的状态和操作状态的方法
16. React render方法的原理,在什么时候会触发?
在类组件中render函数指的就是render方法;而在函数组件中,指的就是整个函数组件。
render函数中的jsx语句会被编译成我们熟悉的js代码,在render过程中,react将新调用的render函数返回的树与旧版本的树进行比较,这一步是决定如何更新 DOM 的必要步骤,然后进行 diff 比较,更新dom树
触发时机:
类组件调用 setState 修改状态
函数组件通过useState hook修改状态
一旦执行了setState就会执行render方法,useState 会判断当前值有无发生改变确定是否执行
render方法,一旦父组件发生渲染,子组件也会渲染
17. React性能优化的手段有哪些?
1.避免使用内联函数
2.使用react fragement 避免额外标记
3.使用immutable,减少渲染的次数,为了避免重复渲染,会在shouldComponentUpdate()中做对比,当返回true,执行render方法。immutable通过is方法完成对比
4.懒加载组件
5.事件绑定方式(在constructor中使用bind绑定性能更高)
6.服务端渲染
7.组件拆分,合理使用hooks
18. 如何通过原生js实现一个节流函数和防抖函数?
//防抖:
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);
}
}
19. 说说你对koa中洋葱模型的理解?
Koa 的洋葱模型指的是以 next() 函数为分割点,先由外到内执行 Request 的逻辑,再由内到外执行 Response 的逻辑。通过洋葱模型,将多个中间件之间通信等变得更加可行和简单。其实现的原理并不是很复杂,主要是 compose 方法。在洋葱模型中,每一层相当于一个中间件,用来处理特定的功能,比如错误处理、Session 处理等等。其处理顺序先是 next() 前请求(Request,从外层到内层)然后执行 next() 函数,最后是 next() 后响应(Response,从内层到外层),也就是说每一个中间件都有两次处理时机
20. 说说如何借助webpack来优化前端性能?
使用监听模式或热更新热替换
开发环境不做无意义的操作
选择一个合适的devtool属性值
代码压缩用ParallelUglifyPlugin代替自带的 UglifyJsPlugin插件
代码压缩用ParallelUglifyPlugin代替自带的 UglifyJsPlugin插件
使用fast-sass-loader代替sass-loader
babel-loader开启缓存
不需要打包编译的插件库换成全局<script>标签引入的方式
使用 DllPlugin 和 DllReferencePlugin
提取公共代码
使用HappyPack来加速构建
优化构建时的搜索路径
理一下打包构建涉及的模块,分析看有哪些包是不需要打包的,只打包需要的模块(导出编译JSON文件)
使用ModuleConcatenationPlugin插件来加快JS执行速度
使用noParse
使用异步的模块加载
以模块化来引入
21.说说你对webSocket的理解?
WebSocket,是一种网络传输协议,位于OSI模型的应用层。可在单个TCP连接上进行全双工通信,
能更好的节省服务器资源和带宽并达到实时通迅.
客户端和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输,
websocket服务器与客户端通过握手连接,连接成功后,两者都能主动的向对方发送或接受数据,
而在websocket出现之前,开发实时web应用的方式为轮询,
不停地向服务器发送 HTTP 请求,问有没有数据,有数据的话服务器就用响应报文回应。如果轮询的频率比较高,那么就可以近似地实现“实时通信”的效果,
轮询的缺点也很明显,反复发送无效查询请求耗费了大量的带宽和 CPU资源