Оптимизация Webpack — удвойте эффективность сборки

Webpack
Оптимизация Webpack — удвойте эффективность сборки

0. Фон

Благодаря постоянному совершенствованию системы конструирования и непрерывной оптимизации процесса строительства Webpack постепенно стал основным повелителем системы предварительного проектирования. наш выбор передовых строительных технологий. варианты, в том числеcreate-react-appа такжеvue-cliСистемы построения распространенных в отрасли инструментов для строительных лесов также основаны на веб-пакете для инкапсуляции верхнего уровня. Однако по мере того, как бизнес-код продолжает увеличиваться, а глубина проекта продолжает увеличиваться, наше время сборки будет соответственно увеличиваться. Постепенно кто-то придет к выводу, что сборки webpack слишком медленные и слишком «тяжелые». Возьмем в качестве примера недавний проект автора, оптимизированный для команды.Как показано на рисунке ниже, мы видим, что с непрерывным стекированием проектов и некоторыми неправильными ссылками время создания одного проекта в команде достигло 40 с.Это приводит к очень плохому опыту, если инженеру необходимо перезапустить devServer или выполнить сборку.

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

Однако, прежде чем начать текст, необходимо заранее объяснить один момент.Представленный в этой статье метод повышения эффективности конструкции основан на webpack4.Для проектов, использующих старую версию, я не буду слишком подробно рассказывать о процессе того, как обновите старую версию до webpack 4. Потому что вы можете найти слишком много качественных статей в Nuggets или различных форумах, поэтому для большинства базовых знаний, таких какwebpack-dev-serverСвязанные конфигурации, а также некоторые распространенные плагины больше не будут упоминаться в этой статье. Для тех студентов, которые продолжают следовать версии веб-пакета, я полагаю, вы также знаете, что на данном этапе готов к выпуску веб-пакет 5. Следующая картина является официальным этапом. Воспользуйтесь тем фактом, что он был обновлен только до версии 64. %, автор быстро присылает волну софтовых статей. , может быть в эпоху 5 методы оптимизации, введенные автором сегодня, возможно, были интегрированы в систему самого вебпака, который заставляет его становиться лучше с каждым днем ​​😊.

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

Без лишних слов, давайте перейдем к теме этого времени.

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

1. Создайте РБИ

Чтобы оптимизировать, мы должны знать, где оптимизировать, верно? Итак, в нашем одноразовом процессе сборки, что снижает эффективность нашей сборки? Как мы можем их измерить?

Для решения этих двух задач нам понадобится инструмент:speed-measure-webpack-plugin, который может измерять время выполнения каждого загрузчика и плагина в процессе сборки.Официальный рендеринг выглядит следующим образом:

И его использование столь же просто, как показано в примере кода ниже, вам нужно только обернуть слой smp.wrap для вашей исходной конфигурации при экспорте конфигурации Webpack, а затем выполнить сборку, и вы сможете консоль Панель видит время выполнения каждого типа модуля, как показано в его демо.

const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
 
const smp = new SpeedMeasurePlugin();
 
module.exports = smp.wrap(YourWebpackConfig);

Советы:так какspeed-measure-webpack-pluginАпгрейд вебпака не идеален, на данный момент (на момент написания статьи) еще есть баг, то есть он не может монтироваться с тем, который вы написали сами.html-webpack-pluginПользовательский плагин на предоставленных хуках (add-asset-html-webpack-pluginВот так) сосуществуют, так что прежде чем управлять, если есть такой Плагин, пожалуйста, удалите его сначала, иначе он выдаст что-то вроде моей статьиissueупомянутый вопрос.

Можно утверждать, что большая часть времени выполнения должна быть потрачена на компиляцию Загрузчика JS и CSS и Плагина, который выполняет операции сжатия над этими двумя типами кода.Если ваш результат выполнения совпадает с тем, что я сказал, пожалуйста, не Не скупитесь на вас Пальчики, пожалуйста, лайкните мою статью 😁.

Почему это так? Потому что в процессе компиляции или сжатия нашего кода нам нужно выполнить такой процесс: компилятору (здесь может относиться к веб-пакету) нужно преобразовать написанный нами строковый код в AST (Дерево синтаксического анализа), который выглядит следующим образом Дерево объект, как показано на рисунке:

Очевидно, что компилятор не может использовать регулярные выражения для явной замены строк для реализации такого сложного процесса компиляции, и все, что нужно сделать компилятору, — это пройти по дереву, найти правильный узел и заменить его скомпилированным значением, процесс как на картинке ниже выглядит так:

Эта часть знаний у меня есть в предыдущей статьеДемистификация Webpack — единственный способ создать высококачественный интерфейсОн был представлен подробно, если вам интересно понять, вы можете прочитать его ~

Вы должны помнить те дни, когда вас тысячи раз издевались над различными алгоритмами древовидной структуры, когда вы изучали "Структура данных и алгоритмы" или во время интервью, вы также должны помнить об идеях реализации обхода в глубину и обхода в ширину. Бар. Вполне возможно, что время построения сосредоточено в процессе компиляции или сжатия кода именно потому, что им нужно пройти по дереву, чтобы заменить символы или преобразовать синтаксис, поэтому всем им нужно пройти через «преобразование AST -> обход дерева -> Преобразование обратно в код" такой процесс, скажете вы, он может занять много времени?

2. Стратегия оптимизации

Теперь, когда мы определили «виновника», замедляющего нашу скорость сборки, давайте разберем его по пунктам! Здесь я перехожу сразу к делу, поскольку все мы знаем причины длительности строительства, мы, естественно, можем нарисовать целевую стратегию. Итак, мы начнем с четырех основных направлений: кеширование, многоядерность, извлечение и разбиение.Вы можете увидеть некоторые знакомые идеи, когда увидите эти четыре слова сейчас, и это здорово.Средства, которые будут представлены далее, несомненно, сделают это легче понять.

2.1 Кэш

Каждый раз, когда мы будем менять проект, мы точно не будем переписывать все файлы, но каждый раз, когда мы будем выполнять сборку, мы будем компилировать все файлы повторно, может ли такая повторяющаяся работа кэшироваться, как браузер, загружающий один и тот же ресурс? Ответ определенно да.На самом деле, большинство загрузчиков предоставляют элементы конфигурации кэша, такие как вbabel-loader, вы можете установитьcacheDirectoryчтобы открыть кеш, так что,babel-loaderОн будет записывать каждый результат компиляции в файл на жестком диске (по умолчанию в корневом каталоге проекта).node_modules/.cache/babel-loaderкаталог, конечно, вы также можете настроить).

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

module.exports = {
  module: {
    rules: [
      {
        test: /\.ext$/,
        use: ['cache-loader', ...loaders],
        include: path.resolve('src'),
      },
    ],
  },
};

Советы:cache-loaderПуть по умолчанию для хранения кеша находится в корневом каталоге проекта..cache-loaderВ каталоге мы привыкли настраивать его на корневой каталог проектаnode_modules/.cacheкаталог, сbabel-loaderПодождите, пока другие кэши плагинов или загрузчиков будут сохранены в одном блоке.

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

module.exports = {
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
      }),
    ],
  },
};

Мы можем включить нашу функцию кеша, включив конфигурацию кеша, или включить функцию многоядерной компиляции, включив параллель, о чем мы поговорим в следующей главе. И еще один плагин, который мы часто используем для сжатия CSS —optimize-css-assets-webpack-plugin, Поддержки кэширования и многоядерной компиляции на данный момент я не нашел.Если у читателей есть свои осадки в этой области, они могут внести исправления в область комментариев.

Советы: В настоящее время автор не рекомендует интегрировать логику кэша в процесс CI, потому что все еще бывают случаи, когда кэш все еще попадает после обновления зависимостей.Это явно ошибка.На машине разработки мы можем вручную удалить cache решить проблему, но в процессе компиляции на машине гораздо хлопотнее. Для обеспечения чистоты каждого результата КИ рекомендуется не включать функцию кэширования во время процесса КИ.

2.2. Многоядерность

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

const HappyPack = require('happypack')
const os = require('os')
// 开辟一个线程池
// 拿到系统CPU的最大核数,happypack 将编译工作灌满所有线程
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })

module.exports = {
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: 'happypack/loader?id=js',
      },
    ],
  },
  plugins: [
    new HappyPack({
      id: 'js',
      threadPool: happyThreadPool,
      loaders: [
        {
          loader: 'babel-loader',
        },
      ],
    }),
  ],
}

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

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

PS: здесь требуется особое упоминаниеproductionЯма, с которой легко столкнуться в моде, потому что появляется специальный персонаж -mini-css-extract-plugin, где яма? Есть два момента (это тоже проблема, которую автор не решил на момент написания статьи):

  1. MiniCssExtractPlugin не может сосуществовать с happypack.Если happypack используется для обертки MiniCssExtractPlugin, возникает следующая проблема:GitHub.com/army_heat/ и приложение….
  2. MiniCssExtractPlugin должен быть помещен вcache-loaderПосле выполнения, иначе не вступит в силу, обратитесь к вопросу:GitHub.com/Webpack-con….

Итак, наконец, вproductionКонфигурация правила CSS в режиме становится следующей:

module.exports = {
    ...,
    module: {
        rules: [
            ...,
            {
                test: /\.css$/
                exclude: /node_modules/,
                use: [
                    _mode === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader,
                    'happypack/loader?id=css'
                ]
            }
        ]
    },
    plugins: [
        new HappyPack({
          id: 'css',
          threadPool: happyThreadPool,
          loaders: [
            'cache-loader',
            'css-loader',
            'postcss-loader',
          ],
        }),
    ],
}

2.3 Извлечение

Для некоторых статических зависимостей, которые изменяются нечасто, таких как общая корзина семейства React в нашем проекте или некоторые используемые библиотеки инструментов, такие как lodash и т. д., мы не хотим, чтобы эти зависимости были интегрированы в каждую логику сборки, потому что они действительно редко меняется, поэтому ввод и вывод каждой сборки должны быть одинаковыми. Поэтому мы попытаемся извлечь эти статические зависимости из каждой логики сборки, чтобы повысить эффективность нашей сборки для каждой сборки. Есть два распространенных решения, одно из которых заключается в использованииwebpack-dll-pluginВ первом билде эти статические зависимости упаковываются отдельно, а дальше нужно только обратиться к этому уже давно типизированному пакету статических зависимостей, что аналогично понятию «предкомпиляция»; в другом тоже распространены в отрасли.ExternalsТаким образом, мы удаляем эти статические ресурсы, которые не нужно упаковывать, из логики сборки и используем метод CDN для ссылки на них.

Так как же нам выбрать между этими двумя методами?

2.3.1 Выбор между webpack-dll-plugin и Externals

В начале проекта команда использовала плагин webpack-dll-plugin для извлечения статических ресурсов. Причина этого заключалась в том, что изначально также использовались Externals, но поскольку ранняя служба CDN компании была незрелой, проект использовал онлайн-ресурс с открытым исходным кодом. служба CDN, у командного проекта были проблемы, поэтому он был заменен на webpack-dll-plugin за одну итерацию, но после того, как компания установила зрелую службу CDN, поддержка команды была вызвана различными причинами. Причина не была обновлена.

И я твердо поддерживаю Externals.Это не то, чего я хочу.Давайте сначала посчитаем три первородных греха webpack-dll-plugin:

  1. Статические зависимости, не участвующие в компиляции, необходимо настраивать при каждой сборке, и для них прекомпилировать JS-файл при первой сборке (далее будет называться lib-файлом), а каждую update-зависимость нужно поддерживать вручную. Если вы добавите или удалите зависимости или измените версию ресурса и забудете обновить, произойдет ошибка или ошибка версии.

  2. Невозможно получить доступ к новым функциям сценария браузера type="module" для введения собственных модулей ES, предоставляемых некоторыми зависимыми библиотеками (такими как vue'sКак представить новую версию) не может поддерживаться, и он не может лучше адаптироваться к превосходным функциям, предоставляемым более поздними браузерами, для достижения лучшей оптимизации производительности.

  3. Предварительно скомпилируйте все ресурсы в файл и явно вставьте этот файл в шаблон HTML, созданный проектом.Этот подход был рекомендован в эпоху HTTP1, поскольку он может уменьшить количество запросов ресурсов, но в эпоху HTTP2, если путем разделения его на несколько CDN Ссылки, функция мультиплексирования HTTP2 может быть использована более полно. Нет никаких доказательств, чтобы сказать, просто проверьте вывод, сделанный непосредственно выше:

    • Используя файл lib, сгенерированный webpack-dll-plugin, весь ресурс загружается как один файл, что занимает более 400 миллисекунд.
    • При использовании Externals с HTTP2 все ресурсы загружаются параллельно, а общая продолжительность не превышает 100 мс.

Вот почему я выбрал внешние.

Однако что делать, если в вашей компании нет полноценного сервиса CDN, но вы хотите извлечь статические зависимости из проекта? Автор предлагает выбратьwebpack-dll-pluginдля оптимизации эффективности сборки. Если вы все еще чувствуете, что очень хлопотно поддерживать файл lib каждый раз, когда вы обновляете зависимость, то я все же напомню вам, что очень важно выбрать надежный CDN при использовании внешних, ведь все эти зависимости, такие как React, Каркас вашего сайта, без них сайт даже не заработает.

2.3.2 Как более изящно написать Externals

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

Здесь я дам вам идею.Давайте сначала рассмотрим и проанализируем.Когда мы настраиваем внешние, нам нужно настроить эти части.

Первый вwebpack.config.jsВнутри файла конфигурации нам нужно добавить элемент конфигурации webpack:

module.exports = {
  ...,
  externals: {
    // key是我们 import 的包名,value 是CDN为我们提供的全局变量名
    // 所以最后 webpack 会把一个静态资源编译成:module.export.react = window.React
    "react": "React",
    "react-dom": "ReactDOM",
    "redux": "Redux",
    "react-router-dom": "ReactRouterDOM"
  }
}

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

https://cdn.bootcss.com/react/16.9.0/umd/react.production.min.js

Вот пример CDN статического ресурса, предоставляемого BootCDN (но это не значит, что автор рекомендует использовать услугу CDN, предоставляемую BootCDN. Последнее изменение имени домена действительно заставило меня наступить на много ям), мы можно обнаружить, что ссылка CDN на самом деле состоит из четырех частей: узла службы CDN, имени пакета, номера версии и пути к пакету, и то же самое верно для других служб CDN. Взяв приведенную выше ссылку в качестве примера, соответствующее содержание этих четырех частей:

  • Хост службы CDN:cdn.bootcss.com/
  • Имя пакета: реагировать
  • Номер версии: 16.9.0
  • Путь к пакету: umd/react.production.min.js

В этот момент все должны были подумать об этом. Мы можем сами написать подключаемый модуль веб-пакета, чтобы автоматически генерировать теги скрипта CDN Link и монтировать их на обработчик событий, предоставленный html-webpack-plugin, для автоматической вставки HTML, и нам нужны четыре части службы CDN Link, CDN. Нам нужно только унифицировать хост с услугами, предоставляемыми компанией.Мы можем передать имя пакета черезcompiler.options.externalsGet, а номер версии нам нужен только для чтения файла проектаpackage.jsonФайла достаточно, а окончательный путь к пакету обычно является фиксированным значением.

Я не буду подробно описывать конкретную реализацию кода.Во время итерации обновления скаффолдинга проекта автор настроил плагин Webpack на основе сервиса CDN, предоставляемого компанией.Логика реализации такая, как показано выше, поэтому последующие инженеры больше не нужно перейти в Обратите внимание на тег синхронный скрипт, все интегрировано в логику плагина для автоматической обработки.Конечно, если вас интересует исходный код плагина, вы можете поместить его в область комментариев. рассмотрит возможность внести свой вклад в сообщество как проект команды с открытым исходным кодом.

2.4. Сплит

Хотя SPA стало мейнстримом в эпоху большого фронтенда, у нас неизбежно будут некоторые проекты, которые необходимо превратить в MPA (многостраничное приложение). в одном Управляется и поддерживается под репо. Однако с постепенным углублением и непрерывной итерацией проекта объем кода неизбежно будет продолжать увеличиваться.Иногда мы меняем файлы только под одной записью, а приходится выполнять сборку для всех записей.Поэтому вот введение для тебя.Компиляция кластераКонцепция чего-либо:

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

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

3. Улучшить опыт

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

3.1. progress-bar-webpack-plugin

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

3.2. webpack-build-notifier

Это способ вывести всплывающее сообщение из таких приложений, как WeChat и Lark, когда ваша сборка завершена, с сообщением о том, что сборка завершена. То есть, когда вы начинаете сборку, вы можете скрыть панель консоли и сосредоточиться на других делах.Когда будет достигнута «точка», она, естественно, позвонит вам.Его эффект заключается в следующем, а также есть подсказка звук~

3.3. webpack-dashboard

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

4. Резюме

Подводя итог, по сути, наши меры по оптимизации построения эффективности с помощью webpack идут по двум основным направлениям: кэширование и многоядерность. Кэш для того, чтобы сделать ненужной повторную работу во время второй сборки, а многоядерность позволяет в полной мере использовать преимущества самого железа (я считаю, что компьютеры у всех должны быть двухъядерными или выше, мой собственный компьютер Низкий -end MAC, выпущенный компанией, имеет два ядра), так что наша сложная работа может полностью использовать наш процессор. И главными действующими лицами воплощения этих двух направлений в жизнь являются также два козыря, которые мы представили ранее, а именно:cache-loaderиhappypack, поэтому, если вы знаете его и хорошо используете, вы можете лучше применять методы оптимизации сборки. Итак, не просто смотрите на это, возьмите свой проект и примените его на практике, пусть ваш оптимизированный командный проект сияет перед вашим руководителем!

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

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

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

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