写点什么

字节前端面试题总结

作者:zhang_a111
  • 2022 年 7 月 27 日
  • 本文字数:5862 字

    阅读完需:约 19 分钟

前端面试题视频讲解

constructor 为什么不先渲染?

由 ES6 的继承规则得知,不管子类写不写 constructor,在 new 实例的过程都会给补上 constructor。


所以:constructor 钩子函数并不是不可缺少的,子组件可以在一些情况略去。比如不自己的 state,从 props 中获取的情况

react 版本差异

react16.8 hooks


React 16 之后有三个生命周期被废弃(但并未删除)


  • componentWillMount

  • componentWillReceiveProps

  • componentWillUpdate


官方计划在 17 版本完全删除这三个函数,只保留 UNSAVE_前缀的三个函数,目的是为了向下兼容,


react 16.4 新增


getSnapshotBeforeUpdate


getDerivedStateFromProps


对于废弃的生命周期函数,官方会采用逐步迁移的方式来实现版本的迁移:


16.3:为不安全的生命周期引入别名,UNSAFE_componentWillMount、UNSAFE_componentWillReceiveProps 和 UNSAFE_componentWillUpdate。(旧的生命周期名称和新的别名都可以在此版本中使用。)


未来 16.x 版本:为 componentWillMount、componentWillReceiveProps 和 componentWillUpdate 启用废弃告警。(旧的生命周期名称和新的别名都将在这个版本中工作,但是旧的名称在开发模式下会产生一个警告。)


17.0:删除 componentWillMount、componentWillReceiveProps 和 componentWillUpdate。(在此版本之后,只有新的 “UNSAFE_” 生命周期名称可以使用。)。

如何在 ReactJS 的 Props 上应用验证?

当应用程序在开发模式下运行时,React 将自动检查咱们在组件上设置的所有 props,以确保它们具有正确的数据类型。对于不正确的类型,开发模式下会在控制台中生成警告消息,而在生产模式中由于性能影响而禁用它。强制的 propsisRequired定义的。下面是一组预定义的 prop 类型:


  • React.PropTypes.string

  • React.PropTypes.number

  • React.PropTypes.func

  • React.PropTypes.node

  • React.PropTypes.bool 例如,咱们为用户组件定义了如下的propTypes


  import PropTypes from "prop-types";  class User extends React.Component {    render() {      return (        <>          <h1>Welcome, {this.props.name}</h1>          <h2>Age, {this.props.age}</h2>        </>      );    }  }    User.propTypes = {    name: PropTypes.string.isRequired,    age: PropTypes.number.isRequired,  };
复制代码


setState 方法的第二个参数有什么用?使用它的目的是什么?它是一个回调函数,当 setState 方法执行结束并重新渲染该组件时调用它。在工作中,更好的方式是使用 React 组件生命周期之——“存在期”的生命周期方法,而不是依赖这个回调函数。


export class App extends Component {  constructor(props) {    super(props);    this.state = {      username: "雨夜清荷",    };  }  render() {    return <div> {this.state.username}</div>;  }  componentDidMount() {    this.setstate(      {        username: "有课前端网",      },      () => console.log("re-rendered success. ")    );  }}
复制代码

跨级组件的通信方式?

父组件向子组件的子组件通信,向更深层子组件通信:


  • 使用 props,利用中间组件层层传递,但是如果父组件结构较深,那么中间每一层组件都要去传递 props,增加了复杂度,并且这些 props 并不是中间组件自己需要的。

  • 使用 context,context 相当于一个大容器,可以把要通信的内容放在这个容器中,这样不管嵌套多深,都可以随意取用,对于跨越多层的全局数据可以使用 context 实现。


// context方式实现跨级组件通信 // Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据const BatteryContext = createContext();//  子组件的子组件 class GrandChild extends Component {    render(){        return (            <BatteryContext.Consumer>                {                    color => <h1 style={{"color":color}}>我是红色的:{color}</h1>                }            </BatteryContext.Consumer>        )    }}//  子组件const Child = () =>{    return (        <GrandChild/>    )}// 父组件class Parent extends Component {      state = {          color:"red"      }      render(){          const {color} = this.state          return (          <BatteryContext.Provider value={color}>              <Child></Child>          </BatteryContext.Provider>          )      }}复制代码
复制代码

使用箭头函数(arrow functions)的优点是什么

  • 作用域安全:在箭头函数之前,每一个新创建的函数都有定义自身的 this 值(在构造函数中是新对象;在严格模式下,函数调用中的 this 是未定义的;如果函数被称为“对象方法”,则为基础对象等),但箭头函数不会,它会使用封闭执行上下文的 this 值。

  • 简单:箭头函数易于阅读和书写

  • 清晰:当一切都是一个箭头函数,任何常规函数都可以立即用于定义作用域。开发者总是可以查找 next-higher 函数语句,以查看 this 的值

何为纯函数(pure function)

一个纯函数是一个不依赖于且不改变其作用域之外的变量状态的函数,这也意味着一个纯函数对于同样的参数总是返回同样的结果。

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

Question:


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


Answer:


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

React 性能优化在哪个生命周期?它优化的原理是什么?

react 的父级组件的 render 函数重新渲染会引起子组件的 render 方法的重新渲染。但是,有的时候子组件的接受父组件的数据没有变动。子组件 render 的执行会影响性能,这时就可以使用 shouldComponentUpdate 来解决这个问题。


使用方法如下:


shouldComponentUpdate(nexrProps) {    if (this.props.num === nexrProps.num) {        return false    }    return true;}复制代码
复制代码


shouldComponentUpdate 提供了两个参数 nextProps 和 nextState,表示下一次 props 和一次 state 的值,当函数返回 false 时候,render()方法不执行,组件也就不会渲染,返回 true 时,组件照常重渲染。此方法就是拿当前 props 中值和下一次 props 中的值进行对比,数据相等时,返回 false,反之返回 true。


需要注意,在进行新旧对比的时候,是**浅对比,**也就是说如果比较的数据时引用数据类型,只要数据的引用的地址没变,即使内容变了,也会被判定为 true。


面对这个问题,可以使用如下方法进行解决:(1)使用 setState 改变数据之前,先采用 ES6 中 assgin 进行拷贝,但是 assgin 只深拷贝的数据的第一层,所以说不是最完美的解决办法:


const o2 = Object.assign({},this.state.obj)    o2.student.count = '00000';    this.setState({        obj: o2,    })复制代码
复制代码


(2)使用 JSON.parse(JSON.stringfy())进行深拷贝,但是遇到数据为 undefined 和函数时就会错。


const o2 = JSON.parse(JSON.stringify(this.state.obj))    o2.student.count = '00000';    this.setState({        obj: o2,    })复制代码
复制代码

何为 JSX

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

constructor

答案是:在 constructor 函数里面,需要用到props的值的时候,就需要调用 super(props)
复制代码


  1. class 语法糖默认会帮你定义一个 constructor,所以当你不需要使用 constructor 的时候,是可以不用自己定义的

  2. 当你自己定义一个 constructor 的时候,就一定要写 super(),否则拿不到 this

  3. 当你在 constructor 里面想要使用 props 的值,就需要传入 props 这个参数给 super,调用 super(props),否则只需要写 super()

React-Router 怎么设置重定向?

使用<Redirect>组件实现路由的重定向:


<Switch>  <Redirect from='/users/:id' to='/users/profile/:id'/>  <Route path='/users/profile/:id' component={Profile}/></Switch>复制代码
复制代码


当请求 /users/:id 被重定向去 '/users/profile/:id'


  • 属性 from: string:需要匹配的将要被重定向路径。

  • 属性 to: string:重定向的 URL 字符串

  • 属性 to: object:重定向的 location 对象

  • 属性 push: bool:若为真,重定向操作将会把新地址加入到访问历史记录里面,并且无法回退到前面的页面。

Redux 怎么实现属性传递,介绍下原理

react-redux 数据传输∶ view-->action-->reducer-->store-->view。看下点击事件的数据是如何通过 redux 传到 view 上:


  • view 上的 AddClick 事件通过 mapDispatchToProps 把数据传到 action ---> click:()=>dispatch(ADD)

  • action 的 ADD 传到 reducer 上

  • reducer 传到 store 上 const store = createStore(reducer);

  • store 再通过 mapStateToProps 映射穿到 view 上 text:State.text


代码示例∶


import React from 'react';import ReactDOM from 'react-dom';import { createStore } from 'redux';import { Provider, connect } from 'react-redux';class App extends React.Component{    render(){        let { text, click, clickR } = this.props;        return(            <div>                <div>数据:已有人{text}</div>                <div onClick={click}>加人</div>                <div onClick={clickR}>减人</div>            </div>        )    }}const initialState = {    text:5}const reducer = function(state,action){    switch(action.type){        case 'ADD':            return {text:state.text+1}        case 'REMOVE':            return {text:state.text-1}        default:            return initialState;    }}
let ADD = { type:'ADD'}let Remove = { type:'REMOVE'}
const store = createStore(reducer);
let mapStateToProps = function (state){ return{ text:state.text }}
let mapDispatchToProps = function(dispatch){ return{ click:()=>dispatch(ADD), clickR:()=>dispatch(Remove) }}
const App1 = connect(mapStateToProps,mapDispatchToProps)(App);
ReactDOM.render( <Provider store = {store}> <App1></App1> </Provider>,document.getElementById('root'))复制代码
复制代码

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>
复制代码

React 中的 setState 批量更新的过程是什么?

调用 setState 时,组件的 state 并不会立即改变, setState 只是把要修改的 state 放入一个队列, React 会优化真正的执行时机,并出于性能原因,会将 React 事件处理程序中的多次React 事件处理程序中的多次 setState 的状态修改合并成一次状态修改。 最终更新只产生一次组件及其子组件的重新渲染,这对于大型应用程序中的性能提升至关重要。


this.setState({  count: this.state.count + 1    ===>    入队,[count+1的任务]});this.setState({  count: this.state.count + 1    ===>    入队,[count+1的任务,count+1的任务]});                                         合并 state,[count+1的任务]                                         执行 count+1的任务复制代码
复制代码


需要注意的是,只要同步代码还在执行,“攒起来”这个动作就不会停止。(注:这里之所以多次 +1 最终只有一次生效,是因为在同一个方法中多次 setState 的合并动作不是单纯地将更新累加。比如这里对于相同属性的设置,React 只会为其保留最后一次的更新)。

(在构造函数中)调用 super(props) 的目的是什么

super() 被调用之前,子类是不能使用 this 的,在 ES2015 中,子类必须在 constructor 中调用 super()。传递 propssuper() 的原因则是便于(在子类中)能在 constructor 访问 this.props

何为 redux

Redux 的基本思想是整个应用的 state 保持在一个单一的 store 中。store 就是一个简单的 javascript 对象,而改变应用 state 的唯一方式是在应用中触发 actions,然后为这些 actions 编写 reducers 来修改 state。整个 state 转化是在 reducers 中完成,并且不应该有任何副作用。

何为高阶组件(higher order component)

高阶组件是一个以组件为参数并返回一个新组件的函数。HOC 运行你重用代码、逻辑和引导抽象。最常见的可能是 Redux 的 connect 函数。除了简单分享工具库和简单的组合,HOC 最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。


练习




  • 写一个反转其输入的 HOC

  • 写一个从 API 提供数据给传入的组件的 HOC

  • 写一个实现 shouldComponentUpdate 来避免 reconciliation 的 HOC

  • 写一个通过 React.Children.toArray 对传入组件的子组件进行排序的 HOC

用户头像

zhang_a111

关注

还未添加个人签名 2021.06.26 加入

还未添加个人简介

评论

发布
暂无评论
字节前端面试题总结_前端面试_zhang_a111_InfoQ写作社区