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

为了解决跨浏览器兼容性问题, React会将浏览器原生事件( Browser Native Event)封装为合成事件( Synthetic Event)并传入设置的事件处理程序中。
这里的合成事件提供了与原生事件雷同的接口,不过它们屏蔽了底层浏览器的细节差别,保障了行为的一致性。另外, React并没有间接将事件附着到子元素上,而是以繁多事件监听器的形式将所有的事件发送到顶层进行解决(基于事件委托原理)。
这样 React在更新DOM时就不须要思考如何解决附着在DOM上的事件监听器,最终达到优化性能的目标。

如果用索引值作为key 会呈现什么样的问题

  • 若对数据进行逆序增加,逆序删除等毁坏程序的操作

    则会产生没有必要的实在DOM更新,界面想过看不出区别,然而效劳低,性能不好

  • 如果构造中还蕴含输出类的DOM

    会产生谬误的DOM 更新===》界面会有问题

如果不存在对数据的逆序增加 逆序删除等毁坏程序操作,仅用于渲染展现,用index作为key也没有问题

react hooks,它带来了那些便当

  • 代码逻辑聚合,逻辑复用
  • HOC嵌套天堂
  • 代替class
React 中通常应用 类定义 或者 函数定义 创立组件:

在类定义中,咱们能够应用到许多 React 个性,例如 state、 各种组件生命周期钩子等,然而在函数定义中,咱们却无能为力,因而 React 16.8 版本推出了一个新性能 (React Hooks),通过它,能够更好的在函数定义组件中应用 React 个性。

益处:

  1. 跨组件复用: 其实 render props / HOC 也是为了复用,相比于它们,Hooks 作为官网的底层 API,最为轻量,而且革新老本小,不会影响原来的组件层次结构和传说中的嵌套天堂;
  2. 类定义更为简单
  3. 不同的生命周期会使逻辑变得扩散且凌乱,不易保护和治理;
  • 时刻须要关注this的指向问题;
  • 代码复用代价高,高阶组件的应用常常会使整个组件树变得臃肿;
  • 状态与UI隔离: 正是因为 Hooks 的个性,状态逻辑会变成更小的粒度,并且极容易被形象成一个自定义 Hooks,组件中的状态和 UI 变得更为清晰和隔离。

留神:

  • 防止在 循环/条件判断/嵌套函数 中调用 hooks,保障调用程序的稳固;
  • 只有 函数定义组件 和 hooks 能够调用 hooks,防止在 类组件 或者 一般函数 中调用;
  • 不能在useEffect中应用useState,React 会报错提醒;
  • 类组件不会被替换或废除,不须要强制革新类组件,两种形式能并存;

重要钩子

  1. 状态钩子 (useState): 用于定义组件的 State,其到类定义中this.state的性能;
// useState 只承受一个参数: 初始状态// 返回的是组件名和更改该组件对应的函数const [flag, setFlag] = useState(true);// 批改状态setFlag(false)// 下面的代码映射到类定义中:this.state = {    flag: true    }const flag = this.state.flagconst setFlag = (bool) => {    this.setState({        flag: bool,    })}
  1. 生命周期钩子 (useEffect):
类定义中有许多生命周期函数,而在 React Hooks 中也提供了一个相应的函数 (useEffect),这里能够看做componentDidMount、componentDidUpdate和componentWillUnmount的联合。

useEffect(callback, [source])承受两个参数

  • callback: 钩子回调函数;
  • source: 设置触发条件,仅当 source 产生扭转时才会触发;
  • useEffect钩子在没有传入[source]参数时,默认在每次 render 时都会优先调用上次保留的回调中返回的函数,后再从新调用回调;
useEffect(() => {    // 组件挂载后执行事件绑定    console.log('on')    addEventListener()    // 组件 update 时会执行事件解绑    return () => {        console.log('off')        removeEventListener()    }}, [source]);// 每次 source 产生扭转时,执行后果(以类定义的生命周期,便于大家了解):// --- DidMount ---// 'on'// --- DidUpdate ---// 'off'// 'on'// --- DidUpdate ---// 'off'// 'on'// --- WillUnmount --- // 'off'

通过第二个参数,咱们便可模拟出几个罕用的生命周期:

  • componentDidMount: 传入[]时,就只会在初始化时调用一次
const useMount = (fn) => useEffect(fn, [])
  • componentWillUnmount: 传入[],回调中的返回的函数也只会被最终执行一次
const useUnmount = (fn) => useEffect(() => fn, [])
  • mounted: 能够应用 useState 封装成一个高度可复用的 mounted 状态;
const useMounted = () => {    const [mounted, setMounted] = useState(false);    useEffect(() => {        !mounted && setMounted(true);        return () => setMounted(false);    }, []);    return mounted;}
  • componentDidUpdate: useEffect每次均会执行,其实就是排除了 DidMount 后即可;
const mounted = useMounted() useEffect(() => {    mounted && fn()})
  1. 其它内置钩子:
  2. useContext: 获取 context 对象
  • useReducer: 相似于 Redux 思维的实现,但其并不足以代替 Redux,能够了解成一个组件外部的 redux:

    • 并不是长久化存储,会随着组件被销毁而销毁;
    • 属于组件外部,各个组件是互相隔离的,单纯用它并无奈共享数据;
    • 配合useContext`的全局性,能够实现一个轻量级的 Redux;(easy-peasy)
  • useCallback: 缓存回调函数,防止传入的回调每次都是新的函数实例而导致依赖组件从新渲染,具备性能优化的成果;
  • useMemo: 用于缓存传入的 props,防止依赖的组件每次都从新渲染;
  • useRef: 获取组件的实在节点;
  • useLayoutEffect

    • DOM更新同步钩子。用法与useEffect相似,只是区别于执行工夫点的不同
    • useEffect属于异步执行,并不会期待 DOM 真正渲染后执行,而useLayoutEffect则会真正渲染后才触发;
    • 能够获取更新后的 state;
  • 自定义钩子(useXxxxx): 基于 Hooks 能够援用其它 Hooks 这个个性,咱们能够编写自定义钩子,如下面的useMounted。又例如,咱们须要每个页面自定义题目:
function useTitle(title) {  useEffect(    () => {      document.title = title;    });}// 应用:function Home() {    const title = '我是首页'    useTitle(title)    return (        <div>{title}</div>    )}

React Portal 有哪些应用场景

  • 在以前, react 中所有的组件都会位于 #app 下,而应用 Portals 提供了一种脱离 #app 的组件
  • 因而 Portals 适宜脱离文档流(out of flow) 的组件,特地是 position: absolute 与 position: fixed的组件。比方模态框,告诉,正告,goTop 等。

以下是官网一个模态框的示例,能够在以下地址中测试成果

<html>  <body>    <div id="app"></div>    <div id="modal"></div>    <div id="gotop"></div>    <div id="alert"></div>  </body></html>
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])接管两个参数,调用形式如下
useEffect(() => {   console.log('mounted');   return () => {       console.log('willUnmount');   } }, [source]);
生命周期函数的调用次要是通过第二个参数[source]来进行管制,有如下几种状况:
  • [source]参数不传时,则每次都会优先调用上次保留的函数中返回的那个函数,而后再调用内部那个函数;
  • [source]参数传[]时,则内部的函数只会在初始化时调用一次,返回的那个函数也只会最终在组件卸载时调用一次;
  • [source]参数有值时,则只会监听到数组中的值发生变化后才优先调用返回的那个函数,再调用内部的函数。

createElement和 cloneElement有什么区别?

createElement是JSX被转载失去的,在 React中用来创立 React元素(即虚构DOM)的内容。cloneElement用于复制元素并传递新的 props。

redux有什么毛病

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

形容事件在 React中的解决形式。

为了解决跨浏览器兼容性问题, React中的事件处理程序将传递 SyntheticEvent的实例,它是跨浏览器事件的包装器。这些 SyntheticEvent与你习惯的原生事件具备雷同的接口,它们在所有浏览器中都兼容。
React实际上并没有将事件附加到子节点自身。而是通过事件委托模式,应用单个事件监听器监听顶层的所有事件。这对于性能是有益处的。这也意味着在更新DOM时, React不须要放心跟踪事件监听器。

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

redux 中间件

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

常见的中间件:

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

在 React中元素( element)和组件( component)有什么区别?

简略地说,在 React中元素(虛拟DOM)形容了你在屏幕上看到的DOM元素。
换个说法就是,在 React中元素是页面中DOM元素的对象示意形式。在 React中组件是一个函数或一个类,它能够承受输出并返回一个元素。
留神:工作中,为了进步开发效率,通常应用JSX语法示意 React元素(虚构DOM)。在编译的时候,把它转化成一个 React. createElement调用办法。

如何应用4.0版本的 React Router?

React Router 4.0版本中对 hashHistory做了迁徙,执行包装置命令 npm install react-router-dom后,依照如下代码进行应用即可。

import { HashRouter, Route, Redirect, Switch } from " react-router-dom";class App extends Component {  render() {    return (      <div>        <Switch>          <Route path="/list" componen t={List}></Route>          <Route path="/detail/:id" component={Detail}>            {" "}          </Route>          <Redirect from="/ " to="/list">            {" "}          </Redirect>        </Switch>      </div>    );  }}const routes = (  <HashRouter>    <App> </App>  </HashRouter>);render(routes, ickt);

key的作用

是给每一个 vnode 的惟一 id,能够依附 key,更精确,更快的拿到 oldVnode 中对应的 vnode 节点
<!-- 更新前 --><div>  <p key="ka">ka</p>  <h3 key="song">song</he></div><!-- 更新后 --><div>  <h3 key="song">song</h3>  <p key="ka">ka</p></div>

如果没有 key,React 会认为 div 的第一个子节点由 p 变成 h3,第二个子节点由 h3 变成 p,则会销毁这两个节点并从新结构。

然而当咱们用 key 指明了节点前后对应关系后,React 晓得 key === "ka" 的 p 更新后还在,所以能够复用该节点,只须要替换程序。

key 是 React 用来追踪哪些列表元素被批改、被增加或者被移除的辅助标记。

在开发过程中,咱们须要保障某个元素的 key 在其同级元素中具备唯一性。在 React diff 算法中,React 会借助元素的 Key 值来判断该元素是早先创立的还是被挪动而来的元素,从而缩小不必要的元素从新渲染。同时,React 还须要借助 key 来判断元素与本地状态的关联关系。

componentWillReceiveProps调用机会

  • 曾经被废除掉
  • 当props扭转的时候才调用,子组件第二次接管到props的时候

如何用 React构建( build)生产模式?

通常,应用 Webpack的 DefinePlugin办法将 NODE ENV设置为 production。这将剥离 propType验证和额定的正告。除此之外,还能够缩小代码,因为 React应用 Uglify的dead-code来打消开发代码和正文,这将大大减少包占用的空间。

react旧版生命周期函数

初始化阶段

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

运行中状态

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

销毁阶段

  • componentWillUnmount:组件行将销毁

在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>    )  }}

React-Router的实现原理是什么?

客户端路由实现的思维:

  • 基于 hash 的路由:通过监听hashchange事件,感知 hash 的变动

    • 扭转 hash 能够间接通过 location.hash=xxx
  • 基于 H5 history 路由:

    • 扭转 url 能够通过 history.pushState 和 resplaceState 等,会将URL压入堆栈,同时可能利用 history.go() 等 API
    • 监听 url 的变动能够通过自定义事件触发实现

react-router 实现的思维:

  • 基于 history 库来实现上述不同的客户端路由实现思维,并且可能保留历史记录等,磨平浏览器差别,下层无感知
  • 通过保护的列表,在每次 URL 发生变化的回收,通过配置的 路由门路,匹配到对应的 Component,并且 render

React 16.X 中 props 扭转后在哪个生命周期中解决

在getDerivedStateFromProps中进行解决。

这个生命周期函数是为了代替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;}

为什么虚构 dom 会进步性能

虚构 dom 相当于在 js 和实在 dom 两头加了一个缓存,利用 dom diff 算法防止了没有必要 的 dom 操作,从而进步性能

具体实现步骤如下:

  1. 用 JavaScript 对象构造示意 DOM 树的构造;而后用这个树构建一个真正的 DOM 树, 插到文档当中;
  2. 当状态变更的时候,从新结构一棵新的对象树。而后用新的树和旧的树进行比拟,记 录两棵树差别;
  3. 把 2 所记录的差别利用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。

setState办法的第二个参数有什么用?应用它的目标是什么?

它是一个回调函数,当 setState办法执行完结并从新渲染该组件时调用它。在工作中,更好的形式是应用 React组件生命周期之——“存在期”的生命周期办法,而不是依赖这个回调函数。

export class App extends Component {  constructor(props) {    super(props);    this.state = {      username: "雨夜清荷",    };  }  render() {    return <div> {this.state.username}</div>;  }  componentDidMount() {    this.setstate(      {        username: "有课前端网",      },      () => console.log("re-rendered success. ")    );  }}

React中setState的第二个参数作用是什么?

setState 的第二个参数是一个可选的回调函数。这个回调函数将在组件从新渲染后执行。等价于在 componentDidUpdate 生命周期内执行。通常倡议应用 componentDidUpdate 来代替此形式。在这个回调函数中你能够拿到更新后 state 的值:

this.setState({    key1: newState1,    key2: newState2,    ...}, callback) // 第二个参数是 state 更新实现后的回调函数