文章目录
- react的生命周期链接
- 1、React 和Vue 的对比?
- 2、JSX 语法?
- 3、渲染列表?
- 4、react事件为何要使用bind 绑定this?
- 5、event 参数?
- 6、表单的使用 (受控组件)?
- 7、父子组件通讯
- 8、setState 讲解?
- 9、react 组件的生命周期?
- 10、react的class组件和函数组件的区别?
- 11、react 的高级特性
- 12、Redux
- 13、 react-router 路由懒加载
- 13、JSX使用babel编译
- 14、React的合成事件机制?
- 15、setState和batchUpdate 的机制 、事务机制?
- 16、 react 组件渲染和更新的过程?
- 17、react的性能优化?
- ajax应该放在哪个生命周期?
- 什么是纯函数?
- 函数组件和class组件的区别?
react的生命周期链接
1、React 和Vue 的对比?
相同点:
- 都支持组件化
- 都是数据驱动试图
- 都是使用虚拟dom操作dom
区别
- React 使用JSX拥抱js,vue使用模板拥抱html
- React 是函数式编程(例如修改state都使用setState()),vue是声明式变成(例如:修改data里的name,就data.name ='hah ')
- React 更多的自己去写,vue是你想要的都给你
- React 和Vue 有很多相通之处,并且正在趋于一致
- vue 给我们定义了很多的指令,很好用;react 除了需要遵守JSX语法之外,所有逻辑都可以使用js来写。(例如:列表渲染:vue v-for ,react 的map)
2、JSX 语法?
- 是 js XML 的简写,表示在javascript 中写XML(HTML)格式的代码,是js的语法扩展
- 使用 JSX 配合嵌入的 JS 表达式、条件渲染、列表渲染,可以描述任意 UI 结构。
1、JSX的本质?
- JSX编译是通过babel 来编译的,因为babel里已经集成了jsx的编译环境
- 编译之后变成一堆的React.createElement(‘tag’,{},childern,children…)
- 所以JSX的本质就是babel编译过来一堆的React.createElement,执行之后就返回虚拟dom(Vnode)
- 再通过patch函数去渲染结构
- React.createElement 第一个参数是一个html标签或者是一个组件(组件名称首字母必须大写,这是react规定的,这也是来区分是标签还是组件)
3、渲染列表?
- 使用map ,key必填,跟vue的v-for 中的key作用类似
- diff算法是通过tage和key来进行判断新旧虚拟dom是否相同的重要值
- 有key值之后就可以减少dom的渲染次数,提升渲染性能
4、react事件为何要使用bind 绑定this?
- 因为react中默认this是undefined,如果想使用state中的属性和方法,就要使用bind改变this的指向,指向当前的实例
如果使用class方式定义的方法,使用bind改变this指向
export default class eventDemo extends Component {
constructor(props) {
super(props)
this.state = {
name: '张三' ,
}
/**
* 修改方法的this指向,指向当前的实例 ,在constructor里面修改this指向只会执行一次,
* 在jsx结构中绑定的话会每次点击都会执行一次bind函数,提高性能
* */
this.changeName = this.changeName.bind(this)
}
changeName(){
this.setState({
name:'李四'
})
}
render() {
return (
<div className="developin" onClick={this.changeName}>
{this.state.name}
</div>
)
}
}
使用静态方法,箭头函数的方式定义,无需使用bind 绑定this,因为箭头函数的this指向当前的实例
export default class eventDemo extends Component {
constructor(props) {
super(props)
this.state = {
name: '张三' ,
}
}
changeName = ()=>{
this.setState({
name:'王五'
})
}
render() {
return (
<div className="developin" onClick={this.changeName}>
{this.state.name}
</div>
)
}
}
5、event 参数?
- 参数怎么获取?
(1)、事件方法没有参数,默认第一个参数就是event
changeName = (event)=>{
console.log(event)
}
render() {
return (
<div className="developin" onClick={this.changeName}>
{this.state.name}
</div>
)
}
(2)、事件方法有参数,默认最后追加的参数是event
changeName = (name,event)=>{
console.log(name,event)
}
render() {
return (
<div className="developin" onClick={this.changeName.bind(this,this.state.name)}>
{this.state.name}
</div>
)
}
- 关于event
- react event 不是原生的event ,是react 封装的,它的原型是SyntheticEvent(组合事件),可以模拟出原生DOM 的所有功能
- 在react中你可以通过event.nativeEvent获取原生事件对象
- 在react 17 之前所有的对象都挂在到document 上,react 17之后都挂在在root组件上,有利于多个react版本并存
- 和DOM事件不一样,vue事件也不一样
changeName = (event)=>{
event.preventDefault() // 阻止默认事件
event.stopPropagation() // 阻止事件冒泡
console.log('event',event) // 不是原生的event (SyntheticEvent),原生的是MouseEvent
console.log('native',event.nativeEvent) // 原生的,MouseEvent
}
render() {
return (
<a className="developin" onClick={this.changeName.bind(this,this.state.name)}>
{this.state.name}
</a>
)
}
- 在react 17 之前所有的对象都挂在到document 上,react 17之后都挂在在root组件上,有利于多个react版本并存,例如微前端
6、表单的使用 (受控组件)?
- 受控组件:表单的值受state控制,需要自行监听onchange,来更新state的值(input 、textarea、select 都是用的value,checkbox,radio 用的是checked)
// 受控组件
constructor(props) {
super(props)
this.state = {
name: '张三' ,
}
}
changeName = (event)=>{
this.setState({
name:event.target.value
})
changeName1 = (event)=>{
this.setState({
name:event.target.value
})
}
render() {
return (
<div>
<input type="checkbox" checked={this.state.name} onChange={this.changeName1} />
<input value={this.state.name} onChange={this.changeName} />
</div>
)
}
7、父子组件通讯
- 父组件向子组件传递数据,子组件组用props接收即可
- 子组件要想传递数据给父组件,回调函数的方式;首先在父组件中定义一个函数,接收子组件传递的值,将函数传递给子组件,子组件在触发某个事件的时候调用父组件的函数。子组件触发,父组件这个函数也会触发,说白了就是传递函数过去
- 跨级组件通讯 : context
- 没有嵌套关系的组件通讯 : redux
8、setState 讲解?
-
在要修改state值的时,不能直接修改state的值,需要将state深拷贝一个变量,再通过setState去改变state的值。(不可变值)
-
setState是异步还是同步?react小于等于17版本之前有异步同步之说,17版本以上就都是异步了。
- 当你使用setState修改state值之后,立即获取state的值是获取不到最新的值的,这时候就是异步的,如果想立即获取state的值,就再setState函数加一个回调函数的参数,再回到函数里可以拿到最新的state
- 在setTimeout中setState是同步的,17版本之前
- 自己定义的事件,setState也是同步的,17版本包含17之前是同步,17以上是异步,因为react 18 更新了一个自动批处理
- 当你使用setState修改state值之后,立即获取state的值是获取不到最新的值的,这时候就是异步的,如果想立即获取state的值,就再setState函数加一个回调函数的参数,再回到函数里可以拿到最新的state
-
setState有时候会合并
- 传入对象的时候,setState会合并,类似Object.assign()如果state修改的值都一样的代码的话
- 传入函数的时候,setState不会合并,因为函数是一个可执行代码,不是对象
- 传入对象的时候,setState会合并,类似Object.assign()如果state修改的值都一样的代码的话
9、react 组件的生命周期?
- 单个组件的生命周期,常用的生命周期
- 多个组件的生命周期,和vue的一样
10、react的class组件和函数组件的区别?
- 函数组件没有state,没有生命周期,没有this
- 纯函数,接收props,输出JSX
11、react 的高级特性
- 非受控组件:表单的值不受 state的控制,只不过拿state 的值当作表单的初始值,表单的值的变化跟state的值没有任何关系,如果要获取表单的值,只能通过ref的方式获取dom的节点去取表单的值。
- 使用场景
- 当需要操作dom去获取值的时候,你就可用非受控组件去做,setState实现不了
- 文件上传
- 某些富文本编辑器,需要传入dom元素
- ref 创建ref : React.createRef()
- defaultValue 、defaultChecked
- 手动操作DOM元素
-
Portals(传送门):组件默认会按照既定层次嵌套渲染,Portals可以让组件渲染到父组件以外,使用ReactDOM.createProtal去包裹dom节点,然后将你需要挂在到哪个dom节点上去,就把dom节点卸载第二个参数那里。不会打乱组件中的结构
-
使用场景: 一般都是用在跟css相关的场景上
- 父组件设置overFlow:hidden,设置了bfc会限制子组件的展示,这时子组件又想展示到其他位置,就可使用Portals
- 父组件的z-index 太小,子组件想展示出来,也可用这个
- fixed需要放在body的第一层,例如弹窗组件
-
context: 层级很深的时候需要传递比较公告信息(语言,主题等),就使用context ,如果使用props 比较麻烦,但是用redux又小题大做
import React, { Component } from 'react'
// 创建Context 填入一个默认值
const themeContext = React.createContext('light')
// 函数组件使用Consumer
function Child (props) {
return (
<themeContext.Consumer>
<div>主题是:{value}</div>
</themeContext.Consumer>
)
}
class LastChile extends Component {
render() {
const them = this.context
return (
<div>主题是: {them}</div>
)
}
}
// 指定contextType 读取当前context
LastChile.contextType = themeContext
export default class eventDemo extends Component {
constructor(props) {
super(props)
this.state = {
them:'light' ,
}
}
changeTheme = ()=>{
this.setState({
them: this.state.them === 'light'?'back':'light'
})
}
render() {
return (
<themeContext.Provider value={this.state.them}>
<Child />
<LastChile />
<button onClick={this.changeTheme}>修改主题</button>
</themeContext.Provider>
)
}
}
-
异步组件:React.lazy(()=>import(‘组件路径’))引入,React.Suspense包裹异步组件,还可加loading状态
-
性能优化
-
shouldComponentUpdate(简称SCU):react 默认父组件更新,子组件无条件更新
-
PureComponent(纯组件) 和React.memo
-
不可变值 immutable.js
-
-
高阶组件HOC:功能:公共逻辑的抽离。创建一个组件,接收一个组件为参数,里面又定义了一个新的组件,将传入的参数组件在render上返回,返回一个新的组件。在高阶组件里面写入我们的公共逻辑即可。高阶组件传入的值,都透传给传入的子组件里。子组件当参数放入高阶组件。
- 其中redux的connect 内部就使用了HOC的模式
- 基本用法
-
Render Props:公共逻辑的抽离;通过一个函数将class组件的state作为props传递给纯函数组件。公共组件放在组件里面
*基本使用
1、高阶组件(HOC)和 Render props 的区别?
- HOC 模式简单,但是会增加组件层级;层级多的话,会增加一些透传的成本,维护上风险就大一些。
- Render Props 代码比较简洁,但是学习成本比较高
12、Redux
1、redux 的基本概念(单项数据流)
-
store 、 state:当state改变时要返回一个全新的值,不能直接修改state
-
action 处理异步和没有异步
-
reducer:是一个纯函数,根据类型来返回新的state
2、 Redux 中间件(上面是redux正常流程,下面是使用redux中间件之后的流程)
- 正常流程 : 比如事件触发之后触发dispatch 回调函数,派发action,reducer 就会接收action ,根据action 上的type 来更改对应的state 值,然后state发生改变,页面也就发生改变
- 使用中间件流程:在dispatch 中多了一步数据处理,也就是redux的中间件,是对dispatch的一个封装,之前dispatch接收action的是一个对象,但是使用中间件之后还可以是一个函数,如果你使用了中间件,dispatch接收的是一个对象,它就会直接派发给store,但是如果接收到一个函数的话,dispatch就会先将函数处理完,处理好之后再派发给store。在这里处理啥呢,可以处理ajax异步请求等操作。
13、 react-router 路由懒加载
13、JSX使用babel编译
- 通过React.createElement函数实现的,执行返回一个vnode
14、React的合成事件机制?
1、合成事件(SyntheticEvent)
- react 16所有的事件都挂在到document上,react 17 事件都挂在到root组件上(这样子可以使多个react版本并存,例如微前端)
- event 不是原生的,是SyntheticEvent合成事件的对象,模拟了DOM事件所有的功能
- 和vue 事件不同、和dom事件也不同
2、 为何要合成事件机制?
- 更好的兼容性和跨平台
- 挂在到document或root组件上,减少内存消耗,避免频繁解绑
- 方便事件的统一管理(如事务机制)
15、setState和batchUpdate 的机制 、事务机制?
1、setState
- 有时候是异步的(调用完之后就获取state值,就是异步),有时候是同步的(setTimeout、DOM 事件)
- 有时合并(对象)执行,有时不合并(函数)
- 下图,处于batch update是一个异步,没有处于batch update是一个同步
2、batchUpdate的机制
- 主要是看isBatchingUpdates这个变量的值,如果执行到setState时这个变量是false则是异步的,数据更新;如果这个变量是true就是同步的,数据不更新
- 这个变量是在生命周或生命周期调用的函数一开始就把这个变量的值设置为true ,生命周期或者函数执行完的时候设置为false,所以setState是异步的情况就是在生命周期或者函数没有执行完的时候就执行了setState,这个时候就会把值保存在dirtyComponetnts 中,不更新值;同步的情况就是setTimeout是一个异步回调,当生命周期或函数的同步代码执行完,也就是isBatchingUpdates的值都变成false后,才去调用这个setState函数,这时候isBatchingUpdates为false,就不会立即更新当前的state ,这时候你就可以立即获取state的值,就当于同步。
哪些能命中batchUpdate机制?
- 生命周期(和她调用的函数)
- react中注册的事件
哪些不能命中batchUpdate机制?
- setTimeout 、setInteval等
- 自定义的react事件和它的函数
3、 transaction 事务机制?
- 先定义一个开始的逻辑、再定义一个结束逻辑,执行的时候是先执行开始的逻辑,再执行函数体,最后执行结束逻辑,这个机制就是事务机制。他们不是再一个函数里定义的
16、 react 组件渲染和更新的过程?
1、渲染过程
- 首先获取props和state 数据
- 然后解析JSX 编译成一堆React.createELement,执行生成虚拟dom,
- 最后执行patch 函数进行渲染
2、更新过程
- 首先setState修改state,然后会触发一个ditryComponent(当前修改的组件或者子组件),然后遍历所有ditryComponents,再去解析执行render函数生成虚拟dom,
- 最后执行patch函数进行更新渲染。
更新的patch再react中分为两个阶段
- 就是reconciliation阶段就是执行diff算法,纯js计算
- commit阶段:将diff结果渲染dom
3、react.fiber 如何进行性能优化的?
-
解决的问题
- JS是单线程且和dom渲染公用一个线程,当组件很复杂的时候,组件更新时计算和渲染都压力大,这是如果再有dom操作的需求(动画或者鼠标拖拽),就会造成卡顿
- 所以react.fiber就是解决这个问题
-
如何做到的
- 将reconciliation阶段拆进行任务拆分,拆分好多个子任务
- 当dom需要渲染时就暂停,空闲的时候就再去执行reconciliation阶段的任务
- 什么时候知道dom需要渲染(window.requestIdleCallback)
17、react的性能优化?
- 列表渲染使用key
- 自定义的事件和dom事件及时销毁
- 合理使用异步组件
- 减少函数bind、this的次数
- 合理使用scu 和meo
- webpack方面的优化
- 前端通用的性能优化(如图片懒加载)
- 使用SSR
ajax应该放在哪个生命周期?
- componentDidMount,DOM已经渲染完了,调接口,是整个应用更加流畅
什么是纯函数?
- 传入什么值返回什么值,例如传入一个数组,返回一个新数组,对源数据没有影响。比如map
函数组件和class组件的区别?
- 纯函数,输入props,输出JSX
- 没有生命周期,没有实例,没有state
- 不能扩展其他方法