乐趣区

关于react.js:阿里前端二面必会react面试题总结

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

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

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

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.flag
const setFlag = (bool) => {
    this.setState({flag: bool,})
}
  1. 生命周期钩子 (useEffect):

类定义中有许多生命周期函数,而在 React Hooks 中也提供了一个相应的函数 (useEffect),这里能够看做 componentDidMount、componentDidUpdate 和 componentWillUnmount 的联合。

useEffect(callback,)承受两个参数

  • callback: 钩子回调函数;
  • source: 设置触发条件,仅当 source 产生扭转时才会触发;
  • useEffect 钩子在没有传入参数时,默认在每次 render 时都会优先调用上次保留的回调中返回的函数,后再从新调用回调;
useEffect(() => {
    // 组件挂载后执行事件绑定
    console.log('on')
    addEventListener()

    // 组件 update 时会执行事件解绑
    return () => {console.log('off')
        removeEventListener()}
}, );


// 每次 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>
    )
}

redux 中间件

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

常见的中间件:

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

shouldComponentUpdate 有什么用?为什么它很重要?

组件状态数据或者属性数据产生更新的时候,组件会进入存在期,视图会渲染更新。在生命周期办法 should ComponentUpdate 中,容许抉择退出某些组件(和它们的子组件)的和解过程。
和解的最终目标是依据新的状态,以最无效的形式更新用户界面。如果咱们晓得用户界面的某一部分不会扭转,那么没有理由让 React 弄清楚它是否应该更新渲染。通过在 shouldComponentUpdate 办法中返回 false, React 将让以后组件及其所有子组件放弃与以后组件状态雷同。

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

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

vue 或者 react 优化整体优化

  1. 虚构 dom

为什么虚构 dom 会进步性能?(必考)

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

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

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

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

展现组件 (Presentational component) 和容器组件 (Container component) 之间有何不同

展现组件关怀组件看起来是什么。展现专门通过 props 承受数据和回调,并且简直不会有本身的状态,但当展现组件领有本身的状态时,通常也只关怀 UI 状态而不是数据的状态。

容器组件则更关怀组件是如何运作的。容器组件会为展现组件或者其它容器组件提供数据和行为 (behavior),它们会调用 Flux actions,并将其作为回调提供给展现组件。容器组件常常是有状态的,因为它们是(其它组件的) 数据源。

在 ReactNative 中,如何解决 adb devices 找不到连贯设施的问题?

在应用 Genymotion 时,首先须要在 SDK 的 platform-tools 中退出环境变量,而后在 Genymotion 中单击 Setting,抉择 ADB 选项卡,单击 Use custom Android SDK tools,浏览本地 SDK 的地位,单击 OK 按钮就能够了。启动虛拟机后,在 cmd 中输出 adb devices 能够查看设施。

redux 有什么毛病

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

React 中的 props 为什么是只读的?

this.props是组件之间沟通的一个接口,原则上来讲,它只能从父组件流向子组件。React 具备浓厚的函数式编程的思维。

提到函数式编程就要提一个概念:纯函数。它有几个特点:

  • 给定雷同的输出,总是返回雷同的输入。
  • 过程没有副作用。
  • 不依赖内部状态。

this.props就是吸取了纯函数的思维。props 的不能够变性就保障的雷同的输出,页面显示的内容是一样的,并且不会产生副作用

对 React 的插槽 (Portals) 的了解,如何应用,有哪些应用场景

React 官网对 Portals 的定义:

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优良的计划

Portals 是 React 16 提供的官网解决方案,使得组件能够脱离父组件层级挂载在 DOM 树的任何地位。艰深来讲,就是咱们 render 一个组件,但这个组件的 DOM 构造并不在本组件内。

Portals 语法如下:

ReactDOM.createPortal(child, container);
  • 第一个参数 child 是可渲染的 React 子项,比方元素,字符串或者片段等;
  • 第二个参数 container 是一个 DOM 元素。

个别状况下,组件的 render 函数返回的元素会被挂载在它的父级组件上:

import DemoComponent from './DemoComponent';
render() {
  // DemoComponent 元素会被挂载在 id 为 parent 的 div 的元素上
  return (
    <div id="parent">
        <DemoComponent />
    </div>
  );
}

然而,有些元素须要被挂载在更高层级的地位。最典型的利用场景:当父组件具备 overflow: hidden 或者 z-index 的款式设置时,组件有可能被其余元素遮挡,这时就能够思考要不要应用 Portal 使组件的挂载脱离父组件。例如:对话框,模态窗。

import DemoComponent from './DemoComponent';
render() {
  // DemoComponent 元素会被挂载在 id 为 parent 的 div 的元素上
  return (
    <div id="parent">
        <DemoComponent />
    </div>
  );
}

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

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

redux 有什么毛病

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

React 和 vue.js 的相似性和差异性是什么?

相似性如下。
(1)都是用于创立 UI 的 JavaScript 库。
(2)都是疾速和轻量级的代码库(这里指 React 外围库)。
(3)都有基于组件的架构。
(4)都应用虚构 DOM。
(5)都能够放在独自的 HTML 文件中,或者放在 Webpack 设置的一个更简单的模块中。
(6)都有独立但罕用的路由器和状态治理库。
它们最大的区别在于 Vue. js 通常应用 HTML 模板文件,而 React 齐全应用 JavaScript 创立虚构 DOM。Vue. js 还具备对于“可变状态”的“reactivity”的从新渲染的自动化检测零碎。

虚构 DOM 的引入与间接操作原生 DOM 相比,哪一个效率更高,为什么

虚构 DOM 绝对原生的 DOM 不肯定是效率更高,如果只批改一个按钮的文案,那么虚构 DOM 的操作无论如何都不可能比实在的 DOM 操作更快。在首次渲染大量 DOM 时,因为多了一层虚构 DOM 的计算,虚构 DOM 也会比 innerHTML 插入慢。它能保障性能上限,在实在 DOM 操作的时候进行针对性的优化时,还是更快的。所以要依据具体的场景进行探讨。

在整个 DOM 操作的演化过程中,其实主要矛盾并不在于性能,而在于开发者写得爽不爽,在于研发体验 / 研发效率。虚构 DOM 不是别的,正是前端开发们为了谋求更好的研发体验和研发效率而发明进去的高阶产物。虚构 DOM 并不一定会带来更好的性能,React 官网也素来没有把虚构 DOM 作为性能层面的卖点对外输入过。** 虚构 DOM 的优越之处在于,它可能在提供更爽、更高效的研发模式(也就是函数式的 UI 编程形式)的同时,依然放弃一个还不错的性能。

React 中发动网络申请应该在哪个生命周期中进行?为什么?

对于异步申请,最好放在 componentDidMount 中去操作,对于同步的状态扭转,能够放在 componentWillMount 中,个别用的比拟少。

如果认为在 componentWillMount 里发动申请能提前取得后果,这种想法其实是谬误的,通常 componentWillMount 比 componentDidMount 早不了多少微秒,网络上任何一点提早,这一点差别都可忽略不计。

react 的生命周期: constructor() -> componentWillMount() -> render() -> componentDidMount()

下面这些办法的调用是有秩序的,由上而下顺次调用。

  • constructor 被调用是在组件筹备要挂载的最开始,此时组件尚未挂载到网页上。
  • componentWillMount 办法的调用在 constructor 之后,在 render 之前,在这办法里的代码调用 setState 办法不会触发从新 render,所以它个别不会用来作加载数据之用。
  • componentDidMount 办法中的代码,是在组件曾经齐全挂载到网页上才会调用被执行,所以能够保证数据的加载。此外,在这办法中调用 setState 办法,会触发从新渲染。所以,官网设计这个办法就是用来加载内部数据用的,或解决其余的副作用代码。与组件上的数据无关的加载,也能够在 constructor 里做,但 constructor 是做组件 state 初绐化工作,并不是做加载数据这工作的,constructor 里也不能 setState,还有加载的工夫太长或者出错,页面就无奈加载进去。所以有副作用的代码都会集中在 componentDidMount 办法里。

总结:

  • 跟服务器端渲染(同构)有关系,如果在 componentWillMount 外面获取数据,fetch data 会执行两次,一次在服务器端一次在客户端。在 componentDidMount 中能够解决这个问题,componentWillMount 同样也会 render 两次。
  • 在 componentWillMount 中 fetch data,数据肯定在 render 后能力达到,如果遗记了设置初始状态,用户体验不好。
  • react16.0 当前,componentWillMount 可能会被执行屡次。

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

当你想去配置 webpack 或 babel presets。

你对 Time Slice 的了解?

工夫分片

  • React 在渲染(render)的时候,不会阻塞当初的线程
  • 如果你的设施足够快,你会感觉渲染是同步的
  • 如果你设施十分慢,你会感觉还算是灵活的
  • 尽管是异步渲染,然而你将会看到残缺的渲染,而不是一个组件一行行的渲染进去
  • 同样书写组件的形式

也就是说,这是 React 背地在做的事件,对于咱们开发者来说,是通明的,具体是什么样的成果呢?

简述 flux 思维

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

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