1. 程式人生 > 其它 >前端必會react面試題及答案

前端必會react面試題及答案

state 和 props 觸發更新的生命週期分別有什麼區別?

state 更新流程: 這個過程當中涉及的函式:

  1. shouldComponentUpdate: 當元件的 state 或 props 發生改變時,都會首先觸發這個生命週期函式。它會接收兩個引數:nextProps, nextState——它們分別代表傳入的新 props 和新的 state 值。拿到這兩個值之後,我們就可以通過一些對比邏輯來決定是否有 re-render(重渲染)的必要了。如果該函式的返回值為 false,則生命週期終止,反之繼續;

注意:此方法僅作為效能優化的方式而存在。不要企圖依靠此方法來“阻止”渲染,因為這可能會產生 bug。應該考慮使用內建的 PureComponent 元件

,而不是手動編寫 shouldComponentUpdate()

  1. componentWillUpdate:當元件的 state 或 props 發生改變時,會在渲染之前呼叫 componentWillUpdate。componentWillUpdate 是 React16 廢棄的三個生命週期之一。過去,我們可能希望能在這個階段去收集一些必要的資訊(比如更新前的 DOM 資訊等等),現在我們完全可以在 React16 的 getSnapshotBeforeUpdate 中去做這些事;
  2. componentDidUpdate:componentDidUpdate() 會在UI更新後會被立即呼叫。它接收 prevProps(上一次的 props 值)作為入參,也就是說在此處我們仍然可以進行 props 值對比(再次說明 componentWillUpdate 確實雞肋哈)。

props 更新流程: 相對於 state 更新,props 更新後唯一的區別是增加了對 componentWillReceiveProps 的呼叫。關於 componentWillReceiveProps,需要知道這些事情:

  • componentWillReceiveProps:它在Component接受到新的 props 時被觸發。componentWillReceiveProps 會接收一個名為 nextProps 的引數(對應新的 props 值)。該生命週期是 React16 廢棄掉的三個生命週期之一。在它被廢棄前,可以用它來比較 this.props 和 nextProps 來重新setState。在 React16 中,用一個類似的新生命週期 getDerivedStateFromProps 來代替它。

React如何獲取元件對應的DOM元素?

可以用ref來獲取某個子節點的例項,然後通過當前class元件例項的一些特定屬性來直接獲取子節點例項。ref有三種實現方法:

  • 字串格式:字串格式,這是React16版本之前用得最多的,例如:<p ref="info">span</p>

  • 函式格式:ref對應一個方法,該方法有一個引數,也就是對應的節點例項,例如:<p ref={ele => this.info = ele}></p>

  • createRef方法:React 16提供的一個API,使用React.createRef()來實現       

props 是什麼

  • react的核心思想是元件化,頁面被分成很多個獨立,可複用的元件

  • 而元件就是一個函式,可以接受一個引數作為輸入值,這個引數就是props,所以props就是從外部傳入元件內部的資料

  • 由於react的單向資料流模式,所以props是從父元件傳入子元件的資料

react代理原生事件為什麼?

通過冒泡實現,為了統一管理,對更多瀏覽器有相容效果

合成事件原理

如果react事件繫結在了真實DOM節點上,一個節點同事有多個事件時,頁面的響應和記憶體的佔用會受到很大的影響。因此SyntheticEvent作為中間層出現了。

事件沒有在目標物件上繫結,而是在document上監聽所支援的所有事件,當事件發生並冒泡至document時,react將事件內容封裝並叫由真正的處理函式執行。

版權宣告:本文為CSDN博主「jiuwanli666」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。

為什麼 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 實現一個全域性的 dialog

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

子類:

//子類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:

.dialog-con{
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.3);
}

參考 前端進階面試題詳細解答

react-router4的核心

  • 路由變成了元件
  • 分散到各個頁面,不需要配置 比如<link> <route></route>

什麼是虛擬DOM?

虛擬 DOM (VDOM)是真實 DOM 在記憶體中的表示。UI 的表示形式儲存在記憶體中,並與實際的 DOM 同步。這是一個發生在渲染函式被呼叫和元素在螢幕上顯示之間的步驟,整個過程被稱為調和。

React.forwardRef有什麼用

forwardRef

  • 使用forwardRefforward在這裡是「傳遞」的意思)後,就能跨元件傳遞ref
  • 在例子中,我們將inputRefForm跨元件傳遞到MyInput中,並與input產生關聯
const MyInput = forwardRef((props, ref) => {
  return <input {...props} ref={ref} />;
});

function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <MyInput ref={inputRef} />
      <button onClick={handleClick}>
        Focus the input
      </button>
    </>
  );
}

useImperativeHandle

除了「限制跨元件傳遞ref」外,還有一種「防止ref失控的措施」,那就是useImperativeHandle,他的邏輯是這樣的:既然「ref失控」是由於「使用了不該被使用的DOM方法」(比如appendChild),那我可以限制「ref中只存在可以被使用的方法」。用useImperativeHandle修改我們的MyInput元件:

const MyInput = forwardRef((props, ref) => {
  const realInputRef = useRef(null);
  useImperativeHandle(ref, () => ({
    focus() {
      realInputRef.current.focus();
    },
  }));
  return <input {...props} ref={realInputRef} />;
});

現在,Form元件中通過inputRef.current只能取到如下資料結構:

{
  focus() {
    realInputRef.current.focus();
  },
}

就杜絕了「開發者通過ref取到DOM後,執行不該被使用的API,出現ref失控」的情況

  • 為了防止錯用/濫用導致ref失控,React限制「預設情況下,不能跨元件傳遞ref」
  • 為了破除這種限制,可以使用forwardRef
  • 為了減少refDOM的濫用,可以使用useImperativeHandle限制ref傳遞的資料結構。

react 父子傳值

父傳子——在呼叫子元件上繫結,子元件中獲取this.props
子傳父——引用子元件的時候傳過去一個方法,子元件通過this.props.methed()傳過去引數

connection

React.Children.map和js的map有什麼區別?

JavaScript中的map不會對為null或者undefined的資料進行處理,而React.Children.map中的map可以處理React.Children為null或者undefined的情況。

Redux內部原理 內部怎麼實現dispstch一個函式的

redux-thunk中介軟體作為例子,下面就是thunkMiddleware函式的程式碼

// 部分轉為ES5程式碼,執行middleware函式會返回一個新的函式,如下:
return ({ dispatch, getState }) => {
    // next實際就是傳入的dispatch
    return function (next) {
        return function (action) {
            // redux-thunk核心
            if (typeof action === 'function') { 
                return action(dispatch, getState, extraArgument);
            }
            return next(action);
        };
    };
}

redux-thunk庫內部原始碼非常的簡單,允許action是一個函式,同時支援引數傳遞,否則呼叫方法不變

  • redux建立Store:通過combineReducers函式合併reducer函式,返回一個新的函式combination(這個函式負責迴圈遍歷執行reducer函式,返回全部state)。將這個新函式作為引數傳入createStore函式,函式內部通過dispatch,初始化執行傳入的combination,state生成,返回store物件
  • redux中介軟體:applyMiddleware函式中介軟體的主要目的就是修改dispatch函式,返回經過中介軟體處理的新的dispatch函式
  • redux使用:實際就是再次呼叫迴圈遍歷呼叫reducer函式,更新state

為什麼 React 元素有一個 $$typeof 屬性

目的是為了防止 XSS 攻擊。因為 Synbol 無法被序列化,所以 React 可以通過有沒有 $$typeof 屬性來斷出當前的 element 物件是從資料庫來的還是自己生成的。

  • 如果沒有 $$typeof 這個屬性,react 會拒絕處理該元素。
  • 在 React 的古老版本中,下面的寫法會出現 XSS 攻擊:
// 服務端允許使用者儲存 JSON
let expectedTextButGotJSON = {
  type: 'div',
  props: {
    dangerouslySetInnerHTML: {
      __html: '/* 把你想的擱著 */'
    },
  },
  // ...
};
let message = { text: expectedTextButGotJSON };

// React 0.13 中有風險
<p>
  {message.text}
</p>

React 元件中怎麼做事件代理?它的原理是什麼?

React基於Virtual DOM實現了一個SyntheticEvent層(合成事件層),定義的事件處理器會接收到一個合成事件物件的例項,它符合W3C標準,且與原生的瀏覽器事件擁有同樣的介面,支援冒泡機制,所有的事件都自動繫結在最外層上。

在React底層,主要對合成事件做了兩件事:

  • 事件委派: React會把所有的事件繫結到結構的最外層,使用統一的事件監聽器,這個事件監聽器上維持了一個對映來儲存所有元件內部事件監聽和處理函式。
  • 自動繫結: React元件中,每個方法的上下文都會指向該元件的例項,即自動繫結this為當前元件。

描述 Flux 與 MVC?

傳統的 MVC 模式在分離資料(Model)、UI(View和邏輯(Controller)方面工作得很好,但是 MVC 架構經常遇到兩個主要問題:
資料流不夠清晰:跨檢視發生的級聯更新常常會導致混亂的事件網路,難於除錯。
缺乏資料完整性:模型資料可以在任何地方發生突變,從而在整個UI中產生不可預測的結果。
使用 Flux 模式的複雜使用者介面不再遭受級聯更新,任何給定的React 元件都能夠根據 store 提供的資料重建其狀態。Flux 模式還通過限制對共享資料的直接訪問來加強資料完整性。

父子元件的通訊方式?

父元件向子元件通訊:父元件通過 props 向子元件傳遞需要的資訊。

// 子元件: Child
const Child = props =>{
  return <p>{props.name}</p>
}
// 父元件 Parent
const Parent = ()=>{
    return <Child name="react"></Child>
}

子元件向父元件通訊:: props+回撥的方式。

// 子元件: Child
const Child = props =>{
  const cb = msg =>{
      return ()=>{
          props.callback(msg)
      }
  }
  return (
      <button onClick={cb("你好!")}>你好</button>
  )
}
// 父元件 Parent
class Parent extends Component {
    callback(msg){
        console.log(msg)
    }
    render(){
        return <Child callback={this.callback.bind(this)}></Child>    
    }
}

對於store的理解

Store 就是把它們聯絡到一起的物件。Store 有以下職責:

  • 維持應用的 state;
  • 提供 getState() 方法獲取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通過 subscribe(listener)註冊監聽器;
  • 通過 subscribe(listener)返回的函式登出監聽器

講講什麼是 JSX ?

Facebook 第一次釋出 React 時,他們還引入了一種新的 JS 方言 JSX,將原始 HTML 模板嵌入到 JS 程式碼中。JSX 程式碼本身不能被瀏覽器讀取,必須使用Babelwebpack等工具將其轉換為傳統的JS。很多開發人員就能無意識使用 JSX,因為它已經與 React 結合在一直了。

class MyComponent extends React.Component {
  render() {
    let props = this.props;
    return (
      <div className="my-component">
        <a href={props.url}>{props.name}</a>
      </div>
    );
  }
}

在React中如何避免不必要的render?

React 基於虛擬 DOM 和高效 Diff 演算法的完美配合,實現了對 DOM 最小粒度的更新。大多數情況下,React 對 DOM 的渲染效率足以業務日常。但在個別複雜業務場景下,效能問題依然會困擾我們。此時需要採取一些措施來提升執行效能,其很重要的一個方向,就是避免不必要的渲染(Render)。這裡提下優化的點:

  • shouldComponentUpdate 和 PureComponent

在 React 類元件中,可以利用 shouldComponentUpdate或者 PureComponent 來減少因父元件更新而觸發子元件的 render,從而達到目的。shouldComponentUpdate 來決定是否元件是否重新渲染,如果不希望元件重新渲染,返回 false 即可。

  • 利用高階元件

在函式元件中,並沒有 shouldComponentUpdate 這個生命週期,可以利用高階元件,封裝一個類似 PureComponet 的功能

  • 使用 React.memo

React.memo 是 React 16.6 新的一個 API,用來快取元件的渲染,避免不必要的更新,其實也是一個高階元件,與 PureComponent 十分類似,但不同的是, React.memo只能用於函式元件。

對React SSR的理解

服務端渲染是資料與模版組成的html,即 HTML = 資料 + 模版。將元件或頁面通過伺服器生成html字串,再發送到瀏覽器,最後將靜態標記"混合"為客戶端上完全互動的應用程式。頁面沒使用服務渲染,當請求頁面時,返回的body裡為空,之後執行js將html結構注入到body裡,結合css顯示出來;

SSR的優勢:

  • 對SEO友好
  • 所有的模版、圖片等資源都存在伺服器端
  • 一個html返回所有資料
  • 減少HTTP請求
  • 響應快、使用者體驗好、首屏渲染快

1)更利於SEO

不同爬蟲工作原理類似,只會爬取原始碼,不會執行網站的任何指令碼使用了React或者其它MVVM框架之後,頁面大多數DOM元素都是在客戶端根據js動態生成,可供爬蟲抓取分析的內容大大減少。另外,瀏覽器爬蟲不會等待我們的資料完成之後再去抓取頁面資料。服務端渲染返回給客戶端的是已經獲取了非同步資料並執行JavaScript指令碼的最終HTML,網路爬中就可以抓取到完整頁面的資訊。

2)更利於首屏渲染

首屏的渲染是node傳送過來的html字串,並不依賴於js檔案了,這就會使使用者更快的看到頁面的內容。尤其是針對大型單頁應用,打包後文件體積比較大,普通客戶端渲染載入所有所需檔案時間較長,首頁就會有一個很長的白屏等待時間。

SSR的侷限:

1)服務端壓力較大

本來是通過客戶端完成渲染,現在統一到服務端node服務去做。尤其是高併發訪問的情況,會大量佔用服務端CPU資源;

2)開發條件受限

在服務端渲染中,只會執行到componentDidMount之前的生命週期鉤子,因此專案引用的第三方的庫也不可用其它生命週期鉤子,這對引用庫的選擇產生了很大的限制;

3)學習成本相對較高 除了對webpack、MVVM框架要熟悉,還需要掌握node、 Koa2等相關技術。相對於客戶端渲染,專案構建、部署過程更加複雜。

時間耗時比較:

1)資料請求

由服務端請求首屏資料,而不是客戶端請求首屏資料,這是"快"的一個主要原因。服務端在內網進行請求,資料響應速度快。客戶端在不同網路環境進行資料請求,且外網http請求開銷大,導致時間差

  • 客戶端資料請求

  • 服務端資料請求

    2)html渲染 服務端渲染是先向後端伺服器請求資料,然後生成完整首屏 html返回給瀏覽器;而客戶端渲染是等js程式碼下載、載入、解析完成後再請求資料渲染,等待的過程頁面是什麼都沒有的,就是使用者看到的白屏。就是服務端渲染不需要等待js程式碼下載完成並請求資料,就可以返回一個已有完整資料的首屏頁面。

  • 非ssr html渲染

  • ssr html渲染