Webpack 4 создает практику/оптимизацию больших проектов

Webpack

Адрес склада из примера, использованного в этой статье:gayhub

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

Оптимизация опыта разработки

Замена горячего модуля

В предыдущей конфигурации мы использовалиMiniCssExtractPlugin.loaderзаменитьstyle-loader, потому что нам нужно отделить CSS от JS. Но в MiniCssExtractPlugin все же есть скрытая опасность, то есть он может повлиять на функцию hmr (горячая замена модуля) До того, как он поддерживает hmr, мы можем использовать его только в продакшене.

webpack.base.conf.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.styl(us)?$/,
        use: [
          process.env.NODE_ENV !== 'production' ?
          'vue-style-loader' : {
            loader: resolve('node_modules/mini-css-extract-plugin/dist/loader.js'),
            options: {
              publicPath: '../'
            }
          },
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2 // 在 css-loader 前执行的 loader 数量
            }
          },
          'postcss-loader',
          {
            loader: 'stylus-loader',
            options: {
              preferPathResolver: 'webpack' // 优先使用 webpack 用于路径解析,找不到再使用 stylus-loader 的路径解析
            }
          }
        ]
      }
    ]
  }
}

На самом деле, когда я видел элемент конфигурации hmr в последний раз, когда я его использовал, я думал, что он уже поддерживается.Пожалуйста, посмотрите, поддерживается он или нет.MiniCssExtractPlugin Docs

Улучшите скорость сборки: используйте DllPlugin и DllReferencePlugin для предварительной упаковки общедоступных зависимостей.

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

DllPlugin и DllReferencePlugin однозначно лучшие инструменты для оптимизации скорости упаковки.Он может заранее упаковать некоторые публичные зависимости.При последующей упаковке эти зависимости не будут упакованы, а будет использоваться непосредственно упакованный код.Обычно его можно уменьшить на 20 % ~ 40 % времени упаковки, конечно, есть и недостатки:

  • Во время инициализации и соответствующих обновлений зависимостей необходимо выполнить дополнительную команду.
  • Обычно dll находится в.htmlВнесенные в файл злоупотребления приведут к замедлению загрузки первого экрана

Но в целом плюсы перевешивают минусы.

  1. новыйwebpack.dll.conf.js

    const webpack = require('webpack')
    const { CleanWebpackPlugin } = require('clean-webpack-plugin')
    const { resolve } = require('./utils')
    
    const libs = {
      _frame: ['vue', 'vue-router', 'vuex'],
      _utils: ['lodash']
    }
    
    module.exports = {
      mode: 'production',
      entry: { ...libs },
      performance: false,
      output: {
        path: resolve('dll'),
        filename: '[name].dll.js',
        library: '[name]' // 与 DllPlugin.name 保持一致
      },
      plugins: [
        new CleanWebpackPlugin({
          cleanOnceBeforeBuildPatterns: []
        }),
        new webpack.DllPlugin({
          name: '[name]',
          path: resolve('dll', '[name].manifest.json'),
          context: resolve('')
        })
      ]
    }
    
  2. существуетwebpack.common.conf.jsИспользование плагина DllReferencePlugin

    webpack.common.conf.js

     const { generateDllReferences, generateAddAssests } = require('./utils')
    
     module.exports = {
       plugins: [
         ...generateAddAssests(),
         ...generateDllReferences()
       ]
     }
    
    # add-asset-html-webpack-plugin 用于把 dll 添加到 `index.html` 的 script 标签中
    # glob 支持正则匹配文件
    yarn add add-asset-html-webpack-plugin glob -D
    

    utils.js

     const webpack = require('webpack')
     const glob = require('glob')
     const AddAssestHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
    
     const generateDllReferences = function() {
       const manifests = glob.sync(`${resolve('dll')}/*.json`)
    
       return manifests.map(file => {
         return new webpack.DllReferencePlugin({
           // context: resolve(''),
           manifest: file
         })
       })
     }
    
     const generateAddAssests = function() {
       const dlls = glob.sync(`${resolve('dll')}/*.js`)
    
       return dlls.map(file => {
         return new AddAssestHtmlWebpackPlugin({
           filepath: file,
           outputPath: '/dll',
           publicPath: '/dll'
         })
       })
     }
    
  3. добавить npm-скрипты

    package.json

     "scripts": {
       "dll": "webpack --config build/webpack.dll.conf.js"
     },
    

тогда вы можете использоватьyarn dllУпакованные и настроенные глобальные общедоступные зависимости упакованы и будутsrc/dllгенерация каталога*dll.jsа также*dll.json, первое зависит от сжатых и объединенных файлов (mode: production), последний*dll.jsфайл и исходный файл сопоставления зависимостей, которые раньше анализировались DllReferencePlugin для установления ссылок и*dll.jsкартографические отношения между ними.

Два наиболее трудоемких шага в построении — это babel и сжатие, которые обычно игнорируются при настройке babel.node_modulesТаким образом, DllPlugin экономит время сжатия некоторых общедоступных зависимостей, поэтому, если вы не хотите использовать DllPlugin, вы также можетеexternalsнастроить их как внешние зависимости, сжать и импортировать другими способами

Улучшите скорость сборки: создайте разумную исходную карту

В webpack 4 то, будет ли генерироваться исходная карта и каким образом, определяетсяdevtoolДля управления конфигурацией выбор разумной исходной карты может эффективно сократить время упаковки. Прежде чем выбирать, мы все же должны понимать, что упаковка является самой быстрой без настройки Source Map.Причина, по которой необходима Source Map, заключается в том, что структура кода и имя файла после упаковки полностью не соответствуют тем, что были до упаковки.При возникновении ошибки мы можем найти только напрямую.После упакованного файла невозможно найти исходный файл, что значительно увеличивает сложность отладки. Карта исходного кода предназначена для улучшения возможности отладки упакованного кода, поэтому она всегда нужна нам в среде разработки и имеет больше возможностей в производственной среде.

devtoolДополнительная конфигурация сnone,eval,cheap-eval-source-mapСуществует 13 видов, и их функции и производительность сравниваются вДокументацияподробно описаны.

Элемент конфигурации состоит из одного или нескольких слов и дефисов. Каждое слово имеет свое значение и снижает производительность. Окончательное значение каждого элемента конфигурации определяется следующими словами:

  • noneНе генерирует исходную карту, производительность +++
  • eavlКаждый модуль состоит изevalВыполнить, не может правильно отображать количество строк, не может использовать производственный режим, производительность +++
  • moduleОшибка отображения исходного кода, производительность -
  • sourceОшибка отображения информации о строке и столбце, отображение перенесенного кода Babel, производительность --
  • cheapРежим с низкими накладными расходами, без сопоставления столбцов, производительность +
  • inlineНе создавать отдельный файл исходной карты, производительность o

среда разработки

Поскольку режим разработки рекомендует отображать исходный код ошибки и информацию о строке, поэтомуmoduleа такжеsourceнужны, а для производительности нам нужноevalа такжеcheap, поэтому обратившись к элементам конфигурации, вы сможете найти наиболее подходящую конфигурацию для среды разработки.devtool: cheap-module-eval-source-map.

Производственная среда

Поскольку в производственной среде почти не требуется отладка (отладка, связанная с JS), рекомендуется установитьdevtool: none, а затем измените настройку наdevtool: cheap-module-source-map.

Повышение скорости сборки: другие оптимизации

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

JS многопоточное сжатие

Как упоминалось ранее, сжатие — это трудоемкая часть построения, поэтому мы можем включить многопоточное сжатие terser-webpack-plugin, чтобы сократить время сжатия.

module.exports = {
  optimization: {
    minimizer: [
      new TerserJSPlugin({
        parallel: true // 开启多线程压缩
      })
    ]
  }
}

область видимости и кэширование babel

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        // 不转译 node_modules
        // exclude: [resolve('node_modules')]
        // 转译 src 目录下的文件
        include: [
          resolve('src')
        ],
        options: {
          cacheDirectory: true // 默认目录 node_modules/.cache/babel-loader
          // cacheDirectory: resolve('/.cache/babel-loader')
        }
      }
    ]
  }
}

кеш vue-загрузчика

vue-loadercacheDirectoryЭлементы конфигурации зависят от загрузчика кеша

yarn add cache-loader -D
module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: {
          loader: 'vue-loader',
          options: {
            prettify: false,
            cacheDirectory: resolve('node_modules/.cache/vue-loader'),
            cacheIdentifier: 'vue'
          }
        }
      }
    ]
  }
}

resolve.modules использует абсолютные пути

resolve.modulesСообщает Webpack, в каких каталогах следует искать при разборе модулей, можно задать как относительные, так и абсолютные пути. При установке относительных путей, таких какresolve.modules: [node_modules], при разрешении зависимостей он будет искать из текущего каталога, пока не найдетnode_modulesсодержание. Установка абсолютного пути может сократить этот процесс обхода и найти каталог напрямую.

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

Как я уже говорил, эта оптимизация лучше, чем ничего.

Используйте ES6

ES6 не только добавляет некоторые общие методы к исходным объектам, но также добавляет некоторые новые лексические и грамматические элементы для удобства разработчиков, что, естественно, не может отсутствовать в этом проекте. Однако разные браузеры и разные версии имеют непоследовательную поддержку ES6, что приводит к некоторым препятствиям для использования ES6.Нам нужно использовать babel-loader для преобразования лексики и синтаксиса ES6 в ES5.

Некоторое время назад я представил babel-loader/babel 7, поэтому не буду повторять введение здесь.руководство по использованию babel-loader

Использование EditorConfig и eslint

Очень важно согласовать стандарты кодирования для совместных проектов с участием нескольких человек, потому что это может эффективно повысить эффективность совместной работы и подавить рост гнева программистов. Конечно, я думаю, что личные проекты также нужны, потому что код 6-месячной давности такой же, как и код всех остальных.

EditorConfig

EditorConfig — это кросс-редакторное решение спецификации кода, поддерживаемое многими редакторами (поддержка реализации редактора или плагина), что означает, что разные редакторы могут форматировать код в одном стиле, например vscode и возвышенное. Метод конфигурации заключается в добавлении.editorconfigФайл, некоторые редакторы могут быть сгенерированы командой в один клик, обычно конфигурация следующая:

.editorconfig

root = true

[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
  • rootЯвляется ли это файлом конфигурации верхнего уровня, обычно установленным вtrueУказывает, что файл конфигурации не будет продолжать искать файл конфигурации после поиска.
  • indent_styleОтступ табуляции
  • indent_sizeРазмер отступа, приведенный выше пример означает: отступ представлен двумя пробелами
  • charsetКодирование
  • trim_trailing_whitespaceудалять ли завершающие пробелы
  • insert_final_newlineзаканчивается ли файл пустой строкой

eslint

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

  1. Установить

    yarn add eslint -D
    
  2. Инициализировать файл конфигурации

    npx eslint --init
    

    Выберите необходимые пункты согласно подсказкам и установите соответствующий плагин и конфиг, мне также нужно установить здесьeslint-config-standardа такжеeslint-plugin-vue

    yarn add eslint-config-standard eslint-plugin-vue -D
    

    .eslintrc.js

    module.exports = {
      "env": {
        "browser": true,
        "es6": true
      },
      "extends": [
        "plugin:vue/essential",
        "standard"
      ],
      "globals": {
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly"
      },
      "parserOptions": {
        "ecmaVersion": 2018,
        "sourceType": "module"
      },
      "plugins": [
        "vue"
      ],
      "rules": {
      }
    }
    
  3. Настройте соответствующие правила в rules

    Видетькитайский документ

Анализируйте информацию о пакетах с помощью webpack-bundle-analyzer

Иногда мы обнаруживаем, что размер некоторых спавнов неправильный, но в консоли трудно увидеть причину, в это время нам нужна помощь инструментов анализа модулей. Здесь я рекомендую использовать webpack-bundle-analyzer, который запустит службу и наглядно отобразит в браузере отношение сопоставления и иерархию между сгенерированными и исходными файлами, как показано на рисунке (рисунок взят с Github):

图片来源于 Github

Установить

yarn add webpack-bundle-analyzer -D

использовать

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

Оптимизация пользовательского опыта

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

Уменьшите оптимизацию HTTP, о которой мы упоминали при использовании url-loader для обработки изображений.

Сжать CSS

Вебпак 4 дюймаproductionВ режиме код JS будет сжат по умолчанию, используйтеTerserWebpackPlugin, но CSS не будет (Webpack 5 делает это как встроенную функцию), поэтому нам нужноOptimizeCSSAssetsPluginс помощь.

Установить

yarn add optimize-css-assets-webpack-plugin -D

использовать

Большинство плагинов Webpack используются таким же образом, но использование этого плагина в Webpack 4 требует особого внимания, и при его использовании он будет переписан.optimization.minimizer, а подключаемый модуль TerserWebpackPlugin для сжатия JS находится в значении по умолчанию для этой опции. Переопределение сделает значение по умолчанию недействительным, поэтому вам также необходимо явно объявить экземпляр TerserWebpackPlugin.

webpack.prod.conf.js

const TerserJSPlugin = require("terser-webpack-plugin")
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")

module.exports = {
  optimization: {
    minimizer: [
      new TerserJSPlugin({
        parallel: true // 开启多线程压缩
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  }
}

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

разделение кода

Разделение кода имеет два преимущества:

  • Удалите общий код и зависимости, чтобы избежать повторной упаковки

  • Избегайте слишком большого размера одного файла

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

Тест перед разлукой

Давайте сначала добавим в проект ресурсы, на которые будут ссылаться home и page-a:src/utils/index.js & src/styles/main.styl, а затем обратитесь к ним отдельно на двух страницах, чтобыpage-a.vueПример.

Для удобства проверки сгенерированного кода установимmode: developmentполучить несжатый код

page-a.vue

<template>
  <div class="page-a">
    <h1>
      This is page-a
    </h1>
  </div>
</template>

<script>
import { counter } from '@/utils'

export default {
  name: 'page-a',
  created() {
    counter()
    console.log('page-a:', counter.count)
  }
}
</script>

<style lang="stylus" scoped>
@import '~@/styles/main.styl';

.page-a {
  background: blue;
}
</style>

воплощать в жизньyarn buildупакуем проект, и мыhome.vueСоответствующий продукт (dist/css/views/home.[contentHash].cssа такжеdist/views/home.[contentHash].js.), они содержатsrc/styles/main.stylа такжеsrc/utils/index.jsжелаемое содержимое в файле. Однако проверимpage-a.vueБыло обнаружено, что соответствующие сгенерированные объекты также содержат это содержимое, поэтому исходный код был упакован в сгенерированные объекты, соответствующие двум страницам.

Он упаковывается повторно, потому что эти две страницы ссылаются на них одновременно.При количестве цитирований 3, 10 и более эти общедоступные ресурсы (включая общедоступные зависимости) могут даже составлять более 95% генерируемого объема, что составляет явно недопустимо.

SplitChunksPlugin

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

использовать

webpack.prod.conf.js

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 1, // 正常设置 20000+ 即 20k+ ,但这里我们的公共文件只有几行代码,所以设置为 1
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '/',
      name(mod, chunks) {
        return ${chunks[0].name}
      },
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
}

воплощать в жизньyarn buildУпаковывая проект, мы видим, что толькоdist/views/home.[contentHash].js.(или в отдельном JS) будет содержатьutils.jsсодержание, при этомdist/views/page-a.[contentHash].js.Только ссылки:_utils__WEBPACK_IMPORTED_MODULE_0__["counter"].

Изменить имя вывода

Мы можем использовать VSCode для отладки упакованного кода конфигурации ( nodejs ) и получитьnameв функцииmod / chunksСтруктура объекта, согласно информации, возвращает нужное нам имя файла.

Конечно, вы также можете использовать-inspectПриходитьотладкакод.

// 命名和代码分离息息相关,这里仅为使用示例,具体命名请根据项目情况更改
name(mod, chunks) {
  if (chunks[0].name === 'app') return 'app.vendor'

  if (/src/.test(mod.request)) {
    let requestName = mod.request.replace(/.*\\src\\/, '').replace(/"/g, '')
    if (requestName) return requestName
  } else if (/node_modules/.test(mod.request)) {
    return 'dependencies/' + mod.request.match(/node_modules.[\w-]+/)[0].replace(/node_modules./, '')
  }

  return null
}

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

tree shaking

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

Встряхивание дерева — это термин, обычно используемый для описания удаления мертвого кода из контекста JavaScript. Он основан на статической структуре синтаксиса модулей ES2015, таких как импорт и экспорт.

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

Оглядываясь назад на файлы, сгенерированные при использовании SplitChunksPlugin, мы видим, чтоsayФункция не используется, но упакована, это фактически бесполезный код, который в документации мёртвый код. Чтобы удалить эти коды, просто введитеmodeпревратиться вproduction(Позвольте встряхиванию дерева подействовать), упаковать снова~

Однако следует отметить, что хотя встряхивание дерева может удалить бесполезный код, оно также имеет определенные побочные эффекты (ошибочное определение бесполезного кода). Например, вы можете столкнутьсяБиблиотека компонентов пользовательского интерфейса не имеет стилейПричина этой проблемы в том, что встряхивание дерева работает не только для JS, но и для CSS. Мы обычно используем при импорте CSSimport 'xxx.min.css', статический импорт производственной среды ES6 + удовлетворяет условиям для вступления в силу встряхивания дерева, и Webpack не может определить, что CSS действителен, поэтому он считается мертвым кодом, а затем удаляется. Чтобы решить эту проблему, вы можетеpackage.jsonдобавитьsideEffectsвозможность сообщить Webpack, какие файлы можно импортировать без дрожания дерева, используя следующее:

package.json

{
  "sideEffects": [
    "*.css",
    "*.styl(us)?"
  ]
}

загрузка ресурсов

Разумная загрузка ресурсов иногда важнее, чем уменьшение размера кода

нагрузка по требованию

Загрузка по требованию, также известная как отложенная загрузка, означает, что при открытии страницы, которая должна быть зависимой, загружается зависимость, что снижает нагрузку на домашнюю страницу и повышает скорость рендеринга первого экрана. А чтобы делать загрузку по требованию, нужно использовать только при импорте зависимостейimport()илиrequire.ensureЭти два метода динамической загрузки. мы добавляемlodashЗависимость для тестирования:yarn add lodash

page-a.vue

// 静态加载
import _ from 'lodash'
// 懒加载
// import(/* webpackChunkName: "dependencies/lodash" */ 'lodash')

export default {
  name: 'page-a',
  created() {
    console.log(_.now())
  }
}

Запустите службу разработки в этот момент, и мы увидим, что, хотя здесь используется статическая загрузка, зависимость lodash будет загружена (отложенная загрузка) только после нажатия на страницу-a. Потому что мы уже использовали его, когда используем маршрут настройкиimport()Динамическая загрузка (забыл об этом), поэтому статические ресурсы страницы-страницы тоже становятся ленивой загрузкой.

Давайте еще раз посмотрим на предыдущий оператор динамической загрузки.import(/* webpackChunkName: "views/home" */ '@/views/home/main.vue'), есть момент знания, на который стоит обратить внимание/* webpackChunkName: "views/home" */, это волшебная аннотация Webpack, здесь имя файла сгенерированного чанка, указанное волшебной аннотацией, поэтомуsrc/views/home/main.vueУпакованный JS файла естьdist/views/home.[contentHash].js.

предварительная загрузка, предварительная выборка

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

  • Блок предварительной загрузки начинает загружаться параллельно, когда загружается родительский блок. Блок предварительной выборки начнет загружаться после завершения загрузки родительского блока.
  • Блок предварительной загрузки имеет средний приоритет и загружается немедленно. Фрагмент предварительной выборки загружается, когда браузер бездействует.

Но в моем тесте и preload, и prefetch загружаются параллельно, но их приоритет будет ниже требуемых текущей страницей зависимостей и не повлияет на загрузку страницы. ты сможешьmain.jsДобавьте следующий код для проверки:

src/main.js

// 对比测试
// import 'lodash'
// 预加载
// import(/* webpackPreload: true, webpackChunkName: "dependencies/lodash" */ 'lodash')
// 预取
import(/* webpackPrefetch: true, webpackChunkName: "dependencies/lodash" */ 'lodash')

Поскольку результаты испытаний в этом разделе не соответствуют документам, надеюсь, вы сможете убедиться в этом сами и не верьте в пристрастность.

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

внешние расширения

Иногда мы напрямую внедряем некоторые JS-библиотеки (например, lodash в этой статье) прямо в проект, а Webpack будет их парсить и сжимать. Но подумайте об этом, у самого lodash есть сжатая версия.lodash/lodash.min.js, а его размер не слишком мал для слияния с другими JS (700k несжатого, 70k после сжатия), нам не имеет особого смысла его парсить и сжимать. так что мы можем положить его вstaticпапку (или CDN) и вindex.htmlкитайское использованиеscriptИмпорт тегов, если проект импортировал lodash (import 'lodash') можно настроитьexternalsИгнорируйте это при упаковке, иначе не делайте этого.

Если вы хотите использовать здесьlink[ref=prefetch/preload]preload, поэтому не забудьте повторно использовать его там, где это уместно.scriptВведены теги, предварительная загрузка только для кеширования

  1. новый/staticпапка, добавитьlodash.min.js

  2. изменения кода

    yarn add copy-webpack-plugin -D
    

    /build/webpack.prod.conf.js

    const CopyWebpackPlugin = require('copy-webpack-plugin')
    
    module.exports = {
      externals: {
        lodash: {
          commonjs: 'lodash',
          umd: 'lodash',
          root: '_' // 默认执行环境已经存在全局变量: _ ,浏览器中就是 window._
        }
      },
      plugins: [
        new CopyWebpackPlugin([
          {
            from: 'static/',
            to: 'static/'
          }
        ])
      ]
    }
    

    /src/main.js

    import _ from 'lodash'
    console.log(_.now())
    

    /index.html

    <body>
      <script type="text/javascript" src="static/lodash.min.js"></script>
    </body>
    

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

import now from 'lodash/now'

console.log(now())

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

Справочная документация