乐趣区

关于react.js:前端react面试题边面边更

diff 算法是怎么运作

每一种节点类型有本人的属性,也就是 prop,每次进行 diff 的时候,react 会先比拟该节点类型,如果节点类型不一样,那么 react 会间接删除该节点,而后间接创立新的节点插入到其中,如果节点类型一样,那么会比拟 prop 是否有更新,如果有 prop 不一样,那么 react 会断定该节点有更新,那么重渲染该节点,而后在对其子节点进行比拟,一层一层往下,直到没有子节点

react 和 vue 的区别

相同点:

  1. 数据驱动页面,提供响应式的试图组件
  2. 都有 virtual DOM, 组件化的开发,通过 props 参数进行父子之间组件传递数据,都实现了 webComponents 标准
  3. 数据流动单向,都反对服务器的渲染 SSR
  4. 都有反对 native 的办法,react 有 React native,vue 有 wexx

不同点:

  1. 数据绑定:Vue 实现了双向的数据绑定,react 数据流动是单向的
  2. 数据渲染:大规模的数据渲染,react 更快
  3. 应用场景:React 配合 Redux 架构适宜大规模多人合作简单我的项目,Vue 适宜小快的我的项目
  4. 开发格调:react 举荐做法 jsx + inline style 把 html 和 css 都写在 js 了

vue 是采纳 webpack +vue-loader 单文件组件格局,html, js, css 同一个文件

在哪个生命周期中你会收回 Ajax 申请?为什么?

Ajax 申请应该写在组件创立期的第五个阶段,即 componentDidMount 生命周期办法中。起因如下。
在创立期的其余阶段,组件尚未渲染实现。而在存在期的 5 个阶段,又不能确保生命周期办法肯定会执行(如通过 shouldComponentUpdate 办法优化更新等)。在销毀期,组件行将被销毁,申请数据变得无意义。因而在这些阶段发岀 Ajax 申请显然不是最好的抉择。
在组件尚未挂载之前,Ajax 申请将无奈执行结束,如果此时发出请求,将意味着在组件挂载之前更新状态(如执行 setState),这通常是不起作用的。
在 componentDidMount 办法中,执行 Ajax 即可保障组件曾经挂载,并且可能失常更新组件。

简述 flux 思维

Flux 的最大特点,就是数据的 ” 单向流动 ”。

  • 用户拜访 View
  • View收回用户的 Action
  • Dispatcher 收到Action,要求 Store 进行相应的更新
  • Store 更新后,收回一个 "change" 事件
  • View 收到 "change" 事件后,更新页面

说说 React 组件开发中对于作用域的常见问题。

在 EMAScript5 语法标准中,对于作用域的常见问题如下。
(1)在 map 等办法的回调函数中,要绑定作用域 this(通过 bind 办法)。
(2)父组件传递给子组件办法的作用域是父组件实例化对象,无奈扭转。
(3)组件事件回调函数办法的作用域是组件实例化对象(绑定父组件提供的办法就是父组件实例化对象),无奈扭转。
在 EMAScript6 语法标准中,对于作用域的常见问题如下。
(1)当应用箭头函数作为 map 等办法的回调函数时,箭头函数的作用域是以后组件的实例化对象(即箭头函数的作用域是定义时的作用域),毋庸绑定作用域。
(2)事件回调函数要绑定组件作用域。
(3)父组件传递办法要绑定父组件作用域。
总之,在 EMAScript6 语法标准中,组件办法的作用域是能够扭转的。

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

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

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

如何防止反复发动 ajax 获取数据?

  • 数据放在 redux 外面

React 如何进行组件 / 逻辑复用?

抛开曾经被官网弃用的 Mixin, 组件形象的技术目前有三种比拟支流:

  • 高阶组件:

    • 属性代理
    • 反向继承
  • 渲染属性
  • react-hooks

setState 到底是异步还是同步?

先给出答案: 有时体现出异步, 有时体现出同步

  • setState只在合成事件和钩子函数中是“异步”的,在原生事件和setTimeout 中都是同步的
  • setState 的“异步”并不是说外部由异步代码实现,其实自身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用程序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,造成了所谓的“异步”,当然能够通过第二个参数 setState(partialState, callback) 中的 callback 拿到更新后的后果
  • setState 的批量更新优化也是建设在“异步”(合成事件、钩子函数)之上的,在原生事件和 setTimeout 中不会批量更新,在“异步”中如果对同一个值进行屡次setStatesetState 的批量更新策略会对其进行笼罩,取最初一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新

Redux 中间件原理

  • 指的是 action 和 store 之间,沟通的桥梁就是 dispatch,action 就是个对象。比方你用了 redux-thunk,action 也能够是个函数,怎么实现这个过程,就是通过中间件这个桥梁帮你实现的。action 达到 store 之前会走中间件,这个中间件会把函数式的 action 转化为一个对象,在传递给 store

HOC(高阶组件)

HOC(Higher Order Componennt) 是在 React 机制下社区造成的一种组件模式,在很多第三方开源库中体现弱小。

简述:

  • 高阶组件不是组件,是 加强函数,能够输出一个元组件,返回出一个新的加强组件;
  • 高阶组件的次要作用是 代码复用,操作 状态和参数;

用法:

  • 属性代理 (Props Proxy): 返回出一个组件,它基于被包裹组件进行 性能加强;
  • 默认参数: 能够为组件包裹一层默认参数;
function proxyHoc(Comp) {
    return class extends React.Component {render() {
            const newProps = {
                name: 'tayde',
                age: 1,
            }
            return <Comp {...this.props} {...newProps} />
        }
    }
}
  1. 提取状态: 能够通过 props 将被包裹组件中的 state 依赖外层,例如用于转换受控组件:
function withOnChange(Comp) {
    return class extends React.Component {constructor(props) {super(props)
            this.state = {name: '',}
        }
        onChangeName = () => {
            this.setState({name: 'dongdong',})
        }
        render() {
            const newProps = {
                value: this.state.name,
                onChange: this.onChangeName,
            }
            return <Comp {...this.props} {...newProps} />
        }
    }
}

应用姿态如下,这样就能十分疾速的将一个 Input 组件转化成受控组件。

const NameInput = props => (<input name="name" {...props} />)
export default withOnChange(NameInput)

包裹组件: 能够为被包裹元素进行一层包装,

function withMask(Comp) {
  return class extends React.Component {render() {
          return (
              <div>
                  <Comp {...this.props} />
                    <div style={{
                      width: '100%',
                      height: '100%',
                      backgroundColor: 'rgba(0, 0, 0, .6)',
                  }} 
              </div>
          )
      }
  }
}

反向继承 (Inheritance Inversion): 返回出一个组件,继承于被包裹组件,罕用于以下操作

function IIHoc(Comp) {
    return class extends Comp {render() {return super.render();
        }
    };
}

渲染劫持 (Render Highjacking)

条件渲染: 依据条件,渲染不同的组件

function withLoading(Comp) {
    return class extends Comp {render() {if(this.props.isLoading) {return <Loading />} else {return super.render()
            }
        }
    };
}

能够间接批改被包裹组件渲染出的 React 元素树

操作状态 (Operate State) : 能够间接通过 this.state 获取到被包裹组件的状态,并进行操作。但这样的操作容易使 state 变得难以追踪,不易保护,审慎应用。

利用场景:

权限管制,通过形象逻辑,对立对页面进行权限判断,按不同的条件进行页面渲染:

function withAdminAuth(WrappedComponent) {
    return class extends React.Component {constructor(props){super(props)
            this.state = {isAdmin: false,}
        } 
        async componentWillMount() {const currentRole = await getCurrentUserRole();
            this.setState({isAdmin: currentRole === 'Admin',});
        }
        render() {if (this.state.isAdmin) {return <Comp {...this.props} />;
            } else {return (<div> 您没有权限查看该页面,请分割管理员!</div>);
            }
        }
    };
}

性能监控,包裹组件的生命周期,进行对立埋点:

function withTiming(Comp) {
    return class extends Comp {constructor(props) {super(props);
            this.start = Date.now();
            this.end = 0;
        }
        componentDidMount() {super.componentDidMount && super.componentDidMount();
            this.end = Date.now();
            console.log(`${WrappedComponent.name} 组件渲染工夫为 ${this.end - this.start} ms`);
        }
        render() {return super.render();
        }
    };
}

代码复用,能够将反复的逻辑进行形象。

应用留神:

  • 纯函数: 加强函数应为纯函数,防止侵入批改元组件;
  • 防止用法净化: 现实状态下,应透传元组件的无关参数与事件,尽量保障用法不变;
  • 命名空间: 为 HOC 减少特异性的组件名称,这样能便于开发调试和查找问题;
  • 援用传递 : 如果须要传递元组件的 refs 援用,能够应用 React.forwardRef;
  • 静态方法 : 元组件上的静态方法并无奈被主动传出,会导致业务层无奈调用;解决:

    • 函数导出
    • 静态方法赋值
  • 从新渲 染: 因为加强函数每次调用是返回一个新组件,因而如果在 Render 中应用加强函数,就会导致每次都从新渲染整个 HOC,而且之前的状态会失落;

React 中的高阶组件使用了什么设计模式?

应用了装璜模式,高阶组件的使用:

function withWindowWidth(BaseComponent) {
  class DerivedClass extends React.Component {
    state = {windowWidth: window.innerWidth,}
    onResize = () => {
      this.setState({windowWidth: window.innerWidth,})
    }
    componentDidMount() {window.addEventListener('resize', this.onResize)
    }
    componentWillUnmount() {window.removeEventListener('resize', this.onResize);
    }
    render() {return <BaseComponent {...this.props} {...this.state}/>
    }
  }
  return DerivedClass;
}
const MyComponent = (props) => {return <div>Window width is: {props.windowWidth}</div>
};
export default withWindowWidth(MyComponent);

装璜模式的特点是不须要扭转 被装璜对象 自身,而只是在里面套一个外壳接口。JavaScript 目前曾经有了原生装璜器的提案,其用法如下:

@testable
   class MyTestableClass {}

在生命周期中的哪一步你应该发动 AJAX 申请

咱们该当将 AJAX 申请放到 componentDidMount 函数中执行,次要起因有下

  • React 下一代和谐算法 Fiber 会通过开始或进行渲染的形式优化利用性能,其会影响到 componentWillMount 的触发次数。对于 componentWillMount 这个生命周期函数的调用次数会变得不确定,React 可能会屡次频繁调用 componentWillMount。如果咱们将 AJAX 申请放到 componentWillMount 函数中,那么不言而喻其会被触发屡次,天然也就不是好的抉择。
  • 如果咱们将 AJAX 申请搁置在生命周期的其余函数中,咱们并不能保障申请仅在组件挂载结束后才会要求响应。如果咱们的数据申请在组件挂载之前就实现,并且调用了setState 函数将数据增加到组件状态中,对于未挂载的组件则会报错。而在 componentDidMount 函数中进行 AJAX 申请则能无效防止这个问题

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

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

组件更新有几种办法

  • this.setState() 批改状态的时候 会更新组件
  • this.forceUpdate() 强制更新
  • 组件件 render 之后,子组件应用到父组件中状态,导致子组件的 props 属性产生扭转的时候 也会触发子组件的更新

组件之间传值

  • 父组件给子组件传值

    在父组件中用标签属性的 = 模式传值

    在子组件中应用 props 来获取值

  • 子组件给父组件传值

    在组件中传递一个函数

    在子组件中用 props 来获取传递的函数,而后执行该函数

    在执行函数的时候把须要传递的值当成函数的实参进行传递

  • 兄弟组件之间传值

    利用父组件

    先把数据通过【子组件】===》【父组件】

    而后在数据通过【父组件】===〉【子组件】

    音讯订阅

    应用 PubSubJs 插件

(在构造函数中)调用 super(props) 的目标是什么

super() 被调用之前,子类是不能应用 this 的,在 ES2015 中,子类必须在 constructor 中调用 super()。传递 propssuper() 的起因则是便于 (在子类中) 能在 constructor 拜访 this.props

diff 虚构 DOM 比拟的规定

  • 【旧虚构 DOM】与【新虚构 DOM】中雷同 key

    若虚构 DOM 中的内容没有产生扭转,间接应用旧的虚构 DOM

    若虚构 DOM 中的内容产生扭转了,则生成新实在的 DOM,随后替换页面中之前的实在 DOM

  • 【旧虚构 DOM】中未找到 与【新虚构 DOM】雷同的 key

    依据数据创立实在 DOM,随后渲染到页面

React 性能优化在哪个生命周期?它优化的原理是什么?

react 的父级组件的 render 函数从新渲染会引起子组件的 render 办法的从新渲染。然而,有的时候子组件的承受父组件的数据没有变动。子组件 render 的执行会影响性能,这时就能够应用 shouldComponentUpdate 来解决这个问题。

应用办法如下:

shouldComponentUpdate(nexrProps) {if (this.props.num === nexrProps.num) {return false}
    return true;
}

shouldComponentUpdate 提供了两个参数 nextProps 和 nextState,示意下一次 props 和一次 state 的值,当函数返回 false 时候,render()办法不执行,组件也就不会渲染,返回 true 时,组件照常重渲染。此办法就是拿以后 props 中值和下一次 props 中的值进行比照,数据相等时,返回 false,反之返回 true。

须要留神,在进行新旧比照的时候,是 浅比照,也就是说如果比拟的数据时援用数据类型,只有数据的援用的地址没变,即便内容变了,也会被断定为 true。

面对这个问题,能够应用如下办法进行解决:
(1)应用 setState 扭转数据之前,先采纳 ES6 中 assgin 进行拷贝,然而 assgin 只深拷贝的数据的第一层,所以说不是最完满的解决办法:

const o2 = Object.assign({},this.state.obj)
    o2.student.count = '00000';
    this.setState({obj: o2,})

(2)应用 JSON.parse(JSON.stringfy())进行深拷贝,然而遇到数据为 undefined 和函数时就会错。

const o2 = JSON.parse(JSON.stringify(this.state.obj))
    o2.student.count = '00000';
    this.setState({obj: o2,})

ref 是一个函数又有什么益处?

  • 不便 react 销毁组件、从新渲染的时候去清空 refs 的货色,避免内存泄露
退出移动版