在学习react之前,我们首先需要了解js一些基础知识,请参考以下链接 js基础总结_是张鱼小丸子鸭的博客-CSDN博客
什么是react?
react是用于构建用户界面的Javscript库,起源于Facebook的内部项目,该公司对市场上的所有Javascript MVC框架都不满意,决定自行开发一套,用于架设自己的网站,React不是一个MVC框架,仅仅是视图(V)层的库
react特点?
-
1.声明式设计 −React采用声明范式,可以轻松描述应用。
-
2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
-
3.灵活 −React可以与已知的库或框架很好地配合。
-
4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
-
5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
-
6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单
react中diff算法的原理?
react 作为一款最主流的前端框架之一,在设计的时候除了简化操作之外,最注重的地方就是节省性能了。diff算法就是为节省性能而设计的,diff算法和虚拟DOM的完美结合是react最有魅力的地方。其中,diff 是 different 的简写,这样一来,diff 算法是什么也就顾名思义了——找不同
diff算法的基本流程:
第一次render在执行的时候会将第一次的虚拟dom做一次缓存,第二次渲染的时候会将新的虚拟dom和老的虚拟dom进行对比。这个对比的过程其实就是diff算法
diff算法的作用?
在DOM需要更新的时候,通过diff算法可以 计算出 虚拟DOM 中真正变化的部分,从而只针对变化的部分进行更新渲染,避免”牵一发而动全身“,造成性能浪费
为什么虚拟dom能够提高性能?
虚拟dom相当于在js和真实的dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作
用js对象表示真实的dom结构,当状态变化的时候再重新创建一个虚拟DOM树结构,然后用新的树和旧的树进行比较,记录两棵树的差异,把所记录的差异应用到所构建的真正的DOM树上,视图就更新了
真实dom和虚拟dom有什么区别?
dom的优缺点:
缺陷
- 牵一发而动全身(频繁操作DOM)
- 每次操作DOM,渲染引擎都需要进行重排、重绘或者合成等操作
- 对于 DOM 的不当操作还有可能引发强制同步布局和布局抖动的问题
- 大大降低渲染效率
优点:
- 保证性能下限
- 无须手动操作dom
- 跨平台
虚拟dom要解决的事情
- 将页面改变的内容应用到虚拟 DOM 上,而不是直接应用到 DOM 上
- 虚拟 DOM 并不急着去渲染页面,而仅仅是调整虚拟DOM 的内部状态
- 在虚拟 DOM 收集到足够的改变时,再把这些变化一次性应用到真实的 DOM 上。
虚拟dom都解决了哪些问题
- 频繁更新DOM引起的性能问题
- 将真实DOM和js操作解耦,减少js操作dom复杂性。
虚拟dom和真实dom的区别?
1:虚拟dom不会进行排版与重绘操作,虚拟dom就是把真实DOM转换为javaScript代码
2:虚拟dom进行频繁修改,然后一次性比较并修改真实DOM中需要修改的部分,最后真实DOM进行排版重绘,减少节点排版与重绘损耗
什么是jsx?
jsx是JavaScript的一种语法扩展,它跟模板语言很接近,但是它充分具备JavaScript的能力
JSX就是用来声明React当中的元素,React使用JSX来描述用户界面
JSX语法糖允许前端开发者使用我们最熟悉的类HTML标签语法来创建虚拟DOM在降低学习成本
jsx语法的特点
表达式写在{}中,在jsx中不能使用if else语句,可以使用三元运算表达式来替代
style需要写成style={{}}形式,写内联样式,也可以使用className属性,但是没有class属性
注释:{/* */}
只能有一个根组件
jsx的优点?
- JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
- 它是类型安全的,在编译过程中就能发现错误。
- 使用 JSX 编写模板更加简单快速
React创建元素的方法?
React.createElement()
class组件和函数组件区别?
语法上:
函数式组件是一个纯函数,它是需要接受props参数并且返回一个React元素就可以了。类组件是需要继承React.Component的,而且class组件需要创建render并且返回React元素,语法上来讲更复杂
状态管理:
函数式组件没有状态管理,类组件有状态管理
调用方式:
函数式组件可以直接调用,返回一个新的React元素;类组件在调用时是需要创建一个实例的,然后通过调用实例里的render方法来返回一个React元素
React 事件绑定的方式?
//直接调用函数
onClick={this.plus} 不要加() 如果加括号自动执行了
//箭头函数的方法来调用
<button onClick={()=>this.plus()}>num++</button>
//bind改变this的指向,还可以解决自己调用的方法
<button onClick={this.plus.bind(this)}>num++</button>
如果我们定义的函数是一个普通函数,调用函数修改数据的时候,使用this的时候提示报错,这个时候我们可以使用bind来绑定事件,改变this指向,或者我们可以把函数定义成箭头函数
state数据操作?
state仅限于在类组件中实现,react中修改数据,需要在setState中进行数据的修改
react的setState的执行机制是什么?
1.将setState传入的partialState参数存储在当前组件实例的state暂存队列中
2.判断当前React是否处于批量更新状态,如果是,将当前组件加入待更新的组件队列中。
3.如果未处于批量更新状态,将批量更新状态标识设置为true,用事务再次调用前一步方法,保证当前组件加入到了待更新组件队列中
4.调用事务的waper方法,遍历待更新组件队列依次执行更新
5.执行生命周期componentWillReceiveProps
6.将组件的state暂存队列中的state进行合并,获得最终要更新的state对象,并将队列置为空。
7.执行生命周期componentShouldUpdate,根据返回值判断是否要继续更新
8.执行生命周期componentWillUpdate。
9.执行真正的更新,render
10.执行生命周期componentDidUpdate
setState是同步执行的,但是state并不一定会同步更新
react数据渲染方式?
通过定义变量的方式,判断内容控制组件的显示和隐藏,相当于v-if
通过display的css样式判断属性是否存在,数据渲染{}
三元运算符控制是否存在
React如何获取表单数据?
- 给文本框绑定value属性,value属性绑定state中定义的变量
- 给表单绑定onChange事件,调用定义的方法
- 在方法中我们获取e.target.value属性,赋给value属性绑定的变量
React怎么实现列表渲染?
react中可以使用map方法渲染列表,return对应的页面结构即可, React 在渲染列表时,会要求开发者为每一个列表元素指定唯一的 key
,我们尽量不要使用index索引值作为key,如果对数据进行:逆序添加、逆序删除等破坏顺序操作:可能会引起页面更新错误问题
React中key的作用是什么?
key是虚拟DOM对象的唯一标识,在更新显示时key起到极其重要的作用 ,简单的来说就是为了提高diff的同级比较的效率,避免原地复用带来的副作用
react采用的是自顶而下的更新策略,每次小的改动都会生成一个全新的的vdom,从而进行diff,如果不写key,就会发生本来应该更新却没有更新
key是react用来追踪哪些列表的元素被修改,被添加或者是被删除的辅助标示。在开发过程中我们需要保证某个元素的key在其同级元素中具有唯一性
React组件样式的定义方式?
-
外联样式
定义css文件,在组件中通过import导入css样式,
import "App.css"
-
内联样式
React推崇的是内联的方式定义样式。这样做的目的就在于让你的组件更加的容易复用
定义一个style属性,属性中定义对应的css样式即可,比如style={{fontSize:'15px'}}
外层花括号是语法,内层花括号是对象边界符
Props校验数据类型
array(数组)、bool(布尔值)、func(函数number(数字)、object(对象)、string(字符串)
受控组件和非受控组件
受控组件
由React控制的输入表单元素而改变其值的方式,称为受控组件。
比如,给表单元素input绑定一个onChange事件,当input状态发生变化时就会触发onChange事件,从而更新组件的state
非受控组件
非受控组件指的是,表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值。
在非受控组件中,可以使用一个ref来从DOM获得表单值
props和state的区别
props是指组件间传递的一种方式,props自然也可以传递state。由于React的数据流是自上而下的,所以是从父组件向子组件进行传递;另外组件内部的this.props属性是只读的不可修改!
state是组件内部的状态(数据),不能够直接修改,必须要通过setState来改变值的状态,从而达到更新组件内部数据的作用
组件传值的方式?
父传子组件通信, 子传父组件通信 兄弟组件通信
组件的概念?
组件是可以重复利用一段HTML的代码片段
组件复用的是HTML的结构和css样式
组件通信的意义是什么?
组件结构或者数据更加的灵活,得到更高的复用效果
组件通信需要符合什么标准?
单项的数据流
父传子通信流程
-
把父组件中需要拆分的html代码拷贝到子组件中,父组件中把组件引入 直接挂载
-
在父组件中的子组件标签上绑定一个自定义的属性,传递数据到子组件中去
-
子组件中props来接受父组件传递过来的数据,class类 this.props 函数组件 props使用
子传父通信流程
-
先在父组件中定义个方法,把这个方法作为属性传递给子组件
-
子组件中通过props接受父组件传递的方法,调用方法,传递参数即可
-
父组件的方法自动执行,子传父的过程
兄弟组件通信
中间人模式,先父传子,在子传父
context状态传递数据 参考Vue中eventBus对象
-
先定义一个空对象 抛出一个React.createContext(默认值), 解构赋值获取Provider和 Consumer组件
-
根组件中定义 在需要的位置使用Context的Provider
-
定义contextType获取数据,或者也可以使用Consumer获取多个内容
为什么keepalive包裹组件后,组件会被缓存呢?
activated:在 keep-alive 组件激活时调用,该钩子函数在服务器端渲染期间不被调用
deactivated:在 keep-alive 组件停用时调用,该钩子在服务器端渲染期间不被调用
使用 keep-alive 会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在 activated 阶段获取数据,承担原来 created 钩子函数中获取数据的任务
context状态树是怎么运行的?
- 在父组件中我们通过createContext() 创建一个空对象,在父组件的最外层我们使用Provider包裹数据,通过value绑定要传递的对象数据。
- 在嵌套的子组件中,我们有两种方式获取数据:
(1) 我们可以使用Customer标签,在标签中绑定一个箭头函数,函数的形参context就是value传递的数据
(2). class组件中我们可以定义static contextType=context对象,组件中直接使用this.context获取数据。
React生命周期分为几个阶段?
Mounting(挂载阶段):已插入真实 DOM
Updating(更新阶段):正在被重新渲染
Unmounting(卸载阶段):已移出真实 DOM
注意:react生命周期只能在类组件中使用
简述React的生命周期函数?
挂载阶段:
- constructor() 在 React 组件挂载之前,会调用它的构造函数。
- componentWillMount: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
- componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用更新运行阶段:
* componentWillReceiveProps: 在接受父组件改变后的props需要重新渲染组件时用到的比较多,外部组件传递频繁的时候会导致效率比较低
* shouldComponentUpdate():用于控制组件重新渲染的生命周期,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
* render(): render() 方法是 class 组件中唯一必须实现的方法。
* *componentWillUpdate(): shouldComponentUpdate返回true以后,组件进入重新渲染完成之前进入这个函数。
* **componentDidUpdate(): 每次state改变并重新渲染页面后都会进入这个生命周期
卸载或销毁阶段
componentWillUnmount (): 在此处完成组件的卸载和数据的销毁
React旧生命周期有哪些问题?
(1) componentWillMount ,在ssr中 这个方法将会被多次调用, 所以会重复触发多遍,同时在这里如果绑定事件,
将无法解绑,导致内存泄漏 , 变得不够安全高效逐步废弃。
(2) componentWillReceiveProps 外部组件多次频繁更新传入多次不同的 props,会导致不必要的异步请求
(3) componetWillupdate, 更新前记录 DOM 状态, 可能会做一些处理,与componentDidUpdate相隔时间如果过长, 会导致 状态不太信
React新生命周期有哪些改变?
- 用 getDerivedStateFromProps替换了 compoentWillMount和compontWillReceiveProps生命周期函数
- 用getSnapshotBeforeUpdate函数替换componetWillUpdate方法,避免和CompoentDidUpdate函数中获取数据不一致的问题
react的路由几种模式,是什么?
两种路由模式:
一种是Hash路由模式,用的是HashRouter组件
一种是历史路由模式,用的是BrowserRouter组件绑定
react路由的基本概念?
路由是根据不同的 url 地址展示不同的内容或页面。
路由是一个针对React而设计的路由解决方案、可以友好的帮你解决React components 到URl之间的同步映射关系
react路由常用的组件有哪些?
HashRouter或BrowserRouter配置路由模式
Route 定义路由组件映射关系
Redirect 设置路由重定向
NavLink 或者Link 页面路由跳转
Switch 路由匹配,当path匹配到一个component之后,将不会再想下继续匹配,提高了程序效率
react路由传参的方式有哪些?
//隐士参数传递
(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}
react路由跳转的方式有哪些?
声明式导航:
使用NavLink或者Link跳转, to属性后面跟字符串或者跟对象
编程式导航跳转:
props.history.push(url) 跳转页面可以返回上一页,保留历史记录
props.history.replace(url) 跳转页面,清空历史记录
props.history.go(num) 返回第几个页面
react路由嵌套如何配置?
- 配置父组件的路由地址,在父组件中配置子组件的路由映射关系
- 关闭父组件路由配置exact属性,避免精准匹配
- 父组件路由地址作为子组件路由地址的开始的一部分。比如父组件是/index 子组件应该是/index/子组件地址
withRouter是干什么的?
不是所有组件都直接与路由相连(比如拆分的子组件)的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,将react-router的history、location、match三个对象传入props对象上,此时就可以使用this.props.history跳转页面了或者接受参数了
react路由v5和v6的区别?
-
V5中Switch换成Routes标签,
-
V5中exact属性没有了,V6默认是精准匹配
-
V5中的component属性,V6用的element,element属性是组件标签
-
V6中重定向导航组件换成Navigate
-
V6中路由嵌套直接采用组件包裹的方式,可以不适用path绝对路径,
-
V6中的<Outlet> 相当于vue中router-view
-
获取参数和Hooks方法的变化
-
props获取V6中props值没有参数数据,必须采用Hooks的路由获取数据。
-
withRouter在V6版本中也被去掉了。
什么是Redux?
在react中每个组件的state是由自身进行管理,包括组件定义自身的state、组件之间的通信通过props传递、使用Context实现数据共享等,如果让每个组件都存储自身相关的状态,理论上来讲不会影响应用的运行,但在开发及后期我们将比较难以维护,所以我们可以把数据进行集中式的管理,redux就是一个实现上述集中管理的容器的工具,redux
并不是只应用在react
中,还与其他界面库一起使用,如Vue
Redux的三大原则
- state数据必须是单一数据源
- redux中的state数据必须 是只读的,只能通过dispatch调用actions修改
- Reducer必须使用纯函数来执行修改
redux的执行原理
React的组件需要获取或者修改页面的数据,通过dispatch方法调用actions进入到Reducer函数中修改state的数据内容,state更新后,通知组件更新页面即可
redux的使用步骤
- 创建一个store文件夹,新建一个index.js文件
- 文件中导入redux的createStore方法,用于创建公共数据区域
- 创建一个reducer纯函数,接受两个参数state,actions分别表示分别表示数据和操作state的方法,返回state数据给组件页面
- 把reducer作为createStore的参数抛出
- 在需要使用的页面导入store文件,通过store.getState获取数据,通过store.dispatch触发action修改state数据
- store.subscrible 方法监听 store 的改变,避免数据不更新
state和props有什么区别
相同点:
两者都是 JavaScript 对象
两者都是用于保存信息
props 和 state 都能触发渲染更新
区别:
props 是外部传递给组件的,而 state 是在组件内被组件自己管理的,一般在 constructor 中初始化
props 在组件内部是不可修改的,但 state 在组件内部可以进行修改
state 是多变的、可以修改
super() 和super(props)有什么区别?
在 React 中,类组件基于 ES6,所以在 constructor 中必须使用 super
在调用 super 过程,无论是否传入 props,React 内部都会将 porps 赋值给组件实例 porps 属性中
如果只调用了 super(),那么 this.props 在 super() 和构造函数结束之间仍是 undefined
说说 React中的setState执行机制
一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state, 当需要修改里面的值的状态需要通过调用setState来改变,从而达到更新组件内部数据的作用
setState第一个参数可以是一个对象,或者是一个函数,而第二个参数是一个回调函数,用于可以实时的获取到更新之后的数据
在使用setState更新数据的时候,setState的更新类型分成:同步更新,异步更新
在组件生命周期或React合成事件中,setState是异步
在setTimeout或者原生dom事件中,setState是同步
对同一个值进行多次 setState, setState 的批量更新策略会对其进行覆盖,取最后一次的执行结果
React的事件机制总结
React事件机制总结如下:
React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
React 自身实现了一套事件冒泡机制,所以这也就是为什么我们 event.stopPropagation()无效的原因。
React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
React 有一套自己的合成事件 SyntheticEvent
说说对React refs 的理解?应用场景?
创建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中间件的理解?常用的中间件有哪些?
Redux中,中间件就是放在就是在dispatch过程,在分发action进行拦截处理
前面我们了解到了Redux整个工作流程,当action发出之后,reducer立即算出state,整个过程是一个同步的操作
那么如果需要支持异步操作,或者支持错误处理、日志监控,这个过程就可以用上中间件,其本质上一个函数,对store.dispatch方法进行了改造,在发出 Action和执行 Reducer这两步之间,添加了其他功能
常用的redux中间件:
redux-thunk:用于异步操作
redux-logger:用于日志记录
const store = createStore(
reducer,
applyMiddleware(thunk, logger)
);
React中常见的Hooks方法有哪些?
useState()用于为函数组件引入状态。在useState()中,数组第一项为一个变量,指向状态的当前值。类似this.state,第二项是一个函数,用来更新状态,类似setState
useEffect()接受两个参数,第一个参数是你要进行的异步操作,第二个参数是一个数组,用来给出Effect的依赖项。只要这个数组发生变化,useEffect()就会执行
useRef:相当于class组件中的createRef的作用,ref.current获取绑定的对象
useContext:接收context状态树传递的数据内容
useReducer:接收reducer函数和状态的初始值作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数
useMemo 和 useCallback接收的参数都是一样,第一个参数为回调,第二个参数为要依赖的数据。共同作用:仅仅依赖数据发生变化, 才会调用,也就是起到缓存的作用。useCallback缓存函数,useMemo 缓存返回值
useMemo, useCallback做了那些性能优化 ?
useMemo 主要缓存复杂运算的数据的结果,第二个参数,定义监听的变量,需要返回一个结果。
当父组件的组件更新的时候会导致子组件的重新渲染,但是如果父组件的更新的数据没有传递给子组件的话,这个时候如果还让子组件重新渲染的化,就会导致组件的更新的性能消耗比较大
所以说这个时候我们可以使用useMemo, 或者React中内置的memo方法对子组件进行缓存,这样的话只有父组件更新跟子组件相关联的数据的时候才会导致子组件的重新渲染,从而提高组件的渲染性能。
但是如果我们给子组件传递方法的时候,上面memo方法的缓存就不起作用了,原因是父组件没更新一次会导致方法的重新调用,进而导致子组件的重新更新,所以这个时候我们可以使用useCallback对传递的方法进行缓存,监听数据更新后才会重新调用方法,从而提高组件的渲染性能
A组件是B组件的父组件,那么A组件加载完成,请详细叙述A,B组件执行流程
执行A组件的constructor, componentWillMount, render方法
2. 执行B组件的 constructor, componentWillMount, render componentDidMount方法
3. 在执行A的的componentDidMount方法,更新的方法类比
redux页面不更新的问题
可以用store.subscribe() 方法监听redux数据的更新
把redux中的数据绑定useState上,解构赋值对数据进行解析
useEffect() 中store.subscribe() 方法监听action的提交
setState直接更新store.getState()的数据即可
vue和react的区别有哪些?
一提到前端框架,相信大家都对Vue和React不陌生,这两个前端框架都是比较主流的,用户也都比较多
1.核心思想不同:
Vue的核心思想是尽可能的降低前端开发的门槛,是一个灵活易用的渐进式双向绑定的MVVM框架
React的核心思想是声明式渲染和组件化、单向数据流,React既不属于MVC也不属于MVVM架构
组件化:组件化指的是尽可能的将页面拆分成一个个较小的、可以复用的组件,这样让我们的代码更加方便组织和管理,并且拓展性页更强
声明式:声明式与之相对应的是命令式,命令式指的是通过DOM操作一步步把网页变成想要的样子,而声明式则是只需要通过状态去形容最后的网页长什么样子即可
单项数据流:React的单向数据流指的是数据主要从父节点通过props传递到子节点,如果顶层某个props改变了,React会重新渲染所有的子节点,但是单向数据流并非单向绑定,React想要从一个组件去更新另一个组件的状态,需要进行状态提升,即将状态提升到他们最近的祖先组件中,触发父组件的状态变更,从而影响另一个组件的显示。单向数据流的好处是能够保证状态改变的可追溯性,假如,父组件维护了一个状态,子组件如果能够随意更改父组件的状态,那么各组件的状态改变就会变得难以追溯
2.组件写法不同
Vue的组件写法是通过template的单文件组件格式
React的组件写法是JSX+inline style,也就是把HTML和CSS全部写进JavaScript中
3.diff算法不同
vue对比节点,如果节点元素类型相同,但是className不同,认为是不同类型的元素,会进行删除重建,但是react则会认为是同类型的节点,只会修改节点属性
vue的列表比对采用的是首尾指针法,而react采用的是从左到右依次比对的方式,当一个集合只是把最后一个节点移动到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点移动到最后一个,从这点上来说vue的对比方式更加高效
4.响应式原理不同
React主要是通过setState()方法来更新状态,状态更新之后,组件也会重新渲染
vue会遍历data数据对象,使用Object.definedProperty()将每个属性都转换为getter和setter,每个Vue组件实例都有一个对应的watcher实例,在组件初次渲染的时候会记录组件用到了那些数据,当数据发生改变的时候,会触发setter方法,并通知所有依赖这个数据的watcher实例调用update方法去触发组件的compile渲染方法,进行渲染数据
React中路由常用的Hooks有哪些?如何进行参数传递
useHistory
const history = useHistory();
function data(){
history.push('需要数据的路由的url', { state:数据 })
}
//接收函数
useEffect(() => {
//做个判断别没收到报错
if (history.location.state) {
//打印看看
console.log(history.location.state.state)
}
}, [])
useLocation
useParams
useRouteMatch
useNavigate
/*
第一种:params传递参数
此方式传递参数:需要注意的是在路由中需要配置占位符
*/
navigate('/login/17');
/* 第二种:search传递参数 */
navigate('/login?name=xiaoming&age=10')
/* 第三种:state属性携带参数 */
navigate('/login',{state: '我从登陆页面过来了!!!'})
如何自定义Hooks方法,有哪些应用场景?
hooks两个核心优点:
是方便进行逻辑复用
是帮助关注分离
自定义Hooks在形式上其实非常简单,就是声明一个名字以use开头的函数,比如useCounter。这个函数在形式上和普通函数没有区别,你可以传递任意参数给这个Hooks。但是要注意,Hooks和普通函数在语义化上是由区别的,就在于函数没有用到其他Hooks。什么意思呢?就是说如果你创建了一个 useXXX 的函数,但是内部并没有用任何其它 Hooks,那么这个函数就不是一个 Hook,而只是一个普通的函数。但是如果用了其它 Hooks ,那么它就是一个 Hook
实际的开发场景一定是比较复杂的,比如对于 API 返回的数据需要做一些数据的转换,进行数据的缓存
为什么React不优先推荐考虑使用Context状态树
1、Context目前还处于实验阶段,可能会在后面的发行版本中有很大的变化,事实上这种情况已经发生了,所以为了避免给今后升级带来大的影响和麻烦,不建议在app中使用context。
2、尽管不建议在app中使用context,但是独有组件而言,由于影响范围小于app,如果可以做到高内聚,不破坏组件树之间的依赖关系,可以考虑使用context
3、对于组件之间的数据通信或者状态管理,有效使用props或者state解决,然后再考虑使用第三方的成熟库进行解决,以上的方法都不是最佳的方案的时候,在考虑context。
4、context的更新需要通过setState()触发,但是这并不是很可靠的,Context支持跨组件的访问,但是如果中间的子组件通过一些方法不影响更新,比如 shouldComponentUpdate() 返回false 那么不能保证Context的更新一定可以使用Context的子组件,因此,Context的可靠性需要关注
React中构造函数有什么作用?是否是必须的?
构造函数并不是必须的,对于无状态组件,内部没有维护自己的state,只接收外部传入的props 是不需要声明构造函数的
Redux和vuex有什么区别?
vuex 和 redux 都是状态管理库,用于单独管理状态的。其中,redux是一个范用的库,可以单独使用。而vuex是专门用来配合vue使用的。他们都应用了flux架构的思想,但是在接口的提供上稍有不同。
1. 核心概念对比
Redux 的核心概念
action (同步action ,或借助 中间件 实现异步操作,action 不会改变 store,只是描述了怎么改变store)| mutation(用于同步操作) 、action(可用于异步操作,提交 mutation)
reducer(纯函数,根据 action 和旧的 store 计算出新的 store)
store(单一数据源)
Vuex 的核心概念
mutation(用于同步操作) 、action(可用于异步操作,提交 mutation)
mutation里面直接修改 state
state(单一数据源)
2. 使用原则:
Redux 的三大原则:
(1)单一数据源(一个Redux应用只有一个store),也是单向的数据流
(2)state只读(唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象)
(3)使用纯函数(reducer)来修改state。
redux的流程:
view——>action——>store——>reducer(返回)——>store——view
Vuex 的三大原则:
(1) 应用层级的状态应该集中到单个 store 对象中。
(2) 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
(3)异步逻辑都应该封装到 action 里面。
vue的流程:
vueComponent——>(dispatch)action——>(commit)——>mutations——>(mutate)state——>(render)vueComponent
3. 处理异步操作
Redux 的中间件机制,利用 redux-thunk ,redux-thunk可以dispatch函数,这个函数用于生成action,所以在这个函数里我们可以进行异步操作,等异步的结果出来后再放在action里面,将这个action用dispatch分发出去,而这个函数被叫做 “action creator”,可以将异步逻辑放在 action creator 里面,给 action creator 传入 dispatch 作为参数,于是就可以 dispatch action,Redux 并没有创造单独的概念出来专门用于异步逻辑,它是利用了 Redux 自己实现的中间件机制,中间件从 dispatch 一个异步 action 到 action 到达 reducer 之间处理 action,在这期间通过异步操作得到的结果可以放到 action 里面再通过 dispatch 分发到 reducer,以前 dispatch 一个 action 之后,这个 action 回立即到达 reducer ,所以是同步 action,现在在 action creator 里面,可以等待异步操作结果再生成 action 分发,所以叫做异步 action
而 Vuex 是用 mutation 来对应 Redux 的 action,另外 Vuex 又创造了一个 action 来提交 mutation 并通过异步提交 mutation 来实现异步操作结果能够修改 state
说说对Fiber架构的理解?解决了什么问题?
React Fiber 是 Facebook 花费两年余时间对 React 做出的一个重大改变与优化,是对 React 核心算法的一次重新实现
在react
中,主要做了以下的操作:
- 为每个增加了优先级,优先级高的任务可以中断低优先级的任务。然后再重新,注意是重新执行优先级低的任务
- 增加了异步任务,调用requestIdleCallback api,浏览器空闲的时候执行
- dom diff树变成了链表,一个dom对应两个fiber(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行
从架构角度来看,Fiber
是对 React
核心算法(即调和过程)的重写
从编码角度来看,Fiber
是 React
内部所定义的一种数据结构,它是 Fiber
树结构的节点单位,也就是 React 16
新架构下的虚拟DOM
Fiber
把渲染更新过程拆分成多个子任务,每次只做一小部分,做完看是否还有剩余时间,如果有继续下一个任务;如果没有,挂起当前任务,将时间控制权交给主线程,等主线程不忙的时候在继续执行
即可以中断与恢复,恢复后也可以复用之前的中间状态,并给不同的任务赋予不同的优先级,其中每个任务更新单元为 React Element
对应的 Fiber
节点
实现的上述方式的是requestIdleCallback
方法
window.requestIdleCallback()
方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件
说说React Jsx转换成真实DOM过程
- 使用React.createElement或JSX编写React组件,实际上所有的 JSX 代码最后都会转换成React.createElement(...) ,Babel帮助我们完成了这个转换的过程。
- createElement函数对key和ref等特殊的props进行处理,并获取defaultProps对默认props进行赋值,并且对传入的孩子节点进行处理,最终构造成一个虚拟DOM对象
- ReactDOM.render将生成好的虚拟DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM
说说 React 性能优化的手段有哪些?
-
避免使用内联函数:我们使用内联函数,则每次调用
render
函数时都会创建一个新的函数实例 -
使用 React Fragments 避免额外标记:
用户创建新组件时,每个组件应具有单个父标签。父级不能有两个标签,所以顶部要有一个公共标签,所以我们经常在组件顶部添加额外标签
div
这个额外标签除了充当父标签之外,并没有其他作用,这时候则可以使用
fragement
-
使用 Immutable:主要体现在减少渲染的次数
-
懒加载组件:而在
react
中使用到了Suspense
和lazy
组件实现代码拆分功能, -
事件绑定方式:从性能方面考虑,在
render
方法中使用bind
和render
方法中使用箭头函数这两种形式在每次组件render
的时候都会生成新的方法实例,性能欠缺 -
服务端渲染:采用服务端渲染端方式,可以使用户更快的看到渲染完成的页面
-
避免不必要的render:主要手段是通过
shouldComponentUpdate
、PureComponent
、React.memo
说说你在React项目是如何捕获错误的
为了解决出现的错误导致整个应用崩溃的问题,react16
引用了错误边界新的概念
错误边界是一种 React
组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript
错误,并打印这些错误,同时展示降级 UI
,而并不会渲染那些发生崩溃的子组件树
错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误
形成错误边界组件的两个条件:
- 使用了 static getDerivedStateFromError()
- 使用了 componentDidCatch()
下面这些情况无法捕获到异常:
- 事件处理
- 异步代码
- 服务端渲染
- 自身抛出来的错误
说说React服务端渲染怎么做?原理是什么?
整体react
服务端渲染原理并不复杂,具体如下:
node server
接收客户端请求,得到当前的请求url
路径,然后在已有的路由表内查找到对应的组件,拿到需要请求的数据,将数据作为 props
、context
或者store
形式传入组件
然后基于 react
内置的服务端渲染方法 renderToString()
把组件渲染为 html
字符串在把最终的 html
进行输出前需要将数据注入到浏览器端
浏览器开始进行渲染和节点对比,然后执行完成组件内事件绑定和一些交互,浏览器重用了服务端输出的 html
节点,整个流程结束
react和umi的区别?
react:
优点
1、React运用一个虚拟的DOM实现了一个非常强大的渲染系统,在React中对DOM只更新不读取;
2、一切都是component,代码更加模块化,重用代码更容易,可维护性高
3、单向数据流
4、同构和服务端渲染可以更好的SEO和解决白屏问题
1、 它不是一个框架,它只是MVC(模型 - 视图 - 控制器)中的view
2、 jsx的一个问题是,渲染函数常常包含大量逻辑,最终看着更像是程序片段,而不是视觉呈现。后期如果发生需求更改,维护起来工作量大
3、 需要配合router和Flux/Mbox才能写大型应用
umi
umi 是一个基于路由的框架,支持类似 next.js 的常规路由和各种高级路由功能,比如路由级的按需加载,umi 是蚂蚁金服的基础前端框架,配合antd使用特别适合企业级别的平台管理系统的框架
- 可插拔:umi 的整个生命周期都是由插件组成的。pwa、按需加载、一键切换preact、一键兼容ie9等功能,都是通过插件实现的。
- 开箱即用:只需要一个 umi 依赖即可开始开发,无需安装 react、preact、webpack、react-router、babel、jest 等
- 常规路由:Next.js 喜欢和功能齐全的路由约定,支持权限、动态路由、嵌套路由等。
- 支持渲染降级:优先使用 SSR,如果服务端渲染失败,自动降级为客户端渲染(CSR),不影响正常业务流程。
- 项目结构很清晰,根据页面路由直接定位目录文件
- 支持插件很多,按需使用(http mock、service worker、layout、高清方案等,都是一个个的插件)
- 性能更优:整合了那么多东西, 运行速度还是快如闪电(PWA、按需加载、tree-shake、scope-hoist、智能提取公共文件、Critical CSS、preload、hash build、preact 等等)
- 和next.js类似,内含webpack配置等多项配置
- Next 和 Umi 都完全支持构建用于生产的 React 应用程序,几乎不需要配置。Next 对编写 CSS 和自定义其 webpack 配置有更完整的支持,而 Umi 更固执己见,对 webpack 配置的支持并不多。
- Next 有自己的一组插件,umi 的内部功能都是第三方插件。
- 命令行支持:Umi 有一些有趣的 CLI 支持来生成页面并检查当前的 webpack 配置,Next 的 CLI 支持仅专注于帮助您部署应用程序