关于react.js:关于前端面试你需要知道的知识点

如何在 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,
    };

React中props.children和React.Children的区别

在React中,当波及组件嵌套,在父组件中应用props.children把所有子组件显示进去。如下:

function ParentComponent(props){
    return (
        <div>
            {props.children}        </div>
    )
}

如果想把父组件中的属性传给所有的子组件,须要应用React.Children办法。

比方,把几个Radio组合起来,合成一个RadioGroup,这就要求所有的Radio具备同样的name属性值。能够这样:把Radio看做子组件,RadioGroup看做父组件,name的属性值在RadioGroup这个父组件中设置。

首先是子组件:

//子组件
function RadioOption(props) {
  return (
    <label>
      <input type="radio" value={props.value} name={props.name} />
      {props.label}    </label>
  )
}

而后是父组件,不仅须要把它所有的子组件显示进去,还须要为每个子组件赋上name属性和值:

//父组件用,props是指父组件的props
function renderChildren(props) {

  //遍历所有子组件
  return React.Children.map(props.children, child => {
    if (child.type === RadioOption)
      return React.cloneElement(child, {
        //把父组件的props.name赋值给每个子组件
        name: props.name
      })
    else
      return child
  })
}
//父组件
function RadioGroup(props) {
  return (
    <div>
      {renderChildren(props)}    </div>
  )
}
function App() {
  return (
    <RadioGroup name="hello">
      <RadioOption label="选项一" value="1" />
      <RadioOption label="选项二" value="2" />
      <RadioOption label="选项三" value="3" />
    </RadioGroup>
  )
}
export default App;

以上,React.Children.map让咱们对父组件的所有子组件又更灵便的管制。

constructor 为什么不先渲染?

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

所以:constructor钩子函数并不是不可短少的,子组件能够在一些状况略去。比方不本人的state,从props中获取的状况

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

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

React-intl提供了两种应用办法,一种是援用React组件,另一种是间接调取API,官网更加举荐在React我的项目中应用前者,只有在无奈应用React组件的中央,才应该调用框架提供的API。它提供了一系列的React组件,包含数字格式化、字符串格式化、日期格式化等。

在React-intl中,能够配置不同的语言包,他的工作原理就是依据须要,在语言包之间进行切换。

用户不同权限 能够查看不同的页面 如何实现?

  1. Js形式
    依据用户权限类型,把菜单配置成json, 没有权限的间接不显示
  2. react-router 形式 在route 标签上 增加onEnter事件,进入路由之前替换到首页
<Route path="/home" component={App} onEnter={(nexState,replace)=>{
      if(nexState.location.pathname!=='/'){
         var  sid = UtilsMoudle.getSidFromUrl(nexState);
         if(!sid){
            replace("/")
         }else{
            console.log(sid);
         }
      }
    }}>
  1. 本人封装一个privateRouter组件 外面判断是否有权限,有的话返回
    <Route path={path} component={component} exact={exact}/>
    没有权限的话component 返回一个提示信息的组件。
  2. 扩大一下,如果是依据用权限来判断是否暗藏组件该怎么做呢?
    react 能够应用高阶组件,在高阶组件外面判断是否有权限,而后判断是否返回组件,无权限返回null
    vue 能够应用自定义指令,如果没有权限移除组件
// 须要在入口处增加自定义权限指令v-auth,显示可操作组件
Vue.directive('auth', {
    bind: function (el, binding, vnode) {
        // 用户权限表
        const rules = auths
        for (let i = 0; i < rules.length; i++) {
            const item = rules[i]
            if(!binding.value || (binding.value == item.auth)){
                // 权限容许则显示组件
                return true
            }
        }
        // 移除组件
        el.parentNode.removeChild(el)
    }
})
// 应用
<template>
  <div>
    <Button v-auth="admin_user_add">增加用户</Button>
    <Button v-auth="admin_user_del">删除用户</Button>
    <Button v-auth="admin_user_edit">编辑用户</Button>
  </div>
</template>

constructor

答案是:在 constructor 函数外面,须要用到props的值的时候,就须要调用 super(props)
  1. class语法糖默认会帮你定义一个constructor,所以当你不须要应用constructor的时候,是能够不必本人定义的
  2. 当你本人定义一个constructor的时候,就肯定要写super(),否则拿不到this
  3. 当你在constructor外面想要应用props的值,就须要传入props这个参数给super,调用super(props),否则只须要写super()

为什么列表循环渲染的key最好不要用index

举例说明

变动前数组的值是[1,2,3,4],key就是对应的下标:0,1,2,3
变动后数组的值是[4,3,2,1],key对应的下标也是:0,1,2,3
  • 那么diff算法在变动前的数组找到key =0的值是1,在变动后数组里找到的key=0的值是4
  • 因为子元素不一样就从新删除并更新
  • 然而如果加了惟一的key,如下
变动前数组的值是[1,2,3,4],key就是对应的下标:id0,id1,id2,id3
变动后数组的值是[4,3,2,1],key对应的下标也是:id3,id2,id1,id0
  • 那么diff算法在变动前的数组找到key =id0的值是1,在变动后数组里找到的key=id0的值也是1
  • 因为子元素雷同,就不删除并更新,只做挪动操作,这就晋升了性能

参考:前端react面试题具体解答

React的严格模式如何应用,有什么用途?

StrictMode 是一个用来突出显示应用程序中潜在问题的工具。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。它为其后辈元素触发额定的检查和正告。
能够为应用程序的任何局部启用严格模式。例如:

import React from 'react';
function ExampleApplication() {
  return (
    <div>
      <Header />
      <React.StrictMode>        
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>      
      <Footer />
    </div>
  );
}

在上述的示例中,不会对 HeaderFooter 组件运行严格模式查看。然而,ComponentOneComponentTwo 以及它们的所有后辈元素都将进行查看。

StrictMode 目前有助于:

  • 辨认不平安的生命周期
  • 对于应用过期字符串 ref API 的正告
  • 对于应用废除的 findDOMNode 办法的正告
  • 检测意外的副作用
  • 检测过期的 context API

React Hook 的应用限度有哪些?

React Hooks 的限度次要有两条:

  • 不要在循环、条件或嵌套函数中调用 Hook;
  • 在 React 的函数组件中调用 Hook。

那为什么会有这样的限度呢?Hooks 的设计初衷是为了改良 React 组件的开发模式。在旧有的开发模式下遇到了三个问题。

  • 组件之间难以复用状态逻辑。过来常见的解决方案是高阶组件、render props 及状态治理框架。
  • 简单的组件变得难以了解。生命周期函数与业务逻辑耦合太深,导致关联局部难以拆分。
  • 人和机器都很容易混同类。常见的有 this 的问题,但在 React 团队中还有类难以优化的问题,心愿在编译优化层面做出一些改良。

这三个问题在肯定水平上妨碍了 React 的后续倒退,所以为了解决这三个问题,Hooks 基于函数组件开始设计。然而第三个问题决定了 Hooks 只反对函数组件。

那为什么不要在循环、条件或嵌套函数中调用 Hook 呢?因为 Hooks 的设计是基于数组实现。在调用时按程序退出数组中,如果应用循环、条件或嵌套函数很有可能导致数组取值错位,执行谬误的 Hook。当然,本质上 React 的源码里不是数组,是链表。

这些限度会在编码上造成肯定水平的心智累赘,老手可能会写错,为了防止这样的状况,能够引入 ESLint 的 Hooks 查看插件进行预防。

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

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

  • 能够应用自定义事件通信(公布订阅模式)
  • 能够通过redux等进行全局状态治理
  • 如果是兄弟组件通信,能够找到这两个兄弟节点独特的父节点, 联合父子间通信形式进行通信。

Redux 原理及工作流程

(1)原理 Redux源码次要分为以下几个模块文件

  • compose.js 提供从右到左进行函数式编程
  • createStore.js 提供作为生成惟一store的函数
  • combineReducers.js 提供合并多个reducer的函数,保障store的唯一性
  • bindActionCreators.js 能够让开发者在不间接接触dispacth的前提下进行更改state的操作
  • applyMiddleware.js 这个办法通过中间件来加强dispatch的性能
const actionTypes = {
    ADD: 'ADD',
    CHANGEINFO: 'CHANGEINFO',
}

const initState = {
    info: '初始化',
}

export default function initReducer(state=initState, action) {
    switch(action.type) {
        case actionTypes.CHANGEINFO:
            return {
                ...state,
                info: action.preload.info || '',
            }
        default:
            return { ...state };
    }
}

export default function createStore(reducer, initialState, middleFunc) {

    if (initialState && typeof initialState === 'function') {
        middleFunc = initialState;
        initialState = undefined;
    }

    let currentState = initialState;

    const listeners = [];

    if (middleFunc && typeof middleFunc === 'function') {
        // 封装dispatch 
        return middleFunc(createStore)(reducer, initialState);
    }

    const getState = () => {
        return currentState;
    }

    const dispatch = (action) => {
        currentState = reducer(currentState, action);

        listeners.forEach(listener => {
            listener();
        })
    }

    const subscribe = (listener) => {
        listeners.push(listener);
    }

    return {
        getState,
        dispatch,
        subscribe
    }
}

(2)工作流程

  • const store= createStore(fn)生成数据;
  • action: {type: Symble(‘action01), payload:’payload’ }定义行为;
  • dispatch发动action:store.dispatch(doSomething(‘action001’));
  • reducer:解决action,返回新的state;

艰深点解释:

  • 首先,用户(通过View)收回Action,收回形式就用到了dispatch办法
  • 而后,Store主动调用Reducer,并且传入两个参数:以后State和收到的Action,Reducer会返回新的State
  • State—旦有变动,Store就会调用监听函数,来更新View

以 store 为外围,能够把它看成数据存储核心,然而他要更改数据的时候不能间接批改,数据批改更新的角色由Reducers来负责,store只做存储,中间人,当Reducers的更新实现当前会通过store的订阅来告诉react component,组件把新的状态从新获取渲染,组件中也能被动发送action,创立action后这个动作是不会执行的,所以要dispatch这个action,让store通过reducers去做更新React Component 就是react的每个组件。

React Hooks 和生命周期的关系?

函数组件 的实质是函数,没有 state 的概念的,因而不存在生命周期一说,仅仅是一个 render 函数而已。
然而引入 Hooks 之后就变得不同了,它能让组件在不应用 class 的状况下领有 state,所以就有了生命周期的概念,所谓的生命周期其实就是 useStateuseEffect()useLayoutEffect()

即:Hooks 组件(应用了Hooks的函数组件)有生命周期,而函数组件(未应用Hooks的函数组件)是没有生命周期的

上面是具体的 class 与 Hooks 的生命周期对应关系

  • constructor:函数组件不须要构造函数,能够通过调用 **useState 来初始化 state**。如果计算的代价比拟低廉,也能够传一个函数给 useState
const [num, UpdateNum] = useState(0)
  • getDerivedStateFromProps:个别状况下,咱们不须要应用它,能够在渲染过程中更新 state,以达到实现 getDerivedStateFromProps 的目标。
function ScrollView({row}) {
  let [isScrollingDown, setIsScrollingDown] = useState(false);
  let [prevRow, setPrevRow] = useState(null);
  if (row !== prevRow) {
    // Row 自上次渲染以来产生过扭转。更新 isScrollingDown。
    setIsScrollingDown(prevRow !== null && row > prevRow);
    setPrevRow(row);
  }
  return `Scrolling down: ${isScrollingDown}`;
}

React 会立刻退出第一次渲染并用更新后的 state 从新运行组件以防止消耗太多性能。

  • shouldComponentUpdate:能够用 **React.memo** 包裹一个组件来对它的 props 进行浅比拟
const Button = React.memo((props) => {  // 具体的组件});

留神:**React.memo 等效于 **`PureComponent,它只浅比拟 props。这里也能够应用 useMemo` 优化每一个节点。

  • render:这是函数组件体自身。
  • componentDidMount, componentDidUpdateuseLayoutEffect 与它们两的调用阶段是一样的。然而,咱们举荐你一开始先用 useEffect,只有当它出问题的时候再尝试应用 useLayoutEffectuseEffect 能够表白所有这些的组合。
// componentDidMount
useEffect(()=>{
  // 须要在 componentDidMount 执行的内容
}, [])
useEffect(() => { 
  // 在 componentDidMount,以及 count 更改时 componentDidUpdate 执行的内容
  document.title = `You clicked ${count} times`; 
  return () => {
    // 须要在 count 更改时 componentDidUpdate(先于 document.title = ... 执行,恪守先清理后更新)
    // 以及 componentWillUnmount 执行的内容       
  } // 当函数中 Cleanup 函数会依照在代码中定义的程序先后执行,与函数自身的个性无关
}, [count]); // 仅在 count 更改时更新

请记得 React 会期待浏览器实现画面渲染之后才会提早调用 ,因而会使得额定操作很不便

  • componentWillUnmount:相当于 useEffect 外面返回的 cleanup 函数
// componentDidMount/componentWillUnmount
useEffect(()=>{
  // 须要在 componentDidMount 执行的内容
  return function cleanup() {
    // 须要在 componentWillUnmount 执行的内容      
  }
}, [])
  • componentDidCatch and getDerivedStateFromError:目前还没有这些办法的 Hook 等价写法,但很快会加上。
class 组件 Hooks 组件
constructor useState
getDerivedStateFromProps useState 外面 update 函数
shouldComponentUpdate useMemo
render 函数自身
componentDidMount useEffect
componentDidUpdate useEffect
componentWillUnmount useEffect 外面返回的函数
componentDidCatch
getDerivedStateFromError

React中的setState和replaceState的区别是什么?

(1)setState() setState()用于设置状态对象,其语法如下:

setState(object nextState[, function callback])
  • nextState,将要设置的新状态,该状态会和以后的state合并
  • callback,可选参数,回调函数。该函数会在setState设置胜利,且组件从新渲染后调用。

合并nextState和以后state,并从新渲染组件。setState是React事件处理函数中和申请回调函数中触发UI更新的次要办法。

(2)replaceState() replaceState()办法与setState()相似,然而办法只会保留nextState中状态,原state不在nextState中的状态都会被删除。其语法如下:

replaceState(object nextState[, function callback])
  • nextState,将要设置的新状态,该状态会替换以后的state。
  • callback,可选参数,回调函数。该函数会在replaceState设置胜利,且组件从新渲染后调用。

总结: setState 是批改其中的局部状态,相当于 Object.assign,只是笼罩,不会缩小原来的状态。而replaceState 是齐全替换原来的状态,相当于赋值,将原来的 state 替换为另一个对象,如果新状态属性缩小,那么 state 中就没有这个状态了。

在React中组件的props扭转时更新组件的有哪些办法?

在一个组件传入的props更新时从新渲染该组件罕用的办法是在componentWillReceiveProps中将新的props更新到组件的state中(这种state被成为派生状态(Derived State)),从而实现从新渲染。React 16.3中还引入了一个新的钩子函数getDerivedStateFromProps来专门实现这一需要。

(1)componentWillReceiveProps(已废除)

在react的componentWillReceiveProps(nextProps)生命周期中,能够在子组件的render函数执行前,通过this.props获取旧的属性,通过nextProps获取新的props,比照两次props是否雷同,从而更新子组件本人的state。

这样的益处是,能够将数据申请放在这里进行执行,须要传的参数则从componentWillReceiveProps(nextProps)中获取。而不用将所有的申请都放在父组件中。于是该申请只会在该组件渲染时才会收回,从而加重申请累赘。

(2)getDerivedStateFromProps(16.3引入)

这个生命周期函数是为了代替componentWillReceiveProps存在的,所以在须要应用componentWillReceiveProps时,就能够思考应用getDerivedStateFromProps来进行代替。

两者的参数是不雷同的,而getDerivedStateFromProps是一个动态函数,也就是这个函数不能通过this拜访到class的属性,也并不举荐间接拜访属性。而是应该通过参数提供的nextProps以及prevState来进行判断,依据新传入的props来映射到state。

须要留神的是,如果props传入的内容不须要影响到你的state,那么就须要返回一个null,这个返回值是必须的,所以尽量将其写到函数的开端:

static getDerivedStateFromProps(nextProps, prevState) {
    const {type} = nextProps;
    // 当传入的type发生变化的时候,更新state
    if (type !== prevState.type) {
        return {
            type,
        };
    }
    // 否则,对于state不进行任何操作
    return null;
}

什么是高阶组件

高阶组件不是组件,是 加强函数,能够输出一个元组件,返回出一个新的加强组件

  • 属性代理 (Props Proxy) 在我看来属性代理就是提取公共的数据和办法到父组件,子组件只负责渲染数据,相当于设计模式里的模板模式,这样组件的重用性就更高了
function proxyHoc(WrappedComponent) {
    return class extends React.Component {
        render() {
            const newProps = {
                count: 1
            }
            return <WrappedComponent {...this.props} {...newProps} />
        }
    }
}
  • 反向继承
const MyContainer = (WrappedComponent)=>{
    return class extends WrappedComponent {
        render(){
            return super.render();
        }
    }
}

当渲染一个列表时,何为 key?设置 key 的目标是什么

Keys 会有助于 React 辨认哪些 items 扭转了,被增加了或者被移除了。Keys 应该被赋予数组内的元素以赋予(DOM)元素一个稳固的标识,抉择一个 key 的最佳办法是应用一个字符串,该字符串能惟一地标识一个列表项。很多时候你会应用数据中的 IDs 作为 keys,当你没有稳固的 IDs 用于被渲染的 items 时,能够应用我的项目索引作为渲染项的 key,但这种形式并不举荐,如果 items 能够从新排序,就会导致 re-render 变慢。

setState 是同步异步?为什么?实现原理?

1. setState是同步执行的

setState是同步执行的,然而state并不一定会同步更新

2. setState在React生命周期和合成事件中批量笼罩执行

在React的生命周期钩子和合成事件中,屡次执行setState,会批量执行

具体表现为,屡次同步执行的setState,会进行合并,相似于Object.assign,雷同的key,前面的会笼罩后面的

当遇到多个setState调用时候,会提取单次传递setState的对象,把他们合并在一起造成一个新的
繁多对象,并用这个繁多的对象去做setState的事件,就像Object.assign的对象合并,后一个
key值会笼罩后面的key值

通过React 解决的事件是不会同步更新 this.state的. 通过 addEventListener || setTimeout/setInterval 的形式解决的则会同步更新。
为了合并setState,咱们须要一个队列来保留每次setState的数据,而后在一段时间后执行合并操作和更新state,并清空这个队列,而后渲染组件。

Redux 申请中间件如何解决并发

应用redux-Saga redux-saga是一个治理redux利用异步操作的中间件,用于代替 redux-thunk 的。它通过创立 Sagas 将所有异步操作逻辑寄存在一个中央进行集中处理,以此将react中的同步操作与异步操作辨别开来,以便于前期的治理与保护。 redux-saga如何解决并发:

  • takeEvery

能够让多个 saga 工作并行被 fork 执行。

import {
    fork,
    take
} from "redux-saga/effects"

const takeEvery = (pattern, saga, ...args) => fork(function*() {
    while (true) {
        const action = yield take(pattern)
        yield fork(saga, ...args.concat(action))
    }
})
  • takeLatest

takeLatest 不容许多个 saga 工作并行地执行。一旦接管到新的发动的 action,它就会勾销后面所有 fork 过的工作(如果这些工作还在执行的话)。
在解决 AJAX 申请的时候,如果只心愿获取最初那个申请的响应, takeLatest 就会十分有用。

import {
    cancel,
    fork,
    take
} from "redux-saga/effects"

const takeLatest = (pattern, saga, ...args) => fork(function*() {
    let lastTask
    while (true) {
        const action = yield take(pattern)
        if (lastTask) {
            yield cancel(lastTask) // 如果工作曾经完结,则 cancel 为空操作
        }
        lastTask = yield fork(saga, ...args.concat(action))
    }
})

hooks父子传值

父传子
在父组件中用useState申明数据
 const [ data, setData ] = useState(false)

把数据传递给子组件
<Child data={data} />

子组件接管
export default function (props) {
    const { data } = props
    console.log(data)
}
子传父
子传父能够通过事件办法传值,和父传子有点相似。
在父组件中用useState申明数据
 const [ data, setData ] = useState(false)

把更新数据的函数传递给子组件
<Child setData={setData} />

子组件中触发函数更新数据,就会间接传递给父组件
export default function (props) {
    const { setData } = props
    setData(true)
}
如果存在多个层级的数据传递,也可按照此办法顺次传递

// 多层级用useContext
const User = () => {
 // 间接获取,不必回调
 const { user, setUser } = useContext(UserContext);
 return <Avatar user={user} setUser={setUser} />;
};

在React中遍历的办法有哪些?

(1)遍历数组:map && forEach

import React from 'react';

class App extends React.Component {
  render() {
    let arr = ['a', 'b', 'c', 'd'];
    return (
      <ul>
        {
          arr.map((item, index) => {
            return <li key={index}>{item}</li>
          })
        }
      </ul>
    )
  }
}

class App extends React.Component {
  render() {
    let arr = ['a', 'b', 'c', 'd'];
    return (
      <ul>
        {
          arr.forEach((item, index) => {
            return <li key={index}>{item}</li>
          })
        }
      </ul>
    )
  }
}

(2)遍历对象:map && for in

class App extends React.Component {
  render() {
    let obj = {
      a: 1,
      b: 2,
      c: 3
    }
    return (
      <ul>
        {
          (() => {
            let domArr = [];
            for(const key in obj) {
              if(obj.hasOwnProperty(key)) {
                const value = obj[key]
                domArr.push(<li key={key}>{value}</li>)
              }
            }
            return domArr;
          })()
        }
      </ul>
    )
  }
}

// Object.entries() 把对象转换成数组
class App extends React.Component {
  render() {
    let obj = {
      a: 1,
      b: 2,
      c: 3
    }
    return (
      <ul>
        {
          Object.entries(obj).map(([key, value], index) => {   // item是一个数组,把item解构,写法是[key, value]
            return <li key={key}>{value}</li>
          }) 
        }
      </ul>
    )
  }
}

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理