Руководство по оптимизации упаковки веб-пакета проекта Vue SPA

JavaScript CDN Vue.js Webpack
Руководство по оптимизации упаковки веб-пакета проекта Vue SPA

Недавно один маленький друг спросил меня, почему фоновый проект их компании Vue загружается в первый раз больше десяти секунд, что можно оптимизировать, поэтому я открыл их сайт и обнаружил, что основное время уходит на загрузку файла vendor.js. До 2M, так что я взглянул на их код и немного подбросил. Наконец-то получил хорошие результаты.

Идеи оптимизации

Для производительности веб-страницы, как улучшить скорость загрузки и другие принципы и операции, висправлятьБосс этой книги«Принципы и практика оптимизации производительности внешнего интерфейса»Книга очень подробная, и заинтересованные друзья могут пойти и прочитать.

В этой статье основное внимание будет уделеноwebpackС точки зрения упаковки оптимизируйте скорость загрузки первого экрана и оптимизируйте скорость упаковки.

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

Я выбрал один, построенный с версией vue-cli2.0+.Vue + Vuex + Vue-router + axios + elment-uiФоновый системный проект для тестирования, там около 20 страниц маршрутизации асинхронной загрузки.

Мы разделяем оптимизацию на3Это основной угол. После оптимизации каждого угла тестируется скорость скорости упаковки. Время, затрачиваемое на упаковку и сборку, указано ниже:

  1. Оптимизируйте resolve.modules, настройте включение и исключение загрузчика, используйте плагин webpack-parallel-uglify-plugin для сжатия кода.

  2. Настройте внешние файлы для загрузки файлов библиотеки с помощью cdn

  3. webpack DllPlugin, webpack DllReferencePlugin отдельные файлы библиотеки фреймворка

Время\Время упаковки (с) исходное время конфигурации Шаг оптимизации 1 Шаг оптимизации 2 Шаг оптимизации 3
1 24.86 ==23.86== 11.22 13.92
2 23.52 14.51 11.04 12.63
3 25.49 14.04 11.29 13.19
4 24.84 14.56 11.25 13.14
5 24.60 15.44 11.86 14

Из этого видно, что все еще может быть достигнуто значительное улучшение более чем на 10 с. Конкретное время, конечно, зависит от вашего проекта. Далее мы подробно расскажем, как это сделать.

Шаги оптимизации

1. Ускорьте упаковку с помощью базовых плагинов webpack

Начнем с изменения основногоwebpackМетод конфигурации улучшает скорость упаковки

1. Оптимизируйте разрешение.модули

принцип:

  1. Resolve.modules веб-пакета используется для настройки расположения библиотеки модулей (т. е. node_modules). Когда в js появляется import 'vue', который не является относительным или абсолютным путем, он перейдет в каталог node_modules, чтобы найти его.

  2. По умолчанию webpack будет искать рекурсивно вверх. Но обычно в каталоге проекта есть только один node_modules, и он находится в корневом каталоге проекта. Чтобы уменьшить область поиска, мы можем напрямую написать полный путь к node_modules.

Так я обычно пишуimportПри импорте модуля, в который импортируется конкретный файл, также имеет определенное влияние на улучшение скорости упаковки.

действовать:

Открытьbuild/webpack.base.conf.jsфайл, добавьте следующееmodulesБлок кода:

module.exports = {
  resolve: {
    ...
    modules: [  
      resolve('src'),
      resolve('node_modules')
    ],       
    ...
  },

2. Настройте включение и исключение загрузчика

принцип:

  1. webpackизloadersКаждый подраздел может иметь атрибуты включения и исключения:
  • include: импортированные файлы будут преобразованы загрузчиком в путь или массив файлов (включая каталог для обработки)
  • исключить: условия, которые не могут быть выполнены (за исключением каталогов, которые не обрабатываются)
  1. Мы можем использовать include, чтобы более точно указать, какие каталоги обрабатывать, что может уменьшить ненужный обход и, таким образом, снизить потери производительности.
  2. В то же время используйте exclude для исключения каталогов, которые уже известны и не нуждаются в обработке, тем самым еще больше повышая производительность.

действовать:

Открытьbuild/webpack.base.conf.jsфайл, добавьте следующееinclude,excludeКонфигурация:

module: {
  rules: [
    {
      test: /\.vue$/,
      loader: 'vue-loader',
      options: vueLoaderConfig,
      include: [resolve('src')],  // 添加配置
      exclude: /node_modules\/(?!(autotrack|dom-utils))|vendor\.dll\.js/ // 添加配置
    },
    {
      test: /\.js$/,
      loader: 'babel-loader',
      include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')], // 添加配置
      exclude: /node_modules/ // 添加配置
    },

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

loader: 'babel-loader?cacheDirectory=true'

3. Используйте плагин webpack-parallel-uglify-plugin для сжатия кода

принцип:

  1. по умолчаниюwebpackиспользоватьUglifyJSПлагин выполняет сжатие кода, но оно работает медленно из-за однопоточного сжатия.
  2. Мы можем использоватьwebpack-parallel-uglify-pluginплагин, который может работать параллельноUglifyJSПлагин может более полно и разумно использовать ресурсы процессора, что значительно сокращает время сборки.Плагин может настроить кеш, чтобы значительно сократить время сборки.

действовать: 1. Установкаwebpack-parallel-uglify-pluginплагин

yarn add webpack-parallel-uglify-plugin -D
// or
npm i webpack-parallel-uglify-plugin -D

2. Открытьbuild/webpack.prod.conf.jsфайл со следующими изменениями

const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
    ...
    // 删掉webpack提供的UglifyJS插件
    //new UglifyJsPlugin({
    //  uglifyOptions: {
    //    compress: {
    //      warnings: false
    //    }
    //  },
    //  sourceMap: config.build.productionSourceMap,
    //  parallel: true
    //}),
    // 增加 webpack-parallel-uglify-plugin来替换
    new ParallelUglifyPlugin({
      cacheDir: '.cache/',
      uglifyJS:{
        output: {
          comments: false
        },
        compress: {
          warnings: false,
          drop_debugger: true, // 去除生产环境的 debugger 和 console.log
          drop_console: true
        }
      }
    }),
    ...

Ускорьте сборку кода с HappyPack

принцип:

  1. Поскольку Webpack, работающий на Node.js, является однопоточной моделью, вещи, с которыми должен иметь дело Webpack, могут выполняться только одна за другой, а не несколько вещей одновременно.
  2. Идея обработки HappyPack заключается в том, чтобы расширить многопроцессорный режим от формы одного процесса до процесса выполнения исходного веб-пакета до загрузчика, тем самым ускорив построение кода.

действовать:

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

Посмотреть эффект

Когда вы выполнили все вышеперечисленные оптимизации, при запуске сборки вы обнаружите, что время первой сборки составляет около 23 секунд, что совпадает с началом, которое составляет чуть меньше 2 секунд (в основном для оптимизации эффекта разрешения). , загрузчик и др.)

При повторной сборке время сильно сокращается, т.к. находится в той же директории.cache/кэшированныйUglifyЕсть много связанных js, которые значительно улучшают скорость строительства. Иди попробуй сейчас. Друзья.

2. Настроить внешние так, чтобы файл библиотеки загружался через cdn

Начните с того, что из-заvendor.jsДомашняя страница загружается медленно из-за большого размера, но из чего сделан упакованный vendor.js vue?

Проект, сгенерированный vue-cli, интегрированwebpack-bundle-analyzerПоложитесь на инструменты визуального анализа

бегать

npm run build --report

vendor.js包构成图
Согласно приведенному выше рисункуvendor.jsРазобран 739kb, пакет в основном содержит такие файлы, какVue,Vue-router,elment-uiНапример, файлы библиотек, которые необходимо импортировать глобально. Эти библиотечные файлы являются некоторыми нечастыми изменениями, поэтому мы можем рассмотреть их разделение и введение библиотеки фреймворка с помощью cdn.

принцип:

использоватьwebpackизexternalsАтрибуты.Документация

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

Популярное объяснение: разрешить установку некоторых пакетов ресурсов, даже если они не установлены локально с помощью npm, черезscriptМожет также использоваться после введения этикетки

действовать:

  • Сначала в файле шаблонаindex.htmlдобавить следующее в
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>XXXX平台</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.4.1/theme-chalk/index.css">
  </head>
  <body>
  <div id="app"></div>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <script src="https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script>
  <script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
  <script src="https://cdn.bootcss.com/axios/0.17.0/axios.min.js"></script>
  <script src="https://cdn.bootcss.com/element-ui/2.4.1/index.js"></script>
    <!-- built files will be auto injected -->
  </body>
</html>

Уведомление! Номер версии должен быть таким же, какpackage.jsonНомера версий в

  • Исправлятьbuild/webpack.base.conf.js
module.exports = {
  ...
  externals: {
    'vue': 'Vue',
    'vuex': 'Vuex',
    'vue-router': 'VueRouter',
    'axios': 'axios',
    'element-ui': 'ELEMENT'
  }
  ...
}

Уведомление! здесьaxiosимя переменной для использованияaxios

Уведомление! здесьelement-uiимя переменной для использованияELEMENT,потому чтоelement-uiизumdимя модуляELEMENT

  • Исправлятьsrc/router/index.js
// import Vue from 'vue'
import VueRouter from 'vue-router'
// 注释掉
// Vue.use(VueRouter)
...
}
  • Исправлятьsrc/store/index.js
...
// 注释掉
// Vue.use(Vuex)
...
}
  • Исправлятьsrc/main.js
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui'

// 注释掉
// import 'element-ui/lib/theme-chalk/index.css'  

// router setup
import router from './router'

// Vuex setup
import store from './store'
Vue.use(ElementUI)
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  template: '<App/>',
  components: { App }
})

Сделанный

После того, как выше настроено, запуститеnpm run buildУстановлено, что время построения составляет около 11-12 с, почему улучшение по сравнению с этапом 1 не велико, т.к. на этапе 1ParallelUglifyPluginВ повторных билдах код не меняется, а кеш играет важную роль

Но на этот раз посмотримvendorТолько после анализа пакета24KBСлева и справа файл кадра использует ускорение cdn и механизм кэширования браузера, что позволяет значительно повысить скорость доступа к домашней странице. Мы можем развернуть файл на сервере и открыть сеть Chrome, чтобы просмотреть конкретное время загрузки.

недостаток

  1. Этот метод нельзя использоватьvue-devtoolsВ конце концов, инструменты отладки Google напрямую используют пакет онлайн-ресурсов. Однако, выделив и изменив часть кода в соответствии со средой, можно реализовать локальный пакет для среды разработки и использовать упакованные ресурсы CDN. За подробностями обращайтесь к практике этого большого человекаПрактика оптимизации загрузки первого экрана Vue SPA, которые могут быть введены в соответствии с окружающей средой.
  2. Стоимость запроса может быть больше, чем стоимость загрузки.В руководстве по веб-оптимизации необходимо максимально интегрировать файлы и уменьшить количество запросов.Не обязательно иметь много ресурсов cdn. .

3.webpack DllPlugin,webpack DllReferencePluginПредварительно скомпилированные файлы сторонних библиотек

Так как у cdn все еще есть свои недостатки, почему бы нам не рассмотреть вопрос о слиянии файлов библиотеки, поэтому мы используемwebpack.DllPlugin + webpack DllReferencePlugin + add-asset-html-webpack-pluginпредварительно скомпилировано и импортировано

принцип:

  1. использоватьwebpack DllPluginПлагин упаковывает сторонние плагины отдельно дляvendor.dll.js
  2. использоватьwebpack DllReferencePluginэто ссылка на эти предварительно скомпилированные модули
  3. использоватьadd-asset-html-webpack-pluginПучокvendor.dll.jsвставка в пакет html

действовать:

Мы по-прежнему продолжаем изменять код после завершения операции 1 (возвращается соответствующий код операции cdn)

  • существуетbuildновый в папкеwebpack.dll.conf.jsСодержимое файла следующее (в основном библиотека, которую нужно заранее скомпилировать и запаковать под конфигурацию):
var path = require('path')
var webpack = require('webpack')

var context = path.join(__dirname, '..')

module.exports = {
  entry: {
    vendor: [
      'vue/dist/vue.common.js',
      'vuex',
      'vue-router',
      'axios',
      'element-ui'
    ]
  },
  output: {
    path: path.join(context, 'static/js'), // 打包后的 vendor.js放入 static/js 路径下
    filename: '[name].dll.js',
    library: '[name]'
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(context, '[name].manifest.json'),
      name: '[name]',
      context: context
    }),
    // 压缩js代码
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      },
      output: { // 删除打包后的注释
        comments: false
      }
    })
  ]
}
  • редактироватьpackage.jsonфайл, добавьте команду компиляции:
"scripts": {
  "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
  "start": "npm run dev",
  "lint": "eslint --ext .js,.vue src",
  "build": "node build/build.js",
  "build:dll": "webpack --config build/webpack.dll.conf.js --progress"
  },

Затем запускается командная строкаnpm run build:dllВ это время он будет сгенерирован в static/jsvendor.dll.js , vendorСвязанные файлы библиотеки в свойствах упакованы.

  • Открытьindex.htmlздесь будетvendor.dll.jsпринести.
<body>
    <div id="app"></div>
    <script src="./static/js/vendor.dll.js"></script>
</body>
  • Открытьbuild/webpack.base.conf.jsфайл, отредактируйте и добавьте следующую конфигурацию, функция заключается в использовании пакета DLL, созданного DllPlugin через DLLReferencePlugin.
const webpack = require('webpack');
module.exports = {
    ...
    plugins: [
    new webpack.DllReferencePlugin({
      // name参数和dllplugin里面name一致,可以不传
      name: 'vendor',
      // dllplugin 打包输出的manifest.json
      manifest: require('../vendor.manifest.json'),
      // 和dllplugin里面的context一致
      context: path.join(__dirname, '..')
    })
  ]
  ...
}
  • Исправлятьbuild/webpack.prod.jsзакомментируйтеCommonsChunkPluginСоответствующий код, так как файл библиотеки был скомпилирован в предыдущей версии vendor.dll.js и не нуждается в компиляции.
module.exports = {
  plugins: [
    ...
    // 去掉这里的CommonsChunkPlugin
    // new webpack.optimize.CommonsChunkPlugin({
    //   name: 'vendor',
    //   minChunks (module) {
    //     // any required modules inside node_modules are extracted to vendor
    //     return (
    //       module.resource &&
    //       /\.js$/.test(module.resource) &&
    //       module.resource.indexOf(
    //         path.join(__dirname, '../node_modules')
    //       ) === 0
    //     )
    //   }
    // }),
    // 去掉这里的CommonsChunkPlugin
    // new webpack.optimize.CommonsChunkPlugin({
    //   name: 'manifest',
    //   minChunks: Infinity
    // }),
    ...
  ]
}

Сделанный

На этом этапе сохраните код, соберите его и обнаружите, что время сборки составляет около 14 секунд. Почему больше времени, чем cdn, ведь файлы стилей element-ui тоже нужно каждый раз запаковывать, а стиль отдельно запаковывать не рекомендуется, либо тоже способ использовать cdn.

Наконец, мы по-прежнему развертываем на сервере и открываем сеть Chrome, чтобы просмотреть конкретное время загрузки веб-страницы.

依赖分析图
Откройте график зависимостей сборки и найдитеvendorФайл пропал, его не нужно каждый раз запаковывать, просто импортируйте напрямуюvendor.dll.jsФайл хороший, в этом есть еще одно преимущество: когда у вас несколько проектов с одинаковыми зависимостями, ссылайтесь на одну и ту же копиюdllВот и все.

Это действительно закончилось?Ты заметилvendor.dll.jsЭто фиксированный файл без суффикса хеш, что фатально для кеша.Когда вы обновляете библиотеку или добавляете файлы библиотеки, переупакованный файл по-прежнему называетсяvendor.dll.jsфайл, не повредив кеш, у программы могут возникнуть проблемы при доступе пользователя.

Иногда среда разработки и тестовая среда могут представлятьvendor.dll.jsПуть другой и менять его приходится вручную, что тоже проблема. Так что делать? ?

К счастью, естьadd-asset-html-webpack-pluginЭтот плагин впрыскивает зависимые ресурсы.Я думал, что нашел спасительную соломинку, когда практиковался. Но я не знаю, то ли поза неправильная, то ли плагин устарел и не обновляется.При запуске программа сообщает об ошибке и не может быть использована.Я также надеюсь, что большие ребята, которые использовали его может дать мне несколько советов. .

Эпилог

Пока что об оптимизации в проекте Vue SPA, введение почти такое же, но это только для того, чтобы дать представление. Оптимизация не статична. Для некоторых проектов может потребоваться только шаг 1, а некоторые проекты могут ссылаться на небольшие ресурсы. и принять метод cdn, в то время как некоторые проекты могут использовать cdn.Если зависимости нескольких проектов одинаковы, можно рассмотреть dll.Конечно, выбор и оптимизация должны осуществляться в соответствии с конкретным сценарием.

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

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

Пример 1. Многие проекты будут использоватьecharts, я обнаружил, что некоторые друзья ставятechartsвведенный вmain.js, это явно лишнее увеличение зряvendor.jsразмер, его следует вводить только на странице, которую необходимо использовать, и вы должны обратить вниманиеechartsКомпонент карты в зависимости от того, следует ли использовать синхронный или асинхронный рендеринг, а также в зависимости от окна.resize, Следует ли обратить внимание на анти-дрожание и дросселирование его.

Пример 2: Когда мы используем jssdk карты Baidu, она находится вindex.htmlпройти внутрьscriptВводятся метки или они загружаются асинхронно, когда странице нужно использовать карту? Все эти вопросы достойны нашего внимания.

Поэтому много размышлений над деталями каждого шага написания кода.

На данный момент я закончил писать, и я также придерживаюсь позиции обучения.Если есть какие-либо ошибки, пожалуйста, поправьте меня, и, кстати, попросите совета.add-asset-html-webpack-pluginправильная осанка.

приложение

Соответствующий код размещен на githubvue-spa-optimization, с 4 ветками на нем

  • master:: исходная версия без каких-либо оптимизаций
  • simple:Сделайте соответствующую оптимизированную версию на шаге 1 выше.
  • cdn:Выполнена оптимизированная версия (cdn) шагов 1 и 2 выше.
  • dll:Выполните приведенный выше шаг 1 и шаг 3 оптимизированной версии (dll)