京东前端二面高频react面试题

2022-09-14 10:38:43 浏览数 (1)

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

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

解释 React 中 render() 的目的。

每个React组件强制要求必须有一个 render()。它返回一个 React 元素,是原生 DOM 组件的表示。如果需要渲染多个 HTML 元素,则必须将它们组合在一个封闭标记内,例如 <form><group><div> 等。此函数必须保持纯净,即必须每次调用时都返回相同的结果。

什么是JSX

jsx 是 JavaScriptXML的简写,是react使用的一种文件,它利用 JavaScript 的表现力和类似 HTML 的模板语法,这使得 HTML 文件非常容易理解。此文件能使应用非常可靠,并能够提高其性能

jsx的语法规则

  • 定义虚拟DOM的时候 不需要写引号
  • 标签中要混入js表达式的时候需要用 {}
  • 在jsx中写标签的类名的时候 用className 代替class
  • 内联样式的时候 ,需要style={{key:value}}
  • 标签必须要闭合
  • 标签首字母的约定

若为小写字母,则将jsx转换为html中同名元素,若html中无该标签明对应的同名元素 则报错

若为大写字母,react就去渲染对应的组件,若没有定义组件 则报错

  • 当根据数据遍历生成的标签,一定要给标签设置单独的key 否则会报错

React 中 keys 的作用是什么?

KeysReact 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识

  • 在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性

react 实现一个全局的 dialog

代码语言:javascript复制
import React, { Component } from 'react';
import { is, fromJS } from 'immutable';
import ReactDOM from 'react-dom';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import './dialog.css';
let defaultState = {
  alertStatus:false,
  alertTip:"提示",
  closeDialog:function(){},
  childs:''
}
class Dialog extends Component{
  state = {
    ...defaultState
  };
  // css动画组件设置为目标组件
  FirstChild = props => {
    const childrenArray = React.Children.toArray(props.children);
    return childrenArray[0] || null;
  }
  //打开弹窗
  open =(options)=>{
    options = options || {};
    options.alertStatus = true;
    var props = options.props || {};
    var childs = this.renderChildren(props,options.childrens) || '';
    console.log(childs);
    this.setState({
      ...defaultState,
      ...options,
      childs
    })
  }
  //关闭弹窗
  close(){
    this.state.closeDialog();
    this.setState({
      ...defaultState
    })
  }
  renderChildren(props,childrens) {
    //遍历所有子组件
    var childs = [];
    childrens = childrens || [];
    var ps = {
        ...props,  //给子组件绑定props
        _close:this.close  //给子组件也绑定一个关闭弹窗的事件    
       };
    childrens.forEach((currentItem,index) => {
        childs.push(React.createElement(
            currentItem,
            {
                ...ps,
                key:index
            }
        ));
    })
    return childs;
  }
  shouldComponentUpdate(nextProps, nextState){
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
   
  render(){
    return (
      <ReactCSSTransitionGroup
        component={this.FirstChild}
        transitionName='hide'
        transitionEnterTimeout={300}
        transitionLeaveTimeout={300}>
        <div className="dialog-con" style={this.state.alertStatus? {display:'block'}:{display:'none'}}>
            {this.state.childs}        </div>
      </ReactCSSTransitionGroup>
    );
  }
}
let div = document.createElement('div');
let props = {
   
};
document.body.appendChild(div);
let Box = ReactD

子类:

代码语言:javascript复制
//子类jsx
import React, { Component } from 'react';
class Child extends Component {
    constructor(props){
        super(props);
        this.state = {date: new Date()};
  }
  showValue=()=>{
    this.props.showValue && this.props.showValue()
  }
  render() {
    return (
      <div className="Child">
        <div className="content">
           Child           <button onClick={this.showValue}>调用父的方法</button>
        </div>
      </div>
    );
  }
}
export default Child;

css:

代码语言:css复制
.dialog-con{
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.3);
}

概述一下 React中的事件处理逻辑。

为了解决跨浏览器兼容性问题, React会将浏览器原生事件( Browser Native Event)封装为合成事件( Synthetic Event)并传入设置的事件处理程序中。

这里的合成事件提供了与原生事件相同的接口,不过它们屏蔽了底层浏览器的细节差异,保证了行为的一致性。另外, React并没有直接将事件附着到子元素上,而是以单一事件监听器的方式将所有的事件发送到顶层进行处理(基于事件委托原理)。

这样 React在更新DOM时就不需要考虑如何处理附着在DOM上的事件监听器,最终达到优化性能的目的。

传入 setstate函数的第二个参数的作用是什么?

第二个参数是一个函数,该函数会在 setState函数调用完成并且组件开始重渲染时调用,可以用该函数来监听渲染是否完成。

代码语言:javascript复制
this.setstate(
  {
    username: "有课前端网",
  },
  () => console.log("re-rendered success. ")
);

refs的作用是什么,你在什么样的业务场景下使用refs

  • 操作DOM,为什么操作DOM?
  • 场景
    • 图片渲染好后,操作图片宽高。比如做个放大镜功能

(组件的)状态(state)和属性(props)之间有何不同

State 是一种数据结构,用于组件挂载时所需数据的默认值。State 可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。

Props(properties 的简写)则是组件的配置。props 由父组件传递给子组件,并且就子组件而言,props 是不可变的(immutable)。组件不能改变自身的 props,但是可以把其子组件的 props 放在一起(统一管理)。Props 也不仅仅是数据--回调函数也可以通过 props 传递。

什么是上下文Context

Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。

  • 用法:在父组件上定义getChildContext方法,返回一个对象,然后它的子组件就可以通过this.context属性来获取
代码语言:javascript复制
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
class Header extends Component{
    render() {
        return (
            <div>
                <Title/>
            </div>
        )
    }
}
class Title extends Component{
    static contextTypes={
        color:PropTypes.string
    }
    render() {
        return (
            <div style={{color:this.context.color}}>
                Title
            </div>
        )
    }
}
class Main extends Component{
    render() {
        return (
            <div>
                <Content>
                </Content>
            </div>
        )
    }
}
class Content extends Component{
    static contextTypes={
        color: PropTypes.string,
        changeColor:PropTypes.func
    }
    render() {
        return (
            <div style={{color:this.context.color}}>
                Content
                <button onClick={()=>this.context.changeColor('green')}>绿色</button>
                <button onClick={()=>this.context.changeColor('orange')}>橙色</button>
            </div>
        )
    }
}
class Page extends Component{
    constructor() {
        super();
        this.state={color:'red'};
    }
    static childContextTypes={
        color: PropTypes.string,
        changeColor:PropTypes.func
    }
    getChildContext() {
        return {
            color: this.state.color,
            changeColor:(color)=>{
                this.setState({color})
            }
        }
    }
    render() {
        return (
            <div>
                <Header/>
                <Main/>
            </div>
        )
    }
}
ReactDOM.render(<Page/>,document.querySelector('#root'));

react有什么优点

  • 提高应用性能
  • 可以方便的在客户端和服务端使用
  • 使用jsx模板进行数据渲染,可读性好

react中key的作用

简单的说:key 是虚拟DOM中的一种标识,在更新显示是key起到了极其重要的作用

复杂的说:当状态中的数据发生改变的时候,react会根据【新数据】生成【新的虚拟DOM】,随后react进行【新虚拟DOM】 和 【旧的虚拟DOM】的diff比较,而在这个比较过程中key就是起到是关键中用

与 ES5 相比,React 的 ES6 语法有何不同

以下语法是 ES5 与 ES6 中的区别:

  1. require 与 import
代码语言:javascript复制
// ES5
var React = require('react');

// ES6
import React from 'react';
  1. export 与 exports
代码语言:javascript复制
// ES5
module.exports = Component;

// ES6
export default Component;
  1. component 和 function
代码语言:javascript复制
// ES5
var MyComponent = React.createClass({
    render: function() {
        return
            <h3>Hello Edureka!</h3>;
    }
});

// ES6
class MyComponent extends React.Component {
    render() {
        return
            <h3>Hello Edureka!</h3>;
    }
}
  1. props
代码语言:javascript复制
// ES5
var App = React.createClass({
    propTypes: { name: React.PropTypes.string },
    render: function() {
        return
            <h3>Hello, {this.props.name}!</h3>;
    }
});

// ES6
class App extends React.Component {
    render() {
        return
            <h3>Hello, {this.props.name}!</h3>;
    }
}
  1. state
代码语言:javascript复制
// ES5
var App = React.createClass({
    getInitialState: function() {
        return { name: 'world' };
    },
    render: function() {
        return
            <h3>Hello, {this.state.name}!</h3>;
    }
});

// ES6
class App extends React.Component {
    constructor() {
        super();
        this.state = { name: 'world' };
    }
    render() {
        return
            <h3>Hello, {this.state.name}!</h3>;
    }
}

组件之间传值

  • 父组件给子组件传值

在父组件中用标签属性的=形式传值

在子组件中使用props来获取值

  • 子组件给父组件传值

在组件中传递一个函数

在子组件中用props来获取传递的函数,然后执行该函数

在执行函数的时候把需要传递的值当成函数的实参进行传递

  • 兄弟组件之间传值

利用父组件

先把数据通过 【子组件】===》【父组件】

然后在数据通过 【父组件】===〉【子组件】

消息订阅

使用PubSubJs插件

React Portal 有哪些使用场景

  • 在以前, react 中所有的组件都会位于 #app 下,而使用 Portals 提供了一种脱离 #app 的组件
  • 因此 Portals 适合脱离文档流(out of flow) 的组件,特别是 position: absolute 与 position: fixed的组件。比如模态框,通知,警告,goTop 等。

以下是官方一个模态框的示例,可以在以下地址中测试效果

代码语言:html复制
<html>
  <body>
    <div id="app"></div>
    <div id="modal"></div>
    <div id="gotop"></div>
    <div id="alert"></div>
  </body>
</html>
代码语言:javascript复制
const modalRoot = document.getElementById('modal');

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {
    modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    modalRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el,
    );
  }
}

React Hooks当中的useEffect是如何区分生命周期钩子的

useEffect可以看成是componentDidMountcomponentDidUpdatecomponentWillUnmount三者的结合。useEffect(callback, source)接收两个参数,调用方式如下

代码语言:javascript复制
useEffect(() => {
   console.log('mounted');

   return () => {
       console.log('willUnmount');
   }
 }, [source]);

生命周期函数的调用主要是通过第二个参数source来进行控制,有如下几种情况:

  • [source]参数不传时,则每次都会优先调用上次保存的函数中返回的那个函数,然后再调用外部那个函数;
  • [source]参数传[]时,则外部的函数只会在初始化时调用一次,返回的那个函数也只会最终在组件卸载时调用一次;
  • [source]参数有值时,则只会监听到数组中的值发生变化后才优先调用返回的那个函数,再调用外部的函数。

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
代码语言:javascript复制
import { useHistory } from "react-router-dom";
let history = useHistory();

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

代码语言:javascript复制
let history = this.props.history;

展示组件(Presentational component)和容器组件(Container component)之间有何不同

展示组件关心组件看起来是什么。展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。

容器组件则更关心组件是如何运作的。容器组件会为展示组件或者其它容器组件提供数据和行为(behavior),它们会调用 Flux actions,并将其作为回调提供给展示组件。容器组件经常是有状态的,因为它们是(其它组件的)数据源。

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

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

createElement过程

React.createElement(): 根据指定的第一个参数创建一个React元素

代码语言:javascript复制
React.createElement(
  type,
  [props],
  [...children]
)
  • 第一个参数是必填,传入的是似HTML标签名称,eg: ul, li
  • 第二个参数是选填,表示的是属性,eg: className
  • 第三个参数是选填, 子节点,eg: 要显示的文本内容
代码语言:javascript复制
//写法一:

var child1 = React.createElement('li', null, 'one');
    var child2 = React.createElement('li', null, 'two');
    var content = React.createElement('ul', { className: 'teststyle' }, child1, child2); // 第三个参数可以分开也可以写成一个数组
      ReactDOM.render(
          content,
        document.getElementById('example')
      );

//写法二:

var child1 = React.createElement('li', null, 'one');
    var child2 = React.createElement('li', null, 'two');
    var content = React.createElement('ul', { className: 'teststyle' }, [child1, child2]);
      ReactDOM.render(
          content,
        document.getElementById('example')
      );

componentWillReceiveProps调用时机

  • 已经被废弃掉
  • 当props改变的时候才调用,子组件第二次接收到props的时候

0 人点赞