React面试

整理了在react前端面试中,常会问到的东西,不仅限于react,还包括前端常用的基础


1.关于react框架

    谈React框架,我们可以从框架出现的原因开始聊。在框架之前,我们使用原生的js去开发,前端的性能极大的受到频繁的dom操作的影响,其次,前端开发没有很好的代码开发规范,代码混乱,不利于开发、迭代

    1.dom节点自身拥有大量的自身属性(我们常用的仅一小部分),每一次的dom操作,都需要重新挂载数量庞大的dom属性(这是频繁操作dom卡顿的一大原因)

    2.从浏览器的渲染流程说起,浏览器在接受到html与css文件后,会根据html生成dom树,根据css生成一个对应的CSSOM树,然后将两棵树进行合并形成一个渲染的render树,接着计算每个dom节点的视觉上的大小与位置,最后将页面渲染到网页上,这是浏览器首次渲染的一个大概流程。   

        在用户操作过程中也会使得渲染中的某些流程再次触发,我们称之为回流或重绘,回流是由于我们的某些js操作使得dom结构、dom几何属性的更改,会重新计算生成render树,计算元素的位置、大小,最后重新渲染,这个过程是很耗性能的,所以频繁的dom操作会造成网页的卡顿,所以在之前,我们常会用文档碎片的方式批量更新dom。

        react框架使用了虚拟dom很好的解决频繁操作dom的性能问题,react用js简单的模拟了dom树,我们在不再直接操作dom,而是操作虚拟dom,通过diff算法计算出前后虚拟dom的不同,再将不同点映射到真实dom上,极大的减少了dom结构的变化次数。

        更重要的是react组件化的开发模式,在框架的组件化开发模式流行前,前端使用amd与cmd等模块化的方式来对项目进行模块化拆分、开发与管理,这种方式只是对js代码进行了一个分割,解决了命名冲突、变量泛滥与js加载顺序的问题,开发上并没有很大的变化,组件化的开发模式却是颠覆了前端的开发,组件化相对于之前的模块化进行了更为细化的整合,组件拥有自己的生命周期,有自己的数据状态管理,拥有自己的UI模板(JSX),我们能够很好的把控在组件的加载、更新与卸载过程中的变化。


2.React面试中常问到的点:

    1.组件间传值:父与子之间的传递,通过props,父传子直接将数据通过props传递给子组件,子传父,依然利用父组件传递一个函数留以payload入口,传递给子组件触发;跨组件的传递,可以利用状态提升,也是利用props,跨多层次的传递就会显得很麻烦;context传递,不必利用组件树显示的逐层传递;利用第三方状态管理器redux、mobox等

    2.如何在父组件中触发子组件实例中的方法:利用refs获取组件实例直接调用即可

    3.redux是如何与react进行关联的:两个关键点,Provider与connect,Provider容器将根组件进行包裹将store注入,在Provider组件中通过props拿到store,将其注册到全局的context中,在需要使用到redux中的状态的组件里利用connect高阶组件,拿到Provider注入到context中的store,将store中需要的state和相关的dispatch方法直接map到需要使用的组件的props中,在组件中就可以利用props拿到redux的state与修改其的方法

    4.redux:

        独立的状态树,通过各子renducer合并后产生store,通过各子reducer将管理的state数据模块化,且各reducer中定义了改变当前state数据的处理函数,store通过dispatch相应的action(纯对象,包含type和payload数据)来改变相应的state数据
        使用redux的好处:解决跨多层的非父子组件间的数据通信的麻烦(否则就是使用状态提升,需通过两组件共同的父组件将数据向下传递)
        核心概念:state、reducer、action、store、dispatch,要熟悉各字面量之间的联系与运作原理
        建议:redux不要滥用,使用redux会使得我们获取数据过程中多一层reducer函数,redux的滥用会使得代码的直观性大大降低,建议仅将需要跨多层组件之间的共享数据使用redux进行管理(这种数据一般来说不会太多),只涉及本组件或者跨越层次不多的(两层内)共享数据直接使用react本身提供的state管理即可。

    4.异步的setState:react是数据驱动的,每个被管理的state数据的更改都会引起组件重新render,state状态数据不允许直接修改,只能通过其提供的setState方法来进行更改,该方法的执行是异步的,调用时不会立即修改state的值,会在一定时间内收集到所有通过setState修改的state,最后进行统一的修改,这也是react自身的一个性能优化,减少了组件更新时不必要的render

    5.react的生命周期:生命周期能够让我们在组件在初始加载、更新、销毁等关键过程中做出我们需要的操作。react的生命周期在c16.0前后有所不同,但都分为创建、更新与卸载过程,具体函数不赘述,理解react的数据驱动和生命周期能够帮助我们开发中避免一些问题,比如为什么不建议在didupdate中去setState

    6.react的性能优化,PrueComponent或shouldComponentUpdate,react中,由于在一个组件发生了重新render后,其所有的子组件都会发生重新render(默认情况下),即使传递给子组件的props没有发生变化,因此,我们可以通过shouComponentUpdate钩子函数来通过前后props的对比来判断传入的参数是否发生了变化,可减少无用的render,PureComponent也是重写了shouldCompoentUpdate来进行了props的浅层对比。

    7.函数式组件与class类组件:(hook出现前)函数式组件(个人认为不能称之为组件)就是一个UI模板,接受特定格式的props参数,将props渲染进JSX模板中并返回出去,没有自身需要管理的状态与生命周期,像页面中某些模块只是起展示的作用,不涉及复杂的逻辑交互,函数式组件是很好的选择;class类组件在实例化后拥有自身的state与生命周期

    8.hook:v16.8提出了hook,让我们可以用函数式的方式来编写拥有自身state状态与生命周期(个人感觉hook中的useEffect不能再算是生命周期,仅仅是在组件更新中起到监听数据变化做出相应操作的作用,类似于vue中的watch监听)的组件,关键api是useState与useEffect

    9.React的路由:React提供了两种前端路由 browserRouter与hashRouter,均属于前端操作,不涉及真正的资源请求,路由与页面级别的组件相对应,路由的变化会触发页面组件的更替,实现页面的更新

        9.1 browserRouter:该路由格式以 / 划分,由于这种路由对应了真实的资源路径,路由变化时浏览器会自动去根据URI去请求服务器资源,但borwserRouter使用h5提供的history API来处理路由,可以使得前端在操作路由时不会根据变化的路由去请求后台资源,造成页面空白。
        注意:在前端本地开发时,由于webpack中使用了webpack-dev-server做了相应配置,路由变化时不会去请求服务端资源不会遇到页面刷新空白,但部署后会出现
        解决方法:后端要配合前端,将所有的前端路由URI变化都指向index.html,浏览器上的path,会自动被React-router处理,进行无刷新跳转

        9.2 hashRouter:利用hash值来作为路由,hash路由来匹配页面组件,不会涉及到资源的请求    


    10.使用react框架开发

       10.1性能优化:首先我们得了解,浏览器的性能是有一定限定的,但js的运行速度很快。

          10.1.1  虚拟dom:react使用虚拟dom来模拟最终要挂载的真实dom,虚拟dom上只存在几个关键的属性,该节点类型,该节点上我们挂载的属性,以及children,相比起真实dom上数量庞大的属性,虚拟dom更简洁也足够描绘出真实dom的结构;

          10.1.2  diff算法:每次虚拟dom的更改(进行一次render)就会产生一棵新的虚拟dom树,与之前旧的树进行对比,将所有变化的点整合到一起,将这些变化再通过真实dom操作进行批量更新。新旧dom树之间的对比就是用的diff算法,注意React只做同一层次节点的对比而不是深度遍历每一个节点。虚拟dom与diff算法是react高性能的关键原因。

        10.2 react组件加载、更新中会触发哪些过程?
             React是数据(state/props)驱动的,state的变化会触发render函数的重新执行,而render是生成虚拟dom的关键。组件在第一次渲染后会执行一遍render形成第一 棵虚拟dom,后续组件中state/props的变化也会造成组件的重新渲染,而每一次的重新渲染都会触发新的虚拟dom树的产生与diff比较(这也是为什么react不允许用户自己改变state而是用setState方法去更新state),再将不同点patch到真实dom上去,实现页面的更新。

       10.3 组件化开发(提供了一种代码的开发组织规范)   
            目前三大主流框架Angular、Vue与React都是组件化开发的模式。
            代码组织与规范的好坏直接影响着开发者的开发与迭代的成本。代码组织混乱,各种变量、逻辑处理满天飞,且随着后续需求的复杂化,代码的迭代、维护成本会变得及其高甚至不能维护,如果代码移交给后来者,那对后来者来说将是场灾难;相反代码组织规范,逻辑清晰,有较好的备注习惯,开发起来则事半功倍,对于后来接手者也能够较快的熟悉代码逻辑与结构。


3.    关于前端性能优化

    (关键还是减少http请求次数)

    进行小图片的整合(目的也是减少http的请求次数);

    函数节流/防抖(能手写最好),解决高频次触发的事件问题;

    合并css样式表与js脚本;

     将静态资源托管到cdn上;

    代码压缩;

    懒加载/预加载;

    路由懒加载(单页面应用解决首屏空白、加载慢的问题);

    利用node中间层进行前端请求的合并,将从后端的拿到数据进行整合再发送给前端,减少了前端请求的次数,还省去了前端整合数据的过程;

    SPA单页面应用:目前最好的用户体验的方式,前端页面无刷新,前端路由利用router实现模块的替换不需要去后端重新请求页面

    服务端渲染,将渲染页面的事放到后端去做,后端返回给前端一个完整的页面,浏览器直接渲染即可。(将前端耗时多的操作放在后端去做,都是对前端的一种性能优化,毕竟服务端的性能是前端无法比较的)

4.   关于开发中常遇到的问题与解决方案

    1.跨域:跨域是因为浏览器的同源策略,只有同协议、同端口与同ip才能相互请求访问。

       解决方法:日常开发利用webpack的proxy配置代理;

                         后端的CORS跨域;

                         jsonp也可以跨域,不过现在用的太少了,而且Jsonp只能适用于get请求


4.    前端开发的基础常识

    1. ES5/6/7

            var/let/const:let与const没有变量提升,且let可形成块级作用域(js本身只有window下的全局与function内的局部两种作用域)

            箭头函数:其this指向在定义时就确定了指向当前实例,常可以解决在组件开发中函数的this指向不为当前组件实例问题,可省去bind的操作

            解构赋值,项目中常用

            展开运算符 ... ,常用于数据的展开合并

            模板字符串

            Promise/generator/async await,解决异步事件的先后执行顺序,解决回调地狱

    2. html5与css3新特性

    3.  闭包、原型链、执行上下文:闭包其实就是函数嵌套,内层函数使用外层函数的变量就会形成闭包,形成闭包的变量不会被释放将一直存在内存中(在不再需要时,可手动设置为null,也算是一种性能优化);原型链能帮助理解js的类与继承方式;执行上下决定了Js执行过程中如何获取变量、函数

    4.  缓存Cookie/Storage区别,cookie常用来携带用户信息,随着请求自动携带,Storage分为localStorage与sessionStorage,localStorage是本地长期的存储,sessionStorage是会话级别的存储,页面关闭即销毁

    5.  Js中的异步:事件、定时器、Promise、ajax、setTimeout/setInterval,异步中还分为宏任务与微任务,Promise的回调如then与catch是微任务,setTimeout/setInterval是宏任务,微任务要先与宏任务执行


5.    面试中常问到的原理性东西

    1.    BFC与IFC:不同元素(Block与Inline)的布局规则,不同属性的设置会形成不同的排列规则的元素

    2.    浏览器渲染流程:domTree+cssStyle=>renderTree=>layout=>渲染出来

    3.    回流与重绘:回流会造成renderTree的结构变化,要重新生成renderTree,计算元素的布局数据,重绘只是某些不影响元素布局的样式更改,回流造成的性能的代价比较高

    4.    Js如何实现异步的:主线程与事件轮询(event loop),同步代码在主线程中先执行,异步事件会放入任务队列,当主线程执行完之后,通过事件轮询将待执行的异步回调放在主线程中去执行

    5.   React中的key:用于同层结构的diff判断,key值相同的元素,不会重复创建

你可能感兴趣的:(React面试)