关于react.js:字节前端面试被问到的react问题

4次阅读

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

redux 中间件

中间件提供第三方插件的模式,自定义拦挡 action -> reducer 的过程。变为 action -> middlewares -> reducer。这种机制能够让咱们扭转数据流,实现如异步actionaction 过滤,日志输入,异样报告等性能

  • redux-logger:提供日志输入
  • redux-thunk:解决异步操作
  • redux-promise:解决异步操作,actionCreator的返回值是promise

React 中 refs 的作用是什么?有哪些利用场景?

Refs 提供了一种形式,用于拜访在 render 办法中创立的 React 元素或 DOM 节点。Refs 应该审慎应用,如下场景应用 Refs 比拟适宜:

  • 解决焦点、文本抉择或者媒体的管制
  • 触发必要的动画
  • 集成第三方 DOM 库

Refs 是应用 React.createRef() 办法创立的,他通过 ref 属性附加到 React 元素上。要在整个组件中应用 Refs,须要将 ref 在构造函数中调配给其实例属性:

class MyComponent extends React.Component {constructor(props) {super(props)
    this.myRef = React.createRef()}
  render() {return <div ref={this.myRef} />
  }
}

因为函数组件没有实例,因而不能在函数组件上间接应用 ref

function MyFunctionalComponent() {return <input />;}
class Parent extends React.Component {constructor(props) {super(props);
    this.textInput = React.createRef();}
  render() {
    // 这将不会工作!return (<MyFunctionalComponent ref={this.textInput} />
    );
  }
}

但能够通过闭合的帮忙在函数组件外部进行应用 Refs:

function CustomTextInput(props) {
  // 这里必须申明 textInput,这样 ref 回调才能够援用它
  let textInput = null;
  function handleClick() {textInput.focus();
  }
  return (
    <div>
      <input
        type="text"
        ref={(input) => {textInput = input;}} />      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );  
}

留神:

  • 不应该适度的应用 Refs
  • ref 的返回值取决于节点的类型:

    • ref 属性被用于一个一般的 HTML 元素时,React.createRef() 将接管底层 DOM 元素作为他的 current 属性以创立 ref
    • ref 属性被用于一个自定义的类组件时,ref 对象将接管该组件已挂载的实例作为他的 current
  • 当在父组件中须要拜访子组件中的 ref 时可应用传递 Refs 或回调 Refs。

diff 算法如何比拟?

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

如何解决 props 层级过深的问题

  • 应用 Context API:提供一种组件之间的状态共享,而不用通过显式组件树逐层传递 props;
  • 应用 Redux 等状态库。

React 的事件和一般的 HTML 事件有什么不同?

区别:

  • 对于事件名称命名形式,原生事件为全小写,react 事件采纳小驼峰;
  • 对于事件函数解决语法,原生事件为字符串,react 事件为函数;
  • react 事件不能采纳 return false 的形式来阻止浏览器的默认行为,而必须要地明确地调用 preventDefault() 来阻止默认行为。

合成事件是 react 模仿原生 DOM 事件所有能力的一个事件对象,其长处如下:

  • 兼容所有浏览器,更好的跨平台;
  • 将事件对立寄存在一个数组,防止频繁的新增与删除(垃圾回收)。
  • 不便 react 对立治理和事务机制。

事件的执行程序为原生事件先执行,合成事件后执行,合成事件会冒泡绑定到 document 上,所以尽量避免原生事件与合成事件混用,如果原生事件阻止冒泡,可能会导致合成事件不执行,因为须要冒泡到 document 上合成事件才会执行。

react-router 里的 Link 标签和 a 标签的区别

从最终渲染的 DOM 来看,这两者都是链接,都是 标签,区别是∶ <Link>是 react-router 里实现路由跳转的链接,个别配合 <Route> 应用,react-router 接管了其默认的链接跳转行为,区别于传统的页面跳转,<Link> 的“跳转”行为只会触发相匹配的<Route> 对应的页面内容更新,而不会刷新整个页面。

<Link>做了 3 件事件:

  • 有 onclick 那就执行 onclick
  • click 的时候阻止 a 标签默认事件
  • 依据跳转 href(即是 to),用 history (web 前端路由两种形式之一,history & hash)跳转,此时只是链接变了,并没有刷新页面而 <a> 标签就是一般的超链接了,用于从以后页面跳转到 href 指向的另一 个页面(非锚点状况)。

a 标签默认事件禁掉之后做了什么才实现了跳转?

let domArr = document.getElementsByTagName('a')
[...domArr].forEach(item=>{item.addEventListener('click',function () {location.href = this.href})
})

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 面试题具体解答

redux 有什么毛病

  • 一个组件所须要的数据,必须由父组件传过来,而不能像 flux 中间接从 store 取
  • 当一个组件相干数据更新时,即便父组件不须要用到这个组件,父组件还是会从新 render,可能会有效率影响,或者须要写简单的 shouldComponentUpdate 进行判断

react 有什么长处

  • 进步利用性能
  • 能够不便的在客户端和服务端应用
  • 应用 jsx 模板进行数据渲染,可读性好

createElement 过程

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

React.createElement(
  type,
  [props],
  [...children]
)
  • 第一个参数是必填,传入的是似 HTML 标签名称,eg: ul, li
  • 第二个参数是选填,示意的是属性,eg: className
  • 第三个参数是选填, 子节点,eg: 要显示的文本内容
// 写法一: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')
      );

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 Context?

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

Dva 工作原理

集成redux+redux-saga

工作原理

扭转产生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,当此类行为会扭转数据的时候能够通过 dispatch 发动一个 action,如果是同步行为会间接通过 Reducers 扭转 State,如果是异步行为(副作用)会先触发 Effects 而后流向 Reducers 最终扭转 State

mobox 和 redux 有什么区别?

(1)共同点

  • 为了解决状态管理混乱,无奈无效同步的问题对立保护治理利用状态;
  • 某一状态只有一个可信数据起源(通常命名为 store,指状态容器);
  • 操作更新状态形式对立,并且可控(通常以 action 形式提供更新状态的路径);
  • 反对将 store 与 React 组件连贯,如 react-redux,mobx- react;

(2)区别 Redux 更多的是遵循 Flux 模式的一种实现,是一个 JavaScript 库,它关注点次要是以下几方面∶

  • Action∶ 一个 JavaScript 对象,形容动作相干信息,次要蕴含 type 属性和 payload 属性∶

    o type∶ action 类型; o payload∶ 负载数据;
    
  • Reducer∶ 定义利用状态如何响应不同动作(action),如何更新状态;
  • Store∶ 治理 action 和 reducer 及其关系的对象,次要提供以下性能∶

    o 保护利用状态并反对拜访状态(getState());
    o 反对监听 action 的散发,更新状态(dispatch(action)); 
    o 反对订阅 store 的变更(subscribe(listener));
    
  • 异步流∶ 因为 Redux 所有对 store 状态的变更,都应该通过 action 触发,异步工作(通常都是业务或获取数据工作)也不例外,而为了不将业务或数据相干的工作混入 React 组件中,就须要应用其余框架配合治理异步工作流程,如 redux-thunk,redux-saga 等;

Mobx 是一个通明函数响应式编程的状态治理库,它使得状态治理简略可伸缩∶

  • Action∶定义扭转状态的动作函数,包含如何变更状态;
  • Store∶ 集中管理模块状态(State)和动作(action)
  • Derivation(衍生)∶ 从利用状态中派生而出,且没有任何其余影响的数据

比照总结:

  • redux 将数据保留在繁多的 store 中,mobx 将数据保留在扩散的多个 store 中
  • redux 应用 plain object 保留数据,须要手动解决变动后的操作;mobx 实用 observable 保留数据,数据变动后主动解决响应的操作
  • redux 应用不可变状态,这意味着状态是只读的,不能间接去批改它,而是应该返回一个新的状态,同时应用纯函数;mobx 中的状态是可变的,能够间接对其进行批改
  • mobx 相对来说比较简单,在其中有很多的形象,mobx 更多的应用面向对象的编程思维;redux 会比较复杂,因为其中的函数式编程思维把握起来不是那么容易,同时须要借助一系列的中间件来解决异步和副作用
  • mobx 中有更多的形象和封装,调试会比拟艰难,同时后果也难以预测; 而 redux 提供可能进行工夫回溯的开发工具,同时其纯函数以及更少的形象,让调试变得更加的容易

react-redux 的实现原理?

通过 redux 和 react context 配合应用,并借助高阶函数,实现了 react-redux

React 中 refs 干嘛用的?

Refs 提供了一种拜访在 render 办法中创立的 DOM 节点或者 React 元素的办法。在典型的数据流中,props 是父子组件交互的惟一形式,想要批改子组件,须要应用新的 pros 从新渲染它。凡事有例外,某些状况下咱们须要在典型数据流外,强制批改子代,这个时候能够应用 Refs
咱们能够在组件增加一个 ref 属性来应用,该属性的值是一个回调函数,接管作为其第一个参数的底层 DOM 元素或组件的挂载实例。

class UnControlledForm extends Component {handleSubmit = () => {console.log("Input Value:", this.input.value);
  };
  render() {
    return (<form onSubmit={this.handleSubmit}>
        <input type="text" ref={(input) => (this.input = input)} />
        <button type="submit">Submit</button>
      </form>
    );
  }
}

请留神,input 元素有一个 ref 属性,它的值是一个函数。该函数接管输出的理论 DOM 元素,而后将其放在实例上,这样就能够在 handleSubmit 函数外部拜访它。
常常被误会的只有在类组件中能力应用 refs,然而 refs 也能够通过利用 JS 中的闭包与函数组件一起应用。

function CustomForm({handleSubmit}) {
  let inputElement;
  return (<form onSubmit={() => handleSubmit(inputElement.value)}>
      <input type="text" ref={(input) => (inputElement = input)} />
      <button type="submit">Submit</button>
    </form>
  );
}

依据上面定义的代码,能够找出存在的两个问题吗?

请看上面的代码:

答案:
1. 在构造函数没有将 props 传递给 super,它应该包含以下行

constructor(props) {super(props);
// ...
}

2. 事件监听器 (通过addEventListener() 调配时)的作用域不正确,因为 ES6 不提供主动绑定。因而,开发人员能够在构造函数中重新分配 clickHandler 来蕴含正确的绑定:

constructor(props) {super(props);
this.clickHandler = this.clickHandler.bind(this);
// ...
}

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

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

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

为什么不间接更新 state 呢 ?

如果试图间接更新 state,则不会从新渲染组件。

// 谬误
This.state.message = 'Hello world';

须要应用 setState() 办法来更新 state。它调度对组件 state 对象的更新。当 state 扭转时,组件通过从新渲染来响应:

// 正确做法
This.setState({message:‘Hello World’});

react16 版本的 reconciliation 阶段和 commit 阶段是什么

  • reconciliation 阶段蕴含的次要工作是对 current tree 和 new tree 做 diff 计算,找出变动局部。进行遍历、比照等是能够中断,歇一会儿接着再来。
  • commit 阶段是对上一阶段获取到的变动局部利用到实在的 DOM 树中,是一系列的 DOM 操作。不仅要保护更简单的 DOM 状态,而且中断后再持续,会对用户体验造成影响。在广泛的利用场景下,此阶段的耗时比 diff 计算等耗时绝对短。
正文完
 0