基础面试合集 比较粗糙没有细分,大部分都是面试中必问的,感兴趣的可以自己查阅资料了解
redux:
给按钮绑定点击事件,在actions里面写逻辑业务,分发动作,在reduceres里面将写好的逻辑,去更新他的dispatch状态,最后在组件中使用数据来渲染ui
函数组件类组件区别等
class组件特点:
- 有组件实例
- 有生命周期
- 有 state 和 setState
函数组件特点:
- 没有组件实例
- 没有生命周期
- 没有 state 和 setState,只能接收 props
- 函数组件是一个纯函数,执行完即销毁,无法存储 state
class 组件存在的问题:
- 大型组件很难拆分和重构,变得难以测试
- 相同业务逻辑分散到各个方法中,可能会变得混乱
- 复用逻辑可能变得复杂
react生命周期
创建时(挂载阶段)
- 触发时机:组件创建时(页面加载时)
- 执行顺序:constructor -> render -> componentDidMount
| 钩子函数 | 触发时机 | 作用 |
|---|---|---|
| constructor | 组件创建时 | 1 初始化state 2 为事件处理程序绑定this |
| render | 每次组件渲染都会触发 | 渲染UI |
| componentDidMount | 组件挂载(完成DOM渲染)后 | 1 发送网络请求 2 DOM操作 |
更新时(更新阶段)
- 触发时机:
- 1、setState()
- 2、 组件接收到新的props
- 3、 forceUpdate()
- 说明:以上三者任意一种变化,都会触发组件重新渲染
- 执行顺序:render -> componentDidUpdate
| 钩子函数 | 触发时机 | 作用 |
|---|---|---|
| render | 每次组件渲染都会触发 | 渲染UI |
| componentDidUpdate | 组件更新(完成DOM渲染)后 | 1 发送网络请求(前提:比较了更新前后的props) 2 DOM操作 |
卸载时(卸载阶段)
-
触发时机:组件从页面中卸载(消失)
-
组件生命周期有助于理解组件的运行过程。钩子函数让开发者可以在特定的时机执行某些功能。
componentDidMount和useEffect的区别
componentDidMount是同步的。
useEffect是异步的
componentDidMount先于useEffect执行,并且子组件优先执行
react优化
库引用时只针对需要的模块进行引用(按需加载)
多个子组件情况,使用key来提高性能,注意尽量避免拿数组的的下标值去当做key
列表项使用 key 属性
reselect(数据获取时优化)它的动作原理:只要相关的状态没有改变,那么就直接使用上一次的缓存结果。
useMemo(对子组件进行一个深度比较,发生变化再渲染,减少render次数,用法:包在子组件的名字外)useMemo、useCallback 实现稳定的 Props 值
按优先级更新,及时响应用户
render加不加括号有没有影响
render进行return时,对于单行的内容,没必要加括号,但对于多行的内容要加括号原因在于,JSX转为js后,js会在每行自动加';',如果return后换行了,那么就会变成return;
hooks
useState
useState在合成事件和钩子函数中是异步的,但在原生事件和setTimeout中是同步的
合成事件(react提供的事件绑定):比如onClick,onChange
原生事件(原生事件就是js的原生事件):document.addEventListener
运用的就是数据控制页面的核心思想
(useState的初始值只会在组件第一次渲染时生效)
第一次渲染的时候,从头执行该组件的代码,这时候获取的是Usestate 的初始值
第二次渲染的时候,调用setState来修改状态,状态发生改变,就会重新渲染组件,
useEffect
如果一个函数或其他操作修改了其局部环境之外的状态变量值,那么它就被称为有副作用
比较常见的副作用:数据(Ajax)请求、手动修改 DOM、localStorage 操作等
第二个参数不传,初始化和更新的时候都会执行
useMemo
缓存数据
useCallback
缓存函数
useRef
存储页面数据
useHistory
路由传参,跳转路由
组件通讯
组件中的状态是私有的,为了多个组件之间可以共享某些数据,所以使用通讯组件
父 -> 子
在父组件中提供数据,给子组件的标签上添加一个属性,这个属性的值就是父组件提供的这个数据
子组件通过 props 来接收父组件中传递过来的数据,并展示
子 -> 父
在父组件中准备一个修改状态的函数,将其传递给子组件,由子组件调用该函数并传递数据
父组件提供一个修改状态的函数,用于接收子组件传回来的数据
将该函数通过 props 传递给子组件
子组件通过一个回调函数,将要传递的数据作为参数,使父组件接收到子组件传递回来的数据
兄弟组件之间
两个兄弟组件要共享的状态提升到离这个两个组件最近的公共父组件中,由公共父组件管理这个状态
公共父组件负责:1 提供共享状态 2 提供操作共享状态的方法
接收数据的组件:通过 props 从父组件接收数据【父 -> 子】
修改数据的组件:通过 回调函数 调用父组件提供的操作共享状态的方法【子 -> 父】
简略:父传子就是通过props把数据传给子组件、子传父就是把一个函数传给子组件,然后子组件把值放在函数里、兄弟组件就是把共享的状态放在一个公共组件里,在这里统一管理,剩下的就还是子传父父传子
路由
导入路由Router / Route / Link
使用 Router 组件包裹整个应用(重要)
使用 Link 组件作为导航菜单(路由入口)
使用 Route 组件配置路由规则和要展示的组件(路由出口)
redux
useSelector 读取 Redux 状态
useDispatch 分发 action 以更新状态
只要调用了 dispatch(action) 来更新状态,Redux 中的状态就会改变
只要 Redux 中的状态改变了,Redux 就会通知所有使用了 useSelector() 的组件
也就是,所有用到 Redux 状态的组件,都会被重新渲染,这样,组件中就可以拿到最新的状态了
-
组件的职责:使用数据渲染UI
-
action 的职责:描述要做什么
-
reducer 的职责:更新状态
redux的状态怎么拿出来
用dispatch,然后带着payload , 通过reducer写更新逻辑 store里面写状态
需要放到 redux 中的状态:
如果一个状态,只在某个组件中使用(比如,表单项的值),就放在组件中
在多个组件中都要使用的数据【涉及组件通讯】
通过 ajax 请求获取到的接口数据【涉及到请求相关逻辑代码放在哪的问题】
react和vue的区别
react整体的思路就是函数式,所以推崇纯组件,数据不可变,单向数据流
而vue是基于可变数据的,支持双向绑定
项目打包和发布
项目打包: yarn build / npm run build
启动项目:yarn start
- 打包后的内容放在
build目录中 - 项目上线,就是将 build 目录中的内容,放到服务器中即可
git和yarn命令
git init 初始化
git status 查看状态
git add . 添加到暂存区
git commit -m 添加到仓库
git branch 查看分支(加上分支名就是创建分支)
git checkout 切换分支
git merge 合并分支
git log 查看提交日志
git reflog 查看所有日志
yarn add 安装包
yarn remove 卸载包
yarn build 项目打包
yarn start 启动项目
JS
rem布局的原理:
em: 相对于当前元素的字体大小
**rem:**相对于根元素字体大小,通过媒体查询的方式动态改变html标签的font-size的大小
内存泄漏
一、闭包引起的内存泄露
二、没有清理的DOM元素引用
三、被遗忘的定时器或者回调
四、子元素存在引起的内存泄露
意外的全局变量引起的内存泄露
简略:闭包、一些没清理的dom、定时器、回调、全局变量
call、apply和bind的区别?
1、bind是创建一个新的函数,这个函数绑定死了 this 指向,而call和aplay是用来调用函数 2、三个可以替换修改函数this的指向
值类型和引用类型的区别?
值类型有number, string, boolean, undefined, null,用typeof检测类型
引用类型就是object,包含function、array、date,new()构造的也是引用类型,用instanceof检测类型
一个存的是值,一个存的是地址
什么是原型和原型链:
原型: 函数都有prototype属性,这个属性的值是个对象,称之为原型
原型链: 对象都有__proto__属性,这个属性指向它的原型对象,原型对象也是对象,也有__proto__属性,指向原型对象的原型对象,这样一层一层形成的链式结构称为原型链.
缓存:
localStorage、sessionStorage和cookie的区别
共同点: 都是保存在浏览器端,且同源的
不同点: 1.cookie
cookie自身有一个过期属性,并且在每次的通信过程中会传送向服务端。
2.localStorage、sessionStorage
-localStorage:永久储存,除非手动删除就一直存在。
-sessionStorage:只存在一个页面周期内,页面关闭就清除了。
1.请求不同
cookie 数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。sessionStorage 和 localStorage不会自动把数据发给服务器,仅在本地保存。
2.存储大小限制不同:
cookie 数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。
localStorage、sessionStorage能存储5MB
3.数据有效期不同:
sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;
cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。
localStorage永久保存
防抖和节流
防抖: 就是指触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间。
节流: 就是指连续触发事件但是在设定的一段时间内中只执行一次函数。
对闭包的理解?并能举出闭包的例子
理解:内部函数可以访问外部函数定义的变量
例子:两个嵌套关系的函数
闭包的优点:
1、形成私有空间,避免全局变量的污染
2、持久化内存,保存数据
闭包的缺点:
1、持久化内存,导致内存泄露
导入样式时,使用 link 和@import 有什么区别?
link是XHTML标签;
@import是CSS,只能用于加载CSS;
页面被加载的时,link会同时被加载,而@import引用的 CSS会等到页面被加载完再加载
var,let,const 区别
一、var声明的变量会挂载在window上,而let和const声明的变量不会
二、var声明变量存在变量提升,let和const不存在变量提升
三、let和const声明形成块作用域
四、同一作用域下let和const不能声明同名变量,而var可以
五、const
1、一旦声明必须赋值,不能使用null占位。
2、声明后不能再修改
3、如果声明的是复合类型数据,可以修改其属性
说说 get 和 post 请求
get请求和post的请求都是向服务器发出请求。以获得响应(获得资源),二者本质上一样,都需要传参,都需要获取资源,只是get请求的参数体现在URL的请求头中,可以被外界看到,而且大小有限制,相比较而言,post请求参数都封装在表单中,不外界看到,有一定的安全性,并且pos数据封装在请求体中可以传输大量的数据。
es6常用方法
1、let、const
2、解构赋值 let { a, b } = { a: 1, b: 2 }
3、箭头函数 ()=>{}
4、字符串模板 ``
5、扩展运算符 ...arr
6、函数参数默认值 fn(a = 1) {}
7、模块化:import--引入、exprot default--导出
8、async/await
数组的方法
| array.findIndex(function(item, index) { return 条件 }) | 遍历查找 | 下标 / -1 |
|---|---|---|
| 对象调用的方法 | 作用 | 返回值 |
| array.find(function(item, index) { return 条件 }) | 遍历查找 | 找到的项 / undefined |
| array.forEach(function(item, index, array){}) | 遍历 | 无 |
| array.map(function(item, index, array){}) | 遍历&收集返回的项 | 新数组 |
| array.filter(function(item, index, array){ return 条件 }) | 过滤&保留return true的项 | 新数组 |
| array.reduce(function(sum, item, index, array) {}, 0) | 遍历&累计求和 | 累计结果 |
| array.every(function(item, index, array){ return 条件}) | 遍历&判断是否都满足条件 | 布尔值 |
| array.some(function(item, index, array){return 条件}) | 遍历&判断是否有某个满足条件 | 布尔值 |
| Array.from(伪数组) | 伪数组转真数组 | 真数组 |
promise使用
promise实质是一个函数,通过这个函数创建一个promise对象 这个promise对象里存储一个状态,可以随着内部执行而转化,是三者之一的状态,分别为: 等待态/初始态(Pending)、完成态(Fulfilled)、拒绝态(Rejected) 设想先设置这样一条预案:设置好等状态从pending变成fulfilled和rejected的预案(当成功后我们做什么,失败时我们做什么),具体如下: 当Promise启动后, 满足成功的条件时我们让状态从pending变成fulfilled(执行resolve) 满足失败的条件时我们让状态从pending变成rejected(执行reject)
什么是async/await?
async/await是写异步代码的新方式,以前的方法有回调函数和promise async/await是基于promose实现的,他不能用于普通的函数 async/await与promise一样,是非阻塞的 async/await使得异步代码看起来像同步代码 1、函数的前面多了一个aynce关键字。await关键字只能用在aync定义的函数内。async函数会隐式地返回一个promise,该promise的reosolve值就是函数return的值。 2、也就是说await只能在aync函数内使用 async/await 出现的异常是无法捕获的,需要借助 try/catch 来捕获异常
跨域
-
同源指的是两个 URL 的协议、域名、端口一致,反之,则是跨域。
-
出现跨域的根本原因:浏览器的同源策略不允许非同源的 URL 之间进行资源的交互。
反向代理
需要跨域的请求操作时发送请求给后端,让后端帮你代为请求,然后将获取的结果发送给你。
反向代理跨域原理: 1、做一个代理服务器 跟浏览器 同协议 同域名 同端口 从而让浏览器将请求发过来 代理服务器接收 2、服务器和服务器是没有同源策略的 因此我们代理服务器接收到浏览器的请求后 再发送给真正的服务器 3、真正的服务器接收到请求后 将数据响应给我们的代理服务器 4、代理服务器接收到之后 再响应给浏览器
一个页面从输入 URL 到页面加载完的过程中都发生了什么事情
浏览器根据请求的URL交给DNS域名解析,找到真实IP,向服务器发起请求;
服务器交给后台处理完成后返回数据,浏览器接收文件(HTML、JS、CSS、图象等);
浏览器对加载到的资源(HTML、JS、CSS等)进行语法解析,建立相应的内部数据结构(如HTML的DOM);
载入解析到的资源文件,渲染页面,完成。
js的运行机制是什么
答:js是单线程执行的,页面加载时,会自上而下执行主线程上的同步任务,当主线程代码执行完毕时,才开始执行在任务队列中的异步任务。具体如下
1.所有同步任务都在主线程上执行,形成一个执行栈。
2.主线程之外,还存在一个"任务队列(eventloop队列或者消息队列)"。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
3.一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。哪些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
4.主线程不断重复上面的第三步。
实现你知道的数组去重方法
Array.from方法
扩展运算符(es6中,单独使用扩展运算符“...”无法去重,可配合Set对象来进行数组去重。去重方法:1、用“new Set(arr)”语句将数组转为Set集合类型,利用Set特性去除重复元素;2、用“[...集合]”语句将去重后的Set集合转为数组。)
原生方法,借助一个空数组来实现去重
响应状态码
200 - 服务器成功返回网页 404 - 请求的网页不存在 503 - 服务不可用
| 分类 | 分类描述 |
|---|---|
| 1开头 | 信息,服务器收到请求,需要请求者继续执行操作 |
| 2开头 | 成功,操作被成功接收并处理 |
| 3开头 | 重定向,需要进一步的操作以完成请求 |
| 4开头 | 客户端错误,请求包含语法错误或无法完成请求 |
| 5开头 | 服务器错误,服务器在处理请求的过程中发生了错误 |
CSS/HTML
克隆
- 浅克隆就是将栈内存中的引用复制一份,赋给一个新的变量,本质上两个指向堆内存中的同一地址,内容也相同,其中一个变化另一个内容也会变化(根本上改变的是同一个对象)。
- 深克隆就是创建一个新的空对象,开辟一块内存,然后将原对象中的数据全部复制过去,完全切断两个对象间的联系。
行内元素、块元素
解决浮动的方法
1.额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear:both;)(不推荐)
2.父级添加overflow属性(父元素添加overflow:hidden)(不推荐)
3.使用after伪元素清除浮动(推荐使用)
4.使用before和after双伪元素清除浮动(优点:代码更简洁
缺点:用zoom:1触发hasLayout)
水平居中的几种方法
方法1:table-cell
方法2:display:flex
方法3:绝对定位和负边距
方法4:绝对定位和0
这种方法跟上面的有些类似,但是这里是通过margin:auto和top,left,right,bottom都设置为0实现居中,很神奇吧。不过这里得确定内部元素的高度,可以用百分比,比较适合移动端。
方法5:display:inline-block
判断数据类型的几种方法
typeof(判断基础数据类型)
类型判断
toString
instanceof
construction(最好别说,原理不理解)
ES5: Array.isArray
新旧dom数是如何比较的
根据key去纵向比较
如果父节点变化,直接销毁,如果没有key属性,插入元素是按索引进行比较,会有性能浪费,因此需要设置key属性……如果只是元素的属性改变,只会更新属性……
递归是什么
执行递归函数将反复调用其自身,每调用一次就进入新的一层。递归函数必须有结束条件。
当函数在一直递推,直到遇到墙后返回,这个墙就是结束条件。