开篇
我们今天主要讲解的内容就是关于 React 面试相关的,我相信你在面试中,也会被问到各种各样的非常多的问题,我举几个例子你看看,在自己心里想一想,你会怎么回答?
为什么 React 选择使用 JSX ?
JSX 映射虚拟 Dom 的原理
setState 到底是同步的,还是异步的?
如何面向组件跨层级通信?
聊聊 Redux 和 Vuex 的设计思想有什么不同和相同之处?
React 事件与 DOM 事件有什么区别?
为什么 React 要加入 Hook ?
说一下你对 diff 算法的理解
谈一谈 React 的数据流管理
上面这些问题,非常的常见,除此之外,类似的问题还有很多,一个一个的讲,根本讲不完,也不可能每个都讲到;当然网上有很多面试宝典,各种典型面试题都会涉及到,大家可能最近都在准备面试,迫切想要知道这些问题的答案,这也是大家最容易陷入的一个职场误区,对自己的现状不满,想要跳槽涨薪,又缺乏日常积累,面试前疯狂的刷一些面试题,应付当前的几场面试,1-2年后,又一次轮回,忽略日常的积累和总结,总想着临阵才磨枪,倒是会有些闪亮,但永远不会锋利;
解这些题,并不是今天的重点,我想给你的,是一套方法论,是解决这一类问题的通用方法:
总体来说分为三个部分:
1:对 React 框架的全面了解;
这需要你在日常的开发中,不断积累总结,有意识的主动探索和思考,今天我就分享一下我自己的总计和思考,没有绝对的正确,但我相信一定对你有所启发;
2:面试问题解答的思路和技巧;
我对 React 的使用有很多经验了,写过几个项目,也有了自己的思考和理解,但是,在面试的时候,是不是又这样的情况呢?知道这个知识点,但不能准确完整地表达、不知道该如何描述,但当面试官提起的时候,又能记忆起来,说,“对对对,就是那个,我刚确实不知道该怎么讲”。肚子里有东西,却在嘴上吃了大亏?
3:经典面试题案例解答
前面介绍了这些思路和技巧,那么如何应用到具体的问题解答上呢?我们使用几个比较典型的面试题案例来解答;
如何理解 React 框架?
先从前端框架的发展来看,之前是没有前端框架这样的工具的,但是前端的业务量越来越大,页面变得越来越复杂,前端的工程也开始庞大了起来,如何组织代码结构,如何有效提升复用率,开始成为大型前端项目迫切需要解决的问题。2009年,带有大量的 Java 开发思想的 AngularJS 问世:https://angular.cn/ ;
AngularJS 提供了一揽子全家桶解决方案,从底层开始深度封装,向上提供了路由、双向绑定、指令、组件等框架特性。现在也一样,你需要的,它全部都有,但是学习成本太高了,各种全新概念应接不暇,让我们一度怀疑我写的不是 前端代码,而是 AngularJS 代码;
而 React 的思维模式是完全不同的,概念也极为简单:“一切皆组件”,而 组件 == Function / Class ;
官方手册 https://react.docschina.org/ 的第一句话就是 “ 用于构建用户界面的 JavaScript 库 ” ;
从各自的 “一句话” 介绍中,我们也可以看到,React 把自己定义为 “ JavaScript 库 ”,而 AngularJS 才是 “框架”,虽然我们经常说 “React 框架”,但是人家从来都说自己是个 库 的,框架这个名字,是我们强加给人家的,不要觉得这仅仅是一个称呼而已,其实从一定程度上,确实误导了很多人,尤其是新手,甚至有过几年工作经验的老油条,也在误会着 React ,那这个误会是怎么来的呢?
其实在 React 中,只有组件,没有页面,没有控制器,也没用模型,在 AngularJS 框架中这些习以为常的概念,React 统统没有。没有页面?那就用组件组合生成一个页面,没有控制器,那就用组件充当控制器。就像搭建乐高玩具一样,组件就是组成整个应用唯一零部件。
但是从实际编码上来讲,可以是纯函数组件或者类组件,也可能在函数中产生影响 UI 生成的副作用,比如直接操作 DOM 或者绑定事件等。在 React 中我们只需要关心两件事:数据与组件。React 负责组件开发者负责数据;这也就是我所理解的 MVVM 框架的概念;程序员负责 MV 的处理,React 负责 VM 的构建;那么对于 React 本身来说就只负责构建视图的工作了,因此在适用场景上远比传统框架更为广泛,你可以使用 React-dom 开发 PC 网页或者移动端网页;使用 React-Native,开发 iOS 与 Android 应用;还有 React-360 可以开发 VR 应用;甚至可以使用 React-ink 开发命令行应用。
当然 React 也有缺点, 由于 React 并不是一个典型的框架,比如路由、状态管理这样的功能,React 团队更希望交给社区来解决。所以导致在技术选型与学习使用上有比较高的成本。但也正因为社区蓬勃发展,非官方的一揽子解决方案还是有的,比如 Redux、 React Router 、组件库 Antd 、长列表 React-window 等填补了生态位的缺失。而日常开发应用,这些总要去学习使用,而更多的人,将这些社区的方案当作了 React 的一部分,因此就觉得 React 是个 “框架” 了,这样的说法虽然不准确,但其实也没必要必须纠正,因为 “React+社区方案” 的组合,确实是一个框架应该有的样子;也正为如此,React 成了一个使用者上限与下限差距极大的框架, 而社区方案的组合和应用能力,也成为了进大厂的必考内容;
虽然我这里在一定程度上拿 AngularJS 和 React 再做对比,但是请注意,如果你觉得 AngularJS 无懈可击宇宙无敌吊炸天,那一定是你说的对,而如果你觉得 Vue 无懈可击宇宙无敌吊炸天,那当然也是你说的对;
对于各种框架的对比调查也有很多,正巧,前一段时间 stateofjs2020 刚刚发布,大家感兴趣可以去看看一下;
stateofjs 2020 年度前端开发者调查报告:https://2020.stateofjs.com/zh-Hans/technologies/front-end-frameworks/
报告显示,React 的占用量明显高于 Vue 和 AngularJS , 80%的调查者使用英语语种,说白了,就是欧美方面的调查,并不能说明国内的情况,而 react 在 Github 的 star 是 164K,Vue2.x 在 Github 的star 是 180K,大家也可以自己去看看,https://github.com/facebook/react、https://github.com/vuejs/vue,如果有时间,还可以去看看 NPM 的下载数;
其实,我想说的是,不要做那个框架好的对比, Vue 和 React 或者AngularJS 或者其他 MVVM 框架,都是非常优秀且值得学习的,也都有各自的优点和缺点;与其在网络上撕逼,不如认真学习学习,奉劝各位,井蛙不可语海,夏虫不可语冰;
总结一下:
React 本质上就是一个构建用户界面的 JavaScript 库,通过组件化的方式解决视图层开发复用的问题;
组件化的优势在于视图的拆分与模块复用,可以更容易做到高内聚低耦合,
通用性更强,一次学习,随处编写。比如 React Native,React 360 等, 这里主要靠虚拟 DOM 来保证实现。
这使得 React 的适用范围变得足够广
但作为一个视图层的框架,React 的劣势也十分明显。它并没有提供传统框架的一整套完整解决方案,在开发大型前端应用时,需要向社区寻找并整合解决方案。虽然一定程度上促进了社区的繁荣,但也为开发者在技术选型和学习适用上造成了一定的成本。
为什么 React 选择使用 JSX ?
这里问 “为什么 React 选择使用 JSX ?”,其引申含义是 “为什么不用 A、B、C?”
举个例子,你二婶儿给你介绍了俩对象,一个温婉可爱小鸟依人,一个上得厅堂下得厨房,结果你依然选择单身不找对象,你二婶儿就问你为啥呀?你如果说单身有多好,你一定会被怼?怎么回答呢?温柔的太粘人,贤惠的长得丑;然后在说单身有多好;
套路就是,之所以选择x,是因为 y 和 z 不好,然后再说明 x 怎么怎么好;
但是,放到技术上,要答好这个问题,为什么 React 选择使用 JSX ?你需要先了解 React 可选的其他解决方案,然后才能知道有什么不好的地方;其实相关方案有很多,最直观的就是 模板,Vue 和 AngularJS 都选择使用模板方案,而 React 团队认为引入模板是一种不佳的实现,你觉得模板不好吗?我觉得还行啊,你觉得丑,我觉得美若天仙啊;这不仅仅是眼光不同,更多的是基于不同的角度来思考,再结合自身的特性做出的选择,React 团队之所以认为模板不是最佳实现,原因在于,React 团队认为模板分离了技术栈,分散了组件内的关注点,其次模板还会引入更多的概念,类似模板语法、模板指令等,JSX 并不会引入太多新的概念,它仍然是 JavaScript,就连条件表达式和循环都仍然是 JavaScript 的方式。更具有可读性,更贴近 HTML。而对于关注点分离这个问题,我们可以用两段代码来展示:
上面的两端代码分别使用了 React 及 Vue 的单文件组件来呈现,在 React 中,声明的 Users 类就是一个组件,全部的 方法、数据及 UI 视图,可以以任意的方式呈现, 而在 Vue 的组件中,很明确的要将 UI 部分写入 template 模板标签中(当然还可以在 component 方法中使用 template 字符串 ),功能及数据相关的 要写入 script 标签中,而相对应的数据展示能力,则需要使用模板指令进行呈现,如:@click
指令绑定点击事件,v-for
循环遍历数据及样式结构;而在 JSX 中,全部都是 JavaScript 的,没什么规矩可言;
两种方式各有不同,我自己也说不上喜欢那个,但是,站在技术角度,我比较喜欢 JSX ,而站在产品研发角度,我更倾向于 Vue 的模板方式;
就类似我妈做饭超级好吃,选媳妇就选小鸟依人的,但是我妈做饭根本没法吃,我还是选下得厨房的媳妇要好一些。毕竟程序员是不需要爱情的……
如果你想解答的更加完善,还可以加入其他方式进行阐述,比如 模板字符、JXON;篇幅有限,我这里就不展开说了;
对于 为什么 React 选择使用 JSX ?到这里,我们其实已经说的差不多了,但是,总觉得少点什么,那就是对于 JSX 本身的阐述还不到位;
那么 JSX 到底是什么呢?
我们知道它不是字符串也不是 HTML,是一个 JavaScript 的语法扩展,用于描述组件 UI 。 实际上,官方手册上早就说的很清楚了,JSX 仅仅只是 React.createElement(component, props, ...children)
函数的语法糖,最终会被编译为 React.createElement() 函数调用,返回称为 “React 元素” 的普通 JavaScript 对象。
我们用一段简单的代码展示一下,具体来看看:
上面的代码中,我们直接将 JSX 的内容打印到控制台,效果如下:
那么,从 JSX 到控制台打印的结果中,到底发生了什么?手册上说 JSX 仅仅只是 React.createElement()
函数的语法糖,那么问题就来了,React.createElement 到底做了什么呢? 走,翻一下源码看看就知道了
对于这段代码,并没有什么高大上的骚操作,在这里,我大致说一下,做了什么事情:
React. createElement 二次处理 key、ref、self、 source 四个属性值;
遍历 config,筛出可以提进 props 里的属性;
提取子元素,推入 childArray(也即 props.children)数组;
格式化 defaultProps;
结合以上数据作为入参,发起 ReactElement调用;
那么接着让我们进入到 ReactElement 方法,继续查看 到底做了什么事情:
而这些代码就更有意思了,就是把传入的内容,组装进 element 对象并返回,注意,这个 element 实际就是我们之前打印到控制台的那个对象,其实这个对象就是我们常说的 ”虚拟 DOM “ ,而从虚拟 DOM 到真实 DOM 的工作,就是我们调用 ReactDOM.render 方法去做的事情了,这里咱们就不再继续分析了;
来波小总结
为什么 React 选择使用 JSX ?本质就是萝卜青菜各有所爱,React 团队认为 JSX 不会引入太多新的概念,编码更纯净,更具有可读性,更贴近 HTML,而对于 JSX 本身来说,是 React.createElement()
函数的语法糖,createElement() 对参数进行拆解后,发起 ReactElement 调用生成虚拟 DOM 对象;