The introduction

A friend of mine has just been hired by Tencent Music (TME) for a position of front-end development (two years of experience). In order to share with each interviewer exchanges ~

Self-assessment interview difficulty: 🌕🌕🌕🌕

No more words, straight to the problem! I’m sure there’s something you want!

  • Note: A good interview process is usually conducted in a chat format and the questions are continuous. Here draw out the core four questions, during the small questions attached with a brush, do not repeat.

The page

Question 1: A new page B is opened from page A, and page B is closed (including an unexpected crash). How do I notify page A?

HTML page communication. You’re not familiar with the page traffic? Just

  1. Url pass parameter;
  2. The local cache can also store value passing in the same field;

Is that true? Is there anything else?

Look at the question again. The requirement is: how the newly opened B page closure (including unexpected crashes) is passed back to A page.

So the question should be broken down as:

  1. If page B closes properly, how does page B notify page A (involving parameter sending and parameter listening)?
  2. B. How to notify A of an unexpected page crash, such as A thread being killed directly (involving A listener page crash);

We should answer separately.

B The page is closed

1. What event is triggered when the page closes?

When the page closes, perform window.onbeforeUnload, then window.onunload

We can set callbacks in window.onbeforeUnload or window.onunload.

2. Then answer how to pass the parameters?

The first thing that comes to mind is to use the window.open method to jump to an already open page (A page). The URL can be hung to pass information.

Here, if you don’t know how to jump to an already open page, you can refer to this article, essentially set the page name.

Blocked popup during beforeUnload.

The HTML specification states that calls to window.alert(), window.confirm(), and window.prompt() in this event may be invalidated. Window: beforeunload event

No error will be reported in Firefox, and you can open page A normally.

3. After the parameter is successfully passed, how does page A listen for the URL?

Onhashchange is the solution for you. Window: HashChange Event: The hashChange event (the part of the URL that follows the # symbol, including the # symbol) is fired when the fragment identifier of the URL changes.

If you pass in a.html? XXX, you need to listen to window.location.href. How do you do that? Give me a little homework! 😀

4. In view of the above, this melon made a Demo, or hands-on! Local service By Live Server.

/ / A page

<html>
<head>
</head>
<body>
<div> This is page A </div> <button onclick="toB()"</button> <script>  window.name = 'A'// Set the page name function toB() {  window.open("B.html"."B") // Open a new page and set the page name }  window.addEventListener('hashchange'.function() {/ / listeninghash  alert(window.location.hash)  }, false);  </script> </body> </html> Copy the code

B / / page

<html>
<head>
</head>
<body>
<div> This is the B page </div> <script>  window.onbeforeunload = function (e) {  window.open('A.html#close'."A"// The url hangs to jump back to the opened A page return 'Sure to leave this page? ';  }  </script> </body> </html> Copy the code

Results show

In fact, the parameters can also be passed through the local cache parameters, A page set listening, B page set loacalStorage, this friendly test is feasible.

// A.html
window.addEventListener("storage".function(e) {// Listen to storage            alert(e.newValue);
        });
// B.html
window.onbeforeunload = function (e) {  localStorage.setItem("name"."close");  return 'Sure to leave this page? ';  } Copy the code

All right! This is the answer to the pass-value problem if the new page is closed normally. What if the page crashes accidentally?

B The page crashes unexpectedly

B page crashes unexpectedly, JS will not run, how to notify A page?

Logging Information on Browser Crashes?

To put it simply: Set a pending state in the webpage onLoad event. Beforeunload event changes the pending state to exit. If the webpage is accessed twice and the state obtained in onload is pending, it determines that the webpage crashed last time. Otherwise, it is closed normally.

But is this state appropriate for sessionStorage? If the user closes the page, the sessionStorage value is lost. Window.sessionStorage

So what’s the other way?

A: We can use a Service Worker to monitor Web crashes (you may have heard of a Web Worker, do you know the difference? Dig a hole 🕳 and fill it in later. .

  1. The Service Worker has its own Worker thread, separate from the web page. If the web page crashes, the Service Worker generally does not crash;
  2. Service workers typically have a life span longer than a web page and can be used to monitor the status of a web page;
  3. Web pages can be through the navigator. ServiceWorker. Controller. PostMessage API to run their own Service Worker send messages.

The specific implementation is realized by the way of heartbeat detection:

  1. Page B sends a heartbeat to its Service Worker every 5S, records a status running and updates the timestamp. Notifies the Service Worker to clear the state when it closes normally.

  2. If the page crashes, the running will not be removed and the timestamp will not be updated. The Service Worker checks the timestamp every 10 seconds. If “The status is RUNNING and the timestamp has not been updated for a while” is found, the page B crashes.

Code implementation (can be directly posted in the local test) :

/ / B page

<html>
<head>
</head>
<body>
<div> This is the B crash page </div> <button onclick='handleCrash()'> </button> <script>  function handleCrash() { var total="";  for(var i=0; i<1000000; i++) {  var dom = document.createElement('span');  dom.innerHTML="Crash";  document.getElementsByTagName("body")[0].appendChild(dom)  }  }  if ('serviceWorker' in navigator) {  navigator.serviceWorker.register('service-worker.js', {  scope: '/'  }).then(function (registration) {  if(navigator.serviceWorker.controller ! == null) { letHEARTBEAT_INTERVAL = 5 * 1000; // The heart beats every five seconds let sessionId = "uuid()";  let heartbeat = function () {  console.log("Page send state:running") // running  navigator.serviceWorker.controller.postMessage({  type: 'running'. id: sessionId, Data: {} // Additional information. If the page crashes, the additional data is reported });  }  window.addEventListener("beforeunload".function () {  console.log("Page send state: Clear") // clear  navigator.serviceWorker.controller.postMessage({  type: 'clear'. id: sessionId  });  });  setInterval(heartbeat, HEARTBEAT_INTERVAL);  heartbeat();  }  }).catch(function (error) {  // Something went wrong during registration. The service-worker.js file  // might be unavailable or contain a syntax error.  });  } else {  // The current browser doesn't support service workers.  }  </script> </body> </html> Copy the code

// service-worker.js

const CHECK_CRASH_INTERVAL = 10 * 1000; // Check every 10sconst CRASH_THRESHOLD = 15 * 1000; // If no heartbeat beats within 15 seconds, the system crashesconst pages = {}
let timer

function checkCrash() {  const now = Date.now()  for (var id in pages) {  let page = pages[id]  if ((now - page.t) > CRASH_THRESHOLD) { / / report to crash console.log("Page crashes.")  delete pages[id]  }  }  if (Object.keys(pages).length == 0) {  clearInterval(timer)  timer = null  } }  this.addEventListener('message', (e) => {  console.log("Service Worker receives", e.data.type)  const data = e.data;  if (data.type === 'running') {// Normal heartbeat pages[data.id] = {  t: Date.now()  }  if(! timer) { timer = setInterval(function () {  checkCrash()  }, CHECK_CRASH_INTERVAL)  }  } else if (data.type === 'clear') {  delete pages[data.id]  } }) Copy the code

Effect display:

We can see that the heartbeat detection between the service-worker thread and the page can tell if the page is crashing. Get the result of the crash, and then back to the A page (small job: you can experience sending parameters through the service-worker).

The DOM to monitor

Question 2: How do I implement Vue data listening? Can I detect the newly bound properties of DOM?

If you know how Vue2 works, you’ll know that bidirectional data binding relies on the hijacking of object.defineProperty (), which has get and set methods that listen for the reading and setting of Object properties.

Well, good, knowing that, you’ve got the opportunity to be asked questions in the next section.

Can object.defineProperty () listen for DOM properties?

Object.defineproperty() monitors objects, and Dom elements’ dom.Attributes collection is also objects, so yes.

One thing to note is the: style property, which is a collection of properties. So, can its children be listened on?

On the blackboard! We need to answer the problem of object.defineProperty () :

  1. Cannot listen for array changes: array methods cannot trigger sets :push, pop, shift, unshift,splice, sort, reverse. Vue can listen because these methods have been hacked.
  2. You can only listen on properties, not the object itself, and you need to iterate over each property of the object. It is hard to listen for properties that are not in an object. Use the vue.set (object, propertyName, value) method in Vue to add reactive properties to nested objects.

Alas, the authorities have already said so. Precautions for detecting changes

How do you listen to a newly created property?

After watching the previous section, the answer is obvious: manually listen for newly created properties.

  • Vue. Set the principle:

When a piece of data is responsive, vue adds an __ob__ attribute to the data, so you can determine whether the target object is responsive by determining whether it has an __ob__ attribute. When the target is non-responsive, we treat it the same way we would add attributes to a normal object. When the target object is reactive, we set the target attribute key to reactive as well and manually trigger the notification to update its attribute values.

defineReactive(ob.value, key, val)
ob.dep.notify()
Copy the code

At the end of this section, Let bengua ask one or two more questions:

  1. Are you good at hand-writing the publish subscriber model? Is ES5 and ES6 ok?
  2. Why did Vue3 change to Proxy data listening? Can you tell me the rules?

Lazy loading

Problem 3: Lazy loading What else is there besides wheel listening?

I know you know: the heart of lazy loading: resources that are not visible can be loaded lazily.

You are good enough to know that you can use the monitor wheel and even know how to use throttling to prevent functions from being fired at high frequencies.

Anything else?

Besides the listening wheel, what else?

  • Cross observer

The IntersectionObserver interface (IntersectionObserver API) is used to provide a method of asynchronously observing the intersecting state of the target element and its ancestor or top-level document viewport. The ancestor element and viewport are called root.

When the child element intersects with the parent element, it is visible.

const box = document.querySelector('.box');
const imgs = document.querySelectorAll('.img');

const observer = new IntersectionObserver(entries => {
// The cross target element collection occurs entries.forEach(item => { // Determine whether the crossover occurred if (item.isIntersecting) { // Replace the target element Url item.target.src = item.target.dataset.src; // Cancel listening on this target element observer.unobserve(item.target);  }  }); }, { Root: box, // parent element rootMargin: '20px 0px 100px 0px'We can set the request to be sent when the target element is 100px from the bottom});  imgs.forEach(item => { // Listen for the target element observer.observe(item); }); Copy the code

Do you know of any other ways to implement lazy loading?

The first screen to load

Question 4: How to calculate the first screen loading time?

First, we need to understand what “first screen load” time is.

A: The “first screen load” time is the time the user can see that all elements in the first screen area have been loaded. The total load time (OnLoad) of a page must be greater than or equal to the first screen load duration.

Most pages that need to consider the first screen time are those that put more image resources in the first screen location.

Image resource processing is asynchronous, the first image length and width will be applied to the page layout, and then as the image data received from top to bottom drawing display. And browsers limit the number of TCP connections per page, so not all images can be downloaded and displayed immediately.

So we need to get the load time of the last image in the first screen (which binds to the load event of all images in the first screen) and subtract the navigationStart time to get the “first screen load” time.

First screen location call API start statistics -> bind the load event of all images in the first screen -> determine whether the image is in the first screen after the page is loaded, find the one with the slowest load -> first screen timeCopy the code

White screen time calculation?

White screen time = start rendering time (first byte time +HTML download completion time)+ header resource loading time.

// PerformanceTiming
performance.timing.responseStart - performance.timing.navigationStart

// Or in the higher version of Chrome
(chrome.loadTimes().firstPaintTime - chrome.loadTimes().startLoadTime)*1000 Copy the code

User operable time (document.ready)

performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart
Copy the code

Onload Total download time?

performance.timing.loadEventEnd - performance.timing.navigationStart
Copy the code

summary

A deep step

In fact, the above question is not difficult to say, say simple is indeed not simple. The interviewer is always looking for a deeper answer than you can give. Not too much, just one step.

Only know “old page pass value to new page”, not enough! You need to know: How to handle “new page passing back to old page and considering new page crash”?

“Object.defineproperty()” is not enough! Need to know: How do I monitor the addition of new DOM properties?

“Lazily loaded scrolling listener” is not enough! Need to know: What other implementations of lazy loading?

Only know “PerformanceTiming API”, not enough! Need to know: the specific is how to do poor, the differences between the monitoring indicators, how to time the image resource loading?

Alas! Is that “build rockets for interviews, screw for jobs”?

Not necessarily! These problems are very likely to be encountered in the actual work, this melon has used to listen to local cache before. PerformanceTiming is more of a good way to monitor performance, all in order to make a better Web service, why refuse?

question

Maybe we’re used to business coding, maybe we’re used to search engine oriented, maybe we’re used to CV, maybe we’re used to not thinking about…… But we are programmers, not programming machines. The machine can not produce doubt, but people need to constantly produce doubt!

O praise

Friend, all see here, still don’t click a “like”?

Code text is not easy, refuse white prostitute.

Also welcome to exchange ha ~

I am Anthony of nuggets, person not hard, also talk a lot. Looking forward to traveling with you!

reference

  • Logging Information on Browser Crashes
  • Window. open Jumps to an already open page
  • A brief introduction to the Progressive Web App(PWA)
  • Crash statistics scheme based on Service Worker
  • Use Service Workers
  • How this.$set() works in vue
  • The cross observer implements lazy loading

This article was typeset using MDNICE