Недавно была проведена общая оптимизация производительности на одном из ПК-сайтов компании.Из-за сложности системного дела и множества зависимостей скорость загрузки очень низкая.После оптимизации были значительно улучшены различные показатели производительности, а также скорость загрузки примерно в 5 раз быстрее.
Я оптимизировал многие аспекты, такие как конструкция, сеть, загрузка ресурсов, время выполнения, сервер, функциональная организация и т. д. Я собираюсь сделать серию и поделиться с вами своим опытом оптимизации в главах.
Сегодня мы начнем с угла сборки, где оптимизация наиболее эффективна.
До оптимизации
Для начала посмотрим на загрузку ресурсов сайта до оптимизации:
самый большой видимыйvendor
В упаковке действительно есть3MB
(проходить черезgzip
После сжатия), если не производится дополнительная настройка,webpack
В этот пакет вносятся все сторонние зависимости, если вводить все больше и больше зависимостей, то пакет будет становиться все больше и больше.
Кроме того, дошел и логический пакет самой системы.600kb
Анализ зависимостей
мы можем использоватьwebpack-bundle-analyzer
Отображая упакованный контент в виде удобной для взаимодействия древовидной диаграммы, мы можем интуитивно увидеть, какие более крупные модули, а затем провести целевую оптимизацию.
npm install --save-dev webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
Введение CDN
Принцип работы CDN заключается в кэшировании ресурсов исходного сайта на узлах CDN, расположенных по всему миру.Когда пользователь запрашивает ресурсы, ресурсы, кэшированные на узле, возвращаются на ближайший узел, а не возвращаются на исходный сайт каждый раз. запрос пользователя.Перегрузка сети, ослабьте нагрузку на исходный сайт и обеспечьте скорость и удобство доступа пользователей к ресурсам.
Все это понимают, ведь сам упакованный товар тоже выгружается вCDN
из. Но что нам нужно сделать, так это отделить более крупные сторонние зависимости и поместить их вCDN
, чтобы эта зависимость не занимала ресурсы упаковки и не влияла на окончательный размер пакета.
Если зависимость имеет один файл, который непосредственно упакован и сжатCDN
ресурсы, такие как на изображении вышеg6
, вы можете использовать его напрямую.
Согласно официальной документации, если мы хотим сослаться на библиотеку, но не хотимwebpack
упакованы и не влияют на наше использование в программеimport、require
илиwindow/global
Если он используется глобально, его можно использовать, настроивexternals
.
externals
Параметры конфигурации позволяют «исключить зависимости из экспортируемых пакетов». Вместо этого созданныйbundle
Зависит от тех, которые существуют в среде потребителя.
Первое местоCDN
Импортированные зависимости добавляются вexternals
середина.
потом с помощьюhtml-webpack-plugin
будетCDN
запись в файлеhtml
:
Здесь следует отметить одно, вhtml
настроен вCDN
Сценарий импорта должен быть вbody
внутри самого дна, потому что:
- если поместить в
body
выше илиheader
внутри загрузка заблокирует отрисовку всей страницы. - если поместить в
body
Кроме того, он будет загружен после загрузки бизнес-кода, и будет сообщено об ошибке, если в модуле используется модуль.
демонтировать поставщика
В некоторых сценариях сторонняя зависимость может быть разделена на несколько подзависимостей, например, как показано выше.monaco
, или не предоставлено, может быть напрямую передано черезCDN
Введены файлы, мы не можем настроитьCDN
файл, чтобы импортировать его.
Тогда нам нужно идтиwebpack
Установите некоторые правила для упаковки зависимостей, которые мы хотим отделить отдельно.vendor
.
Dynamic import
Посмотримv8
оimport
описание:
This syntactic form for importing modules is a static declaration: it only accepts a string literal as the module specifier, and introduces bindings into the local scope via a pre-runtime “linking” process. The static import syntax can only be used at the top-level of the file.
CommonJS
иES Module
Есть большая разница в использовании,CommonJS
Позволяет загружать модуль тогда, когда он вам нужен, вместо того, чтобы загружать все сразу. иES Module
Синтаксис статический, статическийimport
Синтаксис можно использовать только на верхнем уровне файла. В этом эталонном методе есть огромный недостаток, то есть невозможно импортировать модули по запросу.
<script type="module">
import * as module from './utils.js';
module.default();
// → logs 'Hi from the default export!'
module.doStuff();
// → logs 'Doing stuff…'
</script>
к счастью,ES Module
В настоящее время также поддерживаетсяDynamic import
использование, динамическийimport
вернетpromise
, вы можете дождаться загрузки модуля, прежде чем что-то делать, вместо того, чтобы загружать его при инициализации страницы.
<script type="module">
const moduleSpecifier = './utils.js';
import(moduleSpecifier)
.then((module) => {
module.default();
// → logs 'Hi from the default export!'
module.doStuff();
// → logs 'Doing stuff…'
});
</script>
<script type="module">
(async () => {
const moduleSpecifier = './utils.js';
const module = await import(moduleSpecifier)
module.default();
// → logs 'Hi from the default export!'
module.doStuff();
// → logs 'Doing stuff…'
})();
</script>
будетvendor
После разделения зависимости будут по-прежнему загружаться на первом экране. Если зависимости не используются на первом экране, это все равно приведет к пустой трате сетевых ресурсов и блокировке рендеринга страницы. Для зависимостей, которые не нужно загружать на первом экране. первый экран, мы можем использовать динамическийimport
Путь.
Например, вышеjs-export-excel
Эта зависимость сама по себе почти500 kb
, но он будет использоваться только тогда, когда пользователь нажмет кнопку [Экспорт].vendor
удалить его.
При использовании,import
Логика изменена с первого экрана на асинхронную загрузку во время выполнения
В таком случае,js-export-excel
Этот пакет зависимостей будет импортирован только тогда, когда пользователь нажмет кнопку [Экспорт] и больше не будет импортирован на первом экране.
Не все зависимости подходят для асинхронной загрузки.Если у вас высокие требования к производительности для использования зависимости, а сама зависимость относительно велика, такая ситуация не подходит, так как могут наблюдаться значительные задержки. Вышеупомянутый экспорт на самом деле является более подходящим сценарием, загрузка самого excel требует времени задержки, плюс приемлемо время для динамической загрузки зависимостей.
Реагировать на ленивую загрузку
Точно так же для некоторых сторонних зависимых компонентов, таких какmonaco editor
, мы используем его только в очень немногих бизнес-сценариях, но он занимает свой собственный пакет5MB
. . Нам приходится загружать его каждый раз, когда мы открываем страницу, что слишком интенсивно для производительности.
Для зависимого пакета мы можем динамическиimport
способ ленивой загрузки, но дляReact
компоненты, используйте динамические напрямуюimport
Это может быть неуместно.Среда выполнения рендеринга компонента может запускаться несколько раз, и невозможно загружать компонент каждый раз, когда компонент рендерится.
React.lazy
Функции позволяют обрабатывать динамически импортированные компоненты так же, как визуализацию обычных компонентов.React.lazy
Принимает функцию, которую необходимо вызвать динамическиimport()
. он должен вернутьPromise
,ДолженPromise
необходимостьresolve
Одинdefault export
изReact
компоненты.
const MonacoEditor = React.lazy(() => import('react-monaco-editor'));
Этот код автоматически импортирует включение при первом отображении компонента.MonacoEditor
пакет компонентов. но использовать напрямуюReact.lazy
Импортированные компоненты нельзя использовать напрямую, потому чтоReact
Невозможно предсказать, когда будет загружен компонент, а прямой рендеринг приведет к сбою страницы.
существуетSuspense
Рендеринг в компонентеlazy
компонент, который можно использовать во время ожидания загрузкиlazy
изящно деградировать компоненты (такие какloading
).fallback
Свойство принимает все, что вы хотите отобразить во время загрузки компонента.React
элемент. ты можешь поставитьSuspense
Компоненты размещаются в любом месте поверх лениво загруженных компонентов. Вы даже можете использоватьSuspense
Компонент оборачивает несколько лениво загруженных компонентов.
положить всеmonaco editor
После перехода на ленивую загрузку первый экран больше не будет загружатьсяmonaco editor
.
Отложенная загрузка маршрута
вышеReact
Метод ленивой загрузки применим и к маршрутизации: если каждый маршрут импортируется методом ленивой загрузки, каждый модуль будет помечен как отдельный модуль.js
, первый экран загрузит только файлы, представленные текущим модулемjs
.
Однако маршрутизация отложенной загрузки имеет и очевидный недостаток, то есть ресурсы каждого модуля загружаются только при загрузке модуля, поэтому при переключении модулей может быть небольшой белый экран или небольшой промежуток времени.
loading
Эффект, это должно сочетаться с ситуацией самого бизнеса, чтобы всесторонне судить, использовать его или нет.
Оптимизация языкового пакета
В некоторых сценариях языковые пакеты могут занимать очень большую часть общего размера пакета. На самом деле логика самой библиотеки будет не очень большой,moment
хороший пример.
Если вы выбираете библиотеку дат в начале, то прямо рекомендуется использоватьdayjs
, если вы выберетеmoment
, обязательно отфильтруйте неиспользуемые языковые пакеты, рекомендуется использоватьContextReplacementPlugin
, это скажетwebpack
Какой локальный файл мы будем использовать:
plugins: [
new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn/),
]
Эффект оптимизации
После окончательной оптимизации мы обнаружим, что модули разобрались очень равномерно, а соответствующие модули будут загружаться только при рендеринге соответствующей страницы, что значительно повышает скорость рендеринга первого экрана.
Если в статье есть ошибка, исправьте ее в области комментариев; если статья вам полезна, ставьте лайк, комментируйте, делитесь и надеюсь помочь большему количеству людей.
Эта статья впервые опубликована на паблике "Code Secret Garden". Просим обратить внимание всех. Оригинальный текст:Как я увеличил производительность веб-страницы в 5 раз — оптимизация сборки
Команде архитектуры внешнего интерфейса ByteDance IES срочно нужны таланты (p5/p7/p7 много HC), добро пожаловать, добавьте меня в WeChat
ConardLi
Давайте делать вещи вместе.