введение
Друг только что занял позицию front-end разработки в Tencent Music (TME) (два года опыта), и Bengua написал об этом статью. С нетерпением жду возможности поделиться и пообщаться с интервьюерами~
Сложность интервью для самооценки: 🌕🌕🌕🌕🌑
Без лишних слов, давайте сразу к теме! Я считаю, что должно быть то, что вы хотите!
- Примечание. Хороший процесс собеседования обычно проводится в режиме чата с последовательными вопросами. Здесь извлекаются основные четыре вопроса, а сопутствующие второстепенные вопросы кратко упоминаются, поэтому я не буду их повторять.
страница связи
❝Вопрос 1: Откройте новую страницу B со страницы A, а страница B закрыта (включая неожиданный сбой), как уведомить страницу A?
Я посмотрел на этот вопрос и подумал, что речь идет об общении на html-странице. Вы не знакомы со страничным общением?
- Передает ли URL-адрес параметры?
- В случае одного и того же домена локальный кеш также может передаваться по значению;
Это действительно так? Что-нибудь еще?
Внимательно изучите вопрос. Требование: как закрытие вновь открытой B-страницы (включая непредвиденный сбой) передается обратно на A-страницу.
Так что тема должна быть разделена на:
- Когда страница B нормально закрывается, как страница B уведомляет страницу A (включая возврат параметров и контроль параметров);
- Неожиданный сбой страницы B, например, непосредственное уничтожение потока, как уведомить страницу A (включая сбои страницы мониторинга);
Мы должны ответить отдельно.
Страница B нормально закрывается
1.Первое, на что нужно ответить, — какое событие срабатывает при закрытии страницы?
Когда страница закрыта, сначала выполните window.onbeforeunload, затем выполните window.onunload.
Мы можем установить обратные вызовы в window.onbeforeunload или window.onunload.
2.Тогда ответьте, как передать параметры?
Первое, что приходит на ум: используйте метод window.open для перехода на уже открытую страницу (страницу А), и вы можете передавать параметры по URL-адресу для передачи информации.
Здесь, если вы не знаете, как перейти на уже открытую страницу, вы можете обратиться кэто, суть в том, чтобы задать имя страницы.
В браузере Chrome будет сообщено об ошибке“Blocked popup during beforeunload.”
Нашел объяснение в MDN: в спецификации HTML указано, что вызов методов window.alert(), window.confirm() и window.prompt() для этого события может завершиться ошибкой.Window: beforeunload event
В Firefox не будет сообщено об ошибке, страницу А можно открыть нормально.
3.После того, как параметры успешно переданы, как страница А отслеживает URL-адрес?
onhashchange здесь, чтобы помочь вам.Window: hashchange event: событие hashchange запускается при изменении идентификатора фрагмента URL-адреса (часть URL-адреса, следующая за знаком #, включая знак #).
Если вы передаете параметры в виде A.html?xxx, вам нужно отслеживать window.location.href. Как это сделать? Оставьте немного домашнего задания! 😀
4.Для вышесказанного, Мело сделал демонстрацию, или вы можете сделать это самостоятельно! Местный сервис поLive Server.
// страница А
<html>
<head>
</head>
<body>
<div>这是 A 页面</div>
<button onclick="toB()">点击打开 B 页面</button>
<script>
window.name = 'A' // 设置页面名
function toB() {
window.open("B.html", "B") // 打开新页面并设置页面名
}
window.addEventListener('hashchange', function () {// 监听 hash
alert(window.location.hash)
}, false);
</script>
</body>
</html>
// страница Б
<html>
<head>
</head>
<body>
<div>这是 B 页面</div>
<script>
window.onbeforeunload = function (e) {
window.open('A.html#close', "A") // url 挂参 跳回到已打开的 A 页面
return '确定离开此页吗?';
}
</script>
</body>
</html>
Показать результаты
На самом деле, также можно пройтипроход в локальный кеш, Установите мониторинг на странице A и установите LoacalStorage на странице B, эта дыня возможна для личного тестирования.
// A.html
window.addEventListener("storage", function (e) {// 监听 storage
alert(e.newValue);
});
// B.html
window.onbeforeunload = function (e) {
localStorage.setItem("name","close");
return '确定离开此页吗?';
}
Хорошо! Это ответ на проблему передачи по значению, когда новая страница обычно закрыта. Что делать, если страница неожиданно падает?
Страница B неожиданно падает
Страница B неожиданно падает, JS не запускается, как уведомить страницу A?
Бог давно дал совет:Регистрация информации о сбоях браузера?
Проще говоря: установите состояние ожидания в событии загрузки веб-страницы, измените состояние ожидания на выход в событии перед разгрузкой, если доступ к странице осуществляется во второй раз, а состояние, полученное при загрузке, является ожиданием, то оно оценивается что в последний раз на веб-странице произошел сбой, в противном случае это нормальное закрытие.
Но уместно ли хранить это состояние в sessionStorage? Если пользователь закрывает страницу, значение sessionStorage теряется.Window.sessionStorage
Так есть ли другой способ?
О: Мы можем использовать Service Worker для мониторинга сбоев веб-страницы (может быть, вы слышали о веб-воркерах, знаете разницу между ними? Выкопайте яму🕳 и засыпьте ее позже.).
- Service Worker имеет свой собственный независимый рабочий поток, который отделен от веб-страницы.В случае сбоя веб-страницы Service Worker не будет аварийно завершать работу при нормальных обстоятельствах;
- Жизненный цикл сервисного работника обычно длиннее, чем у веб-страницы, и его можно использовать для отслеживания состояния веб-страницы;
- Веб-страницы могут отправлять сообщения своему собственному Service Worker через API navigator.serviceWorker.controller.postMessage.
Конкретная реализация заключается в использованииОбнаружение сердцебиенияспособ достижения:
-
Страница B отправляет тактовый импульс своему Service Worker каждые 5 секунд, записывает статус работы и обновляет метку времени. Уведомите Service Worker, чтобы он очистил это состояние во время корректного завершения работы.
-
В случае сбоя страницы работа не будет очищена, а временная метка не будет обновлена. Service Worker проверяет временную метку каждые 10 с. Если он обнаруживает, что "состояние выполняется, а временная метка не обновлялась в течение определенного периода времени", это означает, что веб-страница B дала сбой.
Реализация кода (вы можете напрямую опубликовать тест локально):
// страница Б
<html>
<head>
</head>
<body>
<div>这是 B crash 页面</div>
<button onclick='handleCrash()'>点击我使页面崩溃</button>
<script>
function handleCrash(){
var total="";
for (var i=0;i<1000000;i++)
{
var dom = document.createElement('span');
dom.innerHTML="崩溃";
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) {
let HEARTBEAT_INTERVAL = 5 * 1000; // 每五秒发一次心跳
let sessionId = "uuid()";
let heartbeat = function () {
console.log("页面发送 state:running") // running
navigator.serviceWorker.controller.postMessage({
type: 'running',
id: sessionId,
data: {} // 附加信息,如果页面 crash,上报的附加数据
});
}
window.addEventListener("beforeunload", function () {
console.log("页面发送 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>
// service-worker.js
const CHECK_CRASH_INTERVAL = 10 * 1000; // 每 10s 检查一次
const CRASH_THRESHOLD = 15 * 1000; // 15s 超过15s没有心跳则认为已经 crash
const pages = {}
let timer
function checkCrash() {
const now = Date.now()
for (var id in pages) {
let page = pages[id]
if ((now - page.t) > CRASH_THRESHOLD) {
// 上报 crash
console.log("页面发生崩溃")
delete pages[id]
}
}
if (Object.keys(pages).length == 0) {
clearInterval(timer)
timer = null
}
}
this.addEventListener('message', (e) => {
console.log("service worker 接收", e.data.type)
const data = e.data;
if (data.type === 'running') { // 正常心跳
pages[data.id] = {
t: Date.now()
}
if (!timer) {
timer = setInterval(function () {
checkCrash()
}, CHECK_CRASH_INTERVAL)
}
} else if (data.type === 'clear') {
delete pages[data.id]
}
})
Показать результаты:
Мы видим, что использование обнаружения сердцебиения между потоками сервис-воркеров может быть отображено страница за страницей в случае сбоя. Получите результат сбоя, прежде чем вернуться на страницу A в строке (небольшая работа: собственный опыт через параметры возврата сервис-воркера).
DOM-мониторинг
❝Вопрос 2: Как реализовать мониторинг данных Vue, можете ли вы обнаружить недавно добавленные свойства привязки DOM?
Любой, кто знаком с принципом Vue2, знает, что двусторонняя привязка данных в основном зависит от захвата данных функцией Object.defineproperty(), которая имеет методы get и set, которые могут отслеживать чтение и настройку свойств объекта.
Что ж, хорошо, зная этот шаг, вы получаете возможность задавать вопросы на следующей сессии.
Может ли Object.defineproperty() прослушивать свойства DOM?
Объект мониторинга Object.defineproperty() является объектом, и коллекция атрибутов [dom.attributes] элемента Dom также является объектом, поэтому, конечно, может.
Следует отметить, что: свойство стиля, представляющее собой набор атрибутов. Ну может это тоже субатрибуты ты слушаешь?
Стучите по доске! Нам нужно ответить на недостатки Object.defineproperty():
- Невозможно отслеживать изменения в массивах: эти методы массивов не могут запускать набор: push, pop, shift, unshift, splice, sort, reverse. Прослушивание во Vue происходит из-за переопределения (хака) этих методов.
- Можно отслеживать только свойства, а не сам объект, и необходимо пройти каждое свойство объекта. Трудно отслеживать свойства, которых изначально нет в объекте. Vue использует метод Vue.set(object, propertyName, value) для добавления реактивных свойств к вложенным объектам.
Упс, чиновник уже сделал заявление.Рекомендации по обнаружению изменений
Как прослушать только что созданное свойство?
После прочтения предыдущего раздела ответ здесь очевиден: вручную прослушивать вновь созданные свойства.
- Принцип Vue.set:
Когда данные реагируют, vue добавит к данным атрибут __ob__, поэтому вы можете судить, является ли цель реагирующими данными, определяя, имеет ли целевой объект атрибут __ob__. Когда целью являются неотвечающие данные, мы обрабатываем их в соответствии со способом добавления атрибутов к обычным объектам; когда целевым объектом являются адаптивные данные, мы также устанавливаем ключ атрибута цели как отвечающий и вручную запускаем и уведомляем об обновлении значение его атрибута;
defineReactive(ob.value, key, val)
ob.dep.notify()
В конце этого раздела позвольте мне задать еще один или два вопроса:
- Можете ли вы написать шаблон публикации-подписки от руки? Можно ли использовать ES5 и ES6?
- Почему Vue3 перешел на использование прокси-сервера для мониторинга данных?Можете ли вы рассказать всю историю?
ленивая загрузка
❝Вопрос 3: Что еще есть для ленивой загрузки, кроме мониторинга колесика прокрутки?
Я знаю, вы знаете: суть ленивой загрузки: ресурсы, которые не находятся в области просмотра, могут быть лениво загружены.
Вам приятно знать, что вы можете использовать колесо монитора, и вы даже знаете, как использовать дросселирование, чтобы предотвратить слишком частое срабатывание функций.
Что-нибудь еще?
Кроме наблюдения за колесом прокрутки, что еще?
- Крест-наблюдатель
использоватьIntersectionObserverИнтерфейс (из Intersection Observer API) предоставляет способ асинхронного наблюдения за пересечением целевого элемента с его предками или окном просмотра документа верхнего уровня. Элемент-предок и окно просмотра называются корнем.
Когда дочерний элемент пересекается с родительским элементом, это означает, что он попадает в видимую область.
const box = document.querySelector('.box');
const imgs = document.querySelectorAll('.img');
const observer = new IntersectionObserver(entries => {
// 发生交叉目标元素集合
entries.forEach(item => {
// 判断是否发生交叉
if (item.isIntersecting) {
// 替换目标元素Url
item.target.src = item.target.dataset.src;
// 取消监听此目标元素
observer.unobserve(item.target);
}
});
}, {
root: box, // 父级元素
rootMargin: '20px 0px 100px 0px' // 设置偏移 我们可以设置在目标元素距离底部100px的时候发送请求
});
imgs.forEach(item => {
// 监听目标元素
observer.observe(item);
});
Знаете ли вы какие-либо другие способы реализации ленивой загрузки?
Загрузка выше сгиба
❝Вопрос 4: Как рассчитать время загрузки первого экрана?
Прежде всего, мы должны понять, что такое время «загрузки верхней части страницы».
О: Время, когда пользователи могут видеть, что все элементы в области первого экрана загружены, называется временем «загрузки первого экрана». «Общее время загрузки» (загрузки) страницы должно быть больше или равно времени «загрузки выше сгиба».
Страницы, которые обычно должны учитывать время в верхней части сгиба, появляются из-за того, что в месте сгиба размещается больше графических ресурсов.
Обработка ресурса изображения является асинхронной, а длина и ширина изображения применяются к макету страницы, а затем рисуются и отображаются сверху вниз по мере получения данных изображения. А браузер ограничивает количество TCP-соединений на страницу, так что не все изображения можно загрузить и отобразить сразу.
Итак, нам нужно получить время загрузки последнего изображения на первом экране (связать событие загрузки всех изображений на первом экране), а затем вычесть время navigationStart, которое является временем «загрузки первого экрана».
首屏位置调用 API 开始统计 -> 绑定首屏内所有图片的 load 事件 -> 页面加载完后判断图片是否在首屏内,找出加载最慢的一张 -> 首屏时间
Расчет времени белого экрана?
Время белого экрана = время начала рендеринга (время первого байта + время завершения загрузки HTML) + время загрузки ресурса заголовка.
// PerformanceTiming
performance.timing.responseStart - performance.timing.navigationStart
// or 在 chrome 高版本下
(chrome.loadTimes().firstPaintTime - chrome.loadTimes().startLoadTime)*1000
Время действия пользователя (т. е. document.ready)
performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart
onload Общее время загрузки?
performance.timing.loadEventEnd - performance.timing.navigationStart
резюме
на шаг впереди
На самом деле, на вышеуказанный вопрос не сложно ответить, и нелегко сказать, что он прост. Ответ, который нужен интервьюеру, всегда на шаг впереди того, что можете ответить вы. Не слишком много, всего один шаг.
Просто знать, что «старая страница передает значение новой странице» недостаточно! Необходимо знать: как справиться с «обратной передачей новой страницы на старую страницу с учетом ситуации сбоя новой страницы»?
Просто знать "Object.defineproperty()" недостаточно! Необходимо знать: как контролировать добавление новых свойств в DOM?
Простого знания «отложенного слушателя прокрутки загрузки» недостаточно! Необходимо знать: как насчет других реализаций отложенной загрузки?
Просто знать "PerformanceTiming API" недостаточно! Нужно знать: как это сделать неправильно, в чем разница между показателями мониторинга и как рассчитать время загрузки ресурсов изображений?
Увы! Это считается "собеседованием по постройке ракеты и работой по завинчиванию гаек"?
не обязательно! Эти проблемы, скорее всего, будут встречаться в реальной работе.Бенгуа раньше использовала прослушивание локального кеша. PerformanceTiming также является хорошим рецептом для мониторинга производительности, и все это с целью улучшения веб-сервисов, зачем отказываться от него?
в сомнениях
Может быть, мы привыкли к бизнес-кодированию, может быть, мы привыкли сталкиваться с поисковыми системами, может быть, мы привыкли к CV, может быть, мы привыкли не думать... Но мы программисты, а не программирующие машины. Машины не должны сомневаться, но люди должны сомневаться!
Нравится
Друзья, вы все это видели здесь, почему вам не нравится?
Код текста не простой, откажитесь от проститутки.
В то же время общение также приветствуется ~
Я Энтони из Наггетс, я не безжалостен и много болтаю. С нетерпением жду прогулки с вами!
использованная литература
- Logging Information on Browser Crashes
- window.open переходит на уже открытую страницу
- Краткое введение в Progressive Web App (PWA)
- Решение для статистики сбоев на основе Service Worker
- Использование сервис-воркеров
- Принцип работы this.$set() в vue
- Ленивая загрузка с помощью Cross-Observer
В этой статье используетсяmdniceнабор текста