Игра с разделением кода и общим извлечением кода с оптимизацией веб-пакета

Webpack
Игра с разделением кода и общим извлечением кода с оптимизацией веб-пакета

предисловие

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

1. Концепция

Прежде чем мы начнем, необходимо прояснить эти три понятия:

  • модуль: модуль, в глазах веб-пакета любой файл, который можно импортировать и экспортировать, является модулем.
  • чанк: чанк разделен веб-пакетом:
    • Каждый входной файл представляет собой фрагмент
    • Код, введенный через import и require, также
    • Код, разделенный splitChunks, также
  • пакет: файлы, упакованные webpack, также можно понимать как результат компиляции, сжатия и упаковки фрагментов.

2. Анализ проблемы

Во-первых, при простом анализе проблема упаковки, которую мы только что упомянули:

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

После понимания причины проблемы выйдет общее решение:

  • Когда мы упаковываем, мы должны извлечь модули, на которые обычно ссылаются разные записи, и поместить их в общий модуль. Таким образом, независимо от того, сколько записей ссылается на этот модуль, он появится только один раз в конечном результате упаковки. —— Устранение избыточности кода.
  • Кроме того, когда мы складываем эти часто используемые модули в один модуль, файл может быть очень большим, что также не способствует сетевым запросам и загрузке страниц. Поэтому нам нужно дополнительно разбить этот общий модуль на несколько файлов модулей по определенным правилам. —— Уменьшите размер файла.
  • Что касается того, как разделить, способ варьируется от человека к проекту и от проекта к проекту. Мой личный принцип разделения:
    • Бизнес-код и сторонние библиотеки разделены и упакованы для обеспечения сегментации кода;
    • Код обслуживания, общий для упаковочного модуля, извлекивает сервисный модуль;
    • Лучше не упаковывать все сторонние библиотеки в один файл,потому что сторонние библиотеки обычно очень большие.Некоторые особо большие библиотеки я упакую отдельно.Если остальные все же очень большие,соберите их вместе.Разрежьте на несколько модули в соответствии с определенным размером.

optimization.splitChunks

webpack предоставляет очень хороший встроенный плагин, который поможет нам выполнить это требование:CommonsChunkPlugin. Но в вебпаке4CommonsChunkPluginудаляется и заменяетсяoptimization.splitChunks, к счастьюoptimization.splitChunksБолее могущественный!

3. Реализация

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

демо структура каталогов:

|--public/
|   |--a.html
|   |--index.html
|--src/
|   |--a.js
|   |--b.js
|   |--c.js
|   |--index.js
|--package.json
|--webpack.config.js

Логика кода очень проста,indexссылка в модулеaа такжеb2 модуля,aссылка в модулеcмодули иjqueryбиблиотека,bТакже упоминается в модулеcмодули иjqueryбиблиотека,cявляется автономным модулем без каких-либо других зависимостей.

Код index.js выглядит следующим образом:

//index.js
import a from './a.js';
import b from './b.js';
function fn() {
    console.log('index-------');
}
fn();

Код A.js выглядит следующим образом:

//a.js
require('./c.js');
const $ = require('jquery')
function fn() {
    console.log('a-------');
}
module.exports = fn();

код b.js выглядит следующим образом:

//b.js
require('./c.js');
const $ = require('jquery')
function fn() {
    console.log('b-------');
}
module.exports = fn();

код c.js выглядит следующим образом:

//c.js
function fn() {
    console.log('c-------');
}
module.exports = fn();

1. Базовая конфигурация

Webpack сначала не оптимизируется, просто выполните базовую настройку и посмотрите на эффект. Проект сконфигурирован с 2 входами, сhtml-webpack-pluginРеализовать многостраничную упаковку:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: {
        index: './src/index.js',
        a: './src/a.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html',
            filename: 'index.html'
        }),
        new HtmlWebpackPlugin({
            template: './public/a.html',
            filename: 'a.html'
        })
    ]
}

Запустите webpack в режиме разработки:

webpack-normal-build.jpg

Как видите, запакованы два html и два больших (более 300 Кб) файла.a.js,index.js.

Войдите в каталог dist, чтобы проверить файл js:

  • a.jsсодержитcкод модуля иjqueryкод
  • index.jsсодержитaмодуль,bмодуль,cмодули иjqueryкод

смотри, тот же кодcа такжеjqueryУпаковано 2 раза.

2. Первоначально добавьте конфигурацию оптимизации splitChunks

Во-первых, чтобы решить проблему упаковки одного и того же кода дважды, нам нужно разрешить webpack помещатьcа такжеjqueryИзвлечен и упакован как общедоступный модуль.

Добавьте splitChunks в файл конфигурации webpack:

//webpack.config.js

optimization: {
    splitChunks: {
        cacheGroups: {
            default: {
                name: 'common',
                chunks: 'initial'
            }
        }
    }
}

- cacheGroups

  • cacheGroupsдаsplitChunksЯдро конфигурации, правила разделения кода — все вcacheGroupsНастраивается в группе cache.
  • Каждое свойство группы cache является конфигурационным правилом, я приведу его здесь.defaultАтрибут настроен, и имя атрибута можно задать самостоятельно вместо значения по умолчанию.
  • Значением свойства является объект, который содержит наше описание правила разделения кода.

- name

  • имя: Извлеченный общедоступный модуль будет назван в соответствии с этим. Его можно оставить ненастроенным. Если он не настроен, будет сгенерировано имя файла по умолчанию. Приблизительный формат:index~a.jsТакой.

- chunks

  • чанки: Указывает, какие типы чанков участвуют в разбиении.Значение может быть строкой или функцией. Если строка, может быть одним из этих трех значений:all, async, initial,allпредставляет все модули,asyncПредставляет только асинхронную загрузку,initialПредставляет модуль, который можно получить во время инициализации. Если это функция, вы можете выполнить более подробную фильтрацию на основе таких атрибутов, как имя параметра фрагмента.

Упакуйте снова:

webpack-optimize-build.jpg

можно увидетьa.js,index.jsУменьшено с более чем 300 К до нескольких К в 6.00. Также добавленcommon.jsфайл, и обе записи упаковки добавляются автоматическиcommon.jsЭтот общедоступный модуль:

webpack-optimize-build2.jpg

Войдите в каталог dist и просмотрите три файла js по очереди:

  • a.jsОн не содержит никакого кода модуля, только код по умолчанию, сгенерированный webpack.
  • index.jsОн также не содержит никакого кода модуля, только код по умолчанию, сгенерированный webpack.
  • common.jsвнутриa,b,c,index,jqueryкод.

Выяснилось, что упоминание было извлечено, но, похоже, оно отличается от того, что мы ожидали.common.jsпошел в.

Это потому, что мы не сказали webpack (splitChunks) какой код является общедоступным кодом,splitChunksПо умолчанию любой модуль будет извлечен.

- minChunks

splitChunksОн поставляется со своей собственной конфигурацией по умолчанию, и группа кеша наследует эти конфигурации по умолчанию, одна из которыхminChunksАтрибуты:

  • Он контролирует, когда извлекается каждый модуль: когда количество ссылок на модуль в разных записях больше или равно этому значению конфигурации, он будет извлечен.
  • Его значение по умолчанию равно 1. То есть любой модуль будет извлечен (модуль входа фактически будет один раз импортирован вебпаком).

У нас нет конфигурации вышеminChunks, только настроеноnameа такжеchunkдва свойства, поэтомуminChunksзначение по умолчанию1эффективный. Неудивительно, что все модули извлекаются вcommon.jsбинго.

Оптимизируйте его и настройте в группе кешаminChunksПереопределить значение по умолчанию:

//webpack.config.js

optimization: {
    splitChunks: {
        cacheGroups: {
            default: {
                name: 'common',
                chunks: 'initial',
                minChunks: 2  //模块被引用2次以上的才抽离
            }
        }
    }
}

затем запустите веб-пакет

webpack-optimize-build3.jpg

Вы можете видеть, что размер 2 файлов изменился:common.jsУменьшено с 314К до 311К,index.jsУвеличено с 6,22К до 7,56К.

Войдите в каталог dist для просмотра:

  • a.jsпо-прежнему не содержит никакого кода модуля (нормально, т.к.aкак модульindexПредставлен один раз и представлен один раз веб-пакетом в качестве записи, поэтомуaцитируется 2 раза).
  • index.jsпоявился вbа такжеindexкод модуля.
  • common.jsосталось толькоa,c,а такжеjqueryкод модуля.

Теперь мы помещаем модуль со ссылкойa, c, jquery,отaа такжеindexИзвлечено из этих двух входных модулейcommon.jsв. Немного соответствует нашим ожиданиям.

3. Настройте несколько правил разделения

3.1 Реализовать разделение кода и разделить сторонние библиотеки

Далее я хочу общедоступный модульcommon.js, бизнес-код и сторонний модуль jquery можно разделить.

Нам нужно добавить еще одно правило разделения.

//webpack.config.js

optimization: {
    splitChunks: {
    	minSize: 30,  //提取出的chunk的最小大小
        cacheGroups: {
            default: {
                name: 'common',
                chunks: 'initial',
                minChunks: 2,  //模块被引用2次以上的才抽离
                priority: -20
            },
            vendors: {  //拆分第三方库(通过npm|yarn安装的库)
            	test: /[\\/]node_modules[\\/]/,
                name: 'vendor',
                chunks: 'initial',
                priority: -10
            }
        }
    }
}

Я добавил атрибут поставщика в cacheGroups (имя атрибута можно взять самостоятельно, если оно не совпадает с именем других определенных атрибутов в группе кеша, иначе последующие правила разделения перезапишут предыдущую конфигурацию).

- minSize

minSize устанавливает минимальный размер сгенерированного файла в байтах. Если модуль соответствует упомянутым ранее правилам разбиения, но окончательный размер сгенерированного файла меньше minSize, он все равно не будет извлечен. Это свойство можно задать в каждом свойстве группы кэша или в свойстве splitChunks, чтобы эта конфигурация наследовалась в каждой группе кэша. Здесь, поскольку файлы в моем демо очень маленькие, чтобы продемонстрировать эффект, я установил minSize в 30 байт, чтобы можно было извлечь общие модули.В обычных проектах не обязательно устанавливать такой маленький размер.

- priority

Значением атрибута приоритета является число, которое может быть отрицательным. В результате, когда в группе кэша установлено несколько правил разделения, и модуль соответствует нескольким правилам одновременно, атрибут приоритета необходим, чтобы решить, какое правило разделения использовать. Выполняется тот, у кого выше приоритет. Я установил приоритет для группы бизнес-кода на -20, а приоритет для группы сторонних библиотек на -10, чтобы при ссылке на стороннюю библиотеку более 2 раз она не была упакована в бизнес модуль. .

- test

Атрибут test используется для дальнейшего управления модулями, выбранными группой кеша, который аналогичен атрибуту chunks, но отличается размерами. Значение test может быть регулярным выражением или функцией. Он может совпадать с абсолютным путем к ресурсу модуля или имени чанка. При совпадении с именем чанка будут выбраны все модули в чанке. Я использовал обычный здесь/[\\/]node_modules[\\/]/Чтобы соответствовать абсолютному пути сторонних модулей, потому что модули, установленные через npm или yarn, будут храниться в каталоге node_modules.

Запустите веб-пакет:

webpack-optimize-build4.jpg

Вы можете видеть, что новый вызовvendor.jsфайл (значение атрибута name), аcommon.jsРазмер файла был уменьшен с исходных 311k до 861 байт!

Перейдите в каталог dist и проверьте файл js:

  • a.jsОн не содержит никакого кода модуля.
  • common.jsсодержит толькоaа такжеcкод модуля.
  • index.jsсодержит толькоbа такжеindexкод модуля.
  • vendor.jsсодержит толькоjqueryкод модуля.

Теперь, на основе предыдущего шага, мы успешноcommon.jsсторонняя библиотекаjqueryВынь и вставьvendor.jsвнутри.

3.2 Разделить указанный файл

Если мы также хотим отдельно упаковать некоторые файлы в проекте (например, библиотеку компонентов, разработанную локально в проекте), мы можем продолжить добавлять правила разделения. Например, под моим src естьlocallib.jsФайлы должны быть упакованы отдельно, предполагаяa.jsОн был введен в .

//a.js
require('./c.js');
require('./locallib.js');  //引入自己本地的库
const $ = require('jquery')
function fn() {
    console.log('a-------');
}
module.exports = fn();

Его можно настроить так:

//webpack.config.js

optimization: {
    splitChunks: {
        minSize: 30,  //提取出的chunk的最小大小
        cacheGroups: {
            default: {
                name: 'common',
                chunks: 'initial',
                minChunks: 2,  //模块被引用2次以上的才抽离
                priority: -20
            },
            vendors: {  //拆分第三方库(通过npm|yarn安装的库)
            	test: /[\\/]node_modules[\\/]/,
                name: 'vendor',
                chunks: 'initial',
                priority: -10
            },
            locallib: {  //拆分指定文件
            	test: /(src\/locallib\.js)$/,
                name: 'locallib',
                chunks: 'initial',
                priority: -9
            }
        }
    }
}

Я добавил новое правило разбиения под группу кеша, и я должен упаковать его отдельно, указав периодичность проверки.src/locallib.jsфайл и установите приоритет равным -9, чтобы при многократном обращении к нему он не попадал в другие группы разделенных правил, поскольку два других правила имеют более низкий приоритет, чем этот.

После запуска упаковки webpack:

webpack-optimize-build5.jpg

Видно, что новое поколениеlocallib.jsдокумент. Войдите в каталог dist для просмотра:

  • a.jsОн не содержит никакого кода модуля.
  • common.jsсодержит толькоaа такжеcкод модуля.
  • index.jsсодержит толькоbа такжеindexкод модуля.
  • vendor.jsсодержит толькоjqueryкод модуля.
  • locallib.jsсодержит толькоlocallibкод модуля.

Теперь мы упаковываем указанный модуль самостоятельно на основе предыдущего шага.locallib.js.

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

Для сравнения, до оптимизации упакованный js имеет общий размер 633 КБ:

webpack-before-optimize.png

После оптимизации упакованный js в сумме составляет менее 330 КБ:

webpack-after-optimize.png

Оптимизированные и упакованные файлы четко классифицированы, а их объем почти на 50% меньше, чем до оптимизации. Дай пять! Это всего лишь простой пример, который я привел: в реальных многостраничных приложениях может потребоваться больше усилий по оптимизации.

резюме

Webpack очень мощный, вышеизложенное — лишь верхушка айсберга, но до тех пор, пока вы овладеваете вышеперечисленнымoptimization.splitChunksБазовая конфигурация, мы можем разделить оптимизированные файлы пакета управления кодом почти произвольно в соответствии с нашими собственными идеями, разве это не круто? Поэкспериментируйте с разделением кода, и вы тоже сможете!

Если вы чувствуете, что они все еще не могут удовлетворить ваши потребности, и вы хотите более точно настроить правила упаковки, вы можете перейти кофициальный сайт вебпакаПроверитьoptimization.splitChunksбольше конфигурации.

Добро пожаловать на общение~

Полную конфигурацию веб-пакета и исходный код демо для этой статьи можно найти здесь:GitHub.com/so 111/Веб боится…

--

Добро пожаловать в перепечатку, пожалуйста, укажите источник:
Найдите Pinyin.com/2019/11/15/…