【译】React前端面试的进阶话题

file

本文由前端翻译小组翻译,首发于:github.com/bigo-fronte… 欢迎关注、转载。

原文链接:Advanced frontend Interview topics with React

首先,请让我明确一点,这篇文章没有教你任何东西。这只对主题和想法进行了组织,并对每个主题和想法进行了简单总结。

目录:

Axios

如果你知道关于axios的安全问题,有人问到了就刚好;如果你不知道的话,剧透警告,在很久以前这个问题就已经解决了。

解决方案: github.com/axios/axios…

可以替代 axios 的是 Request 库或者 Fetch API(但是它也存在一些问题,状态码为失败的时候不会传到 catch 处理,而是变成由 then 处理,同时需要进行一些额外的步骤,比如 response.json() ,没有拦截器或所有能让 axios 和其他库用起来更方便的东西)。

懒加载(Lazyload)

代码分割是由诸如 Webpack,Rollup 和 Browserify(factor-bundle)这类打包器支持的一项技术,能够创建多个 bundle 并在运行时动态加载。

如何实现:

函数可以动态导入:

import("./math").then(math => {
  console.log(math.add(16, 26));
});
复制代码

或者组件可以使用 React.Lazy :

const OtherComponent = React.lazy(() => import('./OtherComponent'));
复制代码

此代码将会在组件首次渲染时,自动导入包含 OtherComponent 组件的 bundle 。 然后应该在 Suspense 组件中渲染 lazy 组件,如此使得我们可以在等待加载 lazy 组件时做优雅降级(如 loading 指示器等)。

const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </div>
  );
}
复制代码

此组件可以当作占位组件。 Semantic库的相关示例: semantic-ui.com/elements/pl…

错误边界(Error Boundaries)

错误边界是一种 React 组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript 错误,并打印这些错误,同时展示降级 UI,而并不会渲染那些发生崩溃的子组件树。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // 你同样可以将错误日志上报给服务器
    logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 你可以自定义降级后的 UI 并渲染
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}
复制代码

然后你可以将它作为一个常规组件去使用:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>
复制代码

Webworker

通过使用Web Workers,Web应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢。

var w;
function startWorker() {
  if (typeof(Worker) !== "undefined") {
    if (typeof(w) == "undefined") {
      w = new Worker("demo_workers.js");
    }
    w.onmessage = function(event) {
      document.getElementById("result").innerHTML = event.data;
    };
  } else {
    document.getElementById("result").innerHTML = "Sorry! No Web Worker   support.";
  }
}
function stopWorker() {
  w.terminate();
  w = undefined;
}
复制代码

IndexDB

IndexDB 是一个内置的数据库,比 localStorage 强大得多。 Key/value 存储:value 可以是(几乎)任何东西,多种 key 类型。 支持事务以确保可靠性。 支持 key 范围查询、索引。 可以存储比 localStorage 多得多的数据。

查看示例

Token、cookies 和 JWT

要进行身份验证令牌流程,我们需要获取两个 token :access token 和 session token 。 保持权限状态的关键点是 access token 。它只是让我们可以访问接收 session token 。一段时间后,这个 session token 会在后端过期。这时我们需要带着 access token 发送新请求,刷新 session token 。通常情况下,服务器会发送401状态码表示未授权。

使用 cookies 的话,这个过程会更简单。设置请求头时,加入“credentials”属性并赋值为隐藏的 cookies 。这些 cookies 应该设置为不被 JS 脚本改写,chrome 会将其隐藏在 cookies 的选项卡中。

另外: 如果你在访问服务器时出现了 CORS 问题,你应该设置 access-control-allow-origin 和/或 access-control-allow-headers 属性。

JSON Web Tokens(JWTs)使得服务器之间(包括内部和外部服务)发送仅读的已签名“claims”更加简单。Claims 可以是任何你想其他人可读和可验证、但不可更改的一切数据。

性能检查

  • Audits:Lighthouse 是一个开源的自动化工具,用于改进网络应用的质量。你可以将其作为一个 Chrome 扩展程序运行,或从命令行运行。你为 Lighthouse 提供一个需要审查的网址,它将针对此页面运行一连串的测试,然后生成一个有关页面性能的报告。
  • Redux devtools:Redux DevTools 用于调试应用程序的状态变更。
  • React devtools:React DevTools 允许你在 Chrome 开发者工具中检查 React 组件层级。你会在 Chrome DevTools 中看到两个新选项卡:“⚛️ Components(查看组件树)” 和 “⚛️ Profiler(对每个组件进行性能测试)”。通过 React DevTools 也能知道你的组件进行了多少次渲染。
  • Performance devtools:Performance 是开发者工具中的一个选项卡,你可以检查应用程序的总体性能。
  • Network devtools:你可以查看所有请求的列表,并跟踪解决它们所花销的时间。why-this-render:检查组件渲染数量的库。
  • Renderer devtools:Renderer 是开发者工具的控制台选项中的一个选项,你可以在这个选项中跟踪渲染的相关信息。FPS是跟踪的信息之一,它可以反映你的页面流畅性,最佳数值是60。所以,如果低于该值,就意味着你的性能仍有待提升。

其他文章: DevtoolsReact PerformanceProfiler

PWA

渐进式网页应用(PWAs)是一种网页端软件应用,它的构建使用了通用的网页技术,包括HTML,CSS,和JavaScript。PWAs的目标是能够运行在使用了标准浏览器的任何平台上。其功能包含离线运行、消息推送、硬件访问,以及接近于桌面端和手机端的原生应用的用户体验。由于PWAs是一个网页或者被称作网页应用的网站,所以对于开发人员或者用户而言,它不需要在苹果的App Store或者Google Play这样的应用程序发布平台上进行安装。 PWAs依赖于manifests(包含一些应用的基本信息)和service workers(属于web worker的一种)。service worker基本上是一个JavaScript文件,独立于浏览器的主线程,可以实现网络请求的拦截、资源的缓存和获取,以及消息推送。

实时性

根据RFC 6455规范的描述,WebSocket协议提供了一种方式,通过持久连接让浏览器端和服务端交换数据。数据被当作“包”来进行双向的传输,不会中断连接,也不会有额外的HTTP请求。 对于需要持续进行数据交换的服务来说,比如线上游戏、实时贸易系统等,WebSocket是一个非常不错的选择。

// First create the connection
let socket = new WebSocket("wss://javascript.info/article/websocket/demo/hello");
// Here you register an action when the connection starts
socket.onopen = function(e) {
  alert("[open] Connection established");
  alert("Sending to server");
  socket.send("My name is John");
};
// When the socket is updated
socket.onmessage = function(event) {
  alert(`[message] Data received from server: ${event.data}`);
};
// When it closes
socket.onclose = function(event) {
  if (event.wasClean) {
    alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
  } else {
    // e.g. server process killed or network down
    // event.code is usually 1006 in this case
    alert('[close] Connection died');
  }
};
// And when some error happens
socket.onerror = function(error) {
  alert(`[error] ${error.message}`);
};
复制代码

服务器发送事件规范里,有一个内置的类EventSource,能够保持和服务器之间的连接,允许从服务器接收事件。与 WebSocket 类似,连接是持久化的。

但是 EventSource 跟 WebSocket 有几个非常重要的不同:

differences between EventSource and WebSocket

根据比较,EventSource 比 WebSocket 在跟服务器的通讯上稍微弱一点。

let eventSource = new EventSource("/events/subscribe");
eventSource.onmessage = function(event) {
  console.log("New message", event.data);
  // will log 3 times for the data stream above
};

// or eventSource.addEventListener('message', ...)
复制代码

使用JavaScript构建实时应用的五种方式

CSS性能

  • 不要用CSS图标,用SVG。
  • 使用单独的选择器,比如说class,比用子孙选择器等复杂选择器要快。
  • 越少的元素在运行的时候计算就越少,所以当需要用子元素的时候,直接用子元素或者单独的选择器。
  • 字母顺序(插件或者依赖可以处理)
  • 能用mixins的时候,不要用extends(SASS)
  • 缩小CSS代码
  • 分割CSS的导入,在使用这些css的元素上面引用。基于CSS的组件化。

CSS组件化

下面这个视频总结的很形象:

CSS性能

视频来源

动画:

动画

观看以下视频了解上面图片列出来的触发CSS动画的方式: youtu.be/0Xg6r_MKGJ4

高阶Hooks

useMemo返回一个memoized值。 把“创建”函数和依赖项数组作为参数传入。它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。 记住,传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。 如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
复制代码

useLayoutEffect函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。 在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。 尽可能使用标准的 useEffect 以避免阻塞视觉更新。

useReducer是 useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)

在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。

以下是用 reducer 重写 useState 一节的计数器示例:

const initialState = {count: 0};
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}
function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}
复制代码

React memo

React memo为高阶组件,它基本的作用是检查组件是否应该重新渲染。如果确实接收到一些变化,在父组件重新渲染的同时,能够在非必要的情况下防止子组件重新渲染。

观看视频了解更多React memo

可以通过如下方式导出组件:

export default React.memo(ComponentName)
复制代码

TDD

测试驱动开发(Test-Driven Development)是一种软件开发过程中的应用方法,它依赖于非常短的开发周期的循环重复:将需求转化为非常具体的测试用例,然后对软件进行优化,通过测试。

TDD

Big lists

假如你有一个包含数千个项目的列表需要展示,但是不想降低手机端的用户体验,这个视频给你提供了处理这种情况的两个选择: www.youtube.com/watch?v=QhP…

在此,我特别感谢应用于本篇文章的所有资源,如 w3schools、javascript.info、MDN、react 文档和来自 Youtube 的几个视频。

Bye :D

Twitter:twitter.com/danilodev Github:github.com/danilosilva… Twitch:www.twitch.tv/gorickisnot…

欢迎大家留言讨论,祝工作顺利、生活愉快!

我是bigo前端,下期见。

猜你喜欢

转载自juejin.im/post/7042590209728315399