Недавно я занимался оптимизацией производительности проекта vue-cli.На этот раз я также применил многие знания, полученные ранее, к реальному проекту.Я столкнулся с множеством трудностей и многому научился.
Прежде чем смотреть эту статью, вам следует иметь некоторое представление о распространенных методах оптимизации внешнего интерфейса (таких как http-кэширование, сжатие кода и т. д.) и веб-пакетах.
Описание Проекта
Это веб-приложение на стороне ПК с vue-cli 3 в качестве каркаса, глобальная многостраничная частичная отдельная страница (разделенная в соответствии с ролями пользователей). По сути, используется конфигурация vue-cli по умолчанию, и никакой оптимизации производительности специально не проводилось. Эта оптимизация надеется ускорить первую скорость загрузки без максимально возможного снижения последующей производительности.
Представление
Прежде чем приступить к оптимизации производительности, мы должны сначала узнать, как оценить производительность проекта, то есть какие показатели используются для ее измерения, чтобы мы могли поставить цель, количественно оценить производительность проекта и узнать, соответствует ли наша оптимизация производительности эффективен и насколько велик.Эффект.
В настоящее время существует два типа методов мониторинга производительности переднего плана.Синтетический мониторинг(Синтетический мониторинг,SYN), другойреальный мониторинг пользователей(Наблюдение за реальными пользователями,RUM). Синтетический мониторинг заключается в имитации запуска страницы с помощью некоторых инструментов и правил в моделируемой среде, регистрации соответствующих показателей производительности и, наконец, получении отчета. Инструменты разработчика браузера Chrome поставляются сLighthouse(требуется научный доступ в Интернет) является своего рода синтетическим мониторингом. Однако объем данных, генерируемых синтетическим мониторингом, невелик, а результаты тестирования тесно связаны с условиями сети и производительностью оборудования тестовой машины, что не может отражать ситуацию реальных пользователей. Мониторинг RUM предназначен для сбора фактических данных о производительности онлайн-пользователей путем захоронения точек в коде и вызова интерфейса браузера. Размер выборки большой, и он может лучше объяснить состояние производительности страницы в реальной сцене. Таким образом, данные, отслеживаемые RUM, должны иметь преимущественную силу в этой оптимизации производительности.
Показатели производительности RUM делятся на две категории: показатели пользовательского опыта и технические показатели.
Метрики пользовательского опыта
на основеОриентированные на пользователя показатели производительности, обычно используемые индикаторы взаимодействия с пользователем:
показатель | описывать | специальный метод расчета |
---|---|---|
FP(First Paint) | Время первого отрисовки страницы (время белого экрана) | Время с момента, когда пользователь начинает посещать страницу, до момента, когда экран перестанет быть пустым. согласно сЧерновая спецификация времени отрисовки W3C, в браузере черезperformance.getEntriesByType('paint') Получите информацию о времени, предоставленную API-интерфейсом PerformancePaintTiming. |
FCP(First Contentful Paint) | Время, когда на странице впервые появился контент | Время между тем, когда пользователь начинает посещать страницу, и тем, когда на ней появляется какой-либо фактический контент. в браузере черезperformance.getEntriesByType('paint') Получать. |
FMP(First Meaningful Paint) | Первое эффективное время прорисовки страницы (время над сгибом) | Время отрисовки после начала доступа пользователя к странице до максимального изменения макета всей страницы.Time to First Meaningful Paint(требуется научный доступ в Интернет) |
TTI(Time To Interactive) | Страница полностью интерактивное время | Время с момента, когда пользователь впервые посещает страницу, до момента, когда страница становится полностью интерактивной.First Interactive and Consistently Interactive(требуется научный доступ в Интернет) |
FID(First Input Delay) | Задержка первого взаимодействия пользователя | Первое взаимодействие пользователей со страницей занимает. с помощьюEvent Timing API, Послушайте событие первого ввода, начальное время обработки FID = события - время возникновения события |
MPFID(Max Potential First Input Delay) | Максимальная задержка, с которой может столкнуться взаимодействие с пользователем | Максимальное количество времени, которое может потребоваться пользователю для первого взаимодействия со страницей. с помощьюLong Tasks API, MPFID=Длинная задача с наибольшим временем |
LOAD | Время, которое потребовалось для полной загрузки страницы (время, когда произошло событие загрузки) | LOAD = loadEventStart - fetchStart |
Изображение изwoooooo.brief.com/afraid/456oh6ohmethod 5…
Технические показатели эффективности
Технические показатели производительности — это показатели производительности, рассчитываемые на основе времени каждого события во время загрузки страницы. Их влияние на пользователей не так интуитивно понятно, как показатели пользовательского опыта, но они могут лучше отражать некоторые проблемы с производительностью и их легче оптимизировать. И в отличие от метрик пользовательского опыта, которые компании определяют по-разному, у него очень четкий и унифицированный способ его расчета.
Navigation Timing 2.0Определенная модель фазы загрузки страницы:Согласно модели Navigtaion Timing 2.0, существуют следующие часто используемые технические индикаторы производительности:
показатель | описывать | специальный метод расчета |
---|---|---|
DNS | Поиск DNS занимает время | domainLookupEnd - domainLookupStart |
TCP | Требуется много времени для установления TCP-соединения | connectEnd - connectStart |
SSL | Требуется много времени для установки SSL-соединения | connectEnd - secureConnectionStart |
TTFB | время отклика первого байта | responseStart - requestStart |
Передача контента | Передача ответа занимает много времени | responseEnd - responseStart |
Разбор DOM | Парсинг браузера Dom требует времени | domInteractive - responseEnd |
загрузка ресурсов | Загрузка внешних ресурсов требует времени | loadEventStart - domContentLoadedEventEnd |
первый байт | Время, которое требуется браузеру, чтобы получить первый ответ | responseStart - fetchStart |
DOM Ready | Требуется много времени для загрузки DOM | domContentLoadedEventEnd - fetchStart |
Основные показатели этого фокуса оптимизации
Поскольку этот проект оптимизации в основном основан на работе пользователя, а не на отображении (например, на новостных веб-сайтах), акцент на FP, FMP и других показателях был ослаблен, а основное внимание было уделеноTTI. Во-вторых, сосредоточьтесь на FCP, FMP и DOM Ready, потому что оптимизация FCP и FMP может уменьшить потерю терпения пользователей из-за того, что они не видят контент.DOM Ready может отражать общее время загрузки проекта.
Основная цель этой оптимизации состоит в том, чтобыНа 30% ниже TTI.
инструмент для анализа
-
webpack-chart
: интерактивная круговая диаграмма статистики webpack -
webpack-visualizer
: Визуализируйте и проанализируйте свой пакет, проверьте, какие модули занимают место, а какие можно использовать повторно. -
webpack-bundle-analyzer
: подключаемый модуль и инструмент командной строки для анализа содержимого пакета и представления его пользователям в виде удобной, интерактивной и масштабируемой древовидной карты. В vue-cli запуститеnpx vue-cli-service build --report
существует/dist
Генерировать подreport.html
просмотреть анализ упаковки - бегать
npx vue-cli-service inspect > output.js --mode production
Создать файл конфигурации рабочей среды webpack - Отладьте плагин веб-пакета:
node --inspect-brk ./node_modules/@vue/cli-service/bin/vue-cli-service.js serve --inline --progress
- Установить глобально
@vue/cli
После этого запуститеvue ui
Визуально отображать структуру зависимостей -
Lighthouse
: инструмент для создания отчетов о производительности, входящий в состав инструментов разработчика Chrome, который может дать множество рекомендаций.
Оптимизации, которые vue-cli сделал по умолчанию
Конфигурация vue-cli по умолчанию добавила много общих оптимизаций, в том числе:
-
cache-loader
Он будет включен по умолчанию для компиляции Vue/Babel/TypeScript. файлы кэшируются вnode_modules/.cache
середина - Использование мультимедийных файлов, таких как изображения
url-loader
, изображения размером менее 4K будут преобразованы в кодировку base64 и встроены в файл js. - Использование в производственной среде
mini-css-extract-plugin
, извлечь css в отдельный файл -
thread-loader
Включает параллельную обработку для транспиляции Babel/TypeScript на машинах с многоядерными процессорами. - Извлечь общий код: две группы кеша
chunk-vendors
а такжеchunk-common
- сжатие кода (
terser-webpack-plugin
) -
preload-webpack-plugin
: Все входные файлы js, css плюсpreload
, загружать файлы по требованию плюсprefetch
Проверьте, где в проекте есть возможности для улучшения
Суть так называемой оптимизации заключается в том, чтобы делать меньше лишних вещей, поэтому мы должны знать, что необходимо, а что нет. Например, в большинстве случаев нет необходимости загружать всю веб-страницу до того, как пользователь пролистнет ее вниз. Так что знайте полное поведение во время полного запуска проекта. Это часто упускается из виду, и трудно получить лучшие результаты.
Перед тем, как приступить к оптимизации, давайте проверим, где еще есть место для оптимизации в некоторых проектах, чтобы избежать бесполезной работы, или отдельная операция похожа на тигра, и данные плохо видны.
статические ресурсы
- Картинка: На аватарке, обложке еще есть место для сжатия, и сейчас средний около 100К, а больший больше 1М
- JSON, возвращенный ajax: в настоящее время каждый ajax в среднем составляет около 1 КБ, а пространство для сжатия невелико.
- js и css: основные загруженные на первом экране в сумме составляют несколько М, что может разделить код для уменьшения нагрузки
- видео, шрифт, документ: не более 50 КБ в сумме, производительность не снижается, сжатие не требуется
- другое: Предварительно загружено слишком много файлов, которые занимают слишком большую полосу пропускания сервера.
TTFB
- аякс: 200-800мс
- js, css: 150-800 мс, большинство из них 700+ мс (позже я узнал, что я действительно открывал прокси, но на самом деле это было 20-100 мс)
- Изображение, видео, шрифт: 50-250 мс
- документ: 60 мс
разное
- Местами можно добавить кеш для уменьшения количества ajax запросов.Можно рассмотреть в какой форме (session storage, cache storage, index db, http cache)
- Некоторые страницы могут отображаться на стороне сервера с учетом структуры документа.
- Ненужные перекраски в некоторых местах
методы, которые могут работать
- Адаптивные изображения:элемент изображения
- Отложенная загрузка Intersection Observer
- Критическая загрузка css асинхронно
- dns-prefetch, preconnect, prefetch, prerender, preload
- Tree-shaking,Scope hoisting
- настройка осколка webpack
- Минификация кода (дальнейшее сжатие)
- Динамический импорт реализует ленивую загрузку маршрутизации
- Сжатие изображения
попытка оптимизации
Оптимизация шардинга
Фрагментация (splitChunks) — это то, как веб-пакет разбивает код, вы можете обратиться кОфициальный сайтилиэтот блог
npx vue-cli-service inspect > output.js --mode production
output.js
splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
},
7-8M
Вот краткое описание того, как выполняется упаковка и сегментация Webpack:
- Для всех записей, начиная с файла записей, находится чанк,
import
(исключая динамическиеimport
а такжеrequire
) добавляются в чанк, а затем добавляются файлы, от которых зависят вновь добавленные файлы, то есть все файлы в цепочке зависимостей. Это начальные чанки. - Вся динамика, обнаруженная на предыдущем шаге
import
Каждый файл представляет собой кусок. Это асинхронные фрагменты. - Извлеките общедоступный код, т.е.
cacheGroups
, будет сгенерирован новый фрагмент. В соответствии с правилами конфигурации дубликаты файлов в нескольких фрагментах извлекаются, чтобы стать новыми фрагментами. - Файлы одного типа в каждом чанке объединяются в один.
Например, на приведенном выше рисунке показан вывод webpack при его выполнении в определенное время.Среди них 767/833 означает, что скомпилировано 767 модулей.Из этих 767 модулей известно, что предстоит скомпилировать 833 модуля. По мере компиляции следующего модуля в todo добавляются и зависимости модулей. Следовательно, число слева увеличивается, а число справа также увеличивается, пока не будут скомпилированы все модули. Таким образом, крайние левые 65% на самом деле не относятся к общему прогрессу компиляции, и вы не знаете, сколько модулей осталось до завершения компиляции.
изменениеcacheGroups
Прежде давайте взглянем на текущую ситуацию с шардингом с помощью инструмента визуализации.Визуальный результат шардинга после запуска анализа упаковки:
можно увидетьchunk-vendors
Объем очень большой, потому что проект зависит от многих библиотек.chunk-common
Объем также очень большой, потому что все общедоступные файлы записи будут упакованы в этот фрагмент. Эти два фрагмента загружаются всеми записями, поэтому они влияют на производительность. Существует также много дублирующегося кода, а некоторый менее часто используемый код упакован в чанк рта. Далее будут рассмотрены эти вопросы.
Принцип модификации шардинга
-
node_modules
Файлы все еще упакованы вchunk-vendors
, слишком много упоминаний, было сложно перейти на загрузку по запросу, частота изменений низкая, и его можно использовать в качестве кеша браузера (304) в течение длительного времени, чтобы не истечь. - Файлы с небольшим количеством ссылок и большим объемом вынесены в отдельный блок.
- Другой общий код делится на несколько фрагментов, что позволяет избежать загрузки всего общего кода при доступе из определенной записи и не приводит к аннулированию кэшем всего общего кода при изменении части кода.
- Небольшие асинхронные фрагменты объединяются в другие связанные асинхронные фрагменты или объединяются в ротовые фрагменты.
Отложенная загрузка маршрута
Фрагмент записи слишком велик. Я просмотрел маршруты, и многие из них являются статическими импортами. Волна динамического импорта была изменена, и только несколько часто используемых маршрутов остаются статичными.
Отложенная загрузка маршрутада
vue-router
предоставляемые функции, вы можете использоватьconst Foo = () => import('./Foo.vue')
загружать по запросуFoo
модуль. Каждый из его модулей, загруженных по запросу, будет разделен на асинхронный фрагмент, и в фактическом вызовеFoo
модуль, только в текущем DOM<head>
а также<body>
вставить в конце<link>
а также<script>
Загрузите файл чанка.
Принцип модификации маршрута в asyncChunk:
высокая частота использования | низкая частота использования | |
---|---|---|
маленький файл | Слить во рту кусок | один кусок |
большой файл | Один фрагмент (плюс предварительная выборка) | один кусок |
Преимущества разделения больших фрагментов записей на более мелкие фрагменты: уменьшение размера файла, который пользователям необходимо загрузить при первом посещении, и объема кода, который необходимо выполнить. Уменьшите дублирование кода между разными записями.
Недостаток: пользователям может потребоваться загружать новые файлы для последующих операций, что увеличивает время ожидания. Асинхронные фрагменты могут зависеть от некоторых файлов, уже находящихся во входном фрагменте, что приводит к загрузке дублирующегося кода.
В соответствии с разделением маршрутов каждый маршрут, соответствующий указанным выше правилам, имеет фрагмент, а остальные сохраняются для прямого статического импорта или объединяются в другие фрагменты (чанки с одинаковыми именами будут объединены).
{
path: '/user/XXX',
name: 'user-XXX',
// 改成动态import
component: () => import(
/* webpackChunkName: "chunk-user-XXX" */
'src/pages/XXX'
),
},
После модификации есть несколько небольших фрагментов, но влияние невелико.Все они загружаются асинхронно, что не приведет к слишком большому количеству файлов в одном запросе для снижения производительности.
Реже используемая библиотека вынесена в отдельный кусок
- к
tinymce
, например, элемент управления редактором форматированного текста. Большие модули могут быть изменены для загрузки по требованию.
const tinymce = () => import(
/* webpackChunkName: "chunk-tinymce" */
'../plugins/tinymce'
);
...
beforeMount() {
// 动态import tinymce,在第一次mount之前再下载这个chunk(压缩后300K)
tinymce().then(() => {
this.loading = false;
});
},
После этих двух шагов общий размер чанков: 16M => 10,8M (проанализировано)
Фрагментация:
Вы можете видеть, что дублирующегося кода стало намного меньше, но два больших фрагмента по-прежнему велики. Продолжайте разделяться.
- echarts (672K): библиотека управления диаграммами, не все записи можно использовать, но она используется слишком во многих местах, и ее трудно изменить для загрузки по требованию.
// 对vue.config.js进行修改
cacheGroups: {
// 增加一个cacheGroup
echarts: {
name: 'chunk-echarts',
test: /[\\/]node_modules[\\/]echarts[\\/]/,
priority: 0,
chunks: 'all',
},
}
Увеличьте количество фрагментов общедоступного кода
Фрагмент входа все еще слишком велик, и количество извлекаемых публичных фрагментов может быть увеличено. Увеличение уменьшит дублирование кода, но увеличится количество файлов, так что чем больше, тем лучше.
splitChunks: {
maxInitialRequests: 5, //默认3
maxAsyncRequests: 6, //默认5
...
},
извлечение поставщиков
Стратегия по умолчанию заключается в извлечении всех зависимостей фрагмента записи.node_modules
В файле ниже есть проблема, асинхронный чанк поставитnode_modules
Приведенные ниже файлы снова упакованы, поэтому некоторые браузеры библиотек загружают две копии. А библиотеку, на которую ссылаются только один раз, извлекать не надо, то есть она не уменьшает ни общий размер чанков, ни количество файлов, а увеличиваетchunk-vendors
, так что общий размер файла всех записей увеличивается.
cacheGroups: {
...
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
// 增加下面两行
minChunks: 2,
chunks: 'all',
},
}
Ступай на яму
После изменения правил сегментирования некоторые файлы js в dom отсутствуют:Метод вставки чанков HtmlWebpackPlugin
чанк-общий раскол
Изначально общая часть кода всех чанков записей (упоминаемая как минимум дважды) упаковывается в чанки, но на самом деле пользователи каждой роли вряд ли получат доступ к другим записям, и все записи должны быть загруженыchunk-common
, так будетchunk-common
Разделить на несколько копий (больше не объединять весь общий код в один файл)
common: {
name: true, // 默认是name: 'chunk-common'
minChunks: 2,
minSize: 60000, // 大小超过60kb的模块才会被提取,防止一堆小chunck
priority: -20,
chunks: 'initial',
reuseExistingChunk: true,
},
После этой обработки общий размер кусков: 10,8M => 15,8M (проанализировано)
Кажется, что общий размер стал больше, потому что стало больше повторяющихся кодов, но файлы, которые необходимо загрузить для каждой записи, были уменьшены с 7-8М до 3-4М, среднее уменьшение на 60%. По сравнению с этим совершенно неважно, что он занимает на сервере на 5М больше места для хранения.
зачем ставить
chunk-common
отдельно, но неchunk-verdors
разделить?
упомянутый ранееchunk-verdors
Это библиотека, от которой зависит проект.Частота изменений низкая.Можно использовать кеш 304. Если его разделить, то увеличится количество файлов и количество запросов при загрузке в первый раз. а такжеchunk-common
Есть код самого проекта, который часто меняется, поэтому кеш часто инвалидируется, и пользователю приходится каждый раз скачивать его заново.
минификация кода
vue-cli настроен по умолчаниюterserДля сжатия кода вы можете изменить его конфигурацию по умолчанию, чтобы еще больше сжать код.
terser уменьшит размер кода за счет упрощения выражений и функций, ускорит время выполнения и уменьшит использование памяти во время выполнения.
правильноvue.config.js
Внесите следующие изменения:
// webpack-chain 的用法见:https://github.com/neutrinojs/webpack-chain
chainWebpack: config => {
if (process.env.NODE_ENV !== 'development') {
config.optimization.minimizer('terser').tap((args) => {
args[0] = {
test: /\.m?js(\?.*)?$/i,
chunkFilter: () => true,
warningsFilter: () => true,
extractComments: false, // 注释是否单独提取成一个文件
sourceMap: true,
cache: true,
cacheKeys: defaultCacheKeys => defaultCacheKeys,
parallel: true,
include: undefined, // 对哪些文件生效
exclude: undefined,
minify: undefined, // 自定义minify函数
// 完整参数见 https://github.com/terser/terser#minify-options
terserOptions: {
compress: {
arrows: true, // 转换成箭头函数
collapse_vars: false, // 可能有副作用,所以关掉
comparisons: true, // 简化表达式,如:!(a <= b) → a > b
computed_props: true, // 计算变量转换成常量,如:{["computed"]: 1} → {computed: 1}
drop_console: true, // 去除 console.* 函数
hoist_funs: false, // 函数提升声明
hoist_props: false, // 常量对象属性转换成常量,如:var o={p:1, q:2}; f(o.p, o.q) → f(1, 2);
hoist_vars: false, // var声明变量提升,关掉因为会增大输出体积
inline: true, // 只有return语句的函数的调用变成inline调用,有以下几个级别:0(false),1,2,3(true)
loops: true, // 优化do, while, for循环,当条件可以静态决定的时候
negate_iife: false, // 当返回值被丢弃的时候,取消立即调用函数表达式。
properties: false, // 用圆点操作符替换属性访问方式,如:foo["bar"] → foo.bar
reduce_funcs: false, // 旧选项
reduce_vars: true, // 变量赋值和使用时常量对象转常量
switches: true, // 除去switch的重复分支和未使用部分
toplevel: false, // 扔掉顶级作用域中未被使用的函数和变量
typeofs: false, // 转换typeof foo == "undefined" 为 foo === void 0,主要用于兼容IE10之前的浏览器
booleans: true, // 简化布尔表达式,如:!!a ? b : c → a ? b : c
if_return: true, // 优化if/return 和 if/continue
sequences: true, // 使用逗号运算符连接连续的简单语句,可以设置为正整数,以指定将生成的最大连续逗号序列数。默认200。
unused: true, // 扔掉未被使用的函数和变量
conditionals: true, // 优化if语句和条件表达式
dead_code: true, // 扔掉未被使用的代码
evaluate: true, // 尝试计算常量表达式
// passes: 2, // compress的最大运行次数,默认是1,如果不在乎执行时间可以调高
},
mangle: {
safari10: true,
},
},
};
return args;
});
}
...
}
对于各参数,应该根据项目具体情况设定,加快运行速度或者减小代码体积的代价是增加了打包时间,如果不知道某个参数的含义最好不要修改。
Общий размер чанков: 15,8 МБ => 15,7 МБ (обработано) (в основном для повышения скорости выполнения)
HtmlWebpackPlugin
HtmlWebpackPlugin
даwebpack
Плагин, который позволяет указать html-шаблон для каждой записи, чтобы упростить создание html-файлов.vue.config.js
в конфигурацииpages
То, что на самом деле указывает опция,HtmlWebpackPlugin
Опции.
Обновлено до V4.3, поддерживает вставку тегов в соответствии с фрагментом записи.
Причину обновленной версии можно увидеть в блоге, который я писал в предыдущем шаге на яме.
PreloadWebpackPlugin
ЭтоHtmlWebpackPlugin
плагин для вставки<link rel="prefetch">
а также<link rel="preload">
Этикетка. из-заPreloadWebpackPlugin
V2.3 не поддерживаетHtmlWebpackPlugin
V4, обновите до v3.0.0-beta.3 здесь (подробнее см.issues).
Но v3.0.0-beta.3 будет вставлять все asyncChunks по умолчанию при множественной записи, должен использовать обычное сопоставление и отказаться от исходного варианта вставки по записи. По оценкамprefetch
Он должен быть специально обозначен, потому что им не злоупотребляют.
prefetchИспользуется, чтобы указать браузеру загружать файлы, которые пользователь может использовать при следующем просмотре, заранее, когда он бездействует,preloadЗатем, поскольку браузер блокирует синтаксический анализ DOM при выполнении css, обычно используется
preload
заранее загрузить файлы, которые будут использоваться сразу на текущей странице
Текущая стратегия изменена на:prefetch
Обычно используемые asyncChunks, связанные с записью,preload
chunk-vendors
и фрагмент входа
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PreloadWebpackPlugin = require('preload-webpack-plugin');
...
chainWebpack: config => {
Object.keys(config.entryPoints.entries()).forEach(page => {
config.plugins.delete(`html-${page}`);
config.plugins.delete(`preload-${page}`);
config.plugins.delete(`prefetch-${page}`);
/**
* vue-cli内置的 v3.2 HtmlWebpackPlugin,插入的chunks必须显示指定
* vue-cli默认指定的chunks是['chunk-vendors', 'chunk-common', page]
* 但是修改了分片规则以后,完成分片之前不知道有哪些chunks
* 这里全部替换为v4.3的HtmlWebpackPlugin
*/
config
.plugin(`html-${page}`)
.use(HtmlWebpackPlugin, [{
filename: `${page}.html`,
// v4.3中,名为chunks,实为entries
chunks: [page],
template: templates[page],
}]);
/**
* PreloadWebpackPlugin v2.3.0 不支持HtmlWebpackPlugin V4 ,
* 这里替换为 v3.0.0-beta.3
* @see https://github.com/GoogleChromeLabs/preload-webpack-plugin/issues/79
* v3.0.0-beta.3 在多入口下会默认插入所有asyncChunks,必须用正则匹配
* @see https://github.com/GoogleChromeLabs/preload-webpack-plugin/issues/96
* 有人提了按入口插入的 pull request,将来也许可以不用这么麻烦
* @see https://github.com/GoogleChromeLabs/preload-webpack-plugin/pull/109
*/
config
.plugin(`prefetch-${page}`)
.use(PreloadWebpackPlugin, [{
rel: 'prefetch',
include: 'asyncChunks',
fileWhitelist: [
// your RegExp here
],
fileBlacklist: [
/\.map$/,
/hot-update\.js$/,
],
// v3.0.0-beta.3 没有includeHtmlNames选项了,只能通过excludeHtmlNames控制
excludeHtmlNames: Object.keys(config.entryPoints.entries())
.filter(entry => entry !== page)
.map(entry => `${entry}.html`),
}]);
config
.plugin(`preload-${page}`)
.use(PreloadWebpackPlugin, [{
rel: 'preload',
include: ['chunk-vendors', page],
fileBlacklist: [
/\.map$/,
/hot-update\.js$/,
],
excludeHtmlNames: Object.keys(config.entryPoints.entries())
.filter(entry => entry !== page)
.map(entry => `${entry}.html`),
}]);
});
...
}
要注意的是, preload 要放在 prefetch 之前。另外 prefetch 不应该被滥用,否则会造成占用大量服务器带宽。
Если это скрипт, добавленный в html-шаблон,
PreloadWebpackPlugin
не добавляется автоматическиpreload
а такжеprefetch
Да, нужно добавить вручную
CorsPlugin
Это встроенный в Vue-CLI плагин webpack дляHtmlWebpackPlugin
вставленtags
плюсcrossorigin
свойство (скрипт междоменный). Поскольку статические ресурсы нашего проекта (включая js и css) в конечном итоге будут размещены в CDN, то оно отличается от доменного имени проекта.crossorigin
атрибут, ошибка jsScript error
, интерфейсный мониторинг не может определить местоположение, в котором сообщается об ошибке js. если<script>
имеютcrossorigin
свойства, тоprefetch
а такжеpreload
также должен иметьcrossorigin
атрибут, иначеprefetch
а такжеpreload
Ресурсы не будут использоваться и вызовут 2 загрузки.CORS settings attributes
ОднакоCorsPlugin
не поддерживаетсяHtmlWebpackPlugin
V4, поэтому его нужно переписатьCorsPlugin
,существуетPreloadWebpackPlugin
После выполнения заменить регулярным выражениемhtml
внутреннийprefetch
а такжеpreload
Этикетка.
Сжатие изображения
существуетvue.config.js
Добавьте загрузчик для сжатия изображений в:image-webpack-loader
npm install image-webpack-loader --save-dev
chainWebpack: config => {
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
bypassOnDebug: true, // webpack 'debug' 模式下不执行
})
.end()
.end()
}
Эффект сжатия:/dist
Размер: 91M=>83,1M, общий размер чанков: 16,3M=>15,7M(при анализе)
Настройте порядок загрузки файлов
HtmlWebpackPlugin
вставит фрагмент входа в<body>
конец, так что пишитеhtml
Скрипты в шаблоне будут загружены перед чанком входа, они будут блокировать парсинг DOM и не начнут загружать чанк входа, пока загрузка не будет завершена. Следовательно, вы должны добавить ненужный приоритет скрипту, загруженному сdefer
Атрибуты для задержки загрузки и добавления сценариев, которые необходимо загрузить в первую очередь.preload
(перед всеми файлами css).
defer: Это логическое свойство настроено для информирования браузера о том, что сценарий будет запущен после завершения синтаксического анализа документа.DOMContentLoadedВыполняется перед событием.
(Картинка взята из интернета, конкретный источник неизвестен)
页面中文件的加载顺序容易被忽略,应当合理调整使得下载和执行能充分并行,不要让浏览器“闲下来”,这能很大程度上提高FMP
Мультиплексирование было введено в HTTP/2. Мультиплексирование — хорошее решение проблемы, связанной с тем, что браузеры ограничивают количество запросов под одним и тем же доменным именем, и в принципе не нужно беспокоиться о слишком большом количестве файлов. К сожалению, CDN проекта не поддерживает HTTP/2.
Ускорить упаковку webpack
Из-за огромного размера проекта время упаковки было очень долгим, добавляяimage-webpack-loader
После этого время на каждый пакет и развертывание увеличилось примерно на 1 минуту, что было невыносимо. Проект упаковывается и развертывается путем извлечения кода из док-контейнера. Проверил, основная часть времени упаковки находится вnpm install
и различныеloader
С точки зрения выполнения, он будет намного быстрее, если он не будет начинаться с 0 каждый раз, когда он упакован. Сначала вы можетеimage-webpack-loader
добавить передcache-loader
, так что этоloader
Данные после одного выполнения будут кэшироваться вnode_modules/.cache
каталог, кеш будет использоваться при следующей упаковке.
config.module
.rule('images')
// 给 image-webpack-loader 加上缓存来加快编译
.use('cache-loader')
.before('url-loader')
.loader('cache-loader')
.options({
cacheDirectory: path.join(__dirname, 'node_modules/.cache/image-webpack-loader'),
})
注意:只有执行时间很长的loader才适合用缓存,因为读写文件也是有开销的,滥用反而会导致变慢
При создании образа упакуйте его один раз, чтобы получитьnode_modules
содержание. После каждого развертывания, после извлечения кода,node_modules
Перейдите непосредственно в каталог кода и выполните его.npm install
а такжеnpm run build
. или использоватьnpm install --cache-min Infinity
Используйте кеш npm для установки, а затем переместите зеркало в.cache
каталог в каталог кодаnode_modules
внутри. И может в значительной степени избежать нестабильности сети, вызваннойnpm install
Проблема медленной загрузки файлов.
а такжеFreight,npmboxОжидание автономных инструментов установки пакетов, которые можно использовать в зависимости от ситуации с проектом.
Кроме того, если производственная среда не требуетSourceMap
,существуетvue.config.js
Конфигурация отключена.
productionSourceMap: false
Сравнение времени упаковки после обработки:
имеют.cache содержание |
нет.cache содержание |
|
---|---|---|
Исходное время развертывания | 190s | 309s |
Удалитьimage-webpack-loader Позже |
159s | 332s |
Датьimage-webpack-loader плюсcache-loader Позже |
151s | 314s |
УдалитьSourceMap Позже |
102s | 257s |
Обычный совет по написанию кода
На самом деле, многие проблемы с производительностью большинства проектов появляются постепенно из-за каких-то неправильных методов написания кода при написании кода Вы пишете немного, я пишу немного, и через долгое время будет сложно оптимизировать, потому он включает в себя широкий спектр изменений. Вот несколько советов по написанию.
- Для нового маршрута подумайте, следует ли лениво загружать его, посмотрите, существует ли существующий фрагмент, который является тем же файлом, следует ли объединить существующий фрагмент или не выполнять предварительную выборку.
- вкладки Учитывать ленивую загрузку (перерисовка при переходе на соответствующую вкладку)
- Большие зависимости лучше всего загружать по запросу (плюс анимация загрузки)
- Разрешение картинок в проекте должно учитывать размер используемой сцены
- Вы можете рассмотреть вопрос о сжатии изображений, загружаемых пользователями.
- Открытие новой вкладки приведет к повторному выполнению большого количества js, попробуйте подумать, нужно ли это
- Лучше не менять зависимую библиотеку часто, а максимально сконцентрировать изменения и выходить в сеть вместе
Эффект оптимизации
Результаты шаринга
Общий размер кусков:16.09 MB(parsed)
Размер каждого входа:2.3-4.8M(-60%)(parsed)
Основные данные индикатора
После предыдущей операции посмотрим, какой эффект она произвела.
avg | 50 баллов | 90-й процентиль | |
---|---|---|---|
TTI | -11% | -11% | -11% |
FCP | -20% | -18% | -23% |
FMP | -11% | -11% | -14% |
DOM Ready | -15% | -16% | -14% |
Кроме того, коэффициент медленного движения был снижен на 67%, а показатель отказов при первой загрузке снижен на 47%.
Видно, что падение TTI не достигло ранее установленного целевого значения, с одной стороны, слишком много загруженных ресурсов, и некоторые из них могут быть изменены для загрузки по требованию, с другой стороны, некоторые Ajax-запросы медленные, который не находится в пределах контролируемого диапазона переднего конца. Я подумаю о других решениях в будущем (может рассмотреть возможность использованияservice worker
).
Продолжение следует (постоянно обновляется)