Принцип асинхронной загрузки и субподрядная стратегия webpack

внешний интерфейс Webpack
Принцип асинхронной загрузки и субподрядная стратегия webpack

Принцип асинхронной загрузки Webpack

Эта статья включена в githubGitHub.com/Майкл-Ли Чжиган…

webpack ensureКто-то называет это асинхронной загрузкой, а кто-то код-обрезкой, по сути, это экспортировать js-модуль в .js-файл самостоятельно, а затем уже при использовании этого модуля создавать другойscriptобъект, добавленный вdocument.headВ объекте браузер автоматически инициирует запрос на запрос js-файла, а затем напишет функцию обратного вызова, позволяющую запрошенному js-файлу выполнять некоторые бизнес-операции.

Например

нужно:main.jsЗависит от двух файлов js:A.jsЭто логика, которая выполняется после нажатия кнопки aBtn,B.jsЭто логика, которая выполняется после нажатия кнопки bBtn.

webpack.config.js, напишем сначалаwebpackУпакованный код конфигурации

const path = require('path') // 路径处理模块
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin') // 引入CleanWebpackPlugin插件

module.exports = {
  entry: {
    index: path.join(__dirname, '/src/main.js'),
  },
  output: {
    path: path.join(__dirname, '/dist'),
    filename: 'index.js',
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, '/index.html'),
    }),
    new CleanWebpackPlugin(), // 所要清理的文件夹名称
  ],
}

index.htmlкод показывает, как показано ниже

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>webpack</title>
  </head>
  <body>
    <div id="app">
      <button id="aBtn">按钮A</button>
      <button id="bBtn">按钮B</button>
    </div>
  </body>
</html>

входной файлmain.jsследующим образом

import A from './A'
import B from './B'

document.getElementById('aBtn').onclick = function () {
  alert(A)
}

document.getElementById('bBtn').onclick = function () {
  alert(B)
}

A.jsа такжеB.jsКоды следующие

// A.js
const A = 'hello A'
module.exports = A

// B.js
const B = 'hello B'
module.exports = B

На данный момент мы работаем над проектомnpm run build, запакованы только два файла

  • index.html
  • index.js

Отсюда видно, что в это времяwebpackПучокmain.js依赖的两个文件都同时打包到同一个 js 文件,并在 index.html 中引入。 ноA.jsа такжеB.jsЭто логика, которая будет выполняться только при нажатии соответствующей кнопки.Если пользователь не нажимает соответствующую кнопку, а два файла относительно велики, это приведет к тому, что файл js, загруженный по умолчанию на домашней странице, будет слишком большой, что приводит к замедлению рендеринга домашней страницы Шерстяная ткань? Итак, можно ли загрузить соответствующий файл зависимостей, когда пользователь нажимает кнопку?

webpack.ensureЭто решило проблему.

require.ensure загружать асинхронно

Ниже мыmain.jsПереход на асинхронную загрузку

document.getElementById('aBtn').onclick = function () {
  //异步加载A
  require.ensure([], function () {
    let A = require('./A.js')
    alert(A)
  })
}

document.getElementById('bBtn').onclick = function () {
  //异步加载b
  require.ensure([], function () {
    let B = require('./B.js')
    alert(B)
  })
}

На этом этапе давайте снова соберемся и найдем больше1.index.jsа также2.index.jsдва файла. И когда мы открываем страницу, мы только представляемindex.jsФайл, который импортируется только при нажатии кнопки A1.index.jsфайл, который импортируется только при нажатии кнопки B2.index.jsдокумент. Это удовлетворяет наш спрос на загрузку по требованию.

require.ensureЭта функция представляет собой разделенную кодом разделительную линию, представляющуюrequireэто то, что мы хотим разделить, то естьrequire('./A.js'), разделите A.js, чтобы сформироватьwebpackУпакованные отдельные файлы js. Его синтаксис следующий

require.ensure(dependencies: String[], callback: function(require), chunkName: String)

мы открыты1.index.jsфайл, нашел его код следующим образом

;(window.webpackJsonp = window.webpackJsonp || []).push([
  [1],
  [
    ,
    function (o, n) {
      o.exports = 'hello A'
    },
  ],
])

Как видно из кода выше:

  1. Асинхронно загружаемый код будет сохранен в глобальномwebpackJsonpсередина.
  2. webpackJsonp.pushЗначение двух параметров — это идентификатор, соответствующий модулю, который должен быть установлен, сохраненный в асинхронно загруженном файле, и список модулей, которые должны быть установлены, сохраненный в асинхронно загруженном файле.
  3. При определенных условиях будет выполняться код в конкретном модуле.

import() загружается по запросу

Официальная документация webpack4 обеспечивает вырезание и загрузку модулей по требованию в сочетании с загрузкой по требованию es6.import()Этот метод может уменьшить объем пакета домашней страницы и ускорить скорость запросов домашней страницы, только другие модули будут загружать соответствующие js только при необходимости.

import()Синтаксис очень прост. Функция принимает только один параметр, являющийся адресом эталонного пакета, и используетpromiseобратный вызов стиля для получения загруженного пакета. в коде всеimport()модули, будут сгруппированы в отдельный пакет, размещенный вchunkкаталог хранения. Когда браузер запустит эту строку кода, он автоматически запросит этот ресурс для асинхронной загрузки.

Ниже мы изменим приведенный выше код наimport()Способ.

document.getElementById('aBtn').onclick = function () {
  //异步加载A
  import('./A').then((data) => {
    alert(data.A)
  })
}

document.getElementById('bBtn').onclick = function () {
  //异步加载b
  import('./B').then((data) => {
    alert(data.B)
  })
}

Упакованные файлы иwebpack.ensureМетод тот же.

Отложенная загрузка маршрута

Зачем нужна ленивая загрузка?

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

Ленивая загрузка маршрутизации Vue имеет следующие три способа:

  • vue асинхронный компонент
  • ES6import()
  • веб-пакетrequire.ensure()

vue асинхронный компонент

Этот метод используется в основномresolveасинхронный механизм, использующийrequireвместоimportРеализовать загрузку по требованию

export default new Router({
  routes: [
    {
      path: '/home',',
      component: (resolve) => require(['@/components/home'], resolve),
    },
    {
      path: '/about',',
      component: (resolve) => require(['@/components/about'], resolve),
    },
  ],
})

require.ensure

Этот режим можно передать в параметреwebpackChunkNameУпакуйте js отдельно.

export default new Router({
  routes: [
    {
      path: '/home',
      component: (resolve) => require.ensure([], () => resolve(require('@/components/home')), 'home'),
    },
    {
      path: '/about',
      component: (resolve) => require.ensure([], () => resolve(require('@/components/about')), 'about'),
    },
  ],
})

ES6 Импорт ()

vue-routerНа официальном сайте предоставлен способ, который можно понимать как способ прохожденияPromiseизresolveмеханизм. потому чтоPromiseвозвращается функциейPromiseдляresolveсам компонент, и мы можем использоватьimportдля импорта компонентов.

export default new Router({
  routes: [
    {
      path: '/home',
      component: () => import('@/components/home'),
    },
    {
      path: '/about',
      component: () => import('@/components/home'),
    },
  ],
})

стратегия подпакета webpack

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

CommonsChunkPlugin

До версии Webapck4.x мы все использовалиCommonsChunkPluginразделять

plugins: [
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks: function (module, count) {
      return (
        module.resource &&
        /\.js$/.test(module.resource) &&
        module.resource.indexOf(path.join(__dirname, './node_modules')) === 0
      )
    },
  }),
  new webpack.optimize.CommonsChunkPlugin({
    name: 'common',
    chunks: 'initial',
    minChunks: 2,
  }),
]

Извлекаем и упаковываем отдельно следующие файлы

  • node_modulesпод папку, модуль
  • по 3 входаchunkобщий модуль

optimization.splitChunks

Самое большое изменение в webpack 4 заключается в том, что он упразднен.CommonsChunkPluginпредставилoptimization.splitChunks. если вашmodeдаproduction, то автоматически запустится webpack4Code Splitting.

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

  • Является ли новый фрагмент общим или получен изnode_modulesмодуль
  • Размер нового фрагмента больше 30 КБ перед сжатием.
  • Количество одновременных запросов на загрузку чанков по запросу меньше или равно 5
  • Количество одновременных запросов при первоначальной загрузке страницы меньше или равно 3

Хотя он автоматически откроется в WebPack4Code Splitting, но при самом крупном проектировании это часто не может удовлетворить наши потребности, и нам необходимо проводить персонифицированную оптимизацию.

Приложения

Сначала мы находим проект с большим пространством для оптимизации. Это фоновый проект системы управления.Большая часть контента разрабатывается 3-4 фронтендами.Цикл разработки обычно короткий, и большинство людей не осведомлены об оптимизации.Они просто пишут бизнес-код для удовлетворения требований.Как время идет, упакованные файлы генерируются больших размеров, что сильно влияет на производительность.

мы сначала используемwebpack-bundle-analyzerПроанализируйте зависимости упакованного модуля и размер файла, чтобы определить направление оптимизации.

Тогда давайте посмотрим на упакованный файл js.

Смотрите две карты времени, мое сердце рухнуло, следующая точка канавки

  • После упаковки создается несколько js-файлов размером около 1 М, многие из которыхvendor.jsБольшие файлы, которые должны быть загружены на главной странице
  • xlsx.jsНет необходимости использовать такой плагин, лучший способ экспортировать Excel должен состоять в том, чтобы вернуть формат файлового потока из бэкэнда во внешний интерфейс для обработки.
  • echartа такжеiviewФайл слишком большой, следует использовать метод, представленный cdn

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

оторваться от echart и iview

Из вышеприведенного анализа видно, чтоechartа такжеiviewФайл слишком большой, в данный момент мы используем webpack4optimization.splitChunksКод разделен, и они отдельно извлекаются и упаковываются в файлы. (Чтобы лучше представить эффект оптимизации, мы сначала удаляем xlsx.js)

vue.config.jsИзмените следующим образом:

chainWebpack: config => {
    config.optimization.splitChunks({
      chunks: 'all',
      cacheGroups: {
        vendors: {
          name: 'chunk-vendors',
          test: /[\\/]node_modules[\\/]/,
          priority: 10,
          chunks: 'initial'
        },
        iview: {
          name: 'chunk-iview',
          priority: 20,
          test: /[\\/]node_modules[\\/]_?iview(.*)/
        },
        echarts: {
          name: 'chunk-echarts',
          priority: 20,
          test: /[\\/]node_modules[\\/]_?echarts(.*)/
        },
        commons: {
          name: 'chunk-commons',
          minChunks: 2,
          priority: 5,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    })
  },

Теперь мы используемwebpack-bundle-analyzerПроанализируйте это

Упакованный файл js

Отсюда видно, что мы успешноechartа такжеiviewвытащил один, и в то же времяvendor.jsСоответственно уменьшается и громкость. Кроме того, мы можем продолжать извлекать другие сторонние модули.

CDN путь

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

  1. существуетindex.htmlВведите соответствующую ссылку cdn
<head>
  <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/iview/3.5.4/styles/iview.css" />
</head>
<body>
  <div id="app"></div>
  <script src="https://cdn.bootcss.com/vue/2.6.8/vue.min.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/iview/3.5.4/iview.min.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.16.8/xlsx.mini.min.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.16.8/cpexcel.min.js"></script>
</body>
  1. vue.config.jsнастроитьexternals
configureWebpack: (config) => {
  config.externals = {
    vue: 'Vue',
    xlsx: 'XLSX',
    iview: 'iView',
    iView: 'ViewUI',
  }
}
  1. Удалите предыдущий метод импорта и удалите соответствующие зависимости npm.
npm uninstall vue iview echarts xlsx --save

На этом этапе давайте посмотрим на ситуацию после упаковки.

Упакованный файл js

молодец!В это время в основном не упакован большой файл, а домашняя страница подгружает нужныйvendor.jsЭто всего несколько десятков килобайт, и мы можем оптимизировать его, внедрив некоторые модули семейства vue через метод cdn, такие какvue-router,vuex,axiosЖдать. В настоящее время производительность страницы, особенно загрузка домашней страницы, была значительно оптимизирована.

рекомендуемая статья