12

background

This morning, I saw a front-end two-sided sharing about BN on Maimai. The author shared a recent interview question for pure purposes.

I thought this was a good set of interview questions, so I shared it with everyone.

Why is this interview question

In the front-end world, what kind of project will use the knowledge behind this type of interview questions?

I have been fortunate enough to be involved in several projects from 0 - 1 , such as:

  • Desktop IM project (Electron, React, Node.js), end-to-end encryption, featuring 200,000 crowd chat function
  • Several large SAAS systems (React)
  • Mini Program (Taro)
  • Hybrid APP
  • WeChat public account
  • Some web3 projects (tens of millions of flow pools, solidity React TypeScript Node.js)
    and many more..
Some of the knowledge behind the need for a certain technical depth are:
  • communication, TCP-based end-to-end encrypted long-link communication,
  • security, user privacy, security, direction like Telegram
  • Performance: processing and display of large amounts of data, front-end task scheduling, re-render control, etc.
  • Understanding and practice of design patterns and object-oriented programming: such as singleton pattern, inversion of control, dependency injection
  • Reading and understanding of react and Vue key node source code
  • Understanding of ES6 asynchronous implementation
  • Browser rendering principle
  • Node.js
  • Basic operation and maintenance knowledge of Linux, docker, K8s, nginx, etc.

Wait... I'm not going to expand here because I haven't eaten at noon at the time of writing this article. Very hungry, and most people don't need other unpopular knowledge at all

Suppose a scene

For example, if two people send you a message at the same time every second, your client (front-end) does not need to do task scheduling.

If a thousand people send you a lot of messages at the same time every second, you need to do task scheduling at this time, because it involves the network layer, DB layer, cache layer (front-end memory, such as redux, etc.), as well as data flow, update Frequency and timing control.

transaction, the same. For example, the price of a currency fluctuates violently within one second. Due to the IM scenario and duplex communication, you may receive multiple pushes per second. If this frequency is refined according to the actual scene of the user, it is an extremely complex requirement. I won't talk about it here

Then at this time, you will use most of the knowledge I mentioned above. When doing performance optimization, when your knowledge is comprehensive enough, it is actually more like playing chess. pros and cons

With the advancement of the Internet, I think the front end will become more and more like a complete client. Now there are technologies such as webContainer technology and webasm. As long as Google solves some key node problems of native-socket and security, it is a complete client. Electron or something like that is no longer needed

About the topic

1.React's time slicing idea

 可以结合我三年前文章 手写mini-react源码看看
https://github.com/JinJieTan/Peter-/tree/master/mini-React
  • Take a look first cpu Scheduling time slice

The time slice is the time allocated by the CPU to each program, and each thread is assigned a time period, called its time slice, that is, the time the process is allowed to run, so that each program appears to be running at the same time. If the process is still running at the end of the time slice, the CPU will be taken away and assigned to another process. If the process blocks or ends before the time slice ends, the CPU switches immediately. without wasting CPU resources. On a macro level: we can open multiple applications at the same time, and each program runs in parallel and runs at the same time. But at the micro level: since there is only one CPU, only a part of the program requirements can be processed at a time, how to deal with fairness, one way is to introduce time slices, and each program executes in turn

  • So what is the time slicing idea of react?

Two years ago, a project in our company was upgraded from react0.14 to react16. I remember that I gave some colleagues in the company a popular science at that time. React16 introduced fiber. In fact, this time slicing idea is the fiber of react16.

At that time, the react0.14 version of the project had a problem, that is, there would be a lag, because before the react16 version, the update was completed in one go. If this process is long, it will result in a long wait (stutter)

After the react16 version, when the react is updated, there will be a Reconcilation phase. This phase will traverse the virtual dom tree, find the updated node, and complete a series of operations. This stage has a lot of calculations, which will occupy the CPU for a long time. This Reconcilation stage can be interrupted (suspended temporarily), allowing the browser to respond to high-priority events first, such as user interaction. This is the so-called time slicing idea, which is essentially task scheduling

  • 2. Why not use requestIdleCallback
    I have remarked in the code, and I have tested requestIdleCallback . At that time, I was doing performance optimization for 1000 people to send frequent messages in 1 second, and I was doing task scheduling with handwritten react.

The reason is: the compatibility of requestIdleCallback is not good. For frequent user interaction and multiple updates, requestAnimation is more timely and has high priority, and requestIdleCallback is suitable for processing tasks that can delay rendering.

We can find that many optimization ideas come from the cognition of the operating system itself, and the cognition of things itself determines the ceiling of development.

Principles and optimization principles such as useMemo

Behind the scenes, the Object.js method is used to traverse and compare the prev and current values of the incoming dependencies.

Use simple comparisons, eliminating unnecessary renders

side effects of react

It's a more general question, and I won't answer it.

vue's nextTick

vue2 has a graceful degradation process

  • first promise.then
  • And then the MutationObserver
  • then setImmediate
  • And finally setTimeout

     let timerFunc // nextTick异步实现fn
    
    if (typeof Promise !== 'undefined' && isNative(Promise)) {
    // Promise方案
    const p = Promise.resolve()
    timerFunc = () => {
      p.then(flushCallbacks) // 将flushCallbacks包装进Promise.then中
    }
    isUsingMicroTask = true
    } else if (!isIE && typeof MutationObserver !== 'undefined' && (
    isNative(MutationObserver) ||
    MutationObserver.toString() === '[object MutationObserverConstructor]'
    )) {
    // MutationObserver方案
    let counter = 1
    const observer = new MutationObserver(flushCallbacks) // 将flushCallbacks作为观测变化的cb
    const textNode = document.createTextNode(String(counter)) // 创建文本节点
    // 观测文本节点变化
    observer.observe(textNode, {
      characterData: true
    })
    // timerFunc改变文本节点的data,以触发观测的回调flushCallbacks
    timerFunc = () => { 
      counter = (counter + 1) % 2
      textNode.data = String(counter)
    }
    isUsingMicroTask = true
    } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
    // setImmediate方案
    timerFunc = () => {
      setImmediate(flushCallbacks)
    }
    } else {
    // 最终降级方案setTimeout
    timerFunc = () => {
      setTimeout(flushCallbacks, 0)
    }
    }
    The reason for this question is to know whether the interviewee really understands the data update-rendering asynchrony of the Vue framework, not just this nextTick.

The remaining macro tasks and micro tasks can be answered together with the sixth question.

What is Inversion of Control and Dependency Injection

This question shows that the interviewer prefers this style mode, otherwise he would not ask this special question, but it should be noted that since this aspect is asked, it will definitely expand and diverge, and ask your actual use and other design modes, etc. . Therefore, the back test questions are not reliable for a slightly higher-level interview.

I personally oppose the back test questions, and pay more attention to past project experience and basic knowledge mastery and practical thinking
  • Inversion of Control (IoC):

In the design of the single responsibility principle, there are few tasks that a single object can complete. Most tasks require multiple objects to cooperate to complete, so that there are dependencies between objects. At the beginning, the dependencies between objects are solved by themselves. If you need any objects, you can use them. The control is in the objects themselves. However, the coupling degree is very high in this way, and a small modification of an object may cause a chain reaction, and the dependent objects need to be modified all the way.

Classic Inversion of Control (IoC) principles:

Upper-level modules should not depend on lower-level modules. They both depend on an abstraction. The abstraction cannot depend on the concrete. The concrete must depend on the abstraction.

In TypeScript, the above sentence can be understood as multiple classes follow an interface, and the corresponding data values of these classes are different, but the fields and types are the same.

When they need to be used individually or in combination, these classes can be used directly

The advantage of control inversion at this time: If you want to update and evolve later, as long as the new interface is compatible with the existing interface, you do not need to change the existing class code to make it compatible. This involves the covariation and contravariance of Ts, and you are interested in learning about it.
  • Dependency Injection (DI—Dependency Injection):

The dependencies between objects are referred to the outside for management, but if the dependencies between components are determined by the container at runtime, to put it figuratively, the container dynamically injects a dependency into the component

For example, the Context of react, use Context.Provider to inject data

such as decorators

 @Foo()

There are also modifiers inside smart contracts, such as access control inside

 modifier onlyOnwer(){
  require(msg.sender == onwer,'msg.sender not onwer');
  __;
}
function _mint () public onlyOnwer(){
    //dosomething
}
Dependency injection, in essence, helps simplify the process of assembling dependencies.

asyncpool implementation

Front-end concurrency control library asyncpol
ES7 implementation version

 async function asyncPool(poolLimit, array, iteratorFn) {
 const ret = []; // 存储所有的异步任务
 const executing = []; // 存储正在执行的异步任务
 for (const item of array) {
   // 调用iteratorFn函数创建异步任务
   const p = Promise.resolve().then(() => iteratorFn(item, array));
   ret.push(p); // 保存新的异步任务

   // 当poolLimit值小于或等于总任务个数时,进行并发控制
   if (poolLimit <= array.length) {
     // 当任务完成后,从正在执行的任务数组中移除已完成的任务
     const e = p.then(() => executing.splice(executing.indexOf(e), 1));
     executing.push(e); // 保存正在执行的异步任务
     if (executing.length >= poolLimit) {
       await Promise.race(executing); // 等待较快的任务执行完成
     }
   }
 }
 return Promise.all(ret);
}

ES6 implementation version:

 function asyncPool(poolLimit, array, iteratorFn) {
  let i = 0;
  const ret = []; // 存储所有的异步任务
  const executing = []; // 存储正在执行的异步任务
  const enqueue = function () {
    if (i === array.length) {
      return Promise.resolve();
    }
    const item = array[i++]; // 获取新的任务项
    const p = Promise.resolve().then(() => iteratorFn(item, array));
    ret.push(p);

    let r = Promise.resolve();

    // 当poolLimit值小于或等于总任务个数时,进行并发控制
    if (poolLimit <= array.length) {
      // 当任务完成后,从正在执行的任务数组中移除已完成的任务
      const e = p.then(() => executing.splice(executing.indexOf(e), 1));
      executing.push(e);
      if (executing.length >= poolLimit) {
        r = Promise.race(executing); 
      }
    }
 
    // 正在执行任务列表 中较快的任务执行完成之后,才会从array数组中获取新的待办任务
    return r.then(() => enqueue());
  };
  return enqueue().then(() => Promise.all(ret));
}

Summarize

The interview questions are relatively close to reality, focusing on the framework principle, front-end asynchrony, and basic investigation. These knowledge points are closely related to the debugging of complex functions in framework development. Learning the source code is an indispensable advanced process. It may be useless to learn it at the time, but after you really understand the essence, you will find that most of the excellent framework source codes are similar, including their use, ideas and concepts. The source code is the most The important thing is to help you use it when you need to debug complex scenarios in the future.

Of course, these are written based on the cognitive basis of my front-end knowledge that I haven't updated for a long time. If there are any problems, you are welcome to point them out.

Written on May 31, 2022

A web2.5 software engineer who writes smart contracts

If you feel that the writing is good, you can give a like and help pay attention to the public account: front-end peak


PeterTan
14.4k 声望30k 粉丝