23个react常见问题
1、setState 是异步还是同步?
合成事件中是异步
钩子函数中的是异步
原生事件中是同步
setTimeout中是同步
相关链接:你真的理解setState吗?:
2、聊聊 react@16.4 + 的生命周期
图片
相关连接:React 生命周期 我对 React v16.4 生命周期的理解
3、useEffect(fn, []) 和 componentDidMount 有什么差异?
useEffect 会捕获 props 和 state。所以即便在回调函数里,你拿到的还是初始的 props 和 state。如果想得到“最新”的值,可以使用ref。
4、hooks 为什么不能放在条件判断里?
以 setState 为例,在 react 内部,每个组件(Fiber)的 hooks 都是以链表的形式存在 memoizeState 属性中:
图片
update 阶段,每次调用 setState,链表就会执行 next 向后移动一步。如果将 setState 写在条件判断中,假设条件判断不成立,没有执行里面的 setState 方法,会导致接下来所有的 setState 的取值出现偏移,从而导致异常发生。
参考链接:烤透 React Hook
5、fiber 是什么?
React Fiber 是一种基于浏览器的单线程调度算法。
React Fiber 用类似 requestIdleCallback 的机制来做异步 diff。但是之前数据结构不支持这样的实现异步 diff,于是 React 实现了一个类似链表的数据结构,将原来的 递归diff 变成了现在的 遍历diff,这样就能做到异步可更新了。
图片
相关链接:React Fiber 是什么?
6、聊一聊 diff 算法
传统 diff 算法的时间复杂度是 O(n^3),这在前端 render 中是不可接受的。为了降低时间复杂度,react 的 diff 算法做了一些妥协,放弃了最优解,最终将时间复杂度降低到了 O(n)。
那么 react diff 算法做了哪些妥协呢?,参考如下:
1、tree diff:只对比同一层的 dom 节点,忽略 dom 节点的跨层级移动
如下图,react 只会对相同颜色方框内的 DOM 节点进行比较,即同一个父节点下的所有子节点。当发现节点不存在时,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。
这样只需要对树进行一次遍历,便能完成整个 DOM 树的比较。
图片
这就意味着,如果 dom 节点发生了跨层级移动,react 会删除旧的节点,生成新的节点,而不会复用。
2、component diff:如果不是同一类型的组件,会删除旧的组件,创建新的组件
图片
3、element diff:对于同一层级的一组子节点,需要通过唯一 id 进行来区分
如果没有 id 来进行区分,一旦有插入动作,会导致插入位置之后的列表全部重新渲染。
这也是为什么渲染列表时为什么要使用唯一的 key。
7、调用 setState 之后发生了什么?
在 setState 的时候,React 会为当前节点创建一个 updateQueue 的更新列队。
然后会触发 reconciliation 过程,在这个过程中,会使用名为 Fiber 的调度算法,开始生成新的 Fiber 树, Fiber 算法的最大特点是可以做到异步可中断的执行。
然后 React Scheduler 会根据优先级高低,先执行优先级高的节点,具体是执行 doWork 方法。
在 doWork 方法中,React 会执行一遍 updateQueue 中的方法,以获得新的节点。然后对比新旧节点,为老节点打上 更新、插入、替换 等 Tag。
当前节点 doWork 完成后,会执行 performUnitOfWork 方法获得新节点,然后再重复上面的过程。
当所有节点都 doWork 完成后,会触发 commitRoot 方法,React 进入 commit 阶段。
在 commit 阶段中,React 会根据前面为各个节点打的 Tag,一次性更新整个 dom 元素。
8、为什么虚拟dom 会提高性能?
虚拟dom 相当于在 JS 和真实 dom 中间加了一个缓存,利用 diff 算法避免了没有必要的 dom 操作,从而提高性能。
9、错误边界是什么?它有什么用?
在 React 中,如果任何一个组件发生错误,它将破坏整个组件树,导致整页白屏。这时候我们可以用错误边界优雅地降级处理这些错误。
例如下面封装的组件:
class ErrorBoundary extends React.Component<IProps, IState> {constructor(props: IProps) {super(props);this.state = { hasError: false };}static getDerivedStateFromError() {// 更新 state 使下一次渲染能够显示降级后的 UIreturn { hasError: true };}componentDidCatch(error, errorInfo) {// 可以将错误日志上报给服务器console.log('组件奔溃 Error', error);console.log('组件奔溃 Info', errorInfo);}render() {if (this.state.hasError) {// 你可以自定义降级后的 UI 并渲染return this.props.content;}return this.props.children;}
}
10、什么是 Portals?
Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。
ReactDOM.createPortal(child, container)
11、React 组件间有那些通信方式?
父组件向子组件通信
1、 通过 props 传递
子组件向父组件通信
1、 主动调用通过 props 传过来的方法,并将想要传递的信息,作为参数,传递到父组件的作用域中
跨层级通信
1、 使用 react 自带的 Context 进行通信,createContext 创建上下文, useContext 使用上下文。
参考下面代码:
import React, { createContext, useContext } from 'react';const themes = {light: {foreground: "#000000",background: "#eeeeee"},dark: {foreground: "#ffffff",background: "#222222"}
};const ThemeContext = createContext(themes.light);function App() {return (<ThemeContext.Provider value={themes.dark}><Toolbar /></ThemeContext.Provider>);
}function Toolbar() {return (<div><ThemedButton /></div>);
}function ThemedButton() {const theme = useContext(ThemeContext);return (<button style={{ background: theme.background, color: theme.foreground }}>I am styled by theme context!</button>);
}export default App;
2、使用 Redux 或者 Mobx 等状态管理库
3、使用订阅发布模式
12、React 父组件如何调用子组件中的方法?
1、如果是在方法组件中调用子组件(>= react@16.8),可以使用 useRef 和 useImperativeHandle:
const { forwardRef, useRef, useImperativeHandle } = React;const Child = forwardRef((props, ref) => {useImperativeHandle(ref, () => ({getAlert() {alert("getAlert from Child");}}));return <h1>Hi</h1>;
});const Parent = () => {const childRef = useRef();return (<div><Child ref={childRef} /><button onClick={() => childRef.current.getAlert()}>Click</button></div>);
};
2、如果是在类组件中调用子组件(>= react@16.4),可以使用 createRef:
const { Component } = React;class Parent extends Component {constructor(props) {super(props);this.child = React.createRef();}onClick = () => {this.child.current.getAlert();};render() {return (<div><Child ref={this.child} /><button onClick={this.onClick}>Click</button></div>);}
}class Child extends Component {getAlert() {alert('getAlert from Child');}render() {return <h1>Hello</h1>;}
}
13、React有哪些优化性能的手段?
类组件中的优化手段
1、使用纯组件 PureComponent 作为基类。
2、使用 React.memo 高阶函数包装组件。
3、使用 shouldComponentUpdate 生命周期函数来自定义渲染逻辑。
方法组件中的优化手段
1、使用 useMemo。
2、使用 useCallBack。
其他方式
1、在列表需要频繁变动时,使用唯一 id 作为 key,而不是数组下标。
2、必要时通过改变 CSS 样式隐藏显示组件,而不是通过条件判断显示隐藏组件。
3、使用 Suspense 和 lazy 进行懒加载,例如:
import React, { lazy, Suspense } from "react";export default class CallingLazyComponents extends React.Component {render() {var ComponentToLazyLoad = null;if (this.props.name == "Mayank") {ComponentToLazyLoad = lazy(() => import("./mayankComponent"));} else if (this.props.name == "Anshul") {ComponentToLazyLoad = lazy(() => import("./anshulComponent"));}return (<div><h1>This is the Base User: {this.state.name}</h1><Suspense fallback={<div>Loading...</div>}><ComponentToLazyLoad /></Suspense></div>)}
}
Suspense 用法可以参考官方文档
相关阅读:21个React性能优化技巧
14、为什么 React 元素有一个 $$typeof 属性?
目的是为了防止 XSS 攻击。因为 Synbol 无法被序列化,所以 React 可以通过有没有 $$typeof 属性来断出当前的 element 对象是从数据库来的还是自己生成的。
如果没有 $$typeof 这个属性,react 会拒绝处理该元素。
在 React 的古老版本中,下面的写法会出现 XSS 攻击:
// 服务端允许用户存储 JSON
let expectedTextButGotJSON = {type: 'div',props: {dangerouslySetInnerHTML: {__html: '/* 把你想的搁着 */'},},// ...
};
let message = { text: expectedTextButGotJSON };// React 0.13 中有风险
<p>{message.text}
</p>
15、React 如何区分 Class组件 和 Function组件?
一般的方式是借助 typeof 和 Function.prototype.toString 来判断当前是不是 class,如下:
function isClass(func) {return typeof func === 'function'&& /^class\s/.test(Function.prototype.toString.call(func));
}
但是这个方式有它的局限性,因为如果用了 babel 等转换工具,将 class 写法全部转为 function 写法,上面的判断就会失效。
React 区分 Class组件 和 Function组件的方式很巧妙,由于所有的类组件都要继承 React.Component,所以只要判断原型链上是否有 React.Component 就可以了:
AComponent.prototype instanceof React.Component
16、HTML 和 React 事件处理有什么区别?
在 HTML 中事件名必须小写:
<button onclick='activateLasers()'>
而在 React 中需要遵循驼峰写法:
<button onClick={activateLasers}>
在 HTML 中可以返回 false 以阻止默认的行为:
<a href='#' onclick='console.log("The link was clicked."); return false;' />
而在 React 中必须地明确地调用 preventDefault():
function handleClick(event) {event.preventDefault()console.log('The link was clicked.')
}
17、什么是 suspense 组件?
Suspense 让组件“等待”某个异步操作,直到该异步操作结束即可渲染。在下面例子中,两个组件都会等待异步 API 的返回值:
const resource = fetchProfileData();function ProfilePage() {return (<Suspense fallback={<h1>Loading profile...</h1>}><ProfileDetails /><Suspense fallback={<h1>Loading posts...</h1>}><ProfileTimeline /></Suspense></Suspense>);
}function ProfileDetails() {// 尝试读取用户信息,尽管该数据可能尚未加载const user = resource.user.read();return <h1>{user.name}</h1>;
}function ProfileTimeline() {// 尝试读取博文信息,尽管该部分数据可能尚未加载const posts = resource.posts.read();return (<ul>{posts.map(post => (<li key={post.id}>{post.text}</li>))}</ul>);
}
Suspense 也可以用于懒加载,参考下面的代码:
const OtherComponent = React.lazy(() => import('./OtherComponent'));function MyComponent() {return (<div><Suspense fallback={<div>Loading...</div>}><OtherComponent /></Suspense></div>);
}
18、为什么 JSX 中的组件名要以大写字母开头?
因为 React 要知道当前渲染的是组件还是 HTML 元素。
19、redux 是什么?
Redux 是一个为 JavaScript 应用设计的,可预测的状态容器。
它解决了如下问题:
跨层级组件之间的数据传递变得很容易
所有对状态的改变都需要 dispatch,使得整个数据的改变可追踪,方便排查问题。
但是它也有缺点:
概念偏多,理解起来不容易
样板代码太多
20、react-redux 的实现原理?
通过 redux 和 react context 配合使用,并借助高阶函数,实现了 react-redux。
参考链接:React.js 小书
21、reudx 和 mobx 的区别?
得益于 Mobx 的 observable,使用 mobx 可以做到精准更新;对应的 Redux 是用 dispath 进行广播,通过Provider 和 connect 来比对前后差别控制更新粒度;
相关阅读:Redux or MobX: An attempt to dissolve the Confusion
22、redux 异步中间件有什么什么作用?
假如有这样一个需求:请求数据前要向 Store dispatch 一个 loading 状态,并带上一些信息;请求结束后再向Store dispatch 一个 loaded 状态
一些同学可能会这样做:
function App() {const onClick = () => {dispatch({ type: 'LOADING', message: 'data is loading' })fetch('dataurl').then(() => {dispatch({ type: 'LOADED' })});}return (<div><button onClick={onClick}>click</button></div>);
}
但是如果有非常多的地方用到这块逻辑,那应该怎么办?
聪明的同学会想到可以将 onClick 里的逻辑抽象出来复用,如下:
function fetchData(message: string) {return (dispatch) => {dispatch({ type: 'LOADING', message })setTimeout(() => {dispatch({ type: 'LOADED' })}, 1000)}
}function App() {const onClick = () => {fetchData('data is loading')(dispatch)}return (<div><button onClick={onClick}>click</button></div>);
}
很好,但是 fetchData(‘data is loading’)(dispatch) 这种写法有点奇怪,会增加开发者的心智负担。
于是可以借助 rudux 相关的异步中间件,以 rudux-chunk 为例,将写法改为如下:
function fetchData(message: string) {return (dispatch) => {dispatch({ type: 'LOADING', message })setTimeout(() => {dispatch({ type: 'LOADED' })}, 1000)}
}function App() {const onClick = () => {
- fetchData('data is loading')(dispatch)
+ dispatch(fetchData('data is loading'))}return (<div><button onClick={onClick}>click</button></div>);
}
这样就更符合认知一些了,redux 异步中间件没有什么奥秘,主要做的就是这样的事情。
相关阅读:Why do we need middleware for async flow in Redux?
23、redux 有哪些异步中间件?
1、redux-thunk
源代码简短优雅,上手简单
2、redux-saga
借助 JS 的 generator 来处理异步,避免了回调的问题
3、redux-observable
借助了 RxJS 流的思想以及其各种强大的操作符,来处理异步问题
相关文章:
23个react常见问题
1、setState 是异步还是同步? 合成事件中是异步 钩子函数中的是异步 原生事件中是同步 setTimeout中是同步 相关链接:你真的理解setState吗?: 2、聊聊 react16.4 的生命周期 图片 相关连接:React 生命周期 我对 Reac…...
【python基础】——Anaconda下包更新的坑及安装与卸载、及安装后Jupyter Notebook没反应的解决方法
文章目录 前言一、起因:如何一步步走到卸载重装anaconda?二、卸载anaconda二、重新安装anaconda三、关于安装Anaconda后,打开Jupyter Notebook运行代码没反应且in[ ]没有*前言 本文主要用来记录自己近期踩坑的一些复盘。其中坑有: ‘.supxlabel’ 不起作用的解决pip list 与…...
CSS 中的 display 和 visibility
CSS 中的 display 和 visibility 都可以设置一个元素在浏览器中的显示或隐藏效果。 display: 隐藏某个元素时,不会占用任何空间。换句话讲,不会影响布局。visibility: 隐藏某个元素时,仍需占用与未隐藏之前一样的空间。换句话讲,…...
解决mysql报错this is incompatible with DISTINCT
环境 centos 9 php7.4 mysql5.7 问题 mysql查询报如下错误: SQLSTATE[HY000]: General error: 3065 Expression #1 of ORDER BY clause is not in SELECT list, references column hst_csc.q.timestamp which is not in SELECT list; this is incompatible with…...
C++-map和set
本期我们来学习map和set 目录 关联式容器 键值对 pair 树形结构的关联式容器 set multiset map multimap 关联式容器 我们已经接触过 STL 中的部分容器,比如: vector 、 list 、 deque 、forward_list(C11)等,这些容器统称为序列式…...
微信小程序AI类目-深度合成-AI问答/AI绘画 互联网信息服务算法备案审核通过教程
近期小程序审核规则变化后,很多使用人类小徐提供的chatGPT系统的会员上传小程序无法通过审核,一直提示需要增加深度合成-AI问答、深度合成-AI绘画类目,该类目需要提供互联网信息服务算法备案并上传资质,一般对企业来说这种务很难实…...
蓝桥杯官网练习题(星期一)
题目描述 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 整个 2020 世纪(1901 年 1 月 1 日至 2000 年 12 月 3131 日之间),一共有多少个星期一?(不要告诉我你不知道今天是星…...
centos7更新podman
实验环境:centos7.7.1908 1.安装podman并查看版本 yum install podman podman -v 当前podman版本信息是1.6.4 2.更新podman版本 通过查看资料显示centos 7 支持最高版本为 3.4.4,更新podman大致有以下四步: golang 安装(本次使用版本: 1.…...
Java特性之设计模式【抽象工厂模式】
一、抽象工厂模式 概述 抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式 在抽象工厂模式中,接口是…...
机器学习简介
引言 为何现在机器学习如此热门? 主要原因是由于“人类无论如何也做不到在短时间内实现从大量的数据中自动的计算出正确的结果操作”。 什么是机器学习? 所谓的机器学习,就是通过对数据进行反复的学习,来找出其中潜藏的规律和模式…...
linux之perf(2)list事件
Linux之perf(2)list事件 Author:Onceday Date:2023年9月3日 漫漫长路,才刚刚开始… 参考文档: Tutorial - Perf Wiki (kernel.org)perf-list(1) - Linux manual page (man7.org) 1. 概述 perf list用于列出可用的性能事件,这…...
将多个EXCEL 合并一个EXCEL多个sheet
合并老版本xls using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using NPOI.HSSF.UserModel; …...
【送书活动】揭秘分布式文件系统大规模元数据管理机制——以Alluxio文件系统为例
前言 「作者主页」:雪碧有白泡泡 「个人网站」:雪碧的个人网站 「推荐专栏」: ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄,vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄ÿ…...
微信小程序——数据绑定
在微信小程序中,可以通过以下代码实现数据绑定: 在WXML中,使用双大括号{{}}绑定数据,将数据渲染到对应的视图中。 <view>{{message}}</view>在JS中,定义一个数据对象,并将其绑定到页面的data…...
libbpf-bootstrap安卓aarch64适配交叉编译
1.为什么移植 疑惑 起初我也认为,像libbpf-bootstrap这样在ebpf程序开发中很常用的框架,理应支持不同架构的交叉编译。尤其是向内核态的ebpf程序本身就是直接通过clang的-target btf直接生成字节码,各个内核上的ebpf虚拟机大同小异…...
【剑指Offer】24.反转链表
题目 定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL限制: 0 < 节点个数 < 5000 解答 源代码 /*** Defin…...
04-docker compose容器编排
Docker Compose简介 Docker Compose是什么 Compose 是Docker公司推出的一个工具软件,可以管理多个Dokcer容器组成一个应用。你需要定义一个YAML格式的配置文件 docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令&#…...
通过位运算打多个标记
通过位运算打多个标记 如何在一个字段上,记录多个标记? 如何在一个字段上,记录不同类型的多个标记? 如何用较少的字段,记录多个标记? 如何在不增加字段的要求下,记录新增的标记? 在实…...
[学习笔记]Node2Vec图神经网络论文精读
参考资料:https://www.bilibili.com/video/BV1BS4y1E7tf/?p12&spm_id_frompageDriver Node2vec简述 DeepWalk的缺点 用完全随机游走,训练节点嵌入向量,仅能反应相邻节点的社群相似信息,无法反映节点的功能角色相似信息。 …...
C# Linq源码分析之Take(五)
概要 本文在C# Linq源码分析之Take(四)的基础上继续从源码角度分析Take的优化方法,主要分析Where.Select.Take的使用案例。 Where.Select.Take的案例分析 该场景模拟我们显示中将EF中与数据库关联的对象进行过滤,然后转换成Web…...
性能监控-grafana+prometheus+node_exporter
Prometheus是一个开源的系统监控和报警工具。它由SoundCloud开发并于2012年发布,后来成为了一个独立的开源项目,并得到了广泛的应用和支持。 Prometheus的主要功能包括采集和存储各种系统和应用程序的监控数据,并提供强大的查询语言PromQL来…...
(STM32H5系列)STM32H573RIT6、STM32H573RIV6、STM32H573ZIT6嵌入式微控制器基于Cortex®-M33内核
一、应用 工业(PLC、工业电机控制、泵和压缩机) 智能家居(空调、冰箱、冰柜、中央警报系统、洗衣机) 个人电子产品(键盘、智能手机、物联网标签、跟踪设备) 智能城市(工业通信、照明控制、数字…...
mysql配置bind-address不生效
1、前言 因为要ip直接访问mysql,故去修改bind-address参数,按照mysql配置文件查找顺序是:/etc/my.cnf、/etc/mysql/my.cnf、~/.my.cnf,服务器上没有 /etc/my.cnf文件,故去修改 /etc/mysql/my.cnf文件,但是一…...
Linux相关指令(下)
cat指令 查看目标文件的内容 常用选项: -b 对非空输出行编号 -n 对输出的所有行编号 -s 不输出多行空行 一个重要思想:linux下一切皆文件,如显示器文件,键盘文件 cat默认从键盘中读取数据再打印 退出可以ctrlc 输入重定向<…...
Codeforces Round 855 (Div 3)(A - F)
Codeforces Round 855 (Div. 3)(A - F) Codeforces Round 855 (Div. 3) A. Is It a Cat?(思维) 思路:先把所有字母变成小写方便判断 , 然后把每一部分取一个字母出来 , 判断和‘meow’是否相同即可。 复杂度 O ( n…...
Friend.tech(FT):社交媒体金融的未来,真的如此美好吗?
Friend.tech(FT)是一个在2023年8月10日正式推出的社交金融平台,它的特点在于允许用户购买和出售创作者的股票(shares),这些股票赋予用户访问创作者内容的权利。FT的推出引发了广泛的关注,吸引了…...
yolov7中Concat之后加注意力模块(最复杂的情况)
1、common.py中找到Concat模块,复制一份 2、要传参进来,dim通道数 3、然后找yolo.py模块,添加 4、yaml里替换 5、和加的位置也有关系...
解除百度安全验证
使用chrome浏览器用百度浏览时,一直弹百度安全验证: 在设置里进行重置: 然后重启浏览器就可以了。...
Codeforces Round 731 (Div 3)(A - F)
Codeforces Round 731 (Div. 3)(A - F) Dashboard - Codeforces Round 731 (Div. 3) - Codeforces A. Shortest Path with Obstacle(思维) 思路:显然要计算 A → B 之间的曼哈顿距离 , 要绕开 F 当且仅当 AB形成的直线平行于坐…...
Python的sort()与sorted()函数详解
目录 sort()函数 sorted()函数 key参数 区别 sort()函数 sort()方法:该方法用于原地对列表进行排序,即直接在原始列表上进行排序操作,并不返回一个新的列表。 my_l…...
网站实现隶书繁体/网站平台如何推广
一般都不会很容易注意到这个问题,因为ID足够大,我没必要在意这个号码是否过大,但是如果你是在做一个数据量特别大的系统的时候,真的得考虑这些东西了。因为近期一个项目,我负责数据库设计这块,然后就感觉到…...
南京美容网站建设/品牌营销策划培训课程
前段时间上了锐捷的技术支持的简历,不久就开始了我的锐捷应聘一战,虽然在二次面试后还是光荣了,不过感觉这次经历确实学了不少 ,拿出来晒晒,一起学习下。<?xml:namespace prefix o ns "urn:schemas-microso…...
wordpress卡片式主题/收录网站查询
1. 可以在平台选择 高级安装,使用下载好的平台zip进行安装,加快安装速度 2.添加库 在 platform.ini 添加依赖的库 lib_deps https://github.com/Seeed-Studio/Seeed_Arduino_LIS3DHTR 3.复用arduino 的库 再 platform.ini里添加 lib_extra_dirs …...
做面食视频网站/正规seo多少钱
http://lx.lanqiao.cn/problem.page?gpidT303算法训练 未名湖边的烦恼 时间限制:1.0s 内存限制:256.0MB问题描述每年冬天,北大未名湖上都是滑冰的好地方。北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后&a…...
深圳大兴汽车集团网站建设/二级域名查询入口
小渣来说动态规划了: 首先说一说斐波拉契数列(1,1,2,3,5......),如图: 这些就是小渣理解的动态规划,欢迎大佬来补充。 这些是经典的动态规划题: h…...
wordpress菜单显示在哪里/网址百度刷排名
苹果设备我参考其他的资料,大多大神写的东西只讲一些概念,必要的判断都没有写,必须iphonex的底部适配的高度是34,所以我对之前的设备适配只做了iphoneX。但是现在新出的设备xs和xr都需要底部适配,所以设备的判断就需要…...