关于前端:社招前端必会react面试题

31次阅读

共计 7897 个字符,预计需要花费 20 分钟才能阅读完成。

什么是 JSX

jsx 是 javascriptML 的简写,是 react 应用的一种文件,它利用 JavaScript 的表现力和相似 HTML 的模板语法,这使得 HTML 文件非常容易了解。此文件能使利用十分牢靠,并可能进步其性能
jsx 的语法规定

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

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

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

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

什么起因会促使你脱离 create-react-app 的依赖

当你想去配置 webpack 或 babel presets。

跨级组件的通信形式?

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

  • 应用 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>
          )
      }
}

什么是状态晋升

应用 react 常常会遇到几个组件须要共用状态数据的状况。这种状况下,咱们最好将这部分共享的状态晋升至他们最近的父组件当中进行治理。咱们来看一下具体如何操作吧。

import React from 'react'
class Child_1 extends React.Component{constructor(props){super(props)
    }
    render(){
        return (
            <div>
                <h1>{this.props.value+2}</h1>
            </div> 
        )
    }
}
class Child_2 extends React.Component{constructor(props){super(props)
    }
    render(){
        return (
            <div>
                <h1>{this.props.value+1}</h1>
            </div> 
        )
    }
}
class Three extends React.Component {constructor(props){super(props)
        this.state = {txt:"牛逼"}
        this.handleChange = this.handleChange.bind(this)
    }
    handleChange(e){
        this.setState({txt:e.target.value})
    }
    render(){
       return (
            <div>
                <input type="text" value={this.state.txt} onChange={this.handleChange}/>
                <p>{this.state.txt}</p>
                <Child_1 value={this.state.txt}/>
                <Child_2 value={this.state.txt}/>
            </div>
       )
    }
}
export default Three

当调用 setState 的时候,产生了什么操作?**

当调用 setState 时,React 做的第一件事是将传递给 setState 的对象合并到组件的以后状态,这将启动一个称为和解(reconciliation)的过程。
和解的最终目标是,依据这个新的状态以最无效的形式更新 DOM。
为此,React 将构建一个新的 React 虚构 DOM 树(能够将其视为页面 DOM 元素的对象示意形式)。
一旦有了这个 DOM 树,为了弄清 DOM 是如何响应新的状态而扭转的,React 会将这个新树与上一个虚构 DOM 树比拟。
这样做,React 会晓得产生的确切变动,并且通过理解产生的变动后,在相对必要的状况下进行更新 DOM,即可将因操作 DOM 而占用的空间最小化。

react 中的 Portal 是什么?

Portals 提供了一种很好的将子节点渲染到父组件以外的 DOM 节点的形式。
第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或碎片。
第二个参数(container)则是一个 DOM 元素。

ReactDOM.createPortal(child, container)

参考 前端进阶面试题具体解答

React 组件中怎么做事件代理?它的原理是什么?

React 基于 Virtual DOM 实现了一个 SyntheticEvent 层(合成事件层),定义的事件处理器会接管到一个合成事件对象的实例,它合乎 W3C 规范,且与原生的浏览器事件领有同样的接口,反对冒泡机制,所有的事件都主动绑定在最外层上。

在 React 底层,次要对合成事件做了两件事:

  • 事件委派: React 会把所有的事件绑定到构造的最外层,应用对立的事件监听器,这个事件监听器上维持了一个映射来保留所有组件外部事件监听和处理函数。
  • 主动绑定: React 组件中,每个办法的上下文都会指向该组件的实例,即主动绑定 this 为以后组件。

你了解“在 React 中,一切都是组件”这句话。

组件是 React 利用 UI 的构建块。这些组件将整个 UI 分成小的独立并可重用的局部。每个组件彼此独立,而不会影响 UI 的其余部分。

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

实质上来说 JSX 是 React.createElement(component, props, ...children) 办法的语法糖。在 React 17 之前,如果应用了 JSX,其实就是在应用 React,babel 会把组件转换为 CreateElement 模式。在 React 17 之后,就不再须要引入,因为 babel 曾经能够帮咱们主动引入 react。

diff 算法如何比拟?

  • 只对同级比拟,跨层级的 dom 不会进行复用
  • 不同类型节点生成的 dom 树不同,此时会间接销毁老节点及子孙节点,并新建节点
  • 能够通过 key 来对元素 diff 的过程提供复用的线索
  • 单节点 diff
  • 单点 diff 有如下几种状况:
  • key 和 type 雷同示意能够复用节点
  • key 不同间接标记删除节点,而后新建节点
  • key 雷同 type 不同,标记删除该节点和兄弟节点,而后新创建节点

React Hooks 在平时开发中须要留神的问题和起因

(1)不要在循环,条件或嵌套函数中调用 Hook,必须始终在 React 函数的顶层应用 Hook

这是因为 React 须要利用调用程序来正确更新相应的状态,以及调用相应的钩子函数。一旦在循环或条件分支语句中调用 Hook,就容易导致调用程序的不一致性,从而产生难以预料到的结果。

(2)应用 useState 时候,应用 push,pop,splice 等间接更改数组对象的坑

应用 push 间接更改数组无奈获取到新值,应该采纳析构形式,然而在 class 外面不会有这个问题。代码示例:

function Indicatorfilter() {let [num,setNums] = useState([0,1,2,3])
  const test = () => {
    // 这里坑是间接采纳 push 去更新 num
    // setNums(num)是无奈更新 num 的
    // 必须应用 num = [...num ,1]
    num.push(1)
    // num = [...num ,1]
    setNums(num)
  }
return (
    <div className='filter'>
      <div onClick={test}> 测试 </div>
        <div>
          {num.map((item,index) => (<div key={index}>{item}</div>
          ))}      </div>
    </div>
  )
}

class Indicatorfilter extends React.Component<any,any>{constructor(props:any){super(props)
      this.state = {nums:[1,2,3]
      }
      this.test = this.test.bind(this)
  }

  test(){
      // class 采纳同样的形式是没有问题的
      this.state.nums.push(1)
      this.setState({nums: this.state.nums})
  }

  render(){let {nums} = this.state
      return(
          <div>
              <div onClick={this.test}> 测试 </div>
                  <div>
                      {nums.map((item:any,index:number) => (<div key={index}>{item}</div>
                      ))}                  </div>
          </div>

      )
  }
}

(3)useState 设置状态的时候,只有第一次失效,前期须要更新状态,必须通过 useEffect

TableDeail 是一个公共组件,在调用它的父组件外面,咱们通过 set 扭转 columns 的值,认为传递给 TableDeail 的 columns 是最新的值,所以 tabColumn 每次也是最新的值,然而理论 tabColumn 是最开始的值,不会随着 columns 的更新而更新:

const TableDeail = ({columns,}:TableData) => {const [tabColumn, setTabColumn] = useState(columns) 
}

// 正确的做法是通过 useEffect 扭转这个值
const TableDeail = ({columns,}:TableData) => {const [tabColumn, setTabColumn] = useState(columns) 
    useEffect(() =>{setTabColumn(columns)},[columns])
}

(4)善用 useCallback

父组件传递给子组件事件句柄时,如果咱们没有任何参数变动可能会选用 useMemo。然而每一次父组件渲染子组件即便没变动也会跟着渲染一次。

(5)不要滥用 useContext

能够应用基于 useContext 封装的状态管理工具。

React 有哪些优化性能的伎俩

类组件中的优化伎俩

  • 应用纯组件 PureComponent 作为基类。
  • 应用 React.memo 高阶函数包装组件。
  • 应用 shouldComponentUpdate 生命周期函数来自定义渲染逻辑。

办法组件中的优化伎俩

  • 应用 useMemo
  • 应用 useCallBack

其余形式

  • 在列表须要频繁变动时,应用惟一 id 作为 key,而不是数组下标。
  • 必要时通过扭转 CSS 款式暗藏显示组件,而不是通过条件判断显示暗藏组件。
  • 应用 Suspense 和 lazy 进行懒加载,例如:
import React, {lazy, Suspense} from "react";

export default class CallingLazyComponents extends React.Component {render() {
    var ComponentToLazyLoad = null;

    if (this.props.name == "Mayank") {ComponentToLazyLoad = lazy(() => import("./mayankComponent"));
    } else if (this.props.name == "Anshul") {ComponentToLazyLoad = lazy(() => import("./anshulComponent"));
    }

    return (
      <div>
        <h1>This is the Base User: {this.state.name}</h1>
        <Suspense fallback={<div>Loading...</div>}>
          <ComponentToLazyLoad />
        </Suspense>
      </div>
    )
  }
}

react 旧版生命周期函数

初始化阶段

  • getDefaultProps: 获取实例的默认属性
  • getInitialState: 获取每个实例的初始化状态
  • componentWillMount:组件行将被装载、渲染到页面上
  • render: 组件在这里生成虚构的 DOM 节点
  • componentDidMount: 组件真正在被装载之后

运行中状态

  • componentWillReceiveProps: 组件将要接管到属性的时候调用
  • shouldComponentUpdate: 组件承受到新属性或者新状态的时候(能够返回 false,接收数据后不更新,阻止 render 调用,前面的函数不会被继续执行了)
  • componentWillUpdate: 组件行将更新不能批改属性和状态
  • render: 组件从新描述
  • componentDidUpdate: 组件曾经更新

销毁阶段

  • componentWillUnmount: 组件行将销毁

高阶组件

高阶函数:如果一个函数 承受一个或多个函数作为参数或者返回一个函数 就可称之为 高阶函数

高阶组件:如果一个函数 承受一个或多个组件作为参数并且返回一个组件 就可称之为 高阶组件

react 中的高阶组件

React 中的高阶组件次要有两种模式:属性代理 反向继承

属性代理 Proxy

  • 操作 props
  • 抽离 state
  • 通过 ref 拜访到组件实例
  • 用其余元素包裹传入的组件 WrappedComponent

反向继承

会发现其属性代理和反向继承的实现有些相似的中央,都是返回一个继承了某个父类的子类,只不过属性代理中继承的是 React.Component,反向继承中继承的是传入的组件 WrappedComponent

反向继承能够用来做什么:

1. 操作 state

高阶组件中能够读取、编辑和删除 WrappedComponent 组件实例中的 state。甚至能够减少更多的state 项,然而 十分不倡议这么做 因为这可能会导致 state 难以保护及治理。

function withLogging(WrappedComponent) {    
    return class extends WrappedComponent {render() {    
            return (    
                <div>;    
                    <h2>;Debugger Component Logging...<h2>;    
                    <p>;state:<p>;    
                    <pre>;{JSON.stringify(this.state, null, 4)}<pre>;    
                    <p>props:<p>;    
                    <pre>{JSON.stringify(this.props, null, 4)}<pre>;    
                    {super.render()}    
                <div>;    
            );    
        }    
    };    
}

2. 渲染劫持(Render Highjacking)

条件渲染通过 props.isLoading 这个条件来判断渲染哪个组件。

批改由 render() 输入的 React 元素树

组件通信的形式有哪些

  • ⽗组件向⼦组件通信: ⽗组件能够向⼦组件通过传 props 的⽅式,向⼦组件进⾏通信
  • ⼦组件向⽗组件通信: props+ 回调的⽅式,⽗组件向⼦组件传递 props 进⾏通信,此 props 为作⽤域为⽗组件⾃身的函 数,⼦组件调⽤该函数,将⼦组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中
  • 兄弟组件通信: 找到这两个兄弟节点独特的⽗节点, 联合上⾯两种⽅式由⽗节点转发信息进⾏通信
  • 跨层级通信: Context 设计⽬的是为了共享那些对于⼀个组件树⽽⾔是“全局”的数据,例如以后认证的⽤户、主题或⾸选语⾔,对于逾越多层的全局数据通过 Context 通信再适宜不过
  • 公布订阅模式: 发布者公布事件,订阅者监听事件并做出反馈, 咱们能够通过引⼊ event 模块进⾏通信
  • 全局状态治理⼯具: 借助 Redux 或者 Mobx 等全局状态治理⼯具进⾏通信, 这种⼯具会保护⼀个全局状态中⼼ Store, 并依据不同的事件产⽣新的状态

React 中有应用过 getDefaultProps 吗?它有什么作用?

通过实现组件的 getDefaultProps,对属性设置默认值(ES5 的写法):

var ShowTitle = React.createClass({getDefaultProps:function(){
    return{title : "React"}
  },
  render : function(){return <h1>{this.props.title}</h1>
  }
});

形容 Flux 与 MVC?

传统的 MVC 模式在拆散数据 (Model)、UI(View 和逻辑(Controller) 方面工作得很好,然而 MVC 架构常常遇到两个次要问题:
数据流不够清晰 : 跨视图产生的级联更新经常会导致凌乱的事件网络,难于调试。
不足数据完整性 : 模型数据能够在任何中央产生渐变,从而在整个 UI 中产生不可预测的后果。
应用 Flux 模式的简单用户界面不再蒙受级联更新,任何给定的 React 组件都可能依据 store 提供的数据重建其状态。Flux 模式还通过限度对共享数据的间接拜访来增强数据完整性。

什么是 prop drilling,如何防止?

在构建 React 应用程序时,在多层嵌套组件来应用另一个嵌套组件提供的数据。最简略的办法是将一个 prop 从每个组件一层层的传递上来,从源组件传递到深层嵌套组件,这叫做 prop drilling
prop drilling 的次要毛病是本来不须要数据的组件变得不必要地简单,并且难以保护。
为了防止 prop drilling,一种罕用的办法是应用React Context。通过定义提供数据的Provider 组件,并容许嵌套的组件通过 Consumer 组件或useContext Hook 应用上下文数据。

redux 是如何更新值得

用户发动操作之后,dispatch 发送 action,依据 type,触发对于的 reducer,reducer 就是一个纯函数,接管旧的 state 和 action,返回新的 state。通过 subscribe(listener)监听器,派发更新。

何为受控组件(controlled component)

在 HTML 中,相似 <input>, <textarea><select> 这样的表单元素会保护本身的状态,并基于用户的输出来更新。当用户提交表单时,后面提到的元素的值将随表单一起被发送。但在 React 中会有些不同,蕴含表单元素的组件将会在 state 中追踪输出的值,并且每次调用回调函数时,如 onChange 会更新 state,从新渲染组件。一个输出表单元素,它的值通过 React 的这种形式来管制,这样的元素就被称为 ” 受控元素 ”。

正文完
 0