1. 说说你对react中Virtual DOM的理解?
虚拟DOM
通过js
模拟网页文档节点,生成虚拟DOM
树,然后进一步生成真实的DOM
树,渲染到页面,如果内容发生改变,React
会重新生成一棵全新的虚拟DOM
树,与前边的旧的虚拟DOM
进行比较,通过diff
算法,将变化的部分打包成patch
,再次应用到真实DOM
中,重新渲染页面。
2. 说说你对react的理解?
raect是什么React
是一个简单的 javascript UI
库,用于构建高效、快速的用户界面。
它是一个轻量级库,因此很受欢迎。它遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效。
它使用虚拟 DOM
来有效地操作 DOM
。
它遵循从高阶组件到低阶组件的单向数据流。
特性
React
特性有很多,如:
JSX
语法- 单向数据绑定
- 虚拟
DOM
- 声明式编程
Component
优势高效灵活
声明式的设计,简单使用
组件式开发,提高代码复用率
单向响应的数据流会比双向绑定的更安全,速度更快
3. 说说React生命周期中有哪些坑?如何避免?
getDerivedStateFromProps
容易编写反模式代码,使受控组件和非受控组件区分模糊componentWillMount
在React
中已被标记弃用,不推荐使用,主要的原因是因为新的异步架构会导致它被多次调用,所以网络请求以及事件绑定应该放到componentDidMount
中componentWillReceiveProps
同样也被标记弃用,被getDerivedStateFromProps
所取代,主要原因是性能问题。shouldComponentUpdate
通过返回true
或者false
来确定是否需要触发新的渲染。主要用于性能优化。componentWillUpdate
同样是由于新的异步渲染机制,而被标记废弃,不推荐使用,原先的逻辑可结合getSnapshotBeforeUpdate
与componentDidUpdate
改造使用。
如果在 componentWillUnmount
函数中忘记解除事件绑定,取消定时器等清理操作,容易引发 bug
。
如果没有添加错误边界处理,当渲染发生异常时,用户将会看到一个无法操作的白屏,所以一定要添加。
4. 说说createElement的过程?
React.createElement()
: 根据指定的第一个参数创建一个React
元素
- 第一个参数是必填,传入的是似
HTML
标签名称,eg: ul, li
- 第二个参数是选填,表示的是属性,
eg: className
- 第三个参数是选填, 子节点,
eg: 要显示的文本内容
React.createElement
函数是用于创建并返回指定类型的新 React
元素。type
表示元素名称,props
表示组件的入参即传入组件的属性,children
表示子组件。
ReactElement
函数实际上就是创建一个对象并返回,这个对象被称为React
元素,它们描述了你希望在屏幕上看到的内容。React
通过读取这些对象,然后使用它们来构建 DOM
以及保持随时更新。
5. 调和阶段setState干了什么?
(1)代码中调用 setState
函数之后,React
会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation
)。
(2)经过调和过程,React
会以相对高效的方式根据新的状态构建 React
元素树并且着手重新渲染整个 UI 界面;
3)在 React
得到元素树之后,React
会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染;
(4)在差异计算算法中,React
能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
6. 说说setState的原理?
- 当
setState
方法被调用后,方法会将状态传递给组件更新器,让组件更新器将状态临时存储起来。每个组件都会有自己的组件更新器,当需要更新组件时调用组件更新器。 - 状态临时保存完成后判断当前是否为批量更新模式,如果是,将组件更新器添加到更新队列中;如果不是,直接更新组件。
批量更新模式是如何设置的: 当触发合成事件时, 在事件处理函数执行之前,会先将批量更新模式设置为 true
,然后执行事件处理函数收集状态。当事件处理函数执行完成后,执行批量更新操作,即从更新队列中获取组件更新器并调用。组件更新器调用完成后再将批量更新模式设置为 false
。
- 更新组件时,先判断是否有状态需要更新,如果有就先计算最新状态,将得出的最新状态重新设置给组件。
计算状态时,如果状态是函数类型,调用函数传入当前状态,返回最新状态。如果状态是对象类型,使用对象状态覆盖原有状态。
- 组件状态计算完成后,通过调用组件内部的
render
方法获取新的VirtualDOM
,再通过DOM
对象获取旧的虚拟DOM
,然后调用diff
方法进行比对,对比完成后将差异更新到真实DOM
对象中。
7. 如何解释react渲染的过程?
渲染过程分为两个阶段
调度阶段: 根据更新数据生成新的Virtual DOM
,通过Diff
算法,找出需要更新的元素,生成新的更新队列,
渲染过程: 遍历更新队列,将变更一次性更新到DOM
上。
而调度阶段过程是不可控的,也就是说React
在执行更新的时候是同步的,浏览器主线程被React
占着用来调度,这段时间如果用户进行操作,那就不会得到反馈。需要同步更新的任务完成后,主线程才被释放。
React
是通过递归的方式来渲染组件的,在 V16
版本之前的版本里,当一个状态发生变更时,react
会从当前组件开始,依次递归调用所有的子组件生命周期钩子,而且这个过程是同步执行的且无法中断的
8. React-router的实现原理及工作方式分别是什么?
React Router
路由的基础实现原理分为两种,如果是切换 Hash
的方式,那么依靠浏览器 Hash
变化即可;如果是切换网址中的 Path
,就要用到 HTML5 History API
中的 pushState
、replaceState
等。在使用这个方式时,还需要在服务端完成 historyApiFallback
配置。
在 React Router 内部主要依靠 history 库完成,这是由 React Router 自己封装的库,为了实现跨平台运行的特性,内部提供两套基础 history,一套是直接使用浏览器的 History API,用于支持 react-router-dom;另一套是基于内存实现的版本,用于支持 react-router-native。
React Router 的工作方式可以分为设计模式与关键模块两个部分。
从设计模式的角度出发,在架构上通过 Monorepo 进行库的管理。Monorepo 具有团队间透明、迭代便利的优点。其次在整体的数据通信上使用了 Context API 完成上下文传递。
在关键模块上,主要分为三类组件:第一类是 Context 容器,比如 Router 与 MemoryRouter;第二类是消费者组件,用以匹配路由,主要有 Route、Redirect、Switch 等;第三类是与平台关联的功能组件,比如 Link、NavLink、DeepLinking 等。
9. 说说redux的实现原理是什么,写出核心代码?
原理
- 将应用的状态统一放到state中,由store来管理state。
- reducer的作用是 返回一个新的state去更新store中对用的state。
- 按redux的原则,UI层每一次状态的改变都应通过action去触发,action传入对应的reducer 中,reducer返回一个新的state更新store中存放的state,这样就完成了一次状态的更新
- subscribe是为store订阅监听函数,这些订阅后的监听函数是在每一次dipatch发起后依次执行
- 可以添加中间件对提交的dispatch进行重写
核心API
- createStore 创建仓库,接受reducer作为参数
- bindActionCreator 绑定store.dispatch和action 的关系
- combineReducers 合并多个reducers
- applyMiddleware 洋葱模型的中间件,介于dispatch和action之间,重写dispatch
- compose 整合多个中间件
10. 为什么react元素有一个$$type属性?
目的是为了防止 XSS 攻击。因为 Synbol 无法被序列化,所以 React 可以通过有没有 $$typeof 属性来断出当前的 element 对象是从数据库来的还是自己生成的。
如果没有 $$typeof 这个属性,react 会拒绝处理该元素。
11. Jsx语法糖的本质是什么?
Jsx是语法糖,实质是js函数,需要babel来解析,核心函数是React.createElement(tag,{attrbuties},children),参数tag是标签名可以是html标签和组件名,attrbuties参数是标签的属性,children参数是tag的子元素。用来创建一个vnode,最后渲染到页面上。
12. React合成事件的原理?
React并不是将click事件绑在该div的真实DOM上,而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装并交由真正的处理函数运行。
收集的事件放在dispatchQueue数组中,而冒泡和捕获的区别在于执行时机和顺序,那么我们只需要对数组按照不同顺序循环执行即可
首先会在fiber节点进入render阶段的complete阶段时,将事件监听绑定在root上。然后调用ensureListeningTo进行事件绑定,生成事件合成对象、收集事件、触发真正的事件。
13. 浏览器从输入url到页面展示的过程?
- 首先,在浏览器地址栏中输入url
- 浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则跳到第三步操作。
- 在发送http请求前,需要域名解析(DNS解析),解析获取相应的IP地址。
- 浏览器向服务器发起tcp连接,与服务器建立tcp三次握手。
- 握手成功后,浏览器向服务器发送http请求,请求数据包。
- 服务器处理收到的请求,将数据返回至浏览器
- 浏览器收到HTTP响应。根据情况选择关闭TCP连接或者保留重用
- 如果得到的资源(静态)可以缓存,进行缓存
- 读取页面内容,浏览器渲染,解析html源码
- 生成Dom树、解析css样式、js交互
- ajax(Asynchronous Javascript And XML)异步处理 可以在不重新加载整个网页的情况下,对网页的某部分进行更新
14. Vue计算属性的实现原理?
1.当组件初始化的时候,computed和data会分别建立各自的响应系统,Observer遍历data中每个属性设置get/set数据拦截
2.初始化computed会调用initComputed函数
注册一个watcher实例,并在其内实例化一个Dep消息订阅器用作后续收集依赖(比如渲染函数的watcher或者其他观察该计算属性变化的watcher)
调用计算属性时会触发其Object.defineProperty的get访问器函数
调用watcher.depend()方法向自身的消息订阅器dep的subs中添加其他属性的watcher
调用watcher的evaluate方法(进而调用watcher的get方法)让自身成为其他watcher的消息订阅器的订阅者,首先将watcher赋给Dep.target,然后执行getter求值函数,当访问求值函数里面的属性(比如来自data、props或其他computed)时,会同样触发它们的get访问器函数从而将该计算属性的watcher添加到求值函数中属性的watcher的消息订阅器dep中,当这些操作完成,最后关闭Dep.target赋为null并返回求值函数结果。
3.当某个属性发生变化,触发set拦截函数,然后调用自身消息订阅器dep的notify方法,遍历当前dep中保存着所有订阅者wathcer的subs数组,并逐个调用watcher的 update方法,完成响应更新。
作者:懂会悟
链接:https://www.jianshu.com/p/7dee0c2407f7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
15. 说一说keep-alive的实现原理?
1. 什么是keep-alive
keep-alive是Vue的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。keep-alive是一个抽象组件,它自身不会渲染一个DOM元素,也不会出现在父组件中。
3. 作用
在组件切换过程中 把切换出去的组件保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性
4. 原理
在 created钩子函数调用时将需要缓存的 VNode 节点保存在 this.cache 中/在 render(页面渲染) 时,如果 VNode 的 name 符合缓存条件(可以用 include 以及 exclude 控制),则会从 this.cache 中取出之前缓存的 VNode实例进行渲染。
5. 参数(Props)
include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max - 数字。最多可以缓存多少组件实例
16. template预编译是什么?
对于 Vue 组件来说,模板编译只会在组件实例化的时候编译一次,生成渲染函数之后在也不会进行编译。因此,编译对组件的 runtime 是一种性能损耗。
而模板编译的目的仅仅是将template转化为render function,这个过程,正好可以在项目构建的过程中完成,这样可以让实际组件在 runtime 时直接跳过模板渲染,进而提升性能,这个在项目构建的编译template的过程,就是预编译。
17. tree shaking的原理是什么?
ES6 Module 引入进行静态分析,故而编译的时候正确判断到底加载了那些模块
静态分析程序流,判断那些模块和变量未被使用或者引用,进而删除对应代码
18. 说说webpack中plugin和loader的区别?
Loader:
用于对模块源码的转换,loader 描述了 webpack 如何处理非 javascript 模块,并且在 build 中引入这些依赖。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或者将内联图像转换为 data URL。比如说:CSS-Loader,Style-Loader 等
Plugin:
目的在于解决 loader 无法实现的其他事,它直接作用于 webpack,扩展了它的功能。在 webpack 运行的生命周期中会广播出许多事件,plugin 可以监听这些事件,在合适的时机通过 webpack 提供的 API 改变输出结果。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务
19. Common.js和es6模块引入的区别?
CommonJS 是一种模块规范,最初被应用于 Nodejs,成为 Nodejs 的模块规范。运行在浏览器端的 JavaScript 由于也缺少类似的规范,在 ES6 出来之前,前端也实现了一套相同的模块规范 (例如: AMD),用来对前端模块进行管理。自 ES6 起,引入了一套新的 ES6 Module规范,在语言标准的层面上实现了模块功能,而且实现得相当简单,有望成为浏览器和服务器通用的模块解决方案。但目前浏览器对 ES6 Module 兼容还不太好,我们平时在 Webpack 中使用的 export和 import,会经过 Babel 转换为 CommonJS 规范
20. 说说你对BFC的理解?
BFC 全称为块级格式化上下文 (Block Formatting Context) 。BFC是 W3C CSS 2.1 规范中的一个概念,它决定了元素如何对其内容进行定位以及与其他元素的关系和相互作用,当涉及到可视化布局的时候,Block Formatting Context提供了一个环境,HTML元素在这个环境中按照一定规则进行布局。一个环境中的元素不会影响到其它环境中的布局。
比如浮动元素会形成BFC,浮动元素内部子元素的主要受该浮动元素影响,两个浮动元素之间是互不影响的。这里有点类似一个BFC就是一个独立的行政单位的意思。可以说BFC就是一个作用范围,把它理解成是一个独立的容器,并且这个容器里box的布局与这个容器外的box毫不相干。