Практика оптимизации проекта под Vue CLI 2&3 - CDN + Gzip + Prerender

внешний интерфейс сервер CDN Vue.js SEO
Практика оптимизации проекта под Vue CLI 2&3 - CDN + Gzip + Prerender

Постоянная ссылка на эту статью:GitHub.com/H AOC также 942…

предисловие

Эти оптимизации применяются кVue CLI 2а такжеVue CLI 3, статья в основном основана наVue CLI 2введение в то, какVue CLI 3относится кwebpackрегулировка, я разместилvue-cli3-optimizationПод этот склад, и с подробными примечаниями, и дополнительным удобствомSassв использованииloader,использоватьSassбольше не нужно вводить переменные иmixinМесто очень хлопотное каждый раз@import. Практические методы и эффекты этих схем оптимизации подробно описаны ниже:

Как и многие друзья, я развиваюсьVueПроект также основан на официальномvue-cli@2изwebpackШаблоны, но по мере того, как проект становится все больше и больше, зависимые сторонниеnpmПакетов становится все больше и больше, и файлы после сборки тоже будут становиться все больше и больше, особенноvendor.js, даже достигая2Mо. В сочетании с одностраничным приложением это может привести к длительным белым экранам при низкой скорости интернета или ограниченной пропускной способности сервера. Чтобы решить эту проблему, я провел небольшое исследование и нашел три решения по оптимизации с очевидными эффектами без изменения бизнес-кода.CDN + Gzip + Prerender. Я разобрал эти методы и поместил их вРепозиторий на гитхабеНа вышеизложенном намерение состоит в том, чтобы показать различные методы оптимизации в разных ветвях.VueВлияние на производительность проекта. Вы можете клонировать его напрямую и попробовать, и он также выигрывает от наличияgitИстория, вы также можете легко просмотреть конкретные детали изменений. Ниже я покажу действие этих трех схем оптимизации на простом проекте.

Во-первых, подготовьтепростой проект

пройти черезvue-cli@2изwebpackШаблон сгенерирован, только самое основноеVueНабор из трех предметов ———vue,vue-router,vuexи широко используетсяelement-uiа такжеaxios. Разделить два маршрута — «Домой» и «Контакты», черезaxiosПолучить список контактов асинхронно и использоватьelement-ui的表格展示。 непосредственныйbuild, без всякой оптимизации, для справки.

1.1 Описание файла после сборки:

  1. app.css: Сжатие объединенных файлов стилей.
  2. app.js: В основном включает элементы в проектеApp.vue,main.js,router,storeи другие бизнес-коды.
  3. vendor.js: в основном содержит зависимости проекта, такие какvuex,axiosДождитесь исходников сторонней библиотеки,поэтому этот файл такой большой.Следующим шагом будет изучение способов оптимизации этого куска.Ведь с развитием проекта их будет все больше и больше зависимые библиотеки.
  4. 数字.js: числа, начинающиеся с 0, 1, 2, 3 и т. д.jsфайлы, эти файлы представляют собой блоки кода, вырезанные каждым маршрутом, потому что я разделил два маршрута и сделалОтложенная загрузка маршрута, так что есть два 0 и 1jsдокумент.
  5. mainfest.js:mainfestна английском естьсписок значений, который содержит логику для загрузки и обработки модуля маршрутизации.

1.2 Отключите кеш браузера и ограничьте скорость интернета доFast 3GвнизNetworkграфик (работает локальноnginxна сервере

можно увидеть неоптимизированныйbaseверсия вFast 3GЗагрузка под сетью занимает около 7 секунд

два,оптимизация CDN

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

  1. будет зависеть отvue,vue-router,vuex,element-uiа такжеaxiosЭти пять библиотек, все изменены, чтобы пройтиCDNСсылка для получения. с помощьюHtmlWebpackPlugin, вы можете легко использовать синтаксис цикла вindex.htmlвставлятьjsа такжеcssизCDNСвязь. здесьCDNв основном используетсяjsDelivrкоторый предоставил.
<!-- CDN文件,配置在config/index.js下 -->
<% for (var i in htmlWebpackPlugin.options.css) { %>
<link href="<%= htmlWebpackPlugin.options.css[i] %>" rel="stylesheet">
<% } %>
<% for (var i in htmlWebpackPlugin.options.js) { %>
<script src="<%= htmlWebpackPlugin.options.js[i] %>"></script>
<% } %>
  1. существуетbuild/webpack.base.conf.jsДобавьте следующий код вCDNВ случае внедрения внешних файлов его все равно можно использовать в проектеimportСинтаксис для введения этих сторонних библиотек, что означает, что вам не нужно менять код проекта, где имя ключаimportизnpmИмя пакета и значение ключа — это глобальная переменная, предоставляемая библиотекой.ссылка на документацию webpack.
  externals: {
    'vue': 'Vue',
    'vue-router': 'VueRouter',
    'vuex': 'Vuex',
    'element-ui':'ELEMENT',
    'axios':'axios'
  }
  1. удалить зависимостиnpmМешок,npm uninstall axios element-ui vue vue-router vuex
  2. удалятьmain.jsвнутриelement-uiсвязанный код.

Подробности можно посмотретьgitистория

2.1 Сравните файлы, созданные до и после добавления CDN:

Оптимизировано:

До оптимизации:
Как можно заметить:

  1. app.css: потому что больше не проходятimport 'element-ui/lib/theme-chalk/index.css', а напрямую черезCDNимпортировать по ссылкеelement-uiстиль, делая файл маленькимbytesуровень, так как теперь он содержит лишь небольшое количество предметовcss.
  2. app.js: Изменений почти нет, потому что это в основном код собственного бизнеса.
  3. vendor.js: будет ли 5 ​​зависеть отjsПреобразовать все вCDNПосле ссылки она уже достаточно мала1KB, на самом деле в нем нет сторонних библиотек.
  4. 数字.jsа такжеmainfest.js: Эти файлы по своей природе маленькие, и изменения практически незначительны.

2.2 Аналогично, отключите кэш браузера и ограничить скорость сети кFast 3GвнизNetworkграфик (работает локальноnginxна сервере

Видно, что в том же сетевом окружении скорость загрузки увеличилась с исходных 7 секунд до нынешних 3 секунд, и улучшение очень очевидно. И что еще более важно, оригинальный способ, все изjsа такжеcssДругие статические ресурсы запрашиваются нашими собственнымиnginxСервер, и теперь большинство статических ресурсов запрашиваются третьими лицамиCDNресурс, Это может не только повысить скорость, но и значительно снизить нагрузку на полосу пропускания вашего собственного сервера при высокой степени параллелизма Представьте себе файл размером более 900 КБ на первом экране. Теперь осталось всего 20 КБ, чтобы запросить собственный сервер!

три,Gzip-оптимизация

использоватьGzipЕсть два очевидных преимущества: во-первых, это может уменьшить пространство для хранения, а во-вторых, при передаче файлов по сети это может сократить время передачи.

3.1 Как включитьgzipкомпрессия

включиgzipПуть в основном заключается в изменении конфигурации сервера наnginxВ качестве примера сервер, на следующем рисунке используется тот же набор кода, только изменение сервераgzipв случае состояния переключателяNetworkСравнительная таблица

Неоткрытыйgzipсжатие:

включиgzipсжатие:

включиgzipСжатые заголовки ответа

Из рисунка выше видно, чтоgzipДо и после разница в размере файла в три или четыре раза, а скорость загрузки также увеличилась с более чем 7 секунд до более чем 3 секунд.

прикреплятьnginxметод конфигурации

http {
  gzip on;
  gzip_static on;
  gzip_min_length 1024;
  gzip_buffers 4 16k;
  gzip_comp_level 2;
  gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
  gzip_vary off;
  gzip_disable "MSIE [1-6]\.";
}

3.2 Что интерфейс может сделать для gzip

мы все знаемconfig/index.jsесть одинproductionGzipопция, так для чего она используется? мы пытаемся выполнитьnpm install --save-dev compression-webpack-plugin@1.x, и положиproductionGzipУстановить какtrue,опять такиbuild, вставитьnginxПод сервером посмотрите, в чем разница:

Мы обнаружим, что файлов стало больше после сборкиjs.gzа такжеcss.gzфайл иvendor.jsстало меньше, что на самом деле потому, что мы включилиnginxизgzip_static on;опции, еслиgzip_staticУстановить какon, то будет использоваться то же имя.gzФайл не будет занимать ресурсы ЦП сервера для сжатия.

3.3 Быстрое построение фронтенда основано наnodeизgzipСлужить

не могу построитьnginxВнешние партнеры среды также могут быстро запустить группу, выполнив следующие действия:gzipизexpressсервер

  1. воплощать в жизньnpm i express compression
  2. Создайте новый в корневом каталоге проектаserve.jsи вставьте следующий код
  var express = require('express')
  var app = express()

  // 开启gzip压缩,如果你想关闭gzip,注释掉下面两行代码,重新执行`node server.js`
  var compression = require('compression')
  app.use(compression())

  app.use(express.static('dist'))
  app.listen(3000,function () {
    console.log('server is runing on http://localhost:3000')
  })
  1. воплощать в жизньnode server.js

На картинке нижеexpressвключиgzipЗаголовки ответа:

Четыре,Пререндеринг

Всем известно: обычноеVueПосле сборки одностраничного приложенияindex.htmlпросто пустая страница, содержащая корневой узел, когда все это необходимоjsПосле завершения загрузки он начнет парсить и создаватьvnode, а затем визуализировать реальныйDOM. когда этиjsКогда файл слишком большой и скорость сети очень низкая или возникает непредвиденная ошибка, появляется так называемый белый экран.VueПартнеры по развитию должны были столкнуться с этой ситуацией. И есть большой недостаток одностраничного приложенияSEOОчень недружелюбно. Итак, как решить эти проблемы? ——SSRКонечно это хорошее решение, но это также означает определенные затраты на обучение и эксплуатацию и обслуживание, а если у вас уже есть готоваяvueодностраничное приложение, обратитесь кSSRИ это не бесшовный процесс. Такпредварительный рендерингкажется более подходящим. Просто установите одинwebpackплагин + что-то простоеwebpackКонфигурация может решить две вышеупомянутые проблемы.

4.1 Как преобразовать одностраничное приложение в предварительный рендеринг

  1. Тебе следуетrouterустановить какhistoryрежиме и соответствующим образом настроить конфигурацию сервера,это не сложно.
  2. npm i prerender-spa-plugin --save-dev

Уведомление! ! ! Предварительный рендеринг требует загрузкиChromium, и по известным вам причинам материалы Google не могут быть загружены в Китае, поэтому они добавляются в корневой каталог.npmrcФайл, чтобы использовать зеркало Taobao для загрузки.Ссылка на ссылку. Если ваш терминал можно перевести за границу, просто пропустите этот шаг, вам может понравитьсяМаленький самолет

  1. существуетbuild/webpack.prod.conf.jsДобавьте следующую конфигурацию ниже (без маршрутизации отложенной загрузки).
  const PrerenderSPAPlugin = require('prerender-spa-plugin')
  ...
  new PrerenderSPAPlugin({
    staticDir: config.build.assetsRoot,
    routes: [ '/', '/Contacts' ], // 需要预渲染的路由(视你的项目而定)
    minify: {
      collapseBooleanAttributes: true,
      collapseWhitespace: true,
      decodeEntities: true,
      keepClosingSlash: true,
      sortAttributes: true
    }
  })
  1. Будуconfig/index.jsвнутриbuildсерединаassetsPublicPathполе установлено на'/', это связано с тем, что при использовании предварительного рендеринга компонент маршрутизации будет скомпилирован в соответствующую папкуindex.html, это будет зависеть отstaticФайлы в каталоге, а если используется относительный путь, то и зависимый путь будет неправильным, что также требует, чтобы пререндеренный проект желательно размещать в корневом каталоге сайта (у меня уже есть эта яма вprerender-spa-pluginупомянутый складISSUEДа, но с помощьюpostProcess, вы также можете написать регулярное выражение самостоятельно, если у вас есть это требование, вы можете обратиться к следующемуЯма, вызванная ленивой загрузкой роутинга).
  2. Корректированиеmain.js
  new Vue({
    router,
    store,
    render: h => h(App)
  }).$mount('#app', true) // https://ssr.vuejs.org/zh/guide/hydration.html

воплощать в жизньnpm run build, ты найдешь,distКаталог уже не тот, что раньше, не только больше папок с тем же именем, что и указанный маршрут, но иindex.htmlСтатические страницы уже отображаются.

4.2 Каков эффект?

Как и прежде, мы по-прежнему отключаем кеш и ограничиваем скорость доFast 3G(работает локальноnginxна сервере). Видно, что вvendor.jsДо завершения загрузки (около 700 кБ, в это время загружается только более 200 кБ) страница была полностью отрисована. На самом деле нужно простоindex.htmlа такжеapp.cssПосле загрузки статическое содержимое страницы может быть хорошо отображено. Предварительный рендеринг, несомненно, является хорошим выбором для этих страниц с большим количеством статического контента.

4.3 Яма, вызванная ленивой загрузкой роутинга

Если ваш проект не реализованОтложенная загрузка маршрута, тогда вы можете быть уверены, что будете практиковать в соответствии с вышеизложенным. Но если вы используете его в своем проекте, вы должны увидетьwebpackJsonp is not definedошибка. это потому чтоprerender-spa-pluginПри рендеринге статической страницы она также будет выглядеть так<script src="/static/js/0.9231fc498af773fb2628.js" type="text/javascript" async charset="utf-8"></script>такая асинхронностьscriptтеги вводятся в сгенерированныйhtmlизheadвнутри этикетки. Это приводит к тому, что он предшествуетapp.js,vendor.js,manifest.js(родыbodyснизу) выполнить. (asyncпросто не будет блокировать спинуDOMсинтаксический анализ, это не значит, что он был выполнен последним). и когда этиjsПосле загрузки будетheadДубликаты ярлыков создают эту асинхронностьscriptЭтикетка. Хотя эта ошибка не повлияет на программу, лучше всего не рендерить эти асинхронные компоненты напрямую в конечный результат.htmlсередина. к счастьюprerender-spa-pluginпри условииpostProcessвариант, который может быть сгенерирован в реальномhtmlПрежде чем файл будет обработан, здесь я использую простое регулярное выражение, чтобы сделать эти асинхронныеscriptВыбраковка этикеток. Эта ветка уже используетсяОтложенная загрузка маршрута, вы можете напрямую просмотретьgitистория, файлы сравнения иbaseВетвь изменяется, чтобы соответствующим образом скорректировать ваш проект.

  postProcess (renderedRoute) {
    renderedRoute.html = renderedRoute.html = renderedRoute.html.replace(/<script[^<]*src="[^<]*[0-9]+\.[0-9a-z]{20}\.js"><\/script>/g,function (target) {
      console.log(chalk.bgRed('\n\n剔除的懒加载标签:'), chalk.magenta(target))
      return ''
    })
    return renderedRoute
  }

В дополнение к этому решению есть два решения, которые не рекомендуются:

  1. Просто не используйте ленивую загрузку маршрутизации.
  2. БудуHtmlWebpackPluginизinjectполе установлено на'head',такapp.js,vendor.js,manifest.jsбудет вставлен вhead, а в асинхронномscriptнад этикеткой. Но из-за обычногоscriptявляется синхронным, страница не может быть отображена до тех пор, пока все они не будут загружены, что нарушаетprerender, а также нужноmain.jsВнесите следующие изменения, чтобы убедиться, чтоVueможно найти при создании экземпляра<div id="app"></div>, и он монтируется правильно.
    const app = new Vue({
      // ...
    })
    document.addEventListener('DOMContentLoaded', function () {
      app.$mount('#app')
    })

Суммировать

Хотя официальный скаффолдинг уже предоставляет множество готовых оптимизаций, таких какcssсжать слияние,jsСжатие и модульность, передача небольших изображенийbase64Подождите, но мы можем сделать гораздо больше. Я не упоминал детали оптимизации на уровне кода и надеюсь предоставить вам некоторые практические решения. Вышеупомянутые три варианта более или менее принесут некоторую пользу вашему проекту. Оптимизация — это тоже метафизика, и есть много вещей, которые можно изучать. Я также надеюсь, что другие мелкие партнеры могут поделиться ценным мнением в области комментариев или непосредственно в моем проекте.vue-optimizationизbaseфиксация веткиPR, Я приму хороший план и организую его. В настоящее время окончательный результат интеграции трех схем размещен вmasterПод веткой вы можете клонировать и развивать свой проект на этой основе.