写点什么

一天梳理 React 面试高频知识点

作者:beifeng1996
  • 2022-10-17
    浙江
  • 本文字数:5966 字

    阅读完需:约 1 分钟

怎么用 React.createElement 重写下面的代码

Question:


const element = (  <h1 className="greeting">    Hello, rdhub.cn!  </h1>);
复制代码


Answer:


const element = React.createElement(  'h1',  {className: 'greeting'},  'Hello, rdhub.cn!');
复制代码

对 React-Intl 的理解,它的工作原理?

React-intl 是雅虎的语言国际化开源项目 FormatJS 的一部分,通过其提供的组件和 API 可以与 ReactJS 绑定。


React-intl 提供了两种使用方法,一种是引用 React 组件,另一种是直接调取 API,官方更加推荐在 React 项目中使用前者,只有在无法使用 React 组件的地方,才应该调用框架提供的 API。它提供了一系列的 React 组件,包括数字格式化、字符串格式化、日期格式化等。


在 React-intl 中,可以配置不同的语言包,他的工作原理就是根据需要,在语言包之间进行切换。

react 有什么特点

  • react 使用过的虚拟 DOM,而不是真实 DOM

  • react 可以用服务器渲染

  • react 遵循单向数据流 或者数据绑定

React 中的 key 是什么?为什么它们很重要?

key 可以帮助 React 跟踪循环创建列表中的虚拟 DOM 元素,了解哪些元素已更改、添加或删除。每个绑定 key 的虚拟 DOM 元素,在兄弟元素之间都是独一无二的。在 React 的和解过程中,比较新的虛拟 DOM 树与上一个虛拟 DOM 树之间的差异,并映射到页面中。key 使 React 处理列表中虛拟 DOM 时更加高效,因为 React 可以使用虛拟 DOM 上的 key 属性,快速了解元素是新的、需要删除的,还是修改过的。如果没有 key,Rεat 就不知道列表中虚拟 DOM 元素与页面中的哪个元素相对应。所以在创建列表的时候,不要忽略 key。

为什么 React 要用 JSX?

JSX 是一个 JavaScript 的语法扩展,或者说是一个类似于 XML 的 ECMAScript 语法扩展。它本身没有太多的语法定义,也不期望引入更多的标准。


其实 React 本身并不强制使用 JSX。在没有 JSX 的时候,React 实现一个组件依赖于使用 React.createElement 函数。代码如下:


class Hello extends React.Component {  render() {    return React.createElement(        'div',        null,         `Hello ${this.props.toWhat}`      );  }}ReactDOM.render(  React.createElement(Hello, {toWhat: 'World'}, null),  document.getElementById('root'));
复制代码


而 JSX 更像是一种语法糖,通过类似 XML 的描述方式,描写函数对象。在采用 JSX 之后,这段代码会这样写:


class Hello extends React.Component {  render() {    return <div>Hello {this.props.toWhat}</div>;  }}ReactDOM.render(  <Hello toWhat="World" />,  document.getElementById('root'));
复制代码


通过对比,可以清晰地发现,代码变得更为简洁,而且代码结构层次更为清晰。


因为 React 需要将组件转化为虚拟 DOM 树,所以在编写代码时,实际上是在手写一棵结构树。而 XML 在树结构的描述上天生具有可读性强的优势。


但这样可读性强的代码仅仅是给写程序的同学看的,实际上在运行的时候,会使用 Babel 插件将 JSX 语法的代码还原为 React.createElement 的代码。


总结: JSX 是一个 JavaScript 的语法扩展,结构类似 XML。JSX 主要用于声明 React 元素,但 React 中并不强制使用 JSX。即使使用了 JSX,也会在构建过程中,通过 Babel 插件编译为 React.createElement。所以 JSX 更像是 React.createElement 的一种语法糖。


React 团队并不想引入 JavaScript 本身以外的开发体系。而是希望通过合理的关注点分离保持组件开发的纯粹性。

React-Router 如何获取 URL 的参数和历史对象?

(1)获取 URL 的参数


  • get 传值


路由配置还是普通的配置,如:'admin',传参方式如:'admin?id='1111''。通过this.props.location.search获取 url 获取到一个字符串'?id='1111' 可以用 url,qs,querystring,浏览器提供的 api URLSearchParams 对象或者自己封装的方法去解析出 id 的值。


  • 动态路由传值


路由需要配置成动态路由:如path='/admin/:id',传参方式,如'admin/111'。通过this.props.match.params.id 取得 url 中的动态路由 id 部分的值,除此之外还可以通过useParams(Hooks)来获取


  • 通过 query 或 state 传值


传参方式如:在 Link 组件的 to 属性中可以传递对象{pathname:'/admin',query:'111',state:'111'};。通过this.props.location.statethis.props.location.query来获取即可,传递的参数可以是对象、数组等,但是存在缺点就是只要刷新页面,参数就会丢失。


(2)获取历史对象


  • 如果 React >= 16.8 时可以使用 React Router 中提供的 Hooks


import { useHistory } from "react-router-dom";let history = useHistory();
复制代码


2.使用 this.props.history 获取历史对象


let history = this.props.history;
复制代码

ref 是一个函数又有什么好处?

  • 方便 react 销毁组件、重新渲染的时候去清空 refs 的东西,防止内存泄露

如何配置 React-Router 实现路由切换

(1)使用<Route> 组件


路由匹配是通过比较 <Route> 的 path 属性和当前地址的 pathname 来实现的。当一个 <Route> 匹配成功时,它将渲染其内容,当它不匹配时就会渲染 null。没有路径的 <Route> 将始终被匹配。


// when location = { pathname: '/about' }<Route path='/about' component={About}/> // renders <About/><Route path='/contact' component={Contact}/> // renders null<Route component={Always}/> // renders <Always/>
复制代码


(2)结合使用 <Switch> 组件和 <Route> 组件


<Switch> 用于将 <Route> 分组。


<Switch>    <Route exact path="/" component={Home} />    <Route path="/about" component={About} />    <Route path="/contact" component={Contact} /></Switch>
复制代码


<Switch> 不是分组 <Route> 所必须的,但他通常很有用。 一个 <Switch> 会遍历其所有的子 <Route>元素,并仅渲染与当前地址匹配的第一个元素。


(3)使用 <Link>、 <NavLink>、<Redirect> 组件


<Link> 组件来在你的应用程序中创建链接。无论你在何处渲染一个<Link> ,都会在应用程序的 HTML 中渲染锚(<a>)。


<Link to="/">Home</Link>   // <a href='/'>Home</a>
复制代码


是一种特殊类型的 当它的 to 属性与当前地址匹配时,可以将其定义为"活跃的"。


// location = { pathname: '/react' }<NavLink to="/react" activeClassName="hurray">    React</NavLink>// <a href='/react' className='hurray'>React</a>
复制代码


当我们想强制导航时,可以渲染一个<Redirect>,当一个<Redirect>渲染时,它将使用它的 to 属性进行定向。

在哪个生命周期中你会发出 Ajax 请求?为什么?

Ajax 请求应该写在组件创建期的第五个阶段,即 componentDidMount 生命周期方法中。原因如下。在创建期的其他阶段,组件尚未渲染完成。而在存在期的 5 个阶段,又不能确保生命周期方法一定会执行(如通过 shouldComponentUpdate 方法优化更新等)。在销毀期,组件即将被销毁,请求数据变得无意义。因此在这些阶段发岀 Ajax 请求显然不是最好的选择。在组件尚未挂载之前,Ajax 请求将无法执行完毕,如果此时发出请求,将意味着在组件挂载之前更新状态(如执行 setState),这通常是不起作用的。在 componentDidMount 方法中,执行 Ajax 即可保证组件已经挂载,并且能够正常更新组件。


参考:前端react面试题详细解答

react 和 vue 的区别

相同点:


  1. 数据驱动页面,提供响应式的试图组件

  2. 都有 virtual DOM,组件化的开发,通过 props 参数进行父子之间组件传递数据,都实现了 webComponents 规范

  3. 数据流动单向,都支持服务器的渲染 SSR

  4. 都有支持 native 的方法,react 有 React native, vue 有 wexx


不同点:


  1. 数据绑定:Vue 实现了双向的数据绑定,react 数据流动是单向的

  2. 数据渲染:大规模的数据渲染,react 更快

  3. 使用场景:React 配合 Redux 架构适合大规模多人协作复杂项目,Vue 适合小快的项目

  4. 开发风格:react 推荐做法 jsx + inline style 把 html 和 css 都写在 js 了


vue 是采用 webpack +vue-loader 单文件组件格式,html, js, css 同一个文件

如何告诉 React 它应该编译生产环境版

通常情况下我们会使用 WebpackDefinePlugin 方法来将 NODE_ENV 变量值设置为 production。编译版本中 React会忽略 propType 验证以及其他的告警信息,同时还会降低代码库的大小,React 使用了 Uglify 插件来移除生产环境下不必要的注释等信息

在 Reducer 文件里,对于返回的结果,要注意哪些问题?

在 Reducer 文件里,对于返回的结果,必须要使用 Object.assign ( )来复制一份新的 state,否则页面不会跟着数据刷新。


return Object.assign({}, state, {  type: action.type,  shouldNotPaint: true,});
复制代码

React-Router 4 的 Switch 有什么用?

Switch 通常被用来包裹 Route,用于渲染与路径匹配的第一个子 <Route><Redirect>,它里面不能放其他元素。


假如不加 <Switch>


import { Route } from 'react-router-dom'
<Route path="/" component={Home}></Route><Route path="/login" component={Login}></Route>
复制代码


Route 组件的 path 属性用于匹配路径,因为需要匹配 /Home,匹配 /loginLogin,所以需要两个 Route,但是不能这么写。这样写的话,当 URL 的 path 为 “/login” 时,<Route path="/" /><Route path="/login" /> 都会被匹配,因此页面会展示 Home 和 Login 两个组件。这时就需要借助 <Switch> 来做到只显示一个匹配组件:


import { Switch, Route} from 'react-router-dom'
<Switch> <Route path="/" component={Home}></Route> <Route path="/login" component={Login}></Route></Switch>
复制代码


此时,再访问 “/login” 路径时,却只显示了 Home 组件。这是就用到了 exact 属性,它的作用就是精确匹配路径,经常与<Switch> 联合使用。只有当 URL 和该 <Route> 的 path 属性完全一致的情况下才能匹配上:


import { Switch, Route} from 'react-router-dom'
<Switch> <Route exact path="/" component={Home}></Route> <Route exact path="/login" component={Login}></Route></Switch>
复制代码

在生命周期中的哪一步你应该发起 AJAX 请求

我们应当将 AJAX 请求放到 componentDidMount 函数中执行,主要原因有下


  • React 下一代调和算法 Fiber 会通过开始或停止渲染的方式优化应用性能,其会影响到 componentWillMount 的触发次数。对于 componentWillMount 这个生命周期函数的调用次数会变得不确定,React 可能会多次频繁调用 componentWillMount。如果我们将 AJAX 请求放到 componentWillMount 函数中,那么显而易见其会被触发多次,自然也就不是好的选择。

  • 如果我们将AJAX 请求放置在生命周期的其他函数中,我们并不能保证请求仅在组件挂载完毕后才会要求响应。如果我们的数据请求在组件挂载之前就完成,并且调用了setState函数将数据添加到组件状态中,对于未挂载的组件则会报错。而在 componentDidMount 函数中进行 AJAX 请求则能有效避免这个问题

何为 JSX

JSX 是 JavaScript 语法的一种语法扩展,并拥有 JavaScript 的全部功能。JSX 生产 React "元素",你可以将任何的 JavaScript 表达式封装在花括号里,然后将其嵌入到 JSX 中。在编译完成之后,JSX 表达式就变成了常规的 JavaScript 对象,这意味着你可以在 if 语句和 for 循环内部使用 JSX,将它赋值给变量,接受它作为参数,并从函数中返回它。

为什么使用 jsx 的组件中没有看到使用 react 却需要引入 react?

本质上来说 JSX 是React.createElement(component, props, ...children)方法的语法糖。在 React 17 之前,如果使用了 JSX,其实就是在使用 React, babel 会把组件转换为 CreateElement 形式。在 React 17 之后,就不再需要引入,因为 babel 已经可以帮我们自动引入 react。

在 React 中元素( element)和组件( component)有什么区别?

简单地说,在 React 中元素(虛拟 DOM)描述了你在屏幕上看到的 DOM 元素。换个说法就是,在 React 中元素是页面中 DOM 元素的对象表示方式。在 React 中组件是一个函数或一个类,它可以接受输入并返回一个元素。注意:工作中,为了提高开发效率,通常使用 JSX 语法表示 React 元素(虚拟 DOM)。在编译的时候,把它转化成一个 React. createElement 调用方法。

非嵌套关系组件的通信方式?

即没有任何包含关系的组件,包括兄弟组件以及不在同一个父级中的非兄弟组件。


  • 可以使用自定义事件通信(发布订阅模式)

  • 可以通过 redux 等进行全局状态管理

  • 如果是兄弟组件通信,可以找到这两个兄弟节点共同的父节点, 结合父子间通信方式进行通信。

描述事件在 React 中的处理方式。

为了解决跨浏览器兼容性问题, React 中的事件处理程序将传递 SyntheticEvent 的实例,它是跨浏览器事件的包装器。这些 SyntheticEvent 与你习惯的原生事件具有相同的接口,它们在所有浏览器中都兼容。React 实际上并没有将事件附加到子节点本身。而是通过事件委托模式,使用单个事件监听器监听顶层的所有事件。这对于性能是有好处的。这也意味着在更新 DOM 时, React 不需要担心跟踪事件监听器。

Redux 实现原理解析

为什么要用 redux


React中,数据在组件中是单向流动的,数据从一个方向父组件流向子组件(通过props),所以,两个非父子组件之间通信就相对麻烦,redux的出现就是为了解决state里面的数据问题


Redux 设计理念


Redux是将整个应用状态存储到一个地方上称为store,里面保存着一个状态树store tree,组件可以派发(dispatch)行为(action)给store,而不是直接通知其他组件,组件内部通过订阅store中的状态state来刷新自己的视图



Redux 三大原则


  • 唯一数据源


整个应用的 state 都被存储到一个状态树里面,并且这个状态树,只存在于唯一的 store 中


  • 保持只读状态


state是只读的,唯一改变state的方法就是触发actionaction是一个用于描述以发生时间的普通对象


  • 数据改变只能通过纯函数来执行


使用纯函数来执行修改,为了描述action如何改变state的,你需要编写reducers


Redux 源码


let createStore = (reducer) => {    let state;    //获取状态对象    //存放所有的监听函数    let listeners = [];    let getState = () => state;    //提供一个方法供外部调用派发action    let dispath = (action) => {        //调用管理员reducer得到新的state        state = reducer(state, action);        //执行所有的监听函数        listeners.forEach((l) => l())    }    //订阅状态变化事件,当状态改变发生之后执行监听函数    let subscribe = (listener) => {        listeners.push(listener);    }    dispath();    return {        getState,        dispath,        subscribe    }}let combineReducers=(renducers)=>{    //传入一个renducers管理组,返回的是一个renducer    return function(state={},action={}){        let newState={};        for(var attr in renducers){            newState[attr]=renducers[attr](state[attr],action)
} return newState; }}export {createStore,combineReducers};
复制代码


用户头像

beifeng1996

关注

还未添加个人签名 2022-09-01 加入

还未添加个人简介

评论

发布
暂无评论
一天梳理React面试高频知识点_React_beifeng1996_InfoQ写作社区