Зафиксируйте опыт фронтенд-разработчиков с конца марта по апрель

опрос

b840c72e59dd5b880ed68bb8d10b6c62.jpeg

Статьи будут постоянно обновляться

1. принцип https (сертификат шифрования)

  1. Клиент использует URL-адрес https для доступа к веб-серверу и требует установить ssl-соединение с сервером.
  2. После того, как веб-сервер получит запрос клиента, он отправит копию сертификата веб-сайта (включая открытый ключ) клиенту.
  3. После того, как клиент получит сертификат веб-сайта, он проверит центр сертификации и время истечения срока действия, и, если нет проблем, он случайным образом сгенерирует секретный ключ.
  4. Клиент шифрует сеансовый ключ открытым ключом и передает его серверу, а сервер расшифровывает сеансовый ключ своим закрытым ключом.
  5. После этого сервер и клиент используют секретный ключ для шифрования передачи.

2. Сетевая безопасность (принцип и меры защиты от атак XSS и CSRF, аутентификация с помощью токена, файлы cookie и сеанс)

XSS

  1. XSS: атака с использованием межсайтовых сценариев, полное название атаки с использованием межсайтовых сценариев.

    XSS означает, что злоумышленники добавляют некоторые коды и встраивают их в веб-страницы, используя недостатки веб-сайтов, которые не ускользают от пользовательских данных или имеют недостаточную фильтрацию. Так что доступ других пользователей будет выполнять соответствующий встроенный код. Это метод атаки, который крадет пользовательские данные, использует личность пользователя для выполнения определенных действий или заражает посетителей вирусами.

  2. К опасностям XSS-атак относятся:

    1. Кража различных учетных записей пользователей, таких как учетные записи для входа в систему, учетные записи пользователей онлайн-банкинга и различные учетные записи администраторов.

    2. Контролируйте корпоративные данные, включая возможность чтения, изменения, добавления и удаления конфиденциальных корпоративных данных.

    3. Хищение важной и коммерчески ценной информации предприятия

    4. Незаконная передача

    5. Принудительно отправлять электронные письма

    6. Подвесная лошадь на сайте

    7. Управляйте компьютером жертвы, чтобы запускать атаки на другие веб-сайты.

  3. Анализ причин

    Основная причина: слишком большое доверие к данным, представленным клиентом!

    Решение: не доверяйте данным, отправленным клиентом, поскольку данные, отправленные клиентом, должны быть соответствующим образом отфильтрованы, прежде чем переходить к следующему шагу.

    дальнейший анализ: Данные, отправленные самим клиентом, требуются приложению, но отвратительный злоумышленник использует доверие веб-сайта к клиенту для отправки отправленных данных, вставляет в данные некоторые символы и код JavaScript, после чего эти данные станут частью кода приложения. ., то злоумышленник может начать атаку недобросовестно

    Поэтому мы никогда не должны доверять данным, представленным любым клиентом.

  4. Типы

    Постоянные (сохраненные) и непостоянные (отражение)

    Постоянный: Постоянный означает, что код атаки записывается в базу данных.Эта атака очень вредна.Если веб-сайт имеет большое количество посещений, это приведет к большому количеству страниц, к которым обычно обращаются, что приведет к большое количество пользователей, которые обычно посещают страницу, подвергаются атаке. Наиболее типична XSS-атака доски объявлений.

    Непостоянный: непостоянный XSS означает, что при отправке запроса код XSS появляется в запрошенном URL-адресе и отправляется на сервер в качестве параметра, а сервер анализирует и отвечает. Результат ответа содержит XSS-код, который, наконец, анализируется и выполняется браузером.Концептуально видно, что отраженный XSS-код сначала появляется в URL-адресе, затем его необходимо проанализировать сервером и, наконец, требуется, чтобы браузер атаковал XSS-код.

  5. способ защиты

    Два способа защиты

    1. Экранирующие символы

    • Во-первых, вводу пользователя никогда нельзя доверять.Наиболее распространенной практикой является экранирование содержимого ввода и вывода, а также экранирование скобок, угловых скобок и косых черт.

    2. CSP

    • Политика безопасности контента (CSP) — это дополнительный уровень безопасности, используемый для обнаружения и смягчения определенных типов атак, включая межсайтовые сценарии (XSS) и атаки с внедрением данных. Будь то кража данных, загрязнение содержимого веб-сайта или распространение вредоносных программ, эти атаки являются основными средствами

      Основная цель CSP — сократить количество XSS-атак и сообщить о них, которые используют доверие браузера к содержимому, полученному с сервера. Вредоносные скрипты запускаются в браузере жертвы, потому что браузер доверяет источнику контента, хотя иногда скрипт находится не там, откуда должен быть.

      CSP позволяют администраторам серверов уменьшать или устранять векторы, на которые опираются XSS-атаки, путем указания действительных доменов, то есть действительных источников исполняемых скриптов, распознаваемых браузерами. Браузер, совместимый с CSP, будет выполнять только файлы сценариев, полученные из доменов из белого списка, игнорируя все остальные сценарии (включая встроенные сценарии и атрибуты обработки событий HTML).

      В качестве окончательной формы защиты сайты, которые никогда не разрешают выполнение сценариев, могут полностью заблокировать выполнение сценариев.

      Суть CSP заключается в установлении белого списка.Разработчик четко сообщает браузеру какие внешние ресурсы можно загружать и выполнять.Нам нужно только настроить правила.Как перехватывать реализовано самим браузером.В этом мы можем минимизировать XSS атаки способ.

    Как включить CSP (если используете CSP)

      1. Вы можете указать свою политику, используя HTTP-заголовок Content-Security-Policy следующим образом:
    设置HTTP Header中的Content-Security-Policy: policy
    
      1. Как установить метатеги
    meta http-equiv=“Content-Security-Policy” content=“default-src ‘self’; img-src https://*; child-src ‘none’;”>
    

    Общие варианты использования (например, настройка заголовка HTTP)

    • Веб-мастер хочет, чтобы на сайте размещался весь контент из одного и того же источника (за исключением его поддоменов), и разрешает загрузку только ресурсов этого сайта.
    Content-Security-Policy: default-src ‘self’
    
    • Веб-мастер разрешает контент из доверенных доменов и их субдоменов (домен не обязательно должен совпадать с доменом, на котором настроен CSP)
    Content-Security-Policy: default-src ‘self’ *.trusted.com
    
    • Веб-мастер разрешает пользователям веб-приложений включать изображения из любого источника в свой собственный контент, но ограничивает аудио или видео от надежных поставщиков ресурсов (полученных), и все сценарии должны получать надежный код с определенного хост-сервера.
    Content-Security-Policy: default-src ‘self’; img-src *; media-src media1.com media2.com; script-src userscripts.example.com
    
    • Разрешить загрузку только изображений протокола HTTPS
    Content-Security-Policy: img-src https://*
    
    • Разрешить загрузку любого исходного кадра
    Content-Security-Policy: child-src ‘none’
    

    Для этого требуется, чтобы разработчик настроил правильные правила, поэтому даже если на веб-сайте есть уязвимости, злоумышленник не может выполнить свой код атаки, а совместимость CSP хорошая.

CSRF

1 Основные понятия

CSRF (Подделка межсайтовых запросов) Подделка межсайтовых запросов, также известная как «Атака одним щелчком мыши» или Session Riding, обычно сокращенно обозначаемая как CSRF или XSRF, представляет собой злонамеренное использование веб-сайтов. Хотя это звучит как межсайтовый скриптинг (XSS), он сильно отличается от XSS, который использует доверенного пользователя на сайте, и CSRF, который использует доверенный веб-сайт, маскируясь под запрос от доверенного пользователя. По сравнению с атаками XSS, атаки CSRF, как правило, менее распространены (и поэтому ресурсов для защиты от них довольно мало), и их трудно предотвратить, поэтому они считаются более опасными, чем XSS.

2 принципа

Принцип заключается в том, что злоумышленник создает адрес внутреннего запроса, чтобы побудить пользователя щелкнуть или автоматически инициировать запрос с помощью некоторых средств. Если пользователь находится в состоянии входа в систему, серверная часть считает, что пользователь работает, чтобы выполнить соответствующую логику.

То есть для завершения CSRF-атаки жертва должна последовательно выполнить два шага: 1 Войдите на надежный веб-сайт и создайте файл cookie локально 2 Доступ к опасным веб-сайтам без выхода из надежных веб-сайтов

У вас может возникнуть вопрос: если я не выполню ни одно из двух вышеперечисленных условий, я не получу CSRF-атаку. Это правда, но вы не можете гарантировать, что: 1 Вы не можете гарантировать, что после входа на веб-сайт вы больше не будете открывать вкладку и посещать другие страницы. 2 Вы не можете гарантировать, что срок действия вашего локального файла cookie истечет сразу после закрытия браузера и завершения вашего последнего сеанса (на самом деле закрытие браузера не завершает сеанс).

3 опасности (что может сделать CSRF)

Злоумышленники крадут вашу личность и отправляют вредоносные запросы от вашего имени.Что может сделать CSRF: отправлять электронные письма от вашего имени, украсть вашу учетную запись или даже покупать товары, переводить виртуальную валюту, раскрывать личную конфиденциальность и безопасность имущества.

Атака CSRF — это неявный механизм аутентификации, исходящий из WEB! Хотя механизм WEB-аутентификации может гарантировать, что запрос исходит из браузера пользователя, он не может гарантировать, что запрос одобрен пользователем!

4 Как защитить

Для предотвращения CSRF-атак можно соблюдать следующие правила:

  • Запросы GET не изменяют данные
  • Запретить сторонним веб-сайтам доступ к файлам cookie
  • Интерфейс запроса блокировки стороннего веб-сайта
  • Запрос сопровождается проверочной информацией, такой как проверочный код или токен.

SameSite

  • Атрибут SameSite может быть установлен в файле cookie, что указывает на то, что файл cookie не отправляется с междоменными запросами, что может значительно снизить атаки CSRF, но этот атрибут в настоящее время совместим не со всеми браузерами.

Проверить HTTP-заголовок Referer

  • Для запросов, которым необходимо предотвратить CSRF, мы можем проверить, инициирован ли запрос сторонним веб-сайтом, проверив Referer.

Токен подтверждения сервера токенов

  • Сервер отправляет случайный токен на сервер для проверки токена и переносит токен каждый раз, когда отправляется запрос, а сервер проверяет, действителен ли токен.

проверяющий код

  • Идея этой схемы такова: каждый пользовательский запрос требует, чтобы пользователь заполнил случайную строку на картинке в форме, а... Эта схема может полностью решить CSRF, но я лично считаю, что это не очень хорошо с точки зрения простоты использования, и слышали, что использование изображений капчи связано с ошибкой, известной как MHTML, которая может быть затронута в некоторых версиях Microsoft IE.

3. Кэш-метод

На самом деле это проблема сильного кеша и кеша согласования.Сильный кеш будет напрямую извлекать кэшированный файл, в то время как кеш согласования отправляет запрос на сервер, чтобы подтвердить, действителен ли документ.

подробно:Tickets.WeChat.QQ.com/Yes/G5FIR W O Жалоба…

Как правило, статические ресурсы, такие как js и css, должны быть сильно кэшированы, а html должен быть согласован и кэширован (без хэша).

4. Междоменный режим (прокси-сервер, политика CORS, междоменный сценарий jsonp, веб-сокет...)

  1. Реализация
  2. В каких сценариях они используются

1. Перекрестный домен JSONP

Принцип jsonp заключается в использовании тега

Недостаток jsonp: для получения можно отправить только один вид запроса.

2. Совместное использование ресурсов между источниками (CORS)

CORS — это стандарт W3C, полное название — «Совместное использование ресурсов кросс-происхождения» (Cross-origin resource sharing). Это позволяет браузерам отправлять запросы XMLHttpRequest к серверам с разными источниками, тем самым преодолевая ограничение, заключающееся в том, что AJAX можно использовать только с одним и тем же источником. CORS требует поддержки как браузера, так и сервера. В настоящее время все браузеры поддерживают эту функцию, и браузер IE не может быть ниже IE10.

Браузеры делят запросы CORS между источниками на простые запросы и сложные запросы.

Пока два условия выполняются одновременно, это простой запрос.

(1) Используйте один из следующих методов:

  • head
  • get
  • post

(2) Запрашиваемый хедер

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type: ограничено тремя значениями: application/x-www-form-urlencoded, multipart/form-data, text/plain

Если два вышеуказанных условия не выполняются одновременно, это непростая заявка. Браузеры обрабатывают эти два типа по-разному.

простой запрос

   Для простых запросов браузер напрямую выдает запросы CORS. В частности, к информации заголовка добавляется поле Origin.

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

В приведенной выше информации заголовка поле Origin используется для указания источника (протокол + доменное имя + порт), из которого исходит этот запрос. На основе этого значения сервер решает, соглашаться ли на запрос.

Все поля заголовка ответа, установленные запросами CORS, начинаются с Access-Control-:

1) Access-Control-Allow-Origin: требуется

   Его значением является либо значение поля Origin на момент запроса, либо *, указывающий, что принимаются запросы от любого доменного имени.

2) Access-Control-Allow-Credentials: необязательно

   Его значение является логическим значением, указывающим, разрешать ли отправку файлов cookie. По умолчанию файлы cookie не включаются в запросы CORS. Если установлено значение true, это означает, что сервер явно разрешает включить файл cookie в запрос и отправить его на сервер вместе. Это значение может быть установлено только в true.Если сервер не хочет, чтобы браузер отправлял файлы cookie, просто удалите это поле.

3) Access-Control-Expose-Headers: необязательно

При запросе   CORS метод getResponseHeader() объекта XMLHttpRequest может получить только 6 основных полей: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma. Если вы хотите получить другие поля, вы должны указать их в Access-Control-Expose-Headers. В приведенном выше примере указано, что getResponseHeader('FooBar') может возвращать значение поля FooBar.

не простой запрос

  Непростой запрос — это тип запроса, который имеет особые требования к серверу, например метод запроса PUT или DELETE или тип поля Content-Type — application/json. Для запросов CORS, которые не являются простыми запросами, перед формальным общением будет добавлен запрос HTTP-запроса, который называется предварительным запросом.

предварительный запрос

Метод запроса, используемый для «предварительного» запроса, – OPTIONS, что означает, что запрос используется для запроса. В заголовке запроса ключевое поле – Origin, указывающее, из какого источника поступил запрос. В дополнение к полю Origin, информация заголовка «предпечатного» запроса включает в себя два специальных поля.

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0..

1) Метод запроса управления доступом: требуется

   используется для указания того, какие методы HTTP будут использоваться браузером для запросов CORS.Приведенный выше пример — PUT.

2) Access-Control-Request-Headers: необязательно

   Это поле представляет собой строку, разделенную запятыми, которая указывает дополнительные поля заголовка, которые будут отправляться запросами CORS браузера. Пример выше — X-Custom-Header.

Ответ на предварительный запрос    После того, как сервер получает «предварительный» запрос, он проверяет поля Origin, Access-Control-Request-Method и Access-Control-Request-Headers, подтверждает, что запросы между источниками разрешены, и может отвечать.

В ответе   HTTP помимо ключевого поля Access-Control-Allow-Origin есть и другие поля, связанные с CORS:

1) Access-Control-Allow-Methods: требуется

   Его значение представляет собой строку, разделенную запятыми, указывающую все методы, поддерживаемые сервером для междоменных запросов. Обратите внимание, что возвращаются все поддерживаемые методы, а не только запрошенный браузером. Это сделано для того, чтобы избежать множественных "предполетных" запросов.

2) Access-Control-Allow-Headers

   Поле Access-Control-Allow-Headers является обязательным, если запрос браузера включает поле Access-Control-Request-Headers. Это также строка с разделителями-запятыми, указывающая все поля заголовка, поддерживаемые сервером, не ограничиваясь теми, которые запрошены браузером в «предпечатной проверке».

3) Access-Control-Allow-Credentials: необязательно

   Это поле имеет то же значение, что и в простом запросе.

4) Access-Control-Max-Age: опционально

   используется для указания периода действия этого запроса предварительной проверки в секундах.

3, междоменный прокси nginx

 Кроссдоменный прокси-сервер nginx, по сути такой же, как и принцип кросс-доменности CORS, задайте заголовок ответа на запрос Access-Control-Allow-Origin... и другие поля через файл конфигурации.

1) Конфигурация nginx решает вопрос междоменного использования iconfont

Междоменный доступ браузеров к обычным статическим ресурсам, таким как js, css и img, разрешен политикой одного источника, за исключением файлов шрифтов iconfont (eot|otf|ttf|woff|svg). Конфигурация может быть добавлена ​​к серверу статических ресурсов nginx.

location / {
  add_header Access-Control-Allow-Origin *;
}

2) междоменный интерфейс обратного прокси nginx

Проблемы с несколькими источниками: политика одного источника — это только политика безопасности для браузеров. Вызов интерфейса HTTP на стороне сервера просто использует протокол HTTP и не требует политики одного и того же источника, поэтому междоменная проблема отсутствует.

Идея реализации: настроить прокси-сервер с тем же именем домена, что и у домена1, но другими портами через Nginx в качестве трамплина, обратный прокси-доступ к интерфейсу домена2, и вы можете изменить информацию о домене в файле cookie, чтобы облегчить запись текущий файл cookie домена и обеспечить междоменный доступ.

Конкретная конфигурация nginx:

#proxy服务器
server {
    listen       81;
    server_name  www.domain1.com;

    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

4. Междоменный прокси промежуточного ПО Nodejs

Промежуточное ПО ноды реализует междоменный прокси.Принцип примерно такой же, как и у nginx.Пересылка данных осуществляется путем запуска прокси-сервера.Также можно изменить доменное имя в куке в заголовке ответа, установив для параметра cookieDomainRewrite значение реализовать запись cookie текущего домена, что удобно для аутентификации входа в интерфейс.

1) Междоменный фреймворк, отличный от Vue

   Используйте node + express + http-proxy-middleware для создания прокси-сервера.

  • Интерфейсный код:
var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问http-proxy-middleware代理服务器
xhr.open('get', 'http://www.domain1.com:3000/login?user=admin', true);
xhr.send();
  • Код сервера промежуточного слоя:
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();

app.use('/', proxy({
    // 代理跨域目标接口
    target: 'http://www.domain2.com:8080',
    changeOrigin: true,

    // 修改响应头信息,实现跨域并允许带cookie
    onProxyRes: function(proxyRes, req, res) {
        res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
        res.header('Access-Control-Allow-Credentials', 'true');
    },

    // 修改响应信息中的cookie域名
    cookieDomainRewrite: 'www.domain1.com'  // 可以为false,表示不修改
}));

app.listen(3000);
console.log('Proxy server is listen at port 3000...');

2) Кросс-домен vue framework

Проект, созданный с помощью   node + vue + webpack + webpack-dev-server, интерфейс междоменных запросов, напрямую изменяет конфигурацию webpack.config.js. В среде разработки служба рендеринга vue и прокси-служба интерфейса — это один и тот же webpack-dev-server, поэтому страница и прокси-интерфейс больше не являются междоменными.

Частичная конфигурация webpack.config.js:

module.exports = {
    entry: {},
    module: {},
    ...
    devServer: {
        historyApiFallback: true,
        proxy: [{
            context: '/login',
            target: 'http://www.domain2.com:8080',  // 代理跨域目标接口
            changeOrigin: true,
            secure: false,  // 当代理某些https服务报错时用
            cookieDomainRewrite: 'www.domain1.com'  // 可以为false,表示不修改
        }],
        noInfo: true
    }
}

5. междоменный документ.домен + iframe

Это решение ограничено сценариями междоменных приложений с одним и тем же основным доменом и разными поддоменами. Принцип реализации: Обе страницы через js принудительно устанавливают document.domain как базовый основной домен, и реализуется один и тот же домен.

6. перекрестный домен location.hash + iframe

Принцип реализации: a хочет общаться с b через домены и реализует это через промежуточную страницу c. Для трех страниц для передачи значений между разными доменами используется location.hash iframe, а для связи между одними и теми же доменами используется прямой js-доступ.

Конкретная реализация: Домен A: a.html -> Домен B: b.html -> Домен A: c.html, разные домены a и b могут обмениваться данными только в одностороннем порядке через хеш-значение, а b и c также являются разными доменами. может быть только однонаправленным Для связи, но c находится в том же домене, что и a, поэтому c может получить доступ ко всем объектам на странице через parent.parent.

7. междоменное имя окна + iframe

Уникальность свойства window.name: значение имени сохраняется после загрузки разных страниц (даже разных доменных имен) и может поддерживать очень длинные значения имени (2 МБ).

8. postMessage междоменный

postMessage — это API в HTML5 XMLHttpRequest Level 2 и один из немногих атрибутов окна, которые могут работать между доменами. Его можно использовать для решения следующих проблем:

  • Передача данных страницы и новое окно, которое она открывает
  • Передача сообщений между несколькими окнами
  • Страницы с вложенными сообщениями iframe
  • Междоменная передача данных в описанных выше трех сценариях

Использование: метод postMessage(data, origin) принимает два параметра:

  • data: Спецификация html5 поддерживает любой базовый тип или воспроизводимый объект, но некоторые браузеры поддерживают только строки, поэтому лучше всего использовать JSON.stringify() для сериализации при передаче параметров.
  • origin: протокол + хост + номер порта, также может быть установлено в "*", что означает, что его можно передать любому окну. Если вы хотите указать тот же источник, что и текущее окно, установите его в "/".

9. Междоменный протокол WebSocket

Протокол WebSocket — это новый протокол в HTML5. Он реализует полнодуплексную связь между браузером и сервером и позволяет осуществлять междоменную связь, что является хорошей реализацией технологии push-уведомлений сервера. Нативный WebSocket API неудобен в использовании, мы используем Socket.io, который хорошо инкапсулирует интерфейс webSocket, предоставляет более простой и гибкий интерфейс и обеспечивает обратную совместимость для браузеров, не поддерживающих webSocket.

резюме

Выше приведены 9 распространенных междоменных решений, jsonp (поддерживает только запросы get, поддерживает старые браузеры IE) подходит для загрузки js, css, img и других статических ресурсов разных доменных имен; CORS (поддерживает все типы HTTP-запросов, но просмотр сервера IE10 или ниже не поддерживается) подходит для различных междоменных запросов ajax; принципы междоменного междоменного прокси-сервера Nginx и междоменного промежуточного программного обеспечения nodejs аналогичны, как построение сервера, так и запрос HTTP-интерфейса непосредственно на стороне сервера, что подходит для разделения front-end и back-end.В проекте настраивается back-end интерфейс. document.domain+iframe подходит для междоменных запросов с одним и тем же основным доменным именем и разными поддоменами. postMessage и websocket — новые функции HTML5, совместимость не очень хорошая, применима только к основным браузерам и IE10+.

5. Понимание трехстороннего рукопожатия TCP и четырехсторонней волны

Раз, три объяснения рукопожатия

  • Клиент отправляет битовый код как syn = 1 и случайным образом генерирует на сервер пакет данных с порядковым номером = 1234567. Сервер знает из SYN = 1, что клиент запрашивает установление соединения (клиент: я хочу соединить вас)
  • После того, как сервер получит запрос, он должен подтвердить онлайн-информацию и отправить номер подтверждения = (последовательность клиента + 1), syn = 1, подтверждение = 1 на А и случайным образом сгенерировать пакет с последовательностью = 7654321 (сервер: ОК, давай подключайся)
  • После того, как клиент его получит, проверьте правильность номера подтверждения, то есть номер последовательности +1, отправленный в первый раз, и равен ли битовый код подтверждения 1. Если он правильный, клиент отправит номер подтверждения =( seq+1 сервера), ack снова =1, после того как сервер получит его и подтвердит значение seq и ack=1, соединение будет успешно установлено. (Клиент: Хорошо, я пришел)

2. Почему для установления HTTP-соединения требуется трехстороннее рукопожатие, а не двух- или четырехстороннее?

Ответ: Три раза — наименее безопасное число, два раза — небезопасно, а четыре раза — пустая трата ресурсов;

3. TCP закрывает процесс соединения

  1. Клиент отправляет серверу пакет FIN, указывая на то, что клиент активно хочет закрыть соединение, а затем переходит в состояние FIN_WAIT_1, ожидая, пока сервер вернет пакет ACK. После этого клиент больше не может отправлять данные на сервер, но может читать данные.

  2. После получения FIN-пакета сервер отправляет клиенту ACK-пакет, а затем переходит в состояние CLOSE_WAIT, после чего сервер больше не может читать данные, но может продолжать отправлять данные клиенту.

  3. После того, как клиент получает пакет ACK, возвращенный сервером, он переходит в состояние FIN_WAIT_2 и ожидает, пока сервер отправит пакет FIN.

  4. После завершения отправки данных сервер отправляет клиенту пакет FIN, затем переходит в состояние LAST_ACK и ждет, пока клиент вернет пакет ACK, после чего сервер не может ни прочитать, ни отправить данные.

  5. После получения пакета FIN клиент отправляет пакет ACK на сервер, затем переходит в состояние TIME_WAIT, затем ждет достаточно долгое время (2MSL), чтобы убедиться, что сервер получил пакет ACK, и, наконец, возвращается в состояние CLOSED для освободить сетевые ресурсы.

  6. После того, как сервер получает пакет ACK, возвращенный клиентом, он возвращается в состояние CLOSED и освобождает сетевые ресурсы.

4. Зачем махать четыре раза?

TCP является полнодуплексным каналом Полнодуплексный означает, что клиент и сервер устанавливают два канала Канал 1: выход клиента соединяется со входом сервера Канал 2: вход клиента соединяется с вывод сервера. Одновременно могут работать два канала: клиент может послать сигнал серверу, а сервер также может послать сигнал клиенту. Итак, когда двойной канал закрыт, это выглядит так:

Клиент: Я закрываю входной канал.

Сервер: Хорошо, можете закрыть, я тоже закрываю этот канал на своей стороне.

Сервер: Мне тоже нужно закрыть входной канал.

Клиент: Хорошо, закройте, я тоже закрою этот канал.

6. Последовательность Фибоначчи

1. Рекурсия

function f(n) {
	if (n === 1 || n === 2) {
    	return 1;
    } else {
    	return f(n-1) + f(n-2);
    }
}

Временная сложность: O(2^N)

Пространственная сложность: O(N)

Временная сложность экспоненциальна, что относится к функции взрывного приращения, и нам следует избегать такой сложности при разработке программы.

Реализация кода последовательности Фибоначчи с помощью рекурсии относительно проста, но когда n относительно велико, это вызовет переполнение стека и не сможет выполнить желаемую функцию.

  1. хвостовой вызов
function f(n, ac1=1, ac2=1) {
	if (n<=2) {
    	return ac2;
    } 
    return f(n-1, ac2, ac1+ac2);
}
  1. итератор
function* f(){
	let [prev, curr] = [0, 1];
    // for(;;)相当于死循环 等于while(1)
    for(;;) {
    	yield curr;
        [prev, curr] = [curr, prev + curr];
    }
}
for(let n of f()) {
	if (n > 1000) break;
    console.log(n);
}
  1. рекурсия с кешем
function memozi(fn) {
	var r = {};
    return function(n) {
    	if (r[n] == null) {
        	r[n] = fn(n);
            return r[n];
        } else {
        	return r[n];
        }
    }
}

var fibfn = memozi(function(n) {
	if (n==0) {
    	return 0;
    } else if (n==1) {
    	return 1;
    } else {
    	return fibfn(n-1) + fibfn(n-2)
    }
})

Здесь используются знания, связанные с замыканиями, поэтому запрашиваются знания, связанные с «замыканиями» (подробности см. в вопросе 15).

7. Вопросы по программированию

Учитывая строку, содержащую только '(', ')', '{', '}', '[', ']', проверьте, является ли строка допустимой. Действительная строка должна удовлетворять: Открывающие скобки должны быть закрыты закрывающими скобками того же типа. Левые скобки должны быть закрыты в правильном порядке. Обратите внимание, что пустые строки могут считаться допустимыми строками.

var isValid = function(s) {
	const n = s.length;
    if (n % 2 === 1) {
    	return false;
    }
    const pairs = new Map([
    	[')','('],
        [']','['],
        ['}','{']
    ]);
    const stk = [];
    for(let i=0; i<s.length; i++) {
    	if (pairs.has(s[i])) {
        	if (!stk.length || stk[stk.length-1] !== pairs.get(s[i])) {
            	return false;
            }
            stk.pop();
        } else {
        	stk.push(s[i]);
        }
    }
    return !stk.length;
}

8. Отправьте данные двух структур данных

Разница и принцип передачи URL по значению и отправки формы

разница:

1. Значение прохода URL-адреса — get, from form — post, get — получение данных с сервера, post — передача данных на сервер

2. Для метода get сервер использует Request.QueryString для получения значения переменной, для метода post сервер использует Request.Form для получения отправленных данных.

3. Объем данных, передаваемых get, невелик и не может превышать 2 КБ. Объем данных, передаваемых по почте, относительно велик и обычно по умолчанию неограничен.

4. Уровень безопасности Get очень низкий, уровень безопасности Post высокий.

9. Оптимизация производительности

    1. Уменьшить HTTP-запросы
    1. Использовать HTTP2
    1. Использовать рендеринг на стороне сервера
    1. Статические ресурсы используют CDN
    1. Поместите CSS вверху файла, а файлы JavaScript внизу.
    1. Используйте значки шрифтов iconfont вместо значков изображений
    1. Эффективно используйте кеш и не загружайте одни и те же ресурсы повторно.
    1. Сжатый файл
    1. Оптимизация изображения
    1. Загружайте код по запросу через веб-пакет, извлекайте код третьей библиотеки и уменьшайте избыточный код с ES6 до ES5.
    1. Уменьшить перерисовку
    1. Использование делегирования событий
    1. Помните о местонахождении программы
    1. если-иначе против переключателя
    1. Справочная таблица
    1. Избегайте задержки страницы
    1. Используйте requestAnimationFrame для реализации визуальных изменений.
    1. Использование веб-воркеров
    1. Используйте битовые манипуляции
    1. Не переопределяйте нативные методы
    1. Уменьшите сложность селекторов CSS
    1. Используйте flexbox вместо старых моделей макетов
    1. Анимация с использованием изменений свойств преобразования и непрозрачности
    1. Используйте правила с умом и избегайте чрезмерной оптимизации

10. Процесс и оптимизация браузера от ввода до отрисовки содержимого страницы

  • 1. Разрешение DNS
  • 2. Этап TCP
  • 3. Этап HTTP
  • 4. Этап парсинга/рендеринга
  • 5. Макет макета/отрисовки страницы

оптимизация:

1. Сообщите браузеру, что мы можем получить ресурсы с определенного URL-адреса в будущем с помощью предварительного разрешения DNS, и когда браузер фактически использует ресурс в домене, разрешение DNS может быть завершено как можно скорее.

Шаг 1. Включите или отключите предварительное разрешение DNS

Вы можете сделать это, отправив заголовок X-DNS-Prefetch-Control на стороне сервера. Или используйте метатег со значением http-equiv в документе:

<meta http-equiv="x-dns-prefetch-control" content="on">

Следует отметить, что в некоторых продвинутых браузерах для всех гиперссылок (тегов ) на странице предварительное разрешение DNS включено по умолчанию. Однако, если на странице используется протокол https, многие браузеры по умолчанию отключают предварительное разрешение DNS для гиперссылок. Если указанная выше строка кода добавлена, это означает, что предварительный анализ браузера принудительно включен. (Если вы можете сказать это предложение в интервью, это должно быть то, чем вы выделяетесь)

Шаг 2. Выполните предварительное разрешение DNS для указанного доменного имени.

Если в будущем мы можем получить изображения или аудиоматериалы с smyhvae.com, мы можем включить в теги в верхней части документа следующее:

<link rel="dns-prefetch" href="http://www.smyhvae.com/">

Когда мы запрашиваем ресурс с этого URL-адреса, нам больше не нужно ждать разрешения DNS. Этот метод особенно полезен для использования сторонних ресурсов.

11. Функция защиты от сотрясений и дросселирования

1. Отказаться

function debounce (f, wait) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      f(...args)
    }, wait)
  }
}

сцены, которые будут использоваться

  • 1. Кнопки, такие как вход в систему и отправка текстовых сообщений, не позволяют пользователям нажимать слишком быстро, поэтому отправляется несколько запросов, и требуется защита от сотрясений.
  • 2. При изменении размера окна браузера количество раз изменения размера слишком велико, что приводит к слишком большому количеству вычислений. В настоящее время он должен быть на месте один раз, поэтому используется защита от сотрясений.
  • 3. Текстовый редактор сохраняет в реальном времени, и сохраняет его через одну секунду без каких-либо изменений

2. Дросселирование

function throttle (f, wait) {
  let timer
  return (...args) => {
    if (timer) { return }
    timer = setTimeout(() => {
      f(...args)
      timer = null
    }, wait)
  }
}

сцены, которые будут использоваться

  • 1. прокручивать событие, вычислять информацию о местоположении каждую секунду и т. д.
  • 2. События воспроизведения браузера, информация о прогрессе рассчитывается каждую секунду и т. д.
  • 3. Поле ввода выполняет поиск в режиме реального времени и отправляет запрос на отображение выпадающего списка, а также отправляет запрос каждую секунду (его также можно использовать для защиты от сотрясений)

Резюме (краткий ответ)

  • Anti-shake: чтобы предотвратить дрожание, триггер события будет сброшен в течение единицы времени, чтобы избежать многократного запуска события из-за случайной травмы. Реализация кода фокусируется на очистке clearTimeout. Антишейк можно сравнить с ожиданием лифта, пока входит один человек, ему нужно какое-то время подождать. Бизнес-сценарии имеют дублированные представления, чтобы избежать многократного нажатия кнопки входа.
  • Регулирование: для управления потоком событие может запускаться только один раз в единицу времени, аналогично ограничению скорости на стороне сервера. Реализация кода фокусируется на разблокировке и закрытии блокировки timer=timeout; timer=null. Троттлинг можно сравнить с прохождением светофора, и вы можете пропускать батч каждый раз, когда ждете красный сигнал светофора.

12. Принцип обещания ES6, чем подробнее, тем лучше, отличие от Generator

Рукописное обещание (простая версия)

function myPromise(fn) {
    this.cbs = [];
    const resolve = (value) => {
        setTimeout(() => {
            this.data = value;
            this.cbs.forEach((cb) => cb(value));
        })
    }
    fn(resolve);
}
myPromise.prototype.then = function (onResolved) {
    return new myPromise((resolve) => {        
        this.cbs.push(() => {
            const res = onResolved(this.data);
            if (res instanceof myPromise) {
                res.then(resolve);
            } else {
                resolve(res);
            }
        });
    });
}
export default myPromise;

Детали обещанияНаггетс.Талант/пост/690555…

13. Как запретить js доступ к файлам cookie

Set-Cookie: name=value; HttpOnly

14. Способ реализации фиксированного левого и адаптивного двухколоночного макета справа

HTML-макет:

<div class="outer">
   <div class="sidebar">固定宽度区(sideBar)</div>
    <div class="content">自适应区(content)</div>
</div>
<div class="footer">footer</div>

метод:

1. Плавающий левый div и установите margin-left на правом div

/*方法1*/
.outer{overflow: hidden; border: 1px solid red;}
.sidebar{float: left;width:200px;height: 150px; background: #BCE8F1;}
.content{margin-left:200px;height:100px;background: #F0AD4E;}
  1. flex
/*方法2*/
.outer7{display: flex; border: 1px solid red;}
.sidebar7{flex:0 0 200px;height:150px;background: #BCE8F1;}
.content7{flex: 1;height:100px;background: #F0AD4E;}

Flex можно назвать лучшим решением, с меньшим количеством кода и простым в использовании. Зато есть совместимость, и однажды все перейдут на современный браузер, и он будет работать.

Следует отметить, что значение свойства по умолчанию flex-контейнера: align-items: stretch;. Это свойство приводит к эффекту равной высоты столбцов. Для того, чтобы сделать высоту двух блоков автоматически, вам нужно установить: align-items: flex-start;

  1. float + метод BFC
/*方法3*/
.outer6{overflow: auto; border: 1px solid red;}
.sidebar6{float: left;height:150px;background: #BCE8F1;}
.content6{overflow:auto;height:100px;background: #F0AD4E;}

В этом решении также используется левосторонний float, но правый блок формирует BFC с overflow: auto;, поэтому правый блок не перекрывает плавающий элемент.

Расширенный вопрос: поскольку в коде используется гибкий макет, я спросил о знаниях, связанных с гибким макетом.

Flex — это аббревиатура от Flexible Box, что означает «гибкая компоновка», которая используется для обеспечения максимальной гибкости блочной модели. Любой контейнер можно указать в качестве макета Flex. Существует два типа контейнеров: блочные гибкие и встроенные гибкие.

.box{
	display: flex; /*webkit需要加前缀*/
    /*display:inline-flex;*/
}

Flex-макет имеет два слоя.Элемент, использующий flex-макет, называется flex-контейнером, а его дочерние элементы автоматически являются flex-элементами, то есть элементами. Примечание: flex отличается от block, свойства float, clear, vertical-align дочерних элементов flex-контейнера будут недействительными.

Гибкая компоновка:

  1. Flex-контейнер имеет две оси: горизонтальная главная ось — это ось X (главная ось), а вертикальная ось — это также ось Y (поперечная ось). Относительное положение двух осей определяется следующим образом:

  2. свойства гибкого контейнера:

  • flex-direction: определяет направление расположения элементов.

  • flex-wrap: то есть, как свернуть, когда ось не подходит.

  • flex-flow: сокращение для свойства flex-direction и свойства flex-wrap Значение по умолчанию — row nowrap.

  • justify-content: определяет выравнивание элементов по главной оси. (оправдывать)

  • align-items: определяет, как элементы выравниваются по поперечной оси.

  • align-content: определяет выравнивание нескольких осей. Это свойство не действует, если элемент имеет только одну ось. (новые строки создадут несколько осей)

Свойства элемента Flex:

  • порядок: определяет порядок сортировки элементов. Чем меньше значение, тем выше рейтинг, по умолчанию 0.

  • flex-grow: определяет коэффициент увеличения элемента, если все элементы имеют свойство flex-grow, равное 1, они будут поровну делить оставшееся пространство (если оно есть). Если один элемент имеет свойство flex-grow, равное 2, а все остальные элементы равны 1, первый займет в два раза больше оставшегося места, чем другие элементы.

  • flex-shrink: определяет коэффициент уменьшения элемента, по умолчанию 1, если свойство flex-shrink всех элементов равно 1, когда места недостаточно, они будут пропорционально уменьшены. Если свойство flex-shrink одного элемента равно 0, а других элементов равно 1, первый не будет сжиматься при недостатке места.

  • flex-basis: определяет основной размер, который занимает элемент перед выделением лишнего пространства.

  • flex: сокращение для flex-grow, flex-shrink и flex-basis, значение по умолчанию — 0 1 auto. Последние два свойства являются необязательными.

  • align-self: позволяет одному элементу иметь выравнивание, отличное от других элементов, переопределяя свойство align-items. Значение по умолчанию — auto, что означает наследование свойства align-items родительского элемента.Если родительского элемента нет, это эквивалентно растягиванию.

15. Закрытие

Что такое «замыкание».

「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。

Какова функция «замыкания».

闭包常常用来「间接访问一个变量」。换句话说,「隐藏一个变量」。

Слухи о закрытии

Замыкания вызывают утечку памяти?

неправильно.

Люди, которые так говорят, понятия не имеют, что такое утечка памяти. Утечка памяти относится к переменной, которую вы не используете (не можете получить доступ), которая по-прежнему занимает место в памяти и не может быть использована снова.

Переменные в замыкании, очевидно, нужные нам переменные (жизни), так почему же это утечка памяти?

Как появился этот слух?

из-за ИЕ. В IE есть ошибка: после того, как мы используем замыкание, IE по-прежнему не может повторно использовать переменные, на которые ссылается замыкание.

Это проблема IE, а не проблема закрытия.

Детальное объяснение:zhuanlan.zhihu.com/p/22486908

16. Принцип двусторонней привязки данных VUE

1. Двусторонняя привязка данных Vue достигается за счет захвата данных в сочетании с режимом публикации-подписки, то есть данные и представления синхронизируются, данные меняются, представления меняются, представления меняются и данные соответственно меняются;

2. Ядро: Что касается двусторонней привязки данных VUE, то ядром является метод Object.defineProperty();

3. Представьте метод Object.defineProperty().

(1) Object.defineProperty(obj, prop, descriptor) В этом синтаксисе есть три параметра, а именно obj (объект, для которого должно быть определено свойство) prop (свойство, которое должно быть определено или изменено) descriptor (конкретное изменение метод)

(2) Проще говоря, этот метод используется для определения значения. При вызове мы используем в нем метод get, а когда присваиваем значение этому свойству, используем в нем метод set;

17. процесс упаковки webpack

Обзор процесса упаковки webpack

Работающий процесс веб-пакета представляет собой последовательный процесс, от начала до конца будут последовательно выполняться следующие процессы:

  • 1. Инициализируйте параметры
  • 2. Начать компиляцию. Используйте параметры, полученные на предыдущем шаге, для инициализации объекта «Компилятор», загрузите все настроенные плагины и начните компиляцию, выполнив метод запуска объекта.
  • 3. Определите запись. Найдите все файлы записей в соответствии с записью в конфигурации.
  • 4. Скомпилируйте модуль. Начиная с файла записи, вызовите все настроенные загрузчики для компиляции модуля, затем найдите модули, от которых зависит модуль, а затем повторите этот шаг, пока все файлы, зависящие от записи, не будут обработаны на этом шаге.
  • 5. Завершение компиляции модуля После перевода всех модулей с помощью Loader на шаге 4 получается окончательно скомпилированное содержимое каждого модуля и зависимости между ними
  • 6. Выходные ресурсы: в соответствии с зависимостями между записью и модулем соберите их в блоки, содержащие несколько модулей, а затем преобразуйте каждый блок в отдельный файл и добавьте его в выходной список.Это последний шаг, который может изменить выходной контент возможность
  • 7. Вывод завершен: после определения содержимого вывода определите путь вывода и имя файла в соответствии с конфигурацией и запишите содержимое файла в файловую систему.

В приведенном выше процессе Webpack будет транслировать определенное событие в определенный момент времени, а плагин будет выполнять определенную логику после прослушивания интересующего события, и плагин может вызвать API, предоставленный Webpack, чтобы изменить текущий результат Webpack. . На самом деле вышеупомянутые семь шагов можно просто свести к трем процессам: инициализация, компиляция и вывод, и этот процесс фактически является расширением базовой модели, упомянутой выше.

18. Разница между картой и объектом

  • В объекте ключ должен быть простого типа данных (целое число, строка или символ), а в карте это могут быть все типы данных, поддерживаемые JavaScript, то есть объект может использоваться в качестве ключа элемента карты. .

  • Порядок элементов Map соответствует порядку вставки, в то время как Object не имеет этой функции.

  • Карта наследуется от объектов Object.

  • новый экземпляр

    • Object поддерживает следующие методы создания новых экземпляров:
    var obj = {...};
    
    var obj = new Object();
    
    var obj = Object.create(null);
    
    • Карта поддерживает только один из следующих методов сборки:
    var map = new Map([[1, 2], [2, 3]]); // map = {1 => 2, 2 => 3}
    
  • доступ к данным

    • Карта Для доступа к элементам вы можете использовать нативные методы самой карты:
    map.get(1) // 2
    
    • Доступ к объекту возможен через . и [ ]
    obj.id;
    obj['id'];
    
    • Определить, доступен ли элемент на карте
    map.has(1);
    
    • Для определения того, находится ли элемент в Object, требуются следующие операции:
    obj.id === undefined;
    // 或者
    'id' in obj;
    

    Следует также отметить, что Object может использовать Object.prototype.hasOwnProperty(), чтобы определить, является ли ключ свойством самого объекта, а свойства, унаследованные от цепочки прототипов, не включаются.

  • добавить данные

    • Картами можно управлять с помощью set() :
    map.set(key, value)       // 当传入的 key 已经存在的时候,Map 会覆盖之前的值
    
    • Объект добавляет новое свойство, которое можно использовать:
    obj['key'] = value;
    obj.key = value;
    // object也会覆盖
    
  • удалить данные

    • В Object нет собственного метода удаления, мы можем использовать следующий метод:
    delete obj.id;
    // 下面这种做法效率更高
    obj.id = undefined
    

    Следует отметить, что использование удаления фактически удалит свойство из объекта, в то время как использование метода присваивания значения undefined просто становится неопределенным. Свойство по-прежнему находится в объекте, а это означает, что доступ к свойству будет по-прежнему осуществляться при использовании for … in … для обхода.

    • Карта имеет собственный метод удаления для удаления элементов:
    var isDeleteSucceeded = map.delete(1);
    console.log(isDeleteSucceeded ); // true
    // 全部删除
    map.clear();
    
  • получить размер

    • Сама карта имеет атрибут размера, который сам может поддерживать изменение размера.

    • Объект должен быть рассчитан с помощью Object.keys()

    console.log(Object.keys(obj).length); 
    
  • Iterating

    Сама карта поддерживает итерацию, а объект — нет.

    Как определить, поддерживает ли тип итерацию? Можно использовать следующие методы:

    console.log(typeof obj[Symbol.iterator]); // undefined
    console.log(typeof map[Symbol.iterator]); // function
    

Когда использовать Map и когда использовать Object?

  • Когда необходимо сохранить простой тип данных, а все ключи представляют собой строки, целые числа или символы, Object предпочтительнее, потому что Object можно создать с использованием символьной переменной, что более эффективно.

  • Объект следует использовать, когда необходимо получить доступ к свойствам или элементам в отдельной логике.

  • JSON напрямую поддерживает объект, но не карту

  • Map — это чистый хэш, а Object также имеет некоторую внутреннюю логику, поэтому при выполнении удаления будут проблемы с производительностью. Таким образом, в случаях интенсивной записи-удаления следует использовать Map.

  • Map будет поддерживать порядок элементов в порядке вставки, чего не может Object.

  • Карта работает лучше при хранении большого количества элементов, особенно когда тип ключа не может быть определен во время выполнения кода.

19. Что происходит с новой функцией

Построить вызов:

  • создать совершенно новый объект
  • Этот объект будет связан с помощью [[Prototype]], связывая [[Prototype]] этого нового объекта с объектом, на который указывает конструктор .prototype.
  • Этот новый объект будет привязан к this вызова функции
  • Если функция не возвращает другой объект, то вызов функции в новом выражении автоматически возвращает новый объект

Если функция возвращает объект, то вызов новой функции возвращает возвращенный объект этой функции, в противном случае он возвращает новый объект, созданный новой функцией.

20. Дедупликация массива

Array.from(new Set([1, 1, 2, 2]))

21. Сглаживание массива

function flatten(arr) {
  let result = [];

  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]));
    } else {
      result = result.concat(arr[i]);
    }
  }

  return result;
}

const a = [1, [2, [3, 4]]];
console.log(flatten(a));

22. Механизм цикла событий (Event Loop)

Механизм цикла событий сообщает нам порядок выполнения кода JavaScript в целом. Event Loop — это цикл событий, который относится к механизму браузеров или Node, решающему проблему, заключающуюся в том, что JavaScript не будет блокироваться при работе в одном потоке, то есть мы часто используем принцип асинхронности. Сначала выполните очередь макрозадач, затем выполните очередь микрозадач, а затем запустите следующий цикл цикла событий, сначала продолжите выполнение очереди макрозадач, а затем выполните очередь микрозадач.

  • Задача макроса: script/setTimeout/setInterval/setImmediate/I/O/UI Rendering
  • Микрозадача: process.nextTick()/Promise

Обращенные setTimeout и setInterval являются источниками задач, и что действительно входит в очередь задач, так это задачи, которые они отправляют.

приоритет

  • setTimeout = setInterval очередь
  • setTimeout > setImmediate 
  • process.nextTick > Promise

Новые микрозадачи, сгенерированные во время выполнения микрозадач, не будут перенесены на следующий цикл, а продолжат выполняться в текущем цикле.

23. Являются ли аргументы функции массивом? Знаете ли вы, как преобразовать массив, подобный массиву, в массив?

Он похож на массив, относится к категории утиного типа, выглядит как массив,

  • ... оператор
  • Array.from
  • Array.prototype.slice.apply(arguments)

24. Написание пользовательских плагинов для веб-пакетов

Типичный код плагина Webpack выглядит следующим образом:

class MyWebpackPlugin {
  constructor(options) {
  }
  
  apply(compiler) {
    // 插入钩子函数
    compiler.hooks.emit.tap('MyWebpackPlugin', (compilation) => {});
  }
}

module.exports = MyWebpackPlugin;

Далее вам нужно добавить этот плагин в webpack.config.js.

module.exports = {
  plugins:[
    // 传入插件实例
    new MyWebpackPlugin({
      param:'paramValue'
    }),
  ]
};

Webpack создаст экземпляр объекта подключаемого модуля при запуске. После инициализации объекта компилятора он вызовет метод применения экземпляра подключаемого модуля, передаст объект компилятора, и экземпляр подключаемого модуля зарегистрирует интересующие хуки в приложении. Вызовите соответствующий хук.

Для получения дополнительной информации, пожалуйста, Google

25. Реализуйте EventEmitter

function EventEmitter() {
  this.__events = {};
}
EventEmitter.VERSION = '1.0.0';

EventEmitter.prototype.on = function(eventName, listener) {
  if (!eventName || !listener) return;
  // 判断回调的 listener(或listener.listener) 是否为函数
  if (!isValidListener(listener)) {
    throw new TypeError('listener must be a function');
  }
  var events = this.__events;
  var listeners = events[eventName] = events[eventName] || [];
  var listenerIsWrapped = typeof listener === 'object';
  // 不重复添加事件, 判断是否有一样的
  if (indexOf(listeners, listener) === -1) {
    listeners.push(listenerIsWrapped ? listener : {
      listener: listener,
      once: false
    })
  }
  return this;
}

EventEmitter.prototype.emit = function(eventName, args) {
  // 通过内部对象获取对应自定义事件的回调函数
  var listeners = this.__events[eventName];
  if (!listeners) return;
  // 考虑多个 listener 的情况
  for (var i = 0; i < listeners.length; i++) {
    var listener = listeners[i];
    if (listener) {
      listener.listener.apply(this, args || []);
      // listener 中 once 为true 的进行特殊处理
      if (listener.once) {
        this.off(eventName, listener.listener)
      }
    }
  }
  return this;
}

EventEmitter.prototype.off = function(eventName, listener) {
  var listeners = this.__events[eventName];
  if (!listeners) return;
  var index;
  for (var i = 0, len = listeners.length; i < len; i++) {
    if (listeners[i] && listeners[i].listener === listener) {
      index = i;
      break;
    }
  }
  if (typeof index !== 'undefined') {
    listeners.splice(index, 1, null);
  }
  return this;
}

EventEmitter.prototype.once = function(eventName, listener) {
  // 调用 on 方法, once 参数传入 true,待执行之后进行 once 处理
  return this.on(eventName, {
    listener: listener,
    once: true
  })
}

EventEmitter.prototype.alloOff = function(eventName) {
  // 如果该 eventName 存在,则将其对应的 listeners 的数组直接清空
  if (eventName && this.__events[eventName]) {
    this.__events[eventName] = [];
  } else {
    this.__events = {};
  }
}

// 判断是否是合法的 listener
function isValidListener(listener) {
  if (typeof listener === 'function') {
    return true;
  } else if (listener && typeof listener === 'object') {
    return isValidListener(listener.listener);
  } else {
    return false;
  }
}

// 判断新增自定义事件是否存在
function indexOf(array, item) {
  var result = -1;
  item = typeof item === 'object' ? item.listener : item;
  for(var i = 0, len = array.length; i<len; i++) {
    if (array[i].listener === item) {
      result = i;
      break;
    }
  }
  return result;
}

Для связи между различными компонентами в среде Vue существует решение под названием EventBus. Подобно идее EventEmitter, его основная цель состоит в том, чтобы использовать EventBus в качестве моста для компонентов для передачи данных.Все компоненты используют один и тот же центр событий, который может регистрироваться для отправки или получения событий, и все компоненты могут получать уведомления. Он очень прост в использовании, удобство, ядром которого, собственно, и является реализация модели публикации-подписки.

26. Сборка мусора: освободите память и улучшите производительность страницы браузера.

1. Управление памятью JavaScript

Базовые типы в памяти стека могут обрабатываться непосредственно операционной системой, в то время как ссылочные типы в памяти кучи могут часто меняться, а размер не является фиксированным, поэтому движок JavaScript должен обрабатываться механизмом сборки мусора.

2. Механизм утилизации памяти Chrome

  1. Рекультивация памяти нового поколения: алгоритм очистки

  2. Повторное использование памяти старого поколения: Mark-Sweep и Mark-Compact

По-прежнему существует большая разница между методом управления памятью старого поколения и методом управления памятью нового поколения. Алгоритм Scavenge больше подходит для обработки с небольшой памятью, но когда в старом поколении большая память и много переменных, по-прежнему необходимо использовать «отметить-очистить» в сочетании с «отметить-очистить» для решения проблем с памятью, и попробовать чтобы избежать проблем с памятью.

3. Утечки памяти и оптимизации

Сцены

  • 1. Не высвобождается слишком много кеша;

  • 2. Не выпущено слишком много закрытий;

  • 3. Не сбрасывается слишком много таймеров или обратных вызовов;

  • 4. Не выпущено слишком много недействительных DOM;

  • 5. Не найдено слишком много глобальных переменных.

27. Говоря о модульной спецификации внешнего интерфейса

Разница между CommonJs и модулем Es

1.CommonJs

  • CommonJs может динамически загружать операторы, код выполняется во время выполнения

  • Смешанный экспорт CommonJs все еще является своего рода синтаксисом, но нет необходимости объявлять предыдущий объект.Когда я экспортирую эталонный объект, предыдущий экспорт перезаписывается

  • Экспортное значение CommonJs является копией, и экспортируемое значение может быть изменено.Когда код неверен, его нелегко проверить и вызвать загрязнение переменных.

2.Es Module

  • Модуль Es является статическим и не может динамически загружать операторы, его можно объявить только в верхней части файла, а код возникает во время компиляции.
  • Es Module смешанный экспорт, одиночный экспорт, экспорт по умолчанию, полностью независимые друг от друга
  • Экспорт модуля Es заключается в том, что перед ссылочным значением существует отношение сопоставления, а значение доступно для чтения и не может быть изменено.

28. CSS div центрируется по вертикали и горизонтали, а высота div всегда составляет половину ширины (ширину можно не указывать)

    html,
      body {
        width: 100%;
        height: 100%;
      }

      .outer {
        width: 400px;
        height: 100%;
        background: blue;
        margin: 0 auto;

        display: flex;
        align-items: center;
      }

      .inner {
        position: relative;
        width: 100%;
        height: 0;
        padding-bottom: 50%;
        background: red;
      }

      .box {
        position: absolute;
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
      }

    <div class="outer">
      <div class="inner">
        <div class="box">hello</div>
      </div>
    </div>

Когда padding-bottom равен %, он основан на нижнем отступе в процентах от ширины родительского элемента.

29. Разница между видимостью и отображением (и непрозрачностью)

  • параметр видимости hidden скроет элемент, но его позиция все еще существует в потоке документов страницы и не будет удалена, поэтому он вызовет перерисовку механизма рендеринга браузера.
  • Отображение, установленное для атрибута none, скроет элемент, и его положение не будет сохранено, поэтому он вызовет перекомпоновку и перерисовку механизма рендеринга браузера.
  • opacity сделает элемент прозрачным, но его позиция также находится в потоке документа страницы и не будет удалена, поэтому он вызовет перерисовку механизма рендеринга браузера.

30.js проблема с загрузкой скрипта, асинхронность, проблема с отсрочкой

  • Если вы полагаетесь на другие скрипты и результаты DOM, используйте defer
  • Используйте асинхронность, если у вас нет сильных зависимостей от DOM и других скриптов.

31. Глубокое копирование

const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
  if (obj.constructor === Date) 
  return new Date(obj)       // 日期对象直接返回一个新的日期对象
  if (obj.constructor === RegExp)
  return new RegExp(obj)     //正则对象直接返回一个新的正则对象
  //如果循环引用了就用 weakMap 来解决
  if (hash.has(obj)) return hash.get(obj)
  let allDesc = Object.getOwnPropertyDescriptors(obj)
  //遍历传入参数所有键的特性
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
  //继承原型链
  hash.set(obj, cloneObj)
  for (let key of Reflect.ownKeys(obj)) { 
    cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
  }
  return cloneObj
}

32. Каков порядок выполнения хуков жизненного цикла родительского и дочернего компонентов Vue

  • 1. Загрузите процесс рендеринга

родитель beforeCreate->родитель создан->родитель beforeMount->дочерний элемент beforeCreate->дочерний элемент создан->дочерний элемент beforeMount->дочерний элемент смонтирован->родительский элемент

  • 2. Процесс обновления подкомпонента

родитель до обновления-> дочерний перед обновлением-> дочерний обновлен-> родительский обновлен

  • 3. Процесс обновления родительского компонента

родитель перед обновлением-> родитель обновлен

  • 4. Процесс разрушения

родитель перед уничтожением-> дочерний перед уничтожением-> дочерний элемент уничтожен-> родитель уничтожен

Аннотация: Снаружи внутрь, затем изнутри наружу.

33. При каких обстоятельствах в приведенном ниже коде будет напечатано 1?

var a = ?;
if(a == 1 && a == 2 && a == 3){
 	conso.log(1);
}

Анализ ответов Поскольку == будет выполнять неявное преобразование типов, мы можем переписать метод toString.

var a = {
  i: 1,
  toString() {
    return a.i++;
  }
}

if( a == 1 && a == 2 && a == 3 ) {
  console.log(1);
}

34. Изучите масштаб

Что выводит следующий код

var a = 10;
(function () {
    console.log(a)
    a = 5
    console.log(window.a)
    var a = 20;
    console.log(a)
})()

Вывод по порядку: undefined -> 10 -> 20

Разобрать:

В функции немедленного выполнения оператор var a = 20; определяет локальную переменную а. Из-за механизма продвижения объявления переменных js объявление локальной переменной а будет продвигаться в начало тела функции немедленно выполняемого функция, и из-за такого продвижения назначения не включены, поэтому первый оператор печати будет печатать undefined, а последний оператор будет печатать 20.

Из-за поощрения объявления переменной a = 5; когда этот оператор выполняется, локальная переменная a была объявлена, поэтому его эффект заключается в присвоении значения локальной переменной a. В это время window.a все еще 10 , который был назначен изначально.

35. Почему в Vue дочерний компонент не может изменить Prop, переданный родительским компонентом?Если он изменен, как Vue отслеживает изменение свойства и выдает предупреждение

1. Почему дочерний компонент не может изменить реквизит, переданный родительским компонентом?

Односторонний поток данных, легко контролировать поток данных, если есть ошибка, вы можете быстрее найти место ошибки.

2. Если оно изменено, как Vue отслеживает изменение свойства и выдает предупреждение

if (process.env.NODE_ENV !== 'production') {
      var hyphenatedKey = hyphenate(key);
      if (isReservedAttribute(hyphenatedKey) ||
          config.isReservedAttr(hyphenatedKey)) {
        warn(
          ("\"" + hyphenatedKey + "\" is a reserved attribute and cannot be used as component prop."),
          vm
        );
      }
      defineReactive$$1(props, key, value, function () {
        if (!isRoot && !isUpdatingChildComponent) {
          warn(
            "Avoid mutating a prop directly since the value will be " +
            "overwritten whenever the parent component re-renders. " +
            "Instead, use a data or computed property based on the prop's " +
            "value. Prop being mutated: \"" + key + "\"",
            vm
          );
        }
      });
    }

В initProps, когда defineReactive оценивается, находится ли он в среде разработки.Если он находится в среде разработки, он будет судить, изменен ли ключ в updateChildren при срабатывании набора.Если нет, это означает, что модификация исходит от подкомпонента и вызывает предупреждение.

Важно отметить, что приглашение срабатывает, когда свойство, которое вы изменяете из дочернего компонента, относится к базовому типу. В этом случае вы не можете изменить источник данных родительского компонента, потому что базовый тип является копией значения при его назначении. При непосредственном назначении этой клавише другого небазового типа (Объект, массив) запрос также будет срабатывать (но это фактически не повлияет на источник данных родительского компонента). приглашение не будет запущено и изменит данные источника данных родительского компонента.

36. И куки, и токены хранятся в заголовке, почему бы не захватить токен?

1. Во-первых, токен не для предотвращения XSS, а для предотвращения CSRF;

2. Причина CSRF-атаки в том, что браузер автоматически доставит куки, но не токен автоматически.

37. Что печатает приведенный ниже код и почему

var b = 10;
(function b(){
    b = 20;
    console.log(b); 
})();

1 Содержание результата печати следующее:

ƒ b() {
b = 20;
console.log(b)
}

причина:

Область действия: контекст выполнения содержит цепочку действий: Прежде чем понять цепочку областей действия, давайте сначала представим область действия.Под областью действия можно понимать область действия переменных и функций, объявленных в контексте выполнения, включая области видимости/функции на уровне блоков;

Особенности: объявление заранее: объявление видно в теле функции, и объявление функции имеет приоритет над объявлением переменной; В неанонимной самовыполняющейся функции переменная функции доступна только для чтения и не может быть изменена;

38. Реализовать Promise.race()

Promise._race = promises => new Promise((resolve, reject) => {
	promises.forEach(promise => {
		promise.then(resolve, reject)
	})
})

39. Выведите результат выполнения следующего кода и объясните, почему

var obj = {
    '2': 3,
    '3': 4,
    'length': 2,
    'splice': Array.prototype.splice,
    'push': Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj)

результат:

截屏2021-03-14 上午11.37.43.png

  • Метод push() добавляет один или несколько элементов в конец массива и возвращает новую длину массива.
  • Согласно MDN, метод push должен основываться на длине массива для создания свойства с индексом длины массива в соответствии с параметрами,
  • Метод push влияет на свойство длины массива и значение соответствующего индекса.

Добавьте к объекту метод атрибута splice, а после атрибута length. Этот объект становится похожим на массив.

Объяснение названия должно быть:

  • 1. Используя первое нажатие, метод push объекта obj устанавливает obj[2]=1;obj.length+=1
  • 2. При втором нажатии метод push объекта obj устанавливается равным obj[3]=2;obj.length+=1
  • 3. При использовании вывода console.log, поскольку у obj есть свойство length и метод splice, он печатается как массив
  • 4. При печати из-за того, что массив не устанавливает значение в индексе 0 1, он печатается как пустой, а активный obj[0] получается как неопределенный

Первый и второй шаги также можно конкретно объяснить так: поскольку при каждом нажатии передается только один параметр, длина obj.length увеличивается только на 1. Сам метод push также может добавлять дополнительные параметры.

40. Разница между for in и for of, как for of traverse объектов

  • for in подходит для перебора объектов
  • for of подходит для обхода массива

Циклы for-of не поддерживают обычные объекты, но если вы хотите перебирать свойства объекта, можно реализовать следующий метод

方法1:
for (var key of Object.keys(someObject)) {
  console.log(key + ": " + someObject[key]);
}

方法2:
var obj = {
    a:1,
    b:2,
    c:3
};
obj[Symbol.iterator] = function*(){
    var keys = Object.keys(obj);
    for(var k of keys){
        yield [k,obj[k]]
    }
};

for(var [k,v] of obj){
    console.log(k,v);
}

Все объекты, у которых есть Symbol.iterator, называются итерируемыми.

41. Процесс упаковки и сборки Webpack, какие классы используются

  • 1. Webpack CLI запускает процесс упаковки;
  • 2. Загрузите основной модуль Webpack и создайте объект Compiler;
  • 3. Используйте объект Compiler для начала компиляции всего проекта;
  • 4. Начиная с входного файла, разберите зависимости модуля, чтобы сформировать дерево зависимостей;
  • 5. Рекурсивное дерево зависимостей, каждый модуль передаем соответствующему Загрузчику для обработки;
  • 6. Объедините результаты, обработанные Загрузчиком, и выведите упакованные результаты в каталог dist.

42. Цепочка прототипов, наследование

43. Принцип отзывчивости Vue? в основном спросить

Связь:woooooooooo.scaler.com/video/BV1G5…

44.Знания, связанные с TS

45. Оптимизация производительности Vue

  • 1. Функциональные компоненты

    Код компонента до оптимизации выглядит следующим образом:

    <template>
      <div class="cell">
        <div v-if="value" class="on"></div>
        <section v-else class="off"></section>
      </div>
    </template>
    
    <script>
    export default {
      props: ['value'],
    }
    </script>
    
    

    Код оптимизированного компонента выглядит следующим образом:

    <template functional>
      <div class="cell">
        <div v-if="props.value" class="on"></div>
        <section v-else class="off"></section>
      </div>
    </template>
    
    

    Функциональный компонент отличается от обычного компонента объектного типа.Он не будет рассматриваться как реальный компонент.Мы знаем, что в процессе исправления, если узел является компонентом vnode, процесс инициализации подкомпонента будет выполняться рекурсивно, в то время как функция Рендеринг компонента типа является обычным vnode, и не будет рекурсивного процесса подкомпонента, поэтому накладные расходы на рендеринг будут намного ниже. Таким образом, функциональные компоненты не имеют состояния, у них нет реактивных данных, у них нет ловушек жизненного цикла. Вы можете думать об этом как об удалении части DOM в обычном шаблоне компонента и отображении его с помощью функции, что является своего рода повторным использованием на уровне DOM.

    1. Child component splitting

46. ​​Сплющивание массива (глубина почти плоская)

47. Одностраничное спа-приложение, как избежать утечки памяти

Введение

Если вы разрабатываете приложение с помощью Vue, остерегайтесь утечек памяти. Эта проблема особенно важна в одностраничных приложениях (SPA), потому что SPA спроектированы таким образом, что пользователям не нужно обновлять браузер, чтобы использовать его, поэтому приложения JavaScript должны сами очищать компоненты, чтобы гарантировать, что сборка мусора работает ожидаемым образом. .

Утечки памяти в приложениях Vue обычно происходят не из самого Vue, а чаще при интеграции в приложение других библиотек.

Более распространенным практическим сценарием является использование Vue Router для маршрутизации к различным компонентам в одностраничном приложении. Когда пользователь перемещается по вашему приложению, Vue Router удаляет элементы из виртуальной модели DOM и заменяет их новыми. Хук жизненного цикла Vue beforeDestroy() — отличное место для решения подобных проблем в приложениях на основе Vue Router. Мы можем поместить очистку в хук beforeDestroy(), например:

beforeDestroy: function () {
  this.choicesSelect.destroy()
}

Суммировать

Vue позволяет очень легко разрабатывать потрясающие реактивные приложения JavaScript, но вам все равно нужно опасаться утечек памяти. Эти утечки памяти, как правило, происходят при использовании сторонних библиотек для манипулирования DOM, отличных от Vue. Обязательно проверьте приложение на наличие утечек памяти и при необходимости выполните необходимую очистку компонентов.

48. Принцип solt и slot-scope во vue

Следующее объяснение основано на Vue 2.6.

  1. Слот и солт-область объединены в функции внутри компонентов

  2. Все их области рендеринга являются дочерними компонентами.

  3. и к ним можно получить доступ через this.$scopedSlots

После того, как родительский компонент пройдет ряд обработок во время инициализации, каждый слот будет преобразован в функцию, соответствующую ключу (имя слота, по умолчанию, если он безымянный) (если есть параметры области видимости, будут переданы параметры переопределения). .$scopedSlots может получить доступ к «функции слота» в родительском компоненте. Если это обычный слот, вызовите функцию напрямую, чтобы сгенерировать vnode, если это слот с ограниченной областью действия, вызовите функцию с реквизитами для генерации vnode.

Резюме: после версии 2.6 Vue была произведена унифицированная интеграция слота и слот-области, сделав их все в виде функций, и ко всем слотам можно получить доступ непосредственно на this.$scopedSlots, что позволяет нам разрабатывать расширенный компонент становится удобнее. С точки зрения оптимизации, Vue 2.6 также пытается сделать так, чтобы обновление слота не запускало рендеринг родительского компонента, насколько это возможно, и максимально избегало ненужного рендеринга с помощью ряда умных суждений и алгоритмов. В версии 2.5, поскольку область действия сгенерированного слота находится в родительском компоненте, обновление слота слота дочернего компонента будет обновляться вместе с родительским компонентом)

Ссылки на конкретные статьи:nuggets.capable/post/684490…

49. Воплощение хуков маршрутизации в жизненном цикле Vue

1. Полный процесс анализа маршрутизации и навигации (исключая другие жизненные циклы)

    1. Навигация срабатывает
    1. Вызовите охрану beforeRouteLeave в деактивированном компоненте.
    1. вызвать глобальный охранник перед каждым
    1. Вызов предохранителей beforeRouteUpdate в повторно используемых компонентах (2.2+)
    1. Вызовите beforeEnter в конфигурации маршрутизации
    1. Анализ компонентов асинхронной маршрутизации
    1. Вызовите beforeRouterEnter в активированном компоненте
    1. Вызвать глобальную защиту beforeResolve (2.5+)
    1. Навигация подтверждена
    1. Вызвать глобальный хук afterEach
    1. Запустить обновление DOM
    1. Вызовите функцию обратного вызова, переданную next в стороже beforeRouteEnter, и созданный экземпляр компонента будет передан в качестве параметра функции обратного вызова.

2. Полная последовательность срабатывания хуков

Сочетание навигации по маршруту, проверки активности и обработок жизненного цикла компонента, триггерная последовательность, предполагающая, что вы покидаете компонент a и входите в компонент b в первый раз:

  • beforeRouteLeave: хук перед тем, как компонент маршрутизации покидает маршрут, что может отменить маршрут, покидающий маршрут.
  • beforeEach: глобальная передняя защита маршрутизации, которую можно использовать для проверки входа в систему, загрузки глобальной маршрутизации и т. д.
  • beforeEnter: эксклюзивная защита маршрута
  • beforeRouteEnter: компонент компонента маршрутизации входит в хук перед маршрутизацией
  • beforeResolve: маршрутизировать глобальную защиту разрешения
  • afterEach: маршрутизировать глобальный почтовый хук
  • beforeCreate: жизненный цикл компонента, не может получить доступ к этому
  • created: жизненный цикл компонента, вы можете получить к нему доступ, но не к dom
  • beforeMount: жизненный цикл компонента
  • деактивировано: оставить кеш-компонент a или активировать хуки уничтожения beforeDestroy и уничтоженного компонента a
  • Mounted: доступ/манипулирование домом
  • активировано: введите компонент кеша, введите вложенный подкомпонент a (если есть)
  • Затем выполните функцию обратного вызова beforeRouterEnte.

50. Жизненный цикл? Какой жизненный цикл может получить настоящий DOM? Изменение данных в данных, какой жизненный цикл будет запущен?

  1. Всего 8 этапов делятся на этапы до/после создания, до/после загрузки, до/после обновления и до/после уничтожения.
  • Функции жизненного цикла при создании
    • beforeCreate: Экземпляр только что создан в памяти. В настоящее время свойства данных и методов не инициализированы.

    • создан: экземпляр создан в памяти ОК, в это время данные и методы созданы ОК, а шаблон еще не скомпилирован

    • beforeMount: На данный момент компиляция шаблона завершена, но он не смонтирован на странице

    • смонтирован: на данный момент скомпилированный шаблон был смонтирован в контейнер, указанный страницей для отображения

  • Функции жизненного цикла во время выполнения:
    • beforeUpdate: эта функция выполняется перед обновлением состояния.В это время значение состояния в данных является последним, но данные, отображаемые в интерфейсе, все еще старые, поскольку узел DOM в это время не подвергался повторному рендерингу.

    • обновлено: эта функция вызывается после обновления экземпляра.В это время значение состояния в данных и данные, отображаемые в интерфейсе, были обновлены, а интерфейс был повторно визуализирован!

  • Функции жизненного цикла при разрушении:
    • beforeDestroy: вызывается перед уничтожением экземпляра. На этом этапе экземпляр все еще полностью доступен.
    • уничтожено: вызывается после уничтожения экземпляра Vue. После вызова все, на что указывает экземпляр Vue, будет развязано, все прослушиватели событий будут удалены, а все дочерние экземпляры уничтожены.
  1. Смонтированный жизненный цикл может получить настоящий DOM

  2. Изменение данных в данных вызовет жизненные циклы перед обновлением и обновлением.

51. Почему данные компонента Vue — это функция

Когда данные являются функцией, атрибут данных каждого экземпляра независим и не влияет друг на друга.

52. Коммуникация компонентов Vue? Вообще говоря, когда вы говорите vuex, вы спросите, как использовать vuex? В чем разница между действиями и мутациями? Принцип реализации и т.д.?

  1. props/$emit

  2. emit/emit/on

  3. vuex

      1. Компоненты Vue: компоненты Vue. На HTML-странице он отвечает за получение интерактивного поведения, такого как пользовательские операции, и за выполнение метода отправки для запуска соответствующего действия для ответа.
      1. диспетчеризация: метод триггера действия, это единственный способ выполнить действие
      1. действия: модуль обработки действия действия, запускаемый $store.dispatch('имя действия', datal) в компоненте. Затем commit() запускает вызов мутации, которая косвенно обновляет состояние и отвечает за обработку всех взаимодействий, полученных компонентами Vue. Содержит синхронные/асинхронные операции, поддерживает несколько методов с одинаковыми именами и запускается в порядке регистрации. Операции, запрашиваемые в фоновом API, выполняются в этом модуле, включая инициирование других действий и отправку мутаций. Этот модуль предоставляет пакет Promise для поддержки последовательного запуска действий.
      1. commit: метод операции фиксации изменения состояния. Отправка мутации — единственный способ выполнить мутацию
      1. мутации: метод операции изменения состояния, запускаемый коммитом('имя мутации') в действиях. это единственный рекомендуемый способ изменения состояния Vuex. Этот метод может выполнять только синхронные операции, а имя метода может быть только глобально уникальным. Во время операции будут обнаружены некоторые крючки для мониторинга состояния и так далее.
      1. state: объект-контейнер управления состоянием страницы. Централизованно храните разрозненные данные объекта данных в компонентах Vue, что является уникальным в глобальном масштабе для унифицированного управления состоянием. Данные, необходимые для отображения страницы, считываются из этого объекта, а детальный механизм ответа данных Vue используется для выполнения эффективных обновлений состояния.
      1. метод чтения объекта состояния
  4. $attrs/$listeners

    • $attrs: содержит привязки атрибутов (кроме класса и стиля), которые не распознаются (и не приобретаются) реквизитами в родительской области. Когда компонент не объявляет никаких реквизитов, все привязки родительской области (кроме класса и стиля) включаются сюда и могут быть переданы внутренним компонентам через v-bind="$attrs" . Обычно используется с параметром interitAttrs.
    • $listeners: содержит прослушиватели событий v-on в родительской области (без декоратора .native). Его можно передать внутренним компонентам через v-on="$listeners"

Проще говоря: $attrs и $listeners — это два объекта,attrsВ родительском компоненте хранится необязательныйPropsАтрибуты,Свойства, не относящиеся к Props, привязанные к родительскому компоненту, хранятся в attrs.Слушатели хранят несобственные события, привязанные к родительскому компоненту.

  1. provide/inject

    Vue2.2.0 добавил API, эту пару параметров необходимо использовать вместе, чтобы компонент-предок мог вводить зависимость всем своим потомкам, независимо от того, насколько глубок уровень компонента, и он всегда будет действовать, когда восходящие и нисходящие отношения установлены. Одним словом: предоставлять переменные в компонентах-предках через провайдер, а затем внедрять переменные в компоненты-потомки через inject. API предоставления/внедрения в основном решает проблему связи между межуровневыми компонентами, но его сценарии использования в основном предназначены для подкомпонентов для получения состояния родительского компонента, а между межуровневыми компонентами устанавливается связь между активным предоставлением и внедрением зависимостей. .

  2. parent/parent / дети и реф.

53. Что делает $nextTick? Принцип реализации? При деградации микрозадач до макрозадач часто просят назвать несколько типов макрозадач и микрозадач.

Что делает $nextTick: Выполняет отложенный обратный вызов после завершения следующего цикла обновления DOM. Используйте этот метод сразу после изменения данных, чтобы получить обновленную модель DOM.

Принцип реализации:

1. Проверка способностей

Эта часть на самом деле очень проста. Как мы все знаем, цикл событий делится на макро-задачи и микро-задачи. Независимо от того, выполняются ли макро-задачи или микро-задачи, он войдет в следующий тик после завершения и будет выполняться между двумя тиками. рендеринг. Однако макрозадачи занимают больше времени, чем микрозадачи, поэтому, если браузер поддерживает это, сначала используются микрозадачи. Если браузер не поддерживает микрозадачи, используйте макрозадачи, однако эффективность различных макрозадач также различна, и необходимо использовать разные макрозадачи в зависимости от поддержки браузера.

2. Выполните очередь обратного вызова различными способами в зависимости от обнаружения возможностей.

Обработка понижения версии: Promise -> MutationObserver -> setImmediate -> setTimeout

  • Задача макроса:
    • Ввод/вывод, каждое событие в очереди событий является макрозадачей

    • setTimeout / setInterval

    • MessageChannel — это API канала связи, поддерживаемый ie11 и выше, а также другими браузерами.

    • setImmediate В настоящее время этот метод реализован только в IE10 и выше. Другие браузеры его не поддерживают. Функция обратного вызова поддерживается узлом.

    • requestAnimationFrame также считается узлом задачи макроса, который не поддерживает.

  • микрозадачи
    • Promise.then catch finally
    • Браузер MutationObserver поддерживает IE11 и выше, а нода его не поддерживает, будет вызываться при изменении указанного DOM.
    • Браузер process.nextTick не поддерживает поддержку узлов
实现一个简易的nextTick

let callbacks = []
let pending = false

function nextTick (cb) {
    callbacks.push(cb)

    if (!pending) {
        pending = true
        setTimeout(flushCallback, 0)
    }
}

function flushCallback () {
    pending = false
    let copies = callbacks.slice()
    callbacks.length = 0
    copies.forEach(copy => {
        copy()
    })
}

54. Какова роль атрибута vue scoped? Принцип реализации?

Когда тег стиля имеет атрибут scoped, его CSS будет применяться только к элементам текущего компонента.

<style scoped>
.example {
 color: red;
}
</style>
<template>
 <div class="example">hi</div>
</template>

<style>
.example[data-v-5558831a] {
 color: red;
}
</style>
<template>
 <div class="example" data-v-5558831a>hi</div>
</template>

Принцип реализации: PostCSS добавляет уникальное динамическое свойство ко всем домам в компоненте, а затем добавляет соответствующий селектор свойств в селектор CSS для выбора дома в компоненте Дом атрибута - внутренний дом компонента

55. Сколько режимов у vue router? Способ реализовать?

  1. хэш-режим

Принцип работы хеш-режима — это событие hashchange, которое может отслеживать изменения хеш-функции в окне. Мы случайным образом добавляем #xx после URL-адреса, чтобы вызвать это событие.

нажать и заменить() HashHistory

  window.onhashchange = function(event){
    console.log(event);
  }
  1. режим истории (соответствует HTML5History)

HTML5History.pushState() и HTML5History.replaceState()

Добавление прослушивателя для изменения URL-адреса адресной строки браузера в HTML5History выполняется непосредственно в конструкторе, и отслеживается событие popstate HTML5History:

constructor (router: Router, base: ?string) {
  
 window.addEventListener('popstate', e => {
 const current = this.current
 this.transitionTo(getLocation(this.base), route => {
 if (expectScroll) {
 handleScroll(router, route, current, true)
 }
 })
 })
}

56. Какова роль ключа? Что будет делать vue, если нет ключа? приведет к проблемам diff

  1. Функция ключа в основном заключается в эффективном обновлении виртуального DOM.Принцип заключается в том, что vue может точно определить, являются ли два узла одинаковыми с помощью ключа во время процесса исправления, тем самым избегая частого обновления различных элементов, что делает весь процесс исправления более эффективные и сокращающие операции DOM. , для повышения производительности.

  2. Кроме того, если ключ не установлен, это может вызвать некоторые скрытые ошибки при обновлении списка.

  3. В Vue ключевой атрибут также используется, когда элементы с одинаковым именем тега используются для чрезмерного переключения.Цель также состоит в том, чтобы позволить Vue различать их, иначе Vue только заменит свои внутренние атрибуты, не вызывая чрезмерных эффектов.

57. vue diff процесс

58.vue 2.x DefineProperty дефект? Как с этим бороться в бизнес-коде? принцип $set? Как vue переопределяет методы массива? Проверьте, действительно ли вы читали исходный код

1.vue 2.x DefineProperty дефект

  • Object.defineProperty не может отслеживать изменение нижнего индекса массива, что приводит к добавлению элементов через нижний индекс массива, который не может реагировать в режиме реального времени;

  • Object.defineProperty может только захватить свойства объекта, поэтому ему необходимо пройти каждый объект и каждое свойство.Если значение свойства является объектом, его также необходимо глубоко пройти. Прокси может захватить весь объект и вернуть новый объект.

  • Изменения динамической длины не могут быть отслежены.

    Например, arr = [1], напрямую изменить arr[10] = 20, чтобы его нельзя было отслеживать, т.к. длина не позволяет перезаписывать в спецификации, а arr[0] можно отслеживать напрямую, если он изменится.

  • Методы массива не могут отслеживать изменения в массиве, такие как push

    Вот почему vue переопределяет эти методы.

  • Изменения данных отслеживаются через геттеры/сеттеры. Из-за этого метода отслеживания в некоторых грамматиках, даже если данные изменились, Vue не может их проверить. Например, добавить свойства к Объекту/удалить свойства Объекта.

  • Обнаружение изменений в массиве, потому что перехватываются только методы unshift shift push pop splice sort reverse, так как

    list[0] = 4
    list.length = 0
    

    не обнаружена

Обработка бизнес-кода: this.$set(this.data,"key",value')

Сам Object.defineProperty имеет определенную возможность отслеживать изменения индексов массива: сам Object.defineProperty может отслеживать изменения индексов массива, но в Vue, учитывая экономическую эффективность производительности/опыта, Youda отказался от этой характеристики.

2. Принцип $set

В методе set целью является массив, а объект обрабатывается отдельно.Когда целью является массив, для наблюдения вручную будет вызываться переписанный метод splice.

Для объекта, если ключ изначально является свойством объекта, непосредственно измените значение, чтобы инициировать обновление, в противном случае вызовите метод defineReactive, чтобы переопределить реактивный объект.

3. Как vue переопределяет методы массива

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {  
  // cache original method
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
  })
})

Перепишите эти методы в массиве, сначала получите __ob__ массива, который является его объектом Observer, если есть новое значение, вызовите visibleArray, чтобы продолжить наблюдать за изменением нового значения, а затем вручную вызовите уведомление, чтобы уведомить об отображении. наблюдатель, выполнить обновление

59. Каковы преимущества и недостатки прокси vue 3.0? Как быть с vue3, не поддерживающим IE?

  • Прокси может проксировать не только объекты, но и массивы. Вы также можете проксировать динамически добавляемые свойства.

60. В чем разница между вычислением и просмотром и каковы сценарии использования? Помимо основных, посмотрите, сможете ли вы отличить трех наблюдателей.

вычислено: вычисляемые свойства

Вычисляемое свойство — это новое значение, полученное из известного значения в данных. Это новое значение будет изменяться только на основе изменений в известном значении, и изменения в других несвязанных данных не повлияют на это новое значение. Вычисляемое свойство не находится в данных, а связанное с ним известное значение нового значения вычисляемого свойства находится в данных. Изменения в других влияют на меня. смотреть: следить за изменениями данных

Отслеживание изменений данных в данных Отслеживаемые данные — это известное значение в данных Мои изменения влияют на другие

1. Сценарии, с которыми хорошо справляются часы: одни данные влияют на несколько данных

2. Сценарии, в которых вычисление хорошо обрабатывается: на одни данные влияет несколько данных.

61. Как реализовать ленивую загрузку картинок

Идея: Как судить, что картинка появляется в текущем окне просмотра (то есть как судить, что мы видим картинку)

Как контролировать загрузку изображений

Решение 1. Расчет позиции + событие прокрутки (Scroll) + DataSet API

  • 1. Расчет позиции: clientTop, offsetTop, clientHeight и scrollTop связаны с высотой изображения для сравнения.

  • 2. Прослушайте событие window.scroll

  • 3. API набора данных:

    • Сначала установите временный атрибут Data data-src и используйте src вместо data-src при управлении загрузкой, что можно реализовать с помощью DataSet API.

    • img.src = img.datset.src

Решение 2: API getBoundingClientRect + Scroll + API DataSet

    1. Метод Element.getBoundingClientRect() возвращает размер элемента и его положение относительно окна просмотра.
      // clientHeight 代表当前视口的高度
       img.getBoundingClientRect().top < document.documentElement.clientHeight
      
  • 2. Слушайте window.scroll
    1. То же

Решение 3: API IntersectionObserver + API DataSet

const observer = new IntersectionObserver((changes) => {
  // changes: 目标元素集合
  changes.forEach((change) => {
    // intersectionRatio
    if (change.isIntersecting) {
      const img = change.target
      img.src = img.dataset.src
      observer.unobserve(img)
    }
  })
})

observer.observe(img)

то есть не поддерживает

Решение 4. Свойство LazyLoading

<img src="shanyue.jpg" loading="lazy">

Почти нет поддержки, кроме хрома

62. дизайн формы

Проверить дизайн формы исходного кода element-ui, Адрес репозитория кода:GitHub.com/administration/over-…

63. Наконец: поделитесь ссылками на некоторые точки знаний, которые вы разобрали