乐趣区

关于前端:前端一面常见react面试题持续更新中

React 组件中怎么做事件代理?它的原理是什么?

React 基于 Virtual DOM 实现了一个 SyntheticEvent 层(合成事件层),定义的事件处理器会接管到一个合成事件对象的实例,它合乎 W3C 规范,且与原生的浏览器事件领有同样的接口,反对冒泡机制,所有的事件都主动绑定在最外层上。

在 React 底层,次要对合成事件做了两件事:

  • 事件委派: React 会把所有的事件绑定到构造的最外层,应用对立的事件监听器,这个事件监听器上维持了一个映射来保留所有组件外部事件监听和处理函数。
  • 主动绑定: React 组件中,每个办法的上下文都会指向该组件的实例,即主动绑定 this 为以后组件。

如何有条件地向 React 组件增加属性?

对于某些属性,React 十分聪慧,如果传递给它的值是虚值,能够省略该属性。例如:

var InputComponent = React.createClass({render: function () {
    var required = true;
    var disabled = false;
    return <input type="text" disabled={disabled} required={required} />;
  },
});

渲染后果:

<input type="text" required>

另一种可能的办法是:

var condition = true;
var component = <div value="foo" {...(condition && { disabled: true})} />;

react 的渲染过程中,兄弟节点之间是怎么解决的?也就是 key 值不一样的时候

通常咱们输入节点的时候都是 map 一个数组而后返回一个 ReactNode,为了不便react 外部进行优化,咱们必须给每一个 reactNode 增加 key,这个key prop 在设计值处不是给开发者用的,而是给 react 用的,大略的作用就是给每一个 reactNode 增加一个身份标识,不便 react 进行辨认,在重渲染过程中,如果 key 一样,若组件属性有所变动,则 react 只更新组件对应的属性;没有变动则不更新,如果 key 不一样,则 react 先销毁该组件,而后从新创立该组件

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

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

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

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

react 旧版生命周期函数

初始化阶段

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

运行中状态

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

销毁阶段

  • componentWillUnmount: 组件行将销毁

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

diff 算法是怎么运作

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

在 Redux 中应用 Action 要留神哪些问题?

在 Redux 中应用 Action 的时候,Action 文件里尽量放弃 Action 文件的污浊,传入什么数据就返回什么数据,最妤把申请的数据和 Action 办法分来到,以放弃 Action 的污浊。

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

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

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

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

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

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

React 中的 setState 批量更新的过程是什么?

调用 setState 时,组件的 state 并不会立刻扭转,setState 只是把要批改的 state 放入一个队列,React 会优化真正的执行机会,并出于性能起因,会将 React 事件处理程序中的屡次React 事件处理程序中的屡次 setState 的状态批改合并成一次状态批改。最终更新只产生一次组件及其子组件的从新渲染,这对于大型应用程序中的性能晋升至关重要。

this.setState({count: this.state.count + 1    ===>    入队,[count+ 1 的工作]
});
this.setState({count: this.state.count + 1    ===>    入队,[count+ 1 的工作,count+ 1 的工作]
});
                                          ↓
                                         合并 state,[count+ 1 的工作]
                                          ↓
                                         执行 count+ 1 的工作

须要留神的是,只有同步代码还在执行,“攒起来”这个动作就不会进行。(注:这里之所以屡次 +1 最终只有一次失效,是因为在同一个办法中屡次 setState 的合并动作不是单纯地将更新累加。比方这里对于雷同属性的设置,React 只会为其保留最初一次的更新)。

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

高阶组件的利用场景

权限管制

利用高阶组件的 条件渲染 个性能够对页面进行权限管制,权限管制个别分为两个维度: 页面级别 页面元素级别

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

// 应用
// pages/page-a.js    
class PageA extends React.Component {constructor(props) {super(props);    
        // something here...    
    }    
    componentWillMount() {// fetching data}    
    render() {// render page with data}    
}    
export default withAdminAuth(PageA);    

可能你曾经发现了,高阶组件其实就是装璜器模式在 React 中的实现:通过给函数传入一个组件(函数或类)后在函数外部对该组件(函数或类)进行性能的加强(不批改传入参数的前提下),最初返回这个组件(函数或类),即容许向一个现有的组件增加新的性能,同时又不去批改该组件,属于 包装模式(Wrapper Pattern) 的一种。

什么是装璜者模式:在不扭转对象本身的前提下在程序运行期间动静的给对象增加一些额定的属性或行为

能够进步代码的复用性和灵活性

再对高阶组件进行一个小小的总结:

  • 高阶组件 不是组件 一个把某个组件转换成另一个组件的 函数
  • 高阶组件的次要作用是 代码复用
  • 高阶组件是 装璜器模式在 React 中的实现

封装组件的准则

封装准则

1、繁多准则:负责繁多的页面渲染

2、多重职责:负责多重职责,获取数据,复用逻辑,页面渲染等

3、明确承受参数:必选,非必选,参数尽量设置以_结尾,防止变量反复

4、可扩大:需要变动可能及时调整,不影响之前代码

5、代码逻辑清晰

6、封装的组件必须具备高性能,低耦合的个性

7、组件具备繁多职责:封装业务组件或者根底组件,如果不能给这个组件起一个有意义的名字,证实这个组件承当的职责可能不够繁多,须要持续抽组件,直到它能够是一个独立的组件即可

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

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

什么起因会促使你脱离 create-react-app 的依赖

当你想去配置 webpack 或 babel presets。

React diff 算法的原理是什么?

实际上,diff 算法探讨的就是虚构 DOM 树发生变化后,生成 DOM 树更新补丁的形式。它通过比照新旧两株虚构 DOM 树的变更差别,将更新补丁作用于实在 DOM,以最小老本实现视图更新。具体的流程如下:

  • 实在的 DOM 首先会映射为虚构 DOM;
  • 当虚构 DOM 发生变化后,就会依据差距计算生成 patch,这个 patch 是一个结构化的数据,内容蕴含了减少、更新、移除等;
  • 依据 patch 去更新实在的 DOM,反馈到用户的界面上。

    一个简略的例子:

import React from 'react'
export default class ExampleComponent extends React.Component {render() {if(this.props.isVisible) {return <div className="visible">visbile</div>;}
     return <div className="hidden">hidden</div>;
  }
}

这里,首先假设 ExampleComponent 可见,而后再扭转它的状态,让它不可见。映射为实在的 DOM 操作是这样的,React 会创立一个 div 节点。

<div class="visible">visbile</div>

当把 visbile 的值变为 false 时,就会替换 class 属性为 hidden,并重写外部的 innerText 为 hidden。这样一个生成补丁、更新差别的过程统称为 diff 算法。

diff 算法能够总结为三个策略,别离从树、组件及元素三个层面进行复杂度的优化:

策略一:疏忽节点跨层级操作场景,晋升比对效率。(基于树进行比照)

这一策略须要进行树比对,即对树进行分层比拟。树比对的解决手法是十分“暴力”的,即两棵树只对同一档次的节点进行比拟,如果发现节点曾经不存在了,则该节点及其子节点会被齐全删除掉,不会用于进一步的比拟,这就晋升了比对效率。

策略二:如果组件的 class 统一,则默认为类似的树结构,否则默认为不同的树结构。(基于组件进行比照)

在组件比对的过程中:

  • 如果组件是同一类型则进行树比对;
  • 如果不是则间接放入补丁中。

只有父组件类型不同,就会被从新渲染。这也就是为什么 shouldComponentUpdate、PureComponent 及 React.memo 能够进步性能的起因。

策略三:同一层级的子节点,能够通过标记 key 的形式进行列表比照。(基于节点进行比照)

元素比对次要产生在同层级中,通过标记节点操作生成补丁。节点操作蕴含了插入、挪动、删除等。其中节点从新排序同时波及插入、挪动、删除三个操作,所以效率耗费最大,此时策略三起到了至关重要的作用。通过标记 key 的形式,React 能够间接挪动 DOM 节点,升高内耗。

传入 setstate 函数的第二个参数的作用是什么?

第二个参数是一个函数,该函数会在 setState 函数调用实现并且组件开始重渲染时调用,能够用该函数来监听渲染是否实现。

this.setstate(
  {username: "有课前端网",},
  () => console.log("re-rendered success.")
);

React 组件的构造函数有什么作用?它是必须的吗?

构造函数次要用于两个目标:

  • 通过将对象调配给 this.state 来初始化本地状态
  • 将事件处理程序办法绑定到实例上

所以,当在 React class 中须要设置 state 的初始值或者绑定事件时,须要加上构造函数,官网 Demo:

class LikeButton extends React.Component {constructor() {super();
    this.state = {liked: false};
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {this.setState({liked: !this.state.liked});
  }
  render() {
    const text = this.state.liked ? 'liked' : 'haven\'t liked';
    return (<div onClick={this.handleClick}>
        You {text} this. Click to toggle.      </div>
    );
  }
}
ReactDOM.render(
  <LikeButton />,
  document.getElementById('example')
);

构造函数用来新建父类的 this 对象;子类必须在 constructor 办法中调用 super 办法;否则新建实例时会报错;因为子类没有本人的 this 对象,而是继承父类的 this 对象,而后对其进行加工。如果不调用 super 办法;子类就得不到 this 对象。

留神:

  • constructor () 必须配上 super(), 如果要在 constructor 外部应用 this.props 就要 传入 props , 否则不必
  • JavaScript 中的 bind 每次都会返回一个新的函数, 为了性能等思考, 尽量在 constructor 中绑定事件

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

react-router4 的外围

  • 路由变成了组件
  • 扩散到各个页面,不须要配置 比方<link> <route></route>
退出移动版