这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天
鼠鼠直接贴之前面试记的笔记
谈谈对React的理解
React是一个网页UI库,通过组件化的方式解决视图层开发复用的问题,本质是一个组件化框架
核心设计思想
声明式 :优势在于直观与组合
组件化 :优势在于视图的拆分与模块复用,容易做到 低耦合 和 高内聚
通用性 : 优势在于一次学习随处编写,比如React Natvie ,React 360 ,主要靠虚拟Dom来保证实现,使得React的使用范围变得足够广,无论是Web,Native ,VR,甚至是Shell应用都可以进行开发
劣势
没有提供一揽子解决方案,在开发大型项目中需要在社区寻找解决方案,虽然一定程度上 促进了社区的发展 ,但也为开发者 在技术选型和学习适用上造成了一定成本
为什么React要使用JSX
一句话解释JSX
JSX是一个Javascript的语法拓展,也可以说是一个类似于XML的ECMAscript的语法拓展
它本身没有太多的语法定义,也不希望引入更多的标准
核心概念
- React本身并不强制使用JSX
- JSX本身是React.createElement的语法糖
- React需要将组件转化为虚拟DOM数
- XML在树结构的描述上天生具有可读性强的优势
- JSX会使代码变得简洁清晰
- babel将Jsx会还原成React.createElement
方案对比
模板 :引入了过多新的概念和语法
模板字符串 :代码结构变得复杂,代码提示困难
JXSON : 没有很好的语法提示
Babel如何将JSX转化为JS的
module.exports = function(babel){
var t = babel.type
return{
name:"custom-jsx-plugin",
visitor:{
JSXElement(path){
var openingElement = path.node.openingElement
var tagName = openingElement.name.name
var args = []
args.push(t.stringLiteral(tagName))
var attribs = t.nullLiteral()
args.push(attribs)
var reactIdentifer = t.identifer("React")
var createElementIdentifer = t.identifer("createElement")
var callee = t.memberExpression(reactIdentifer,createElementIdentifer)
var callExpression = t.callExpression(callee,args)
callExpression.arguments = callExpression.arguments.concat(path.node.children)
path.replaceWith(callExpression,path.node)
}
}
}
}
React组件生命周期
概念
挂载阶段
constructor :常用于初始化 (社区不推荐这么写) 直接顶层 初始化state
getDerviedStateFromProps : 在props变化时候更新state
UNSAFE_componentWillMount : 用于组件将要加载前做一些操作,但已经被标记为弃用,因为在React的异步渲染机制下,该方法可能会被多次调用
render :返回JSX结构,用于描述具体的渲染内容
componentDidMount:用于组件加载完成时候做某些事情
更新阶段
UNSAFE_componentWillReceiveProps
getDerviedStateFromProps
shouldComponentUpdate : 进行浅比较判断是否进行更新
UNSAFE_componentWillUpdate
render
getSnapshotBeforeUpdate :返回值作为componentDidUpdate第三个参数使用
componentDidUpdate :更新完成
卸载阶段
componentWillUnmount:用于清理工作,比如说定时器
其他
- 函数组件没有生命周期,任何时候都会进行重新渲染,但官方提供React.memo来跳过渲染
- PureComponent默认实现了shouldComponentUpdate,仅在props和state进行浅比较后,确认变更后才会触发重新渲染
- 错误边界:
getDerivedStateFromError,componentDidCatch捕获错误,渲染时的报错只能通过componentDidCatch捕获
进阶提问
React的请求应该放在哪里
- 对于异步请求,应该放在componentDidMount里面
- 从时间顺序来看,除componentDidMount还可以有constructor和componentWillMount,一个很少用,一个已经被标记为废弃并且易引发bug,不利于代码维护
- 最好放在componentDidmount
类组件和函数组件的区别
共同点
实际用途一样,无论是 高阶组件 还是 异步加载组件,都可以作为 基础展示UI
不同点
基础认知
本质代表了两种不同的设计思想和心智模式
类组件的根基是OOP,面向对象的编程
函数组件的根基是FP,也就是函数式编程
相较于类组件,函数组件更纯粹,简单,易测试
使用场景
- 在不使用Recompose或者hooks的情况下,如需使用生命周期,就用
类组件,限定场景是固定的- 在hooks的加持下,类组件和函数组件的边界就变得模糊了
- 在需要继承的情况下用类组件,但是目前来说组合优于继承,所以,这方面优势也在淡出
性能优化
- 类组件通过
shouldComponentUpdate函数来阻断渲染- 函数组件通过
React.memo来优化
未来趋势
- 函数组件成为未来的主推方案
- 因为类组件this的模糊性,业务逻辑散落在生命周期中,代码缺乏标准的拆分模式
- hooks提供比原先更细腻的逻辑拆分和复用,适合时间切片和并发模式
如何设计React组件
设计分类
展示组件
概念
- 只做展示,不额外增加功能的组件
- 受制于外部的
props- 具有极强的通用性,复用性
分类
代理组件:用于封装常用属性减少代码重复
样式组件:把样式内聚在自己的内部
布局组件:把布局内聚在自己的内部
灵巧组件
概念
- 处理业务逻辑和数据状态
- 面向业务,功能更丰富,复杂性更高,复用度更低
分类
容器组件:把网络请求和逻辑处理放在容器组件中处理
高阶组件:逻辑复用的高阶手段
使用场景
检查登录态
封装埋点
可以基于装饰器进行链式调用
渲染元素的劫持,比如loading状态的展示
缺陷
- 无法获取静态方法,可以通过复制调用,社区也有解决方案
- refs无法透传,但可以通过React.forwardRefs来解决
进阶
如何在渲染劫持中为原本的渲染结果添加新的样式?
const withLoading = (WrappedComponent)=>{
return class extends WrappedComponent{
render(){
if(this.props.isLoading){
return <Loading/>
}else{
return super.render()
}
}
}
}