Помните о практике оптимизации проекта VUE

сервер HTTP Vue.js HTML

ПК для проверки здоровья iKang (www.tijianbao.com/) это "старый" проект, почему вы говорите "старый", потому что технология фронтенда меняется с каждым днем, есть новые знания, новые концепции и даже новые фреймворки сегодня, он по-прежнему основан на vue- cli 2.x, webpack 3.x Сборка, очевидно, немного устарела. Во-вторых, когда проект запускался в первые дни, из-за поспешного запуска вопросы производительности и загрузки не рассматривались особо, в настоящее время используемые на сайте картинки не обрезаны, а все библиотеки упакованы в поставщик.Время загрузки первого экрана слишком долгое и т.д.Эти проблемы делают пользовательский опыт веб-сайта не очень хорошим.Основываясь на различных причинах, было решено оптимизировать его, в основном по следующим аспектам:

  • Используйте https и обновите протокол до http/2
  • Разумный контроль кеша
  • Добросовестное использование изображений
  • Обновите до веб-пакета4
  • Оптимизация загрузки первого экрана
  • Сравнение данных до и после оптимизации

Следующие раскрываются отдельно

Используйте https и обновите протокол до http/2

https в основном приносит улучшения безопасности, а http/2 зависит от https, только сайты, использующие протокол https, могут обновлять протокол http/2.

http/2 содержит ряд изменений и оптимизаций, в основном следующих:

  • Используйте только одно соединение на сервер. HTTP/2 использует только одно соединение на сервер, а не одно соединение на файл. Таким образом экономится время на установление нескольких подключений, что особенно заметно для TLS, поскольку TLS-соединения требуют времени.
  • Ускорьте доставку TLS. HTTP/2 требует только одного трудоемкого рукопожатия TLS и обеспечивает оптимальную производительность за счет использования мультиплексирования по одному соединению. HTTP/2 также сжимает данные заголовков, экономя часть работы по оптимизации, необходимой в эпоху HTTP/1.x, например объединение файлов, тем самым улучшая использование кэша.
  • Упрощение веб-приложений. Использование HTTP/2 может сэкономить веб-разработчикам много работы, поскольку им не нужно выполнять оптимизацию для HTTP/1.x.
  • Подходит для страниц со смешанным содержанием. HTTP/2 особенно хорошо подходит для традиционных страниц, на которых смешаны HTML, CSS, JavaScript, изображения и ограниченное количество мультимедиа. Браузеры могут расставлять приоритеты для этих важных файловых запросов, позволяя ключевым частям страницы появляться первыми и раньше.
  • безопаснее. Снижая потери производительности от TLS, больше приложений могут использовать TLS, что повышает безопасность пользовательской информации.

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

Конфигурация в основном предназначена для добавления модуля with-http_ssl_module и модуля with-http_v2_module при компиляции nginx.

./configure --with-http_v2_module --with-http_ssl_module

настроить файл конфигурации сервера

server {
 listen 443 ssl http2 default_server;

 ssl_certificate server.crt;
 ssl_certificate_key server.key;
 ...
}

Затем перезапустите сервер, чтобы завершить обновление~

Разумный контроль кеша

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

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

  • Включите gzip (общий балл за оптимизацию сервера, а не категорию кеша)
  • включить etag
  • Установите время истечения 80 с для файлов типа html.
  • Отключить кеширование запросов API
  • Настройте долгосрочный кеш для статических ресурсов

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

# 配置 gzip
        gzip on;
        gzip_min_length 0k;
        gzip_comp_level 1;
        gzip_types text/plain application/json application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
        gzip_vary on;
        gzip_disable "MSIE [1-6]\.";

# 开启 etag
        etag on;

# 不缓存接口
        location ~* \.(?:manifest|appcache|xml|json)$ {
                add_header Cache-Control "no-cache";
        }
        
# 设置 html 过期时间为 80s
        location ~* \.(?:html)$ {
                add_header Cache-Control "max-age=80";
        }

# 给静态资源设置一个长期缓存
        location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
                expires 1M;
                access_log off;
                add_header Cache-Control "public";
        }
        # CSS and Javascript
        location ~* \.(?:css|js)$ {
                expires 1y;
                access_log off;
                add_header Cache-Control "public";
        }

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

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

Так как запрос API должен получить самый последний контент, кеш браузера не устанавливается.Ожидаемый результат в том, что передачу можно оптимизировать через etag, но на практике установка API через nginx недействительна, и конкретный процесс все еще находится в процессе исследовал.

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

Добросовестное использование изображений

Существует два источника изображений, используемых самим веб-сайтом, а именно Paiyun и наш собственный idc (ускоренный Alibaba Cloud), которые не обрезаются в процессе использования, что приводит к относительно большому общему загружаемому ресурсу изображений. было сделано. :

Для ресурсов изображения от Youpaiyun

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

Например, для определенного ресурса-изображения нам нужно изображение шириной 200px. Раньше мы напрямую ссылались на этот ресурс: domain.url/images/001.jpg.Это исходный ресурс, который может быть очень большое изображение. Прямые ссылки расточительны

Благодаря автоматическому кадрированию мы можем ссылаться на изображение с указанной шириной: domain.url/images/001.jpg!/fw/200, добавляя !/fw/200 после URL-адреса, мы можем ссылаться на изображение с шириной 200px, и максимальная экономия ресурсов

Для браузеров, поддерживающих webp, вы также можете разрешить вывод версии webp: domain.url/images/001.jpg!/fw/200/format/webp и указать формат выходного ресурса как webp через ключевое слово /format/webp.

В целом Paiyun предоставляет удобные и гибкие методы управления ресурсами, подробнее смотрите в официальной документации:help.upcloud.com/knowledge-no…

Для изображений из idc

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

Однако нет возможности добиться аналогичной функции через модуль nginx.Этот модуль: ngx_http_image_filter_module.Через этот модуль указанные ресурсы можно резать по условиям, и их можно резать, когда CDN возвращается к исходнику Достаточно хорошей картинки, частично реализована функция облачного хранилища, конкретная реализация такова:

1. Добавьте --with-http_image_filter_module при компиляции nginx

2. Настройте nginx

    location ~* /images/(.+)$ {
        set $width -; #图片宽度默认值
        set $height -; #图片高度默认值
        if ($arg_width != "") {
            set $width $arg_width;
        }
        if ($arg_height != "") {
            set $height $arg_height;
        }
        #image_filter_jpeg_quality 85;
        image_filter resize $width $height; #设置图片宽高
        image_filter_buffer 10M;   #设置Nginx读取图片的最大buffer。
        image_filter_interlace on; #是否开启图片图像隔行扫描
        if ($arg_info = "yes") {
        #        image_filter size;
        }
        error_page 415 = 415.png; #图片处理错误提示图,例如缩放参数不是数字
    }

Благодаря приведенной выше конфигурации, когда мы хотим получить доступ к ресурсу, мы можем получить изображение шириной 200 пикселей через: domain.url/images/002.jpg?width=200.

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

Официальная документация для этого модуля:Nginx.org/en/docs/red-glowing…

Ленивая загрузка изображений и webp

Использование ленивой загрузки изображений в основном зависит от модуля npm Vue-Lazyload.Подробнее см.Уууу, эта лошадь plus.com/package/v ue…

Здесь мы в основном говорим о двух функциях Progressive и WebP:

Vue.use(VueLazyload,{
  observer: true,
  attempt: 10,
  filter: {
    progressive (listener, options) {
      const is_upyun_CDN = /upyunimages\./
      if (is_upyun_CDN.test(listener.src)) {
        listener.el.setAttribute('lazy-progressive', 'true')
        listener.loading = listener.src.replace(/fw.+/, 'fw/10')
      }
    },
    webp (listener, options) {
      if (!options.supportWebp) return
      const is_upyun_CDN = /upyunimages\./
      if (is_upyun_CDN.test(listener.src)) {
        listener.src += '/format/webp'
      }
    }
  }
})

Фильтруйте и контролируйте все лениво загруженные ресурсы в целом через фильтр:

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

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

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

Обновите до веб-пакета4

Говорят, что webpack 3 работает хорошо, зачем нам обновляться до 4? Причина в том, что новая версия принесла нам много хороших фильмов, и в настоящее время это стабильная версия, в основном включающая следующее:

  • Более быстрая скорость компиляции.В интернете говорят,что она улучшилась на 98%.Хотя у меня нет проверочных данных,но интуитивно быстрее и намного быстрее.
  • Упаковка модуля с нулевой конфигурацией, хотя нет способа достичь «нуля», но все более и более разумные конфигурации по умолчанию делают построение проекта более удобным.
  • Заброшенный CommonChunksPlugin, используйте более продвинутый SplitChunksPlugin для извлечения общих ресурсов
  • Используйте встряхивание дерева для эффективного сокращения объема бизнес-кода
  • Ввести атрибут режима, который может быть определен как разработка и производство, без написания слишком большого количества программ настройки для производственных сред и сред разработки.
  • и т. д. другие неупомянутые и стандартные оптимизации

В процессе обновления могут возникнуть различные проблемы, но, к счастью, естьОфициальное руководство по обновлениюЭто может помочь нам охватить часть этого, но это руководство будет слишком кратким и точным, и в проекте будет много ям. К счастью, через подсказку об ошибке в сочетании с великим Google вы, наконец, можете найти ответ (если вы используете baidu, то очень вероятно, что вы в итоге не сможете залезть :<...>

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

Оптимизация загрузки первого экрана

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

Как было сказано ранее, за счет уменьшения размера изображения, обновления вебпака 4, оптимизации пакета общедоступных ресурсов и т.д., это все для этого сервиса (конечно, не все :>) Эти методы - все операции над ресурсами. Когда всеми этими ресурсами манипулируют, то, как разумно обращаться с этими ресурсами, приходит к механизму рендеринга браузера.Как улучшить скорость рендеринга первого экрана за счет оптимизации процесса рендеринга - наше следующее рассмотрение.

Процесс рендеринга страницы браузером в основном делится на пять шагов (пропустите часть запроса и обсудите только то, как браузер обрабатывает запрос после получения ресурса):

  • Обработка разметки HTML и построение дерева DOM.
  • Обработка разметки CSS и построение дерева CSSOM.
  • Объедините DOM и CSSOM в единое дерево рендеринга.
  • Разложите по дереву рендеринга, чтобы рассчитать геометрию каждого узла.
  • Нарисуйте каждый узел на экране.

Как видно из вышеизложенного, браузер может отображать страницу до тех пор, пока загружена структура html и css.

Для структуры, полученной выше, есть 3 альтернативы:

  • рендеринг на стороне сервера
  • Предварительный рендеринг с помощью prerender-spa-plugin
  • Реализация загрузки в HTML

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

рендеринг на стороне сервера

Что такое рендеринг на стороне сервера? отОфициальный сайтОбъяснение таково: отображать компоненты в виде строк HTML на стороне сервера, отправлять их непосредственно в браузер и, наконец, «активировать» эти статические теги как полностью интерактивное приложение на стороне клиента.

Основные преимущества рендеринга на стороне сервера (SSR):

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

Я написал статью ранее, в которой подробно обсуждалосьКак реализовать проект рендеринга на стороне сервера.

Но у такого, казалось бы, красивого решения есть и недостатки:

  • условия развития. Код, зависящий от браузера, который можно использовать только в определенных обработчиках жизненного цикла; некоторые внешние библиотеки могут потребовать специальной обработки для запуска в приложениях, отображаемых на сервере.
  • Дополнительные требования, связанные с установкой и развертыванием сборки. В отличие от полностью статических одностраничных приложений (SPA), которые можно развернуть на любом статическом файловом сервере, для приложений, отображаемых на сервере, требуется среда выполнения сервера Node.js.
  • Больше нагрузка на сервер. Рендеринг полного приложения в Node.js, очевидно, потребует более интенсивных ресурсов ЦП (интенсивный ЦП - интенсивный ЦП), чем просто предоставление статического файлового сервера, поэтому, если вы планируете использовать в среде с высоким трафиком (высокий трафик), пожалуйста, подготовьте соответствующую нагрузку на сервер и разумно используйте стратегии кэширования.

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

Предварительный рендеринг с помощью prerender-spa-plugin

Предварительный рендеринг может достичь той же цели, что и SSR.Он компилирует указанную страницу в html на этапе компиляции и напрямую отправляет html-контент клиенту, когда есть запрос, но у него также есть проблемы:

  • Это делается на этапе компиляции, могут быть скомпилированы только ограниченные страницы (например, /, /about, /contact и т. д.), нет возможности сделать все статическим, и если содержимое будет обновлено, ранее скомпилированные страницы не будут быть обновленным.
  • Страничка трясется.Например первый раз заходим это динамический контент /article/id-1234.Так как этот динамический контент ранее не компилировался в html, то этот запрос упадет на /index.html.Пожалуйста отрендерите страницу сначала на домашнюю страницу. , когда в динамическом интерфейсе есть контент, страница будет обновлена ​​снова, и страница будет преобразована в целевую страницу, что вызовет путаницу у пользователей.

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

Реализация загрузки в HTML

Это решение, которое мы в итоге выбрали. Это решение относительно простое от принципа до реализации. Он использует html-webpack-plugin для вставки заданного html и css в шаблон. До того, как запросы js и api не будут возвращены, самая высокая скорость Velocity дает пользователю запрос на загрузку, чтобы сообщить пользователю, что ответ был получен.

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

Разделите html эффекта загрузки наloading.htmlа такжеloading.css, размещенные в /src/preLoad/loading.html и /src/preLoad/loading.css соответственно.

Прочитайте эти два файла в файле конфигурации WebPack:

module.exports = {
  loading: {
    html: fs.readFileSync(path.join(__dirname, '../src/preLoad/loading.html')),
    css: '<style>' + fs.readFileSync(path.join(__dirname, '../src/preLoad/loading.css')) + '</style>'
  }
  // ...
}

Внесите в файл конфигурации сборки:

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      loading: config.loading
      // ...
    })
    // ...
  ]
  // ...
}

Вставьте переменные в файл шаблона:

<!DOCTYPE html>
<html>
<head>
  <!-- ... -->
  <%= htmlWebpackPlugin.options.loading.css %>
</head>
<body>
  <div id="app">
    <%= htmlWebpackPlugin.options.loading.html %>
  </div>
</body>
</html>

Таким образом, на страницу можно вставить эффект динамической загрузки.

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

// vue-loader.conf.js
module.exports = {
  loaders: utils.cssLoaders({
    sourceMap: sourceMapEnabled,
    extract: false // 不提取
  }),
  // ...
}

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

Наконец, есть проблема, которую необходимо решить, поскольку программа зависит от различных сторонних пакетов, эти пакеты будут упакованы в vendor.js, что делает этот файл очень большим, даже больше 1M.Его необходимо оптимизировать. Идея состоит в том, чтобы сказать webpack упаковке Нет, не вводить некоторые пакеты в vendor.js, а затем мы вручную импортируем эти файлы в html.

Теперь, когда сторонний CDN предоставляет стабильный доступ ресурсов и использовать через различные характеристики http / 2, чтобы эти сторонние ресурсы нагрузовали очень быструю, конкретные практики следующие:

Определите ресурсы для импорта от третьих лиц в конфигурации веб-пакета:

Определение этих пакетов в базовом файле конфигурации webpack не требует упаковки в vendor.js:

// webpack.base.conf.js
module.exports = {
  externals: {
    'vue': 'Vue',
    'vuex': 'Vuex',
    'iview': 'iview'
  }
  //...
}

Укажите сторонние ресурсы в конфигурационном файле webpack:

module.exports = {
  loading: {
    html: fs.readFileSync(path.join(__dirname, '../src/preLoad/loading.html')),
    css: '<style>' + fs.readFileSync(path.join(__dirname, '../src/preLoad/loading.css')) + '</style>'
  },
  css: [
    'https://cdn.jsdelivr.net/npm/iview@2.14.3/dist/styles/iview.css'
  ],
  js: [
    'https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js',
    'https://cdn.jsdelivr.net/npm/vuex@3.0.1/dist/vuex.min.js',
    'https://cdn.jsdelivr.net/npm/iview@2.14.3/dist/iview.min.js'
  ]
  // ...
}

Как и загрузка, она представлена ​​в файле конфигурации сборки:

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      loading: config.loading,
      externals_js: config.js, // 引入 js
      externals_css: config.css, // 引入 css
      // ...
    })
    // ...
  ]
  // ...
}

Вставьте переменные в файл шаблона:

<!DOCTYPE html>
<html>
<head>
  <!-- ... -->
  <%= htmlWebpackPlugin.options.loading.css %>
  <% for (var i in htmlWebpackPlugin.options.externals_css) { %>
  <link href="<%= htmlWebpackPlugin.options.externals_css[i] %>" rel="stylesheet">
  <% } %>
</head>
<body>
  <div id="app">
    <%= htmlWebpackPlugin.options.loading.html %>
  </div>
  

  <% for (var i in htmlWebpackPlugin.options.externals_js) { %>
  <script src="<%= htmlWebpackPlugin.options.externals_js[i] %>"></script>
  <% } %>
</body>
</html>

Таким образом, большие пакеты удаляются из vendor.js и импортируются через сторонний CDN.

Сравнение данных до и после оптимизации

Через обработку этих аспектов сравним данные до и после оптимизации:

проект До оптимизации Оптимизировано
общая загрузка ресурсов 6.7M 939K
Общее время завершения загрузки 19.05s 5.11s
Время рендеринга первого экрана 808ms 391ms
первый рендер контента 2.53s 1.62s
Рейтинг PageSpeed ​​Insights 13 баллов 83 балла

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