🔥🔥react面试题精简版🔥🔥
简介
最近闲来无事 就把之前入职时候的面试题总结一下 一是怕自己忘记 二是最近有跳槽想法 巩固一下 现在手上的活比较清闲 所以抓紧时间复习 废话不多说 直接上干货
题目
深度了解项目经验
//心态很重要 去面试前得对自己简历上的项目经验倒背如流 甚至得精确到具体技术点
//因为很多面试官基本都是抓着你的技术点 深挖 比如经典问题
-- '你在这个项目中为什么遇到的技术难点 你是怎么解决的'
1、首先沟通比较重要 很多时候调用后端接口时不是自己想要的数据结构
2、兼容性问题 ios与安卓 大屏与小屏 api版本问题
3、组件的二次封装 组件的样式改不动 权重等问题
4、UI原型图不规范 产品链路不规范 测试等多角度
-- '你在项目中学到 知道哪些知识点'
1、代码规范的重要性(团队) 多写注释
2、团队的协同、沟通 提交时候的注释
3、具体到技术点也行
//像这种开发性的问题 千万不要卡壳 心里得早点备几个答案
git操作
-- git add //暂存到缓存区
-- git commit -m 'xxx' //提交暂存的更改
-- git pull //从远程仓库拉去最新代码 (提交代码前记得操作)
-- git fetch //从远程拉取代码 不会自动合并
-- git branch //新建本地分支
-- git checkout 'xx' //切到xx分支
-- git reset --hard 'xx' //代码回滚到哪次提交
react版本、常用的hooks有哪些、如何模拟生命周期
react 16.8 推出了hooks 实际上hooks与vue3的compoostion API类似 都是为了提升开发效率
1、'useState' //响应式数据
2、'useEffect' //模拟生命周期
-- 模拟 componentDidMount - 'useEffect依赖 []';
-- 模拟 componentDidUpdate - 'useEffect 无依赖,或者依赖 [a, b]'
-- 模拟 componentWillUnMount - 'useEffect 中返回一个函数'
3、'useRef' //可以直接获取元素
4、'useContent' //定义子组件可以复用的方法、变量
5、'useReducer' //与redux类似的全局状态管理
6、'useMemo、useCallback' //性能优化的时候需要用到 前者缓存数据 后者缓存函数 都是避免子组件不必要的更新
7、还可以'自定义hooks' //比如封装一个请求数据的axios
setState是异步还是同步
setState里面有两个参数一个是'对象'一个是'函数'
对象:你需要更改的data
函数:'第一个函数'接受两个参数第一个是当前state,第二个是当前的props,该函数返回一个对象,与直接传对象一致
'第二个函数'可以拿到最新的state 是修改state的回调
//标准回答
'由React事件处理函数、生命周期调用setState是异步更新'
'由setTimout/setInterval等 是同步更新'
如何实现Promise.all()
'Promise 是异步编程的一种解决方案'
promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变 就不会再变
all参数接收一个数组,每个数组是一个Promise,都成功则最终结果为成功,否则为失败
let Promise1 = new Promise(function(resolve, reject){})
let Promise2 = new Promise(function(resolve, reject){})
let Promise3 = new Promise(function(resolve, reject){})
let p = Promise.all([Promise1, Promise2, Promise3])
p.then(funciton(){
// 三个都成功则成功
}, function(){
// 只要有失败,则失败
})
深/浅拷贝、如何实现深拷贝
'栈':自动分配空间 系统自动释放 里面存放的是基本类型的值与引用类型的地址
'堆':动态分配内存 大小不定 也不会自动释放 里面存放引用类型的值
基本类型与引用类型最大区别就是'值传递'与'址传递'
'浅拷贝'
//循环遍历是最简单的浅拷贝方法
//数组浅拷贝slice()与concat()
let arr1 = [1, 2, 3]
let arr2 = []
for (let i in arr1) {
arr2[i] = arr1[i]
}
arr2.push(4)
console.log(arr1) // [1, 2, 3]
console.log(arr2) // [1, 2, 3, 4]
'深拷贝'
JSON.stringify():把一个js对象序列化为一个JSON字符串
JSON.parse():把JSON字符串反序列化为一个js对象
7.如何数组去重、字符串与数组怎么互转
//ES6数组去重方法
function unique(array) {
return [...new Set(array)];
}
'split()':将字符串转成数组
'join()':将数组元素连起来构成一个字符串
js event loop 事件循环机制
js是一种单线程语言 当要异步时 就会启动时间循环
先把整体代码放进调用栈(后进先出)通过webAPI分为宏任务与微任务
同步代码执行完后先执行执行宏任务代码 再执行宏任务所产生的微任务 循环执行 这称为事件循环
'执行一个宏任务,如果遇到微任务就将它放到微任务的事件队列中'
'当前宏任务执行完成后,会查看微任务的事件队列,然后将里面的所有微任务依次执行完'
宏任务
- script(整体代码)
- setTimeout/setInterval定时器
微任务
- Promise 承诺
//常见
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
//输出:script start, script end, promise1, promise2, setTimeout
diff算法
'虚拟Dom'
- 虚拟dom是用js对象来描述节点 真实Dom的抽象 一系列的操作会映射在真实Dom上面
1、用js对象构建Dom树结构 当状态变更时 重新构建新的Dom树
2、新的Dom树与旧的Dom树比较 记录两个树的差距 在应用到一个Dom上
3、通过diff算法同层树节点比较逐步搜索遍历 时间复杂度只有O(n) 是一种高效的算法
'diff特点'
1、同级比较 key值比较 组件比较
2、加入了Fiber算法 将耗时长的任务碎片化 碎片中间还可以执行其它任务
3、减少DOM节点跨节点跨层移动 对同一层级的子节点 通过唯一ID来区分
'react的key值'
1、每一个key是唯一个标识 判断两个元素是否为同一个 没有修改时候 就会复用之前元素
2、key相同,组件有所变化,react会只更新组件对应变化的属性
3、key不同,组件会销毁之前的组件,将整个组件重新渲染
redux通讯原理
'基本流程(原理)'
1、用户的行为动作会触发一个action 动作里面包含一些列数据
2、store通过dispatch调用reducer 并传入两个参数一个是当前state 一个是当前动作
3、reducer通过深拷贝state并返回新的state
4、使用subscribe 发布订阅更新 重新渲染组件
'使用原则'
1、某个组件状态 需要其它组件可以随时拿到
2、一个组件需要更改另一个组件的状态
3、总体原则:能不用就不用
父子组件如何通讯
'父子组件'
//props 回调
'兄弟组件'
//pubs-sub 发布订阅
'祖孙组件'
//createContext、生产者 消费者
//redux
1、从react中解构出createContent在从中解构出Provider Consumer 提供者与消费者
2、Provider组件有参数value其参数可以为参数与方法
3、Cuonsumer组件里面调用value值、方法
12. 防抖、节流
//都是使高频事件减少执行次数
//react里面可以使用自定义hooks八防抖节流封装
'防抖' //是规定时间内再次触发,清除上一次定时器重新计时(类似乘坐电梯)
const inpChange = ()=>{
console.log("11111");
}
useEffect(()=>{
const inp = document.querySelector("#inp");
inp?.addEventListener('input',debounce(inpChange,500))
},[])
//防抖
const debounce = (fun,time)=>{
console.log("xxxxx");
let timer
return ()=>{
clearTimeout(timer)
let args = arguments
timer = setTimeout(()=>{
fun.apply(this,args)
},time)
}
}
'节流' //是规定时间内,多次触发只会触发一次 (类似帧率1s 24张图片足够)
const throttle = (fun,time)=>{
let start = 0;
return ()=>{
let now:any = new Date() //当前时间
if(now-start>time){
fun.apply(this,arguments)
start = now
}
}
}
302是什么意思?协商缓存/强缓存
'常见状态码'
-'1xx':服务器收到请求
-'2xx':请求成功 //例如200
-'3xx':重定向 //例如302
//301为永久重定向
常见场景为域名到期 老域名会返回301状态
//302为临时重定向
百度搜索进入菜鸟教程 先到一个百度临时页面 再到结果页
-'4xx':客户端错误 //例如404
-'5xx':服务器错误 //例如500
'缓存'
是浏览网页时候的一种副本技术 下次浏览直接使用副本 提高速度
//强制缓存
- 是文件直接从本地缓存中获取 不需要发送请求 资源有过期时间 过期之后需要重新向服务器拉去资源
//协商缓存
- 也叫对比缓存通过服务端判断某件事情是不是可以被缓存 如果资源一致返回304 反之返回200与最新资源
css如何实现垂直居中
/*据说一共有16种方法 这里说常见*/
1、弹性布局(🔥)
.son{
display: flex;
justify-content: center;
align-items: center;
}
2、使用transfrom
.son{
position:absolute;
left:50%;
transform:translate(-50%,-50%);
}
3、使用定位
.son{
position:absolute;
height:固定;
left:50%;
margin-left:-0.5宽度;
}
.son{
position:absolute;
hight:固定;
left:0;
right:0;
margin:0 auto;
}
两个数组的交集、并集、差集
const arr1 = [1, 8, 3, 4, 5 , 2 ,9]
const arr2 = [5, 6, 7, 8, 9];
//数组交集
'方式一'
const filterArr = ()=>{
return (
arr1.filter((val)=>{
return arr2.indexOf(val) > -1
})
)
}
'方式二' 🔥
const filterArr = ()=>{
return(
arr1.filter((val)=>{
return arr2.includes(val)
})
)
}
'方式三'
const filterArr = ()=>{
return(
Array.from(
new Set(
arr1.filter((val)=>{
return new Set(arr2).has(val)
})
)
)
)
}
//数组并集
'方式一'
const concatArr = ()=>{
return (
arr1.concat(arr2.filter((val)=>{
return (
!arr1.includes(val)
)
}))
)
}
'方式二'
const concatArr = ()=>{
return(
Array.from(
new Set(arr1.concat(arr2))
)
)
}
//数组差值
'方式一'
const unFilterArr = ()=>{
return (
arr1.filter((val)=>{
return (
!new Set(arr2).has(val)
)
})
)
}
如何获取url参数
//获取url参数有很多种
1、'原生方法'
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());//转为对象
console.log(params) // {id: '2', isShare: 'true'}
console.log(params.id) // 2
2、'react获取路由参数'
//普通传参方式
- params
优势:刷新地址栏 参数依赖保留
缺点:传值太多 url长而丑
- query
优势:传参优雅,传递参数可传对象
缺点:刷新地址栏,参数丢失
- state与search 都类似
//利用hooks传参 🔥
import {
useHistory,//路由跳转
useLocation,//路由路径各种参数
useParams,//路由参数信息
useRouterMatch
} from 'react-router-dom';
<Route path='/query' component={Query}/>
<Link to={{ path : ' /query' , query : { name : 'sunny' }}}>
this.props.history.push({pathname:"/query",query: { name : 'sunny' }});
读取参数用: this.props.location.query.name
- 如何联调、如何mock数据
1、'本地模拟数据'
按照数据格式 编写相对应的json文件进行测试 //只适用于只有几个接口的项目
2、'免费的接口管理平台'
比如easyMock、yapi等快速生成随机数据 接口文档跟`Mock`一体化
后端同学修改了文档后也会一并修改`Mock`接口。 //比较依赖后端同学
3、'Apifox' 🔥🔥
官网链接放最后 超级推荐
4、'postman'
用来测试接口是否能否请求成功数据
apifox:www.apifox.cn/
扩充
- react fiber
- Fiber是数据结构 'react在16以上版本引入Fiber'
- 使用Fiber可以增量渲染、暂停 终止 复用渲染、不同更新的优先级、并发方面新基础能力
- 没有使用Fiber之前 遇到页面元素很多 且频繁刷新的场景下 'react15会出现掉帧现象'
- 将大任务碎片化 中间插入其它任务执行
1.'react15'渲染步骤
//普通的HTMl都被react转出一个对象
let element = (
<div id='0' className="red">
<div id='1'>1</div>
<div id="2">2</div>
</div>
)
//渲染函数
const render = (element,rootDom)=>{
//创建DOM
let dom = document.createElement(element.type)
//将元素添加上
Object.keys(element.props).filter(pro=>pro!='children').forEach(v=>{
dom[v] = element[v]
})
//把子元素添加
if(Array.isArray(element.props.children)){
element.props.children.forEach(c=>render(c,dom))
}else{
dom.innerHtml = element.props.children
}
rootParent.appendChild(dom)
}
//调用
render(element,document.getElementById('root'))
'问题':
1、如果节点多 层次深 递归渲染比较耗时
2、js是单线程的 而且UI线程与js线程互斥
- ES6模块与CommonJS模块区别
优点:避免命名冲突、更好的分离按需加载、更高的复用性、高维护性
'COMMONJS':
1、js代码脱离浏览器 在服务器运行 同步加载
2、module.export 完整暴露 exports.xxx 局部暴露
'AMD':
1、实现异步加载插件 require.js
2、define 暴露,require 引入
'CMD':
1、玉伯开发 异步加载一个sea.js类库
2、define export暴露, define require引入
- react事件代理机制
1、react有一套自己的事件系统 其叫做合成事件
- setState 两次会render几次
1、只会执行'一次' React 可以将多个setState() 调用合并成一个调用来提高性能
react为了提高整体的渲染性能,会将一次渲染周期中的state进行合并
在这个渲染周期中你对所有setState的所有调用都会被合并起来之后,再一次性的渲染
这样可以避免频繁的调用setState导致频繁的操作dom,提高渲染性能。
具体的实现方面,可以简单的理解为react中存在一个状态变量isBatchingUpdates
当处于渲染周期开始时,这个变量会被设置成true,渲染周期结束时
会被设置成false,react会根据这个状态变量,当出在渲染周期中时
仅仅只是将当前的改变缓存起来,等到渲染周期结束时,再一次性的全部render
- web worker什么用
- js执行是单线程 执行时会阻塞浏览器的响应 而web worker 弥补了这一缺点
//基本用法
var myWorker = new Worker(jsUrl, options)
//第一个参数是脚本网址 '必填且尊书同源策略' 只能是js脚本 是worker所要执行的任务
//第二个参数是配置对象 该对象可选 它作用是指定worker的名称 用来区分多个worker线程
// 主线程
var myWorker = new Worker('worker.js', { name : 'myWorker' });
// Worker 线程
self.name // myWorker
css animation-timing-function 的作用机制
css中动画可以分为以下属性
| 名称 | 描述 |
|---|---|
| animation-name | 规定与需要绑定到选择器的keyframe名称 |
| animation-duration | 规定完成动画所花费的时间,以秒或者毫秒计算 |
| animation-timing-function | 规定动画的速度曲线 |
| animation-delay | 规定动画开始之前的延时 |
| animation-iteration-count | 规定动画应该播放多少次数 |
| animation-direction | 规定是否应该轮流反向播放动画 |
animation-timing-function : 动画的速度曲线
- 这个时间函数可以用上图坐标表示
cubic-bezier(n,n,n,n)贝塞尔曲线 - 前面两个n分别对应了p1的坐标(x,y)。后面的对应p2(x,y)
- 像liner、ease、ease-out都是基于这个属性来实现不同的速度运动
有100个台阶 每次可以1步或者2步 有多少步
function climbStairs(n) {
if (n <= 2) {
return n;
}
return climbStairs(n - 1) + climbStairs(n - 2);
}
console.log(climbStairs(100));
9.其它
