答:React是一个用于构建用户界面的Javascript库,提供了UI层面的解决问题。遵循组件式设计声明式编程和函数式编程概念,以使前端更加高效,采用虚拟DOM代替真实DOM,来有效的操控DOM。遵循从高阶组件流向低阶组件。特性:采用JSX语法,单项数据绑定,虚拟DOM,声明式编程,Component。优势就是高效灵活,声明式的设计,简单使用,组件化开发,提高代码复用率,单向响应比双向绑定更加的安全,速度更快。
答:虚拟DOM不会进行重绘和回流,真实DOM会频繁的进行重绘和回流。真实DOM的消耗是真实DOM完全增删改+重排。优点就是直接操作HTML比较易用。缺点就是解析速度慢,效率低,内存占用高,性能差,频繁的操作真实DOM导致重绘和回流。
虚拟DOM的消耗是虚拟DOM增删改+真实DOM增删改+重排。优点是减少操作真实DOM,减少重绘和回流,占用内存少。跨平台的方式也可使得代码一段代码多端使用。缺点是页面在首次渲染时,由于多了一层虚拟DOM的运算,速度比较慢一些。
答:初始化阶段,更新阶段,卸载阶段。
初始化阶段:constructor用来定义状态,或者来放一些this方法;
getDerivedStateFromProps()从props获取state,初始挂载和后续更新会被调用,返回一个对象更新state,如果返回null就不更新
Render()render函数会插入jsx生成的dom结构,生成虚拟dom树,在每一次组件更新的时候,通过diff算法比较新旧dom树,重新渲染
ComponentDidMount()在组件挂载后立即调用,在这里可以调用Ajax请求,返回的数据可以通过setState使得组件重新渲染,添加定时器,订阅消息等。
更新阶段:getDerivedStateFromProps()会调用render方法之前调用,并且在初始化挂载以及后续更新时都会被调用。他应该立即返回一个对象来更新state,返回null就不更新任何东西。
ShouldComponentUpdate()当props或state发生变化时,会在渲染之前调用。
Render()渲染页面
GetSnapshotBeforeUpdate()该函数在组件更新之前执行
ComponentDidUpdate()该组件在更新之后执行,是组件更新的最后一个环节
卸载阶段:componentWillUnMount()在组件卸载和销毁之前调用,一般在这里清楚定时器,取消网络请求,取消订阅,取消监听等
答:一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state
当需要修改里面的值的状态需要通过调用setState来改变,从而达到更新组件内部数据的作用
答:一个组件的显示形态可以由数据状态和外部参数来决定,而数据状态就是state当需要修改里面的值的状态通过调用setState来改变,从而达到组件内部数据更新的作用。
setState第一个参数可以是一个对象,或者是一个函数,而第二个参数是一个回调,用于可以实时的获取到更新之后的数据。在使用setState更新数据的时候,更新类型分为同步更新和异步更新。在组件生命周期或react合成事件中setState是异步,在setTimeout或者原生dom事件中setstate是同步。
答:父组件向子组件传递:在父组件中的子组件标签上绑定自定义属性,挂载传递的数据,子组件通过props属性能接收父组件传递过来的参数
子组件向父组件传递:在父组件中的子组件标签上绑定一个属性,传递一个方法给子组件,子组件通过props接受这个方法,直接调用,传递相应的参数。
非父子之间的通信:状态提升(中间人模式)React中的状态提升概括来说就是将多个组件需要共享的状态提升到它们最近的父组件上,在父组件上改变这个状态然后通过props分发给子组件。Context状态数传参
7.说说你对受控组件和非受控组件的理解?应用场景?
答:受控组件:由React控制的输入表单而改变其值的方式,成为受控组件。比如给一个表单元素input绑定一个onChange事件,当input状态发生给改变的时候就会触发事件,从而更新组件的state
非受控组件:表单数据由dom本身处理,既不受setState控制,与传统的表单输入相似,input输入值即显示最新值,可以使用一个ref来从dom获得最新值
答:Fiber是为了解决大量渲染出现浏览器掉帧的解决方案。以前架构下,渲染是同步的,堆栈调用一条路走到黑,Fiber架构允许执行能暂停任务,在60hz的间隔内让渡主线程给浏览器执行渲染任务,从而让UI上感觉不到掉帧,提升性能。
答:主要遵循三个层级的策略:tree层级、component层级、element层级
Tree层级:DOM节点跨层级的操作不做优化,只会对相同层级的节点进行比较,只有删除、创建操作没有移动操作
Component层级:同一个类的组件,会继续往下diff运算
Element层级:用唯一的key作为标识,通过key可以准确地发现新旧集合中的节点都是相同的节点,因此无需进行节点删除和创建,只需要将旧集合中节点的位置进行移动,更新为新集合中节点的位置
答:Redux中,中间件就是放在dispatch过程,在分发action进行拦截处理,工作流程就是当action发出之后,reducer立即算出state,整个过程是一个同步的操作。
常用的中间件,如:redux-thunk:用于异步操作。Redux-logger:用于日志操作。
实现原理:中间件都需要通过applyMiddlewares进行注册,作用是将所有的中间件组成一个数组,依次执行然后作为第二个参数传入到createStore中。
答:写一个宽高为0的盒子
选择三角形的底边
Div{
Width:0,
Height:0,
Border-top:20px solid red,
Border-bottom:20px solid white,
Border-left:20px solid white
Border-right:20px solid white
}
答:我们可以采用通过对页面进行性能优化来加快页面的渲染,提高用户体验。浏览器缓存就是其中之一。浏览器缓存分为强缓存和协商缓存。强缓存不需要对服务器发送请求。在控制台会显示200的状态码,并会显示from disk cache和 from memory cache。而跟它相关的header有expires,它是reponse header所携带的缓存时间,如果浏览器再一次发送请求,看是否在这个时间内,如果在,那么就命中强缓存,它是以一个字符串的形式返回。另一个相关的header是cache control。它是一个相对时间,有一个参数是max-age,以秒为单位,如果这个参数的值为max-age:300,则代表在距离下一次请求的五分钟之内,那么就命中强缓存,直接通知浏览器读取本地缓存即可。它们两个没有什么区别,expires是http1.0的产物,cache control是http1.1的产物,cache control的优先级要高于expires。协商缓存是跟服务器有关,服务器会根据request header里面携带的参数看是否命中协商缓存。跟他相关的header有last modified,它是服务器向浏览器发出响应,最后一次修改的时间以此形式响应。但是他有缺点就是可能不会捕捉到修改时间或者改变了修改时间,但是内容却没有改变。于是就有了Etag和if-None-Match。E-tag是所携带的唯一标识,当浏览器第一次向服务器发送请求,就会生成一个E-tag,服务器接收到E-tag后,就把它放在if-None-Match里面,当浏览器下一次发送请求的时候,就可以直接判断E-tag是否变化了,如果没有改变,那么就命中协商缓存。如果改变了,就会重新生成E-tag。他们两个相比较的话,精确度来说E-tag要高于last modified,因为last modified的单位是秒,如果在一秒之内,修改了很多次那么久没法很精确的捕捉到。从性能方面last modified要高于E-tag,因为last modified只需要记录时间就可以,但是E-tag要经过算法判断出是否相同。从优先级方面考虑E-tag要高于last modified。
答:1.编译jsx:jsx本质是JavaScript的语法拓展,和模板语法非常接近,但是充分具备JavaScript的能力,但是需要使用babel进行编译,在编译后会变成一个针对React.createElement的调用
2.React.createElement:首先大致流程为二次处理key ref self source四个属性值。然后遍历config,筛选可以提到的props中的属性;然后将children中的子元素推入到childArray数组,然后格式化defaultProps。最后将以上数据作为入参,发起ReactElement的调用,最终返回虚拟DOM对象。
答:react-redux: react官方推出的redux绑定库,react-redux将所有组件分为两大类:UI组件和容器组件,其中所有容器组件包裹着UI组件,构成父子关系。容器组件负责和redux交互,里面使用redux API函数,UI组件负责页面渲染,不使用任何redux API。容器组件会给UI组件传递redux中保存对的状态和操作状态的方法
@reduxjs/toolkit :Redux 官方强烈推荐,开箱即用的一个高效的 Redux 开发工具集。它旨在成为标准的 Redux 逻辑开发模式,使用 Redux Toolkit 都可以优化你的代码,使其更可维护
答:Render有两种形式:在类组件中,指的是render方法。在函数组件中,值的是函数组件本身。
接受三个参数:标签,标签属性,标签的子节点。这些虚拟DOM树最终会渲染成真实DOM。在render过程中,React将新调用的render函数返回的树与旧版本的树进行比较,这一步是决定如何更新DOM的必要步骤,然后进行diff比较,更新DOM树
触发时机:类组件调用setState修改状态,函数组件通过useState hook修改状态
答:1.使用纯组件
2.使用 React.memo 进行组件记忆(React.memo 是一个高阶组件),对于相同的输入,不重复执行;
3.如果是类组件,使用 shouldComponentUpdate(这是在重新渲染组件之前触发的其中一个生命周期事件)生命周期事件,可以利用此事件来决定何时需要重新渲染组件;
4.路由懒加载;
5.不要使用内联函数定义(如果我们使用内联函数,则每次调用“render”函数时都会创建一个新的函数实例);
6.避免在Willxxx系列的生命周期中进行异步请求,操作dom等;
7.如果是类组件,事件函数在Constructor中绑定bind改变this指向;
8.避免使用内联样式属性;
9.优化 React 中的条件渲染;
10.不要在 render 方法中导出数据;
11.列表渲染的时候加key;
12.在函数组件中使用useCallback和useMemo来进行组件优化,依赖没有变化的话,不重复执行;
答:使用节流和防抖也是为了进行前端性能优化,提高页面反应速度,从而提升用户体验。节流就是控制次数,防抖就是控制频率。
防抖:
Function debounce(fn,delay){
Let timer;
Return function(){
Var args=arguments;
If(timer){
clearTimeout(timer)
}
Timer=setTimeout(()=>{
Fn.apply(this,args)
},delay)
};
}
Window.addEventListener(
“scroll”,
Debounce(()=>{
Console.log(111)
},1000)
)
节流:
Function throttle(fn,delay){
Let flag=true;
Return ()=>{
If(!flag) return;
Flag=false
Timer=setTimeout(()=>{
Fn();
Flag=true
},delay)
}
}
Window.addEventListener(
“scroll”,
throttle(()=>{
Console.log(111)
},1000)
)
答:Loader用于对模块的源代码进行转换,在import或加载模块时预处理文件,
Webpack是分析出各种模块的依赖关系,然后形成资源列表,最终打包成到指定文件中,是webpack的核心,用于对模块的源代码进行转换。
常见的loader:style-loader,将css添加到dom的内联样式标签style里
Css-loader:允许将css文件通过require的方式引入,并返回css代码
Less-loader:处理less
Babel-loader:用babel来转换es6文件到es5
答:JS代码压缩、CSS代码压缩、HTML文件代码压缩、文件大小压缩、图片压缩、代码分离、内联chunk、利用cdn加速以及提取公共第三方库
答:首先什么是内存泄漏呢,就是由于疏忽或错误造成程序未能释放不在使用的内存,如果不及时释放,会造成内存占比越来越高,对项目中的内存会造成很大的影响。那么内存泄露常见的几种情况有:
1.意外的全局变量2.定时器,定时器的回调函数及其内部依赖的变量都不能被回收,这样也会造成内存泄漏3.console.log,记录错误的对象,会将大量的数据保留在内存中,是不能被垃圾回收,所以没有去掉console.log可能会存在内存泄漏。可以通过减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;其次注意程序逻辑,避免“死循环”之类的 ;也要避免创建过多的对象 。