Оптимизация упаковки Webpack

внешний интерфейс JavaScript CDN Webpack

От браузера, grunt и gulp до сегодняшнего роллапа и веб-пакета появились интерфейсные инструменты упаковки. Появилось много превосходных инструментов упаковки, и самым популярным из них, несомненно, является веб-пакет. Будь то текущая популярная платформа или библиотека инструментов, многие выбирают ее используется в качестве инструмента для упаковки, поэтому webpack — хороший выбор в качестве инструмента для создания пакетов в процессе разработки. Я также использовал webpack в недавней разработке проекта и столкнулся со многими проблемами оптимизации.Вот некоторые детали и методы оптимизации пакетов webpack.

Прежде всего, в этом проекте используется семейная корзина Vue и напрямую используется конфигурация webpack.vue-cliСгенерированная конфигурация по умолчанию, после упаковки проекта обнаружено, что сгенерированнаяvendor.jsРазмер файла очень большой, а процесс упаковки довольно медленный, поэтому я хочу попробовать различные способы его оптимизации.

Найдите громоздкие модули

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

webpack-bundle-analyzer

Использование этого плагина может быть сделано непосредственно черезnpm install webpack-bundle-analyzer --save-devУстановлено, и в информации о конфигурации веб-пакетаplugins: [new BundleAnalyzerPlugin()]можно добавить в. дляvue-cliВ методе конфигурации плагин установлен по умолчанию, но не включен, найдитеconfig/index.jsФайлbuildНиже будетbundleAnalyzerReportконфигурация, по умолчаниюprocess.env.npm_config_report, здесь предлагается вpackage.jsonизscriptsдобавить строку в"analyz": "npm_config_report=true npm run build", поэтому каждый раз, когда вы хотите включить плагин, вам нужно толькоnpm run analyzeВот и все.

Извлечь общие модули

Для веб-пакета у него есть две основные функции в модульной упаковке: первая — поддержка большого количества типов модулей, будь тоTypeScript,CoffeeScriptещеsass,stylusОн поддерживает все остальные языки.Во-вторых, он может контролировать степень детализации упакованных файлов через конфигурацию, которая будет обсуждаться ниже.

При разработке мы обычно извлекаем все зависимые библиотеки отдельно, не смешивая их с кодом нашего проекта.На данный момент мы будем использовать плагин, который поставляется с webpack.CommonsChunkPlugin, как следует из названия, это плагин, который извлекает общедоступные модули. Из его документации видно, что объект может быть передан в качестве самого параметра.Три наиболее часто используемых параметра:

  • name(names)
  • minChunks
  • chunks

nameЭто легко понять, это относится к имени конечного запакованного файла, и если вы используетеnamesЕсли это так, переданный должен быть массивом строк.minChunksЕсли передается число, это означает, что модуль будет упакован, если количество ссылок на модуль из других модулей достигнет этого числа. Если передается функция, возвращаемое значение должно быть логическим, чтобы указать, должен ли модуль быть упакован в общедоступный модуль. а такжеchunksБудет задан строковый массив, если этот параметр установлен, то из модулей, указанных в пакете, будут извлекаться только публичные подмодули.

Вот несколько примеров, иллюстрирующих работу этого плагина.

Предположим, есть два модуляchunk1.jsа такжеchunk2.jsи два файла проектаa.jsа такжеb.js, структура примерно такая:

// a.js
require('./chunk1');
require('./chunk2');
require('jquery');
// b.js
require('./chunk1');
require('./chunk2');
require('vue');
// webpack配置如下
module.exports = {
  entry: {
    main: './main.js', 
    main1: './main1.js',        
    jquery:["jquery"],     
    vue:["vue"]   
  },    
  output: {      
    path: __dirname + '/dist',   
    filename: '[name].js' 
  },   
  plugins: [  
    new CommonsChunkPlugin({   
      name: ["common","jquery","vue","load"],   
      minChunks:2 
    })   
  ] };

Конечный результат упаковки:jqueryупакован вjquery.js,vueупакован вvue.js,common.jsУпакованы общие модули (chunk1 и chunk2). При упаковке с этим плагином он удовлетворитminChunksМодули упакованы вnameВ первый блок массива последовательно упаковываются последующие в массиве, сначала изentryЕсли его нет, генерируется пустой блок.nameПоследний блок в массиве упаковывает исполняемый код webpack, который должен быть загружен первым при его использовании.

Взгляните сейчасvue-cliДля файла конфигурации плагина:

new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: function (module, count) {
    // 将node_modules中的依赖模块全部提取到vendor文件中
    return (
      module.resource &&
      /\.js$/.test(module.resource) &&
      module.resource.indexOf(
        path.join(__dirname, '../node_modules')
      ) === 0
    )
  }
}),
// webpack在使用CommonsChunkPlugin时会生成一段runtime代码,并且打包进vendor中。
// 这样即使不改变vendor代码,每次打包时runtime会变化导致vendor的hash变化,这里
// 把独立的runtime代码抽离出来来解决这个问题
new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  chunks: ['vendor']
}),

Удалите ненужные файлы

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

Две библиотеки, поставляемые с webpack, могут сделать это:

  • IgnorePlugin
  • ContextReplacementPlugin

IgnorePluginиспользуется следующим образом:

// 插件配置
plugins: [
  // 忽略moment.js中所有的locale文件
  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
// 使用方式
const moment = require('moment');
// 引入zh-cn locale文件
require('moment/locale/zh-cn');
moment.locale('zh-cn');

ContextReplacementPluginиспользуется следующим образом:

// 插件配置
plugins: [
  // 只加载locale zh-cn文件
  new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn/),
],
// 使用方式
const moment = require('moment');
moment.locale('zh-cn');

С помощью двух вышеуказанных методов,moment.jsОбъем можно уменьшить примерно до четверти исходного размера.

Модульное введение

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

import {chain, cloneDeep} from 'lodash';
// 可以改写为
import chain from 'lodash/chain';
import cloneDeep from 'lodash/cloneDeep';

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

Цитирование через CDN

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

// 在html中添加script引用
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
// 这里externals的key指的是使用时需要require的包名,value指的是该库通过script引入后在全局注册的变量名
externals: {
  jquery: 'jQuery'
}
// 使用方法
require('jquery')

пройти черезDLLPluginа такжеDLLReferencePluginразделить файл

Если проект слишком большой, время упаковки будет довольно долгим, а если его часто обновлять, то код будет постоянно компилироваться и упаковываться, на что уходит много времени. В настоящее время мы можем компилировать и упаковывать те фреймворки и библиотеки, которые не часто обновляются (например, vue.js и т. д.), так что каждый раз, когда мы разрабатываем и выходим в онлайн, нам нужно только компилировать и упаковывать наши файлы разработки, что может значительно сэкономить ненужное время упаковки. И этот метод требуетDLLPluginа такжеDLLReferencePluginСотрудничество двух плагинов завершено.

DLLPlugin

При использовании этого плагина нам нужно создать отдельный файл конфигурации с именем здесьwebpack.dll.config.js, настроенный следующим образом:

module.exports = {
  entry: {
    lib: ['vue', 'vuex', 'vue-resource', 'vue-router']
  },
  output: {
    path: path.resolve(__dirname, '../dist', 'dll'),
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath,
    library: '[name]_library'
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': '"production"'
    }),
    /**
     * path: manifest.json输出文件路径
     * name: dll对象名,跟output.library保持一致
     */ 
    new webpack.DllPlugin({
      context: __dirname,
      path: path.resolve(__dirname, '../dist/dll', 'lib.manifest.json'),
      name: '[name]_library'
    })
  ]
}

Несколько замечаний:

  1. entryВсе модули, которые должны быть упакованы отдельно, написаны на
  2. outputизlibraryАтрибуты могут выставлять пакеты dll
  3. DLLPluginконфигурация,pathуточнитьmanifest.jsonпуть для создания файла,nameРаскройте имя функции dll

Запуск этого файла конфигурации создает файл пакета иmanifest.jsonдокумент.

DLLReferencePlugin

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

new webpack.DllReferencePlugin({       
  context: __dirname,                  // 同dll配置的路径保持一致
  manifest: require('../dist/dll/lib.manifest.json') // manifest的位置
}),

Затем запустите webpack и обнаружите, что скорость упаковки значительно улучшилась, и нет необходимости перекомпилировать и упаковывать эти зависимые библиотеки каждый раз при обновлении кода.

разное

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

Включить сжатие Gzip

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

Сжать запутанный код

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

Суммировать

На этом статья заканчивается.В основном в ней объясняется часть оптимизации упаковки webpack.Конечно возможности и время ограничены.Изучаются только некоторые методы.Могут быть и другие методы оптимизации,будь то от объема скомпилированной упаковки или Скорость можно лучше оптимизировать. Я некоторое время общался с webpack и обнаружил, что он слишком сложен в качестве инструмента для упаковки.Будь то из официальной документации в начале или новых расширенных функций, его трудно полностью понять, и необходимо продолжать практику и проводить углубленные исследования.