Оптимизация рендеринга первого экрана клиента Vue

Webpack

Извлечение сторонних библиотек, кеширование, уменьшение размера упаковки

1. Динамическая библиотека dll, используйте DllPlugin DllReferencePlugin, извлеките стороннюю библиотеку и упакуйте ее, а затем динамически импортируйте html. Может повысить скорость упаковки и кэшировать сторонние библиотеки. Этот способ упаковки можно увидеть в плане gaea команды Jingdong.Уууу, эта лошадь plus.com/package/GAE…

2. SplitChunks webpack4 или CommonsChunkPlugin webpack3 взаимодействуют с внешними (внешними ресурсами) В основном отдельные сторонние библиотеки, кастомные модули (кастомные модули введенные более 3-х раз разделены), код времени выполнения webpack (рантайм, минифест). Под экстерналами подразумевается экстернализация сторонней библиотеки и внедрение ее в виде cdn, что может уменьшить объем упаковки. Подробный код В webpack.config.js (в среде peoduction)

externals: {
    'vue': 'Vue', //vue 是包名 Vue是引入的全局变量
    'vue-router': 'VueRouter',
    'vuex': 'Vuex',
    'axios': 'axios',
    'iview': 'iview' //iview
},

Затем main.js или больше нигде не вводите такие, как vue, и используйте переменные, указанные выше, напрямую.

Импорта vue выше нет, а в проекте как обычно используется глобальная переменная Vue. Поскольку импорта vue нет, vue не будет упакован, и тогда вы обнаружите, что ваш vendor.js уменьшится с 700 КБ+ до 30-40 КБ, что является отличной оптимизацией.
Что касается того, комментировать ли, вот две карты проверки, в случае, если веб-пакет настроен с внешними
Конфигурация webpack4 splitChunk

//提取node_modules里面的三方模块
module.exports = {
    optimization: {
        splitChunks: {
            cacheGroups: {
                vendor: {
                    chunks: "initial",
                    test: path.resolve(__dirname, "node_modules") // 路径在 node_modules 目录下的都作为公共部分
          name: "vendor", // 使用 vendor 入口作为公共部分
                    enforce: true,
                },
            },
        },
    },
}
//提取 manifest (webpack运行代码)
{
    runtimeChunk: true;
}

Конфигурация webpack3 CommonsChunkPlugin написана в плагинах

 // split vendor js into its own file
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks (module) {
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),
    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
    // This instance extracts shared chunks from code splitted chunks and bundles them
    // in a separate chunk, similar to the vendor chunk
    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),

Смотрите следующие два вопроса для получения подробной информацииwoohoo.brief.com/afraid/23 награжден 35 до CA ...

Фиксированный идентификатор модуля, для кэширования

Чанк: Относится к файлам (таким как: js, css, изображения и т. д.), на которые есть ссылки в коде, которые будут объединены в один или несколько пакетов в соответствии с конфигурацией. Мы называем пакет фрагментом. модуль: относится к разделению кода на отдельные функциональные блоки в соответствии с их функциями. Разделенный кодовый блок называется модулем. Можно просто понять, что экспорт/импорт — это модуль.
решение: Плагин HashedModuleIdsPlugin или оптимизация.moduleIds='hash' для webpack4

фиксированный идентификатор чанка

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

import(/* webpackChunkName: "my-chunk-name" */ "module");

Мы можем написать это в сочетании с ленивой загрузкой Vue.

{
    path: '/test',
    component: () => import(/* webpackChunkName: "test" */ '@/views/test')
  },

Также не забудьте настроить chunkFilename

output: {
            path: path.resolve(__dirname, 'dist'),
            publicPath: config.publicPath + '/',//静态文件的处理,生产环境有效.开发环境其实是从内存中拿文件的
            filename: 'js/[name].[chunkhash].js',
            chunkFilename: 'js/[name].[chunkhash].js' //写成[name].xxxx,便于查找chunk源  详细见 NamedChunkPlugin 
        },

После упаковки создается фрагмент файла с именем test, После того, как у чанка будет имя, ошибка в NamedChunksPlugin без имени может быть решена. Глядя на упакованный код, мы обнаруживаем, что chunkId больше не является простым самоувеличивающимся идентификатором.
Первый рекомендуется.Вы можете исправить идентификатор блока (заменив имя блока), и вы можете понять детали упаковки проекта, например, когда вы столкнетесь с большим файлом, в каком блоке есть проблема, и напрямую сопоставить источник эта проблема

Мы можем непосредственно видеть, что большой файл размером 786 КБ исходит из пакета поставщика (сторонней библиотеки) test1.vue и test2.vue, а затем входит в test1.vue, echarts является источником проблемы, и решение состоит в том, чтобы внешние библиотеки, такие как echarts. Дополнительные сведения см. в разделе об экстернализации ресурсов выше.

Второй: Принцип: По идентификатору модуля в каждом чанке имя чанка уникально, пока модуль в нем не увеличивается и не уменьшается, его имя не изменится.

new webpack.NamedChunksPlugin(chunk => {
  if (chunk.name) {
    return chunk.name;
  }
  return Array.from(chunk.modulesIterable, m => m.id).join("_");
});

Конечно, это решение все же имеет некоторые недостатки, потому что id может быть очень длинным, а если чанк зависит от многих модулей, то id может состоять из десятков цифр, поэтому нам нужно сократить его длину. Сначала мы помещаем сплайсированный хэш id ниже, и нам нужно убедиться, что количество цифр результата хеширования может быть слишком длинным, что приводит к трате байтов, но оно слишком короткое и подвержено коллизиям, поэтому в конце мы выбираем длину 4 цифры и сделать это вручную с помощью Set Collision check, в случае коллизии количество цифр увеличивается на 1 до тех пор, пока не произойдет коллизия. Подробный код выглядит следующим образом:

const seen = new Set();
const nameLength = 4;

new webpack.NamedChunksPlugin(chunk => {
  if (chunk.name) {
    return chunk.name;
  }
  const modules = Array.from(chunk.modulesIterable);
  if (modules.length > 1) {
    const hash = require("hash-sum");
    const joinedHash = hash(modules.map(m => m.id).join("_"));
    let len = nameLength;
    while (seen.has(joinedHash.substr(0, len))) len++;
    seen.add(joinedHash.substr(0, len));
    return `chunk-${joinedHash.substr(0, len)}`;
  } else {
    return modules[0].id;
  }
});

Извлечь css как отдельные файлы и сжать

mini-css-extract-плагин для webpack4
ExtractTextPlugin для webpack3

Сжать JS-файлы

webpack3 UglifyJsPlugin webpack4 поставляется с функцией UglifyJsPlugin, настройка не требуется, и необходимо включить режим производства

сотрясение дерева и побочные эффекты

Удалите код без ссылок, webpack4 поддерживает его по умолчанию.
Поскольку функция Tree Shaking основана на обнаружении статических функций модулей ES6 для поиска неиспользуемого кода, если вы используете плагин babel, такой как: babel-preset-env, он по умолчанию упаковывает модуль в commonjs, так что он сделает встряхивание дерева недействительным.

sideEffects — это функция, доступная только в webpack4, цель которой — загружать сторонние библиотеки без каких-либо побочных эффектов по запросу. В этом могут помочь побочные эффекты webpack. Теперь в файле package.json ES-версии lodash уже есть объявление sideEffects: false. Когда модуль имеет это объявление в файле package.json, веб-пакет будет думать, что модуль не имеет побочных эффектов и используется только извне. используемый модуль, то при упаковке будет выполнена некоторая дополнительная обработка. Например, вы используете lodash следующим образом:


import { forEach, includes } from 'lodash-es'

forEach([1, 2], (item) => {
    console.log(item)
})

console.log(includes([1, 2, 3], 1))

Поскольку файл package.json модуля lodash-es имеет объявление sideEffects: false, webpack преобразует приведенный выше код в следующий код для обработки:

import { default as forEach } from 'lodash-es/forEach'
import { default as includes } from 'lodash-es/includes'
// ... 其他代码

В конце концов, webpack не будет упаковывать все содержимое кода lodash-es, а упаковывает только два используемых вами метода.Это роль sideEffects.

ленивая загрузка импорта()

Babel необходимо настроить @babel/plugin-syntax-dynamic-import
Загрузка импорта по запросу (/* webpackChunkName: "Index" */ "xxx.vue")
Правило настройки именования находится в chunkFilename (если оно не задано, оно будет называться в соответствии со значением по умолчанию 1.xxxx.js. На самом деле оно также будет упаковано отдельно для удобства отладки. Если вы видите, что чанк относительно большой при упаковке можно просмотреть vue-файл, соответствующий чанку) chunkFilename: utils.assetsPath('js/[name].[chunkhash].js') Так как он загружается по требованию, он не будет упакован в app.js (основной входной фрагмент), он обязательно будет упакован отдельно, а затем загружен по требованию

Babel вводит полифиллы по мере необходимости

Cabel по умолчанию конвертировать только синтаксис JavaScript, без преобразования новых API, таких как обещание, генератор, набор, карты, символ и другие глобальные объекты, некоторые методы, определенные на глобальном объекте (например, Object.Ascign), не будут транскодированы. Если вы хотите Transcode API, не может работать должным образом в условиях низкой версии, которая требует использования полифиллирования.

Наиболее распространенное решение для babel6

использоватьtransform-runtimeилиbabel-polyfill
Сравниватьtransform-runtimeа такжеbabel-polyfillПредставляем отличия прокладок:
использоватьtransform - runtimeОн импортируется по запросу.Среда выполнения автоматически подскажет, какие полифиллы вам нужно использовать.Нет необходимости вручную настраивать плагины один за другим.Однако введенные полифиллы не являются глобальными и имеют некоторые ограничения. И полифилл, введенный средой выполнения, не будет переписывать некоторые методы экземпляра, такие как методы в цепочке прототипов Object и Array, как упоминалось выше.Array.protype.includes.

Обратите внимание на использованиеtransform-runtimeнеобходимо установитьbabel-runtime,babel-runtimeэто библиотека для импорта, поместите --save иbabel-plugin-transform-runtimeпомочь представитьbabel-runtimeэтой библиотеки (автоматически)
babel-runtimeа такжеbabel-plugin-transform- runtimeРазница в том, что первая — это ручная передача, а вторая — автоматическая.Всякий раз, когда API нужно перевести, требуется вручную добавить require('babel-runtime'). а такжеbabel - plugin - transform - runtimeОн будет автоматически добавлен инструментом.Основная функция состоит в том, чтобы предоставить решение для прокладки песочницы для API, которое не будет загрязнять глобальный API, поэтому оно подходит для использования в сторонних продуктах разработки. Повторное введение будет дедуплицировано общим ChunkPlugin, установленным webpackbabel - polyfillОн может решить эти проблемы времени выполнения.Его прокладки глобальны и всемогущи.По сути, полифиллы, используемые в ES6, все в babel-polyfill, который обеспечивает полную среду ES6 +. Babel официально рекомендует, пока вас не волнует объем babel-polyfill, лучше всего внедрять его глобально, потому что это самый безопасный способ. Общая рекомендация — использовать фреймворки или библиотеки, которые не загрязняют глобальную область видимости, при разработке некоторых фреймворков или библиотек.babel - runtime, и babel-polyfill можно внедрить глобально при разработке веб-приложений, чтобы избежать ненужных ошибок, и его можно внедрить глобально в больших веб-приложениях.babel - polyfillЭто также может уменьшить размер ваших упакованных файлов (по сравнению с введением дубликатов полифиллов для каждого модуля).

Ниже приведены три способа, с помощью которых babel6 может решить вопрос о внедрении ES6 API pollyfill.
① Используйте babel-polyfill глобально (не устанавливайтеbabel-preset-envэлемент опцийuseBuiltIns) Конкретное использование заключается в следующем: А. Непосредственно импортировать адрес polyfill js или CDN непосредственно в заголовок файла index.html; б) Добавьте зависимость babel-polyfill в package.json и добавьте запись в файл конфигурации webpack: например,entry: ["babel-polyfill", './src/app.js'], полифилл будет упакован в этот входной файл и должен быть размещен в самом начале файла; C. Непосредственно в верхней части входного файлаimport ''babel-polyfill'; Преимущество этого решения в том, что оно простое и может решить все проблемы совместимости браузера с полифиллами за один раз, а недостаток в том, что все полифиллы ES6+ внедряются одновременно, и упакованный js-файл будет слишком большим, что не требуется в современных браузерах.Полифилл и, во-вторых, загрязняет глобальный объект, что не подходит для разработки классов фреймворка.Разработка классов фреймворка рекомендует следующую② схему. Примечание. Библиотека polyfill.io выполнит соответствующий полифилл в соответствии с используемым вами браузером, что может значительно решить проблему введения слишком большого размера.

② глобальное использованиеbabel-polyfill(настраиватьbabel-preset-envuseBuiltIns пункта опций) Конкретное использование заключается в следующем:

  1. представлятьbabel-preset-envМешок;
  2. Используйте элемент параметров babel-preset-env в предустановленных пресетах в файле .babelrc.useBuiltins: usage | entry(использование: загружать только те полифиллы, которые используются в коде. Запись: в соответствии с поддержкой версии браузера требования полифилла разделены и введены, и вводятся только те полифиллы, которые не поддерживаются браузером)targets.browsers: 浏览器兼容列表 modules: false
  3. непосредственно в верхней части входного файлаimport ''babel - polyfill';

Это решение подходит для разработки на уровне приложений, и Babel автоматически введет все необходимые полифилы в соответствии с указанным списком совместимости браузера.

③ Используйте плагиныbabel-runtimeилиbabel-plugin-tranform-runtime babel-runtimeВозникает проблема дублирования цитат, в то время какbabel-plugin-tranform-runtimeОбщие модули извлекаются, и повторное введение избегается.Следующая конфигурация в основном основана наbabel-plugin-tranform-runtimeсказать.

  1. представлятьbabel-plugin-tranform-runtimeМешок;
  2. существует.babelrcДобавьте в файл плагиныbabel-plugin-tranform-runtime: "plugins": ["transform-runtime"];
  3. Сопоставьте настройки предустановленных предустановок на шаге 2 описанного выше метода ②;

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

"babel-core": "^6.22.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",

.babelrc от vue-cli


{
    "presets": [
        ["env", {
            "modules": false,
            "targets": {
                "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
            }
        }],
        "stage-2"
    ],
        "plugins": ["transform-vue-jsx", "transform-runtime"]
}

решение babel7 (обратите внимание на версию core-js)

полифиллы загружаются по запросу Модуль @babel/polyfill включает corejs и настраиваемый модуль времени выполнения регенератора для имитации полной среды ES2015+. Ручное введение больше не требуетсяimport ''babel-polyfill'; 只需要简单的配置就能自动智能化引入@babel/polyfill,设置useBuiltIns按需加载 .babelrc

{
    "presets": [
        ["@babel/preset-env",
            {
                "modules": false,
                "targets": {
                    "browsers": ["> 1%", "last 2 versions", "not ie <= 8", "Android >= 4", "iOS >= 8"]
                },
                "useBuiltIns": "usage"

            }]
    ],
        "plugins": [
            "@babel/plugin-syntax-dynamic-import"

        ]
}

Обновление до 7 необходимо установить на @Babel

"@babel/core": "^7.1.2",
"@babel/plugin-syntax-dynamic-import": "7.0.0", //用于import()
"@babel/polyfill": "7.0.0",
 "@babel/preset-env": "7.1.0",
  "babel-loader": "8.0.4",

Сводка по использованию Babel, рекомендуется использовать babel7, скорость сборки выше, рекомендуется использовать @babel/preset-env», рекомендуется включить свойство useBuiltIns, чтобы babel-polyfill мог загружаться по требованию. О включении или не включенииuseBuiltInРазмер пакета сборки подробно описан на https://github.com/ab164287643/studyBabel/tree/master/7-babel-env.

Разница между webpack3 и webpack4

1. Добавлен режим конфигурации, есть только два значения development|production, он будет включать разные конфигурации для разных сред.
2. Многие оптимизации кода (минимизация, разделение) запускаются в производственной среде по умолчанию.
3. Откройте взгляд и проверьте при разработке, и добавьте Evel devtool
4. Производственная среда не поддерживает просмотр, а среда разработки оптимизирует скорость упаковки
5. Включите конкатенацию модулей в производственной среде (ранее ModulecondatenationPlugin)
6. Автоматически устанавливать process.env.NODE_EVN для разных окружений, то есть не использовать DefinePlugin
7. Если режим не установлен, все настройки по умолчанию удаляются.
8. До webpack4 мы работали с общими модулями с помощью CommonsChunkPlugin, а затем конфигурация этого плагина была громоздкой для разработки, а извлечение общего кода было недостаточно тщательным и подробным, поэтому новые splitChunks улучшили эти возможности.
9. По умолчанию включен кеш и параллель uglifyjs-webpack-plugin, то есть кеширование и параллельная обработка, что может сильно повысить скорость сжатия кода в продакшн режиме.
В производственной среде и среде разработки добавляется множество конфигураций по умолчанию (например, UglifyJsPlugin используется по умолчанию в производственной среде), а скорость упаковки выше.

Сжатие изображения

Используйте tinify для сжатия используемого изображения. Подробный сценарий см.git ee.com/dusting-способность II…

Об оптимизации формата изображения

jpeg сжатие с потерями, маленький размер, не поддерживает прозрачность.
png сжатие без потерь, высокая точность, поддержка прозрачности.
png - 8 2^8 цветов 256
png - 24 2^24 цвета 1600 Вт
png - 32 2^24^8 (плюс 8 цветовых каналов прозрачности)
Чем больше поддерживаемых цветов, тем больше объем
svg векторная иллюстрация небольшого размера без искажений, подходящая для маленьких иконок
base64 уменьшает HTTP-запросы, но не должен обрабатывать большие изображения, потому что большие изображения увеличивают размер страницы, URL-загрузчик веб-пакета уже поддерживает
WebP — это новый формат, который поддерживает сжатие с потерями и без потерь, поддерживает прозрачность и чрезвычайно мал по размеру. По сравнению с PNG он обычно обеспечивает в 3 раза больший размер файла, имеет низкую совместимость с браузерами и большие ограничения.
Поддержка webp в проекте (см. Jingdong gaea): Определите, поддерживается ли webP в index.html

window.supportWebp = false;
if (document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0) {
    document.body.classList.add('webp');
    window.supportWebp = true;
}

Затем используйте приведенную выше картинку для сжатия. Скрипт для сжатия картинки сгенерирует файл webp под IMG, представляющий собой картинку конвертированного формата Webp. Напишите два набора шаблонов в CSS, например

.banner{ background - image: url("xxxx.png") }
.webp .banner{ background - image: url("xxxx.webp") }

В js решите, какое изображение использовать, в соответствии с window.supportWebp.

включить сжатие

использовать сжатие-webpack-plugin Открыть в производственной среде

if (config.productionGzip) {
    const CompressionWebpackPlugin = require('compression-webpack-plugin');
    //增加浏览器CPU(需要解压缩), 减少网络传输量和带宽消耗 (需要衡量,一般小文件不需要压缩的)
    //图片和PDF文件不应该被压缩,因为他们已经是压缩的了,试着压缩他们会浪费CPU资源而且可能潜在增加文件大小。
    webpackConfig.plugins.push(
        new CompressionWebpackPlugin({
            asset: '[path].gz[query]',
            algorithm: 'gzip',
            test: /\.(js|css)$/,
            threshold: 10240,//达到10kb的静态文件进行压缩 按字节计算
            minRatio: 0.8,//只有压缩率比这个值小的资源才会被处理
            deleteOriginalAssets: false//使用删除压缩的源文件
        })
    )
}

Когда сжатие gzip включено, сервер необходимо настроить соответствующим образом, чтобы сервер мог передавать сжатые файлы. Включите оптимизацию производительности gzip на стороне сервера nginx. Найдите файл конфигурации nginx и добавьте следующий код в конфигурацию http, затем перезапустите службу nginx.

http: {
    gzip on;
    gzip_static on;
    gzip_buffers 4 16k;
    gzip_comp_level 5;
    gzip_types text / plain application / javascript text / css application / xml text / javascript application / x - httpd - php image / jpeg
    image / gif image / png;
}

Включить сжатие apache gzip Настроить в http.conf Найдите следующее предложение и удалите #

LoadModule deflate_module modules / mod_deflate.so

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

< IfModule mod_deflate.c >
# 告诉 apache 对传输到浏览器的内容进行压缩
SetOutputFilter DEFLATE
# 压缩等级 9
DeflateCompressionLevel 9
#设置不对后缀gif,jpg,jpeg,png的图片文件进行压缩
SetEnvIfNoCase Request_URI.(?: gif | jpe ? g | png)$ no - gzip dont - vary
</IfModule >
可以看到如下效果,http传输大小为173kb,而解压缩后大小为619kb

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

Ленивая загрузка изображения

поставить ссылкуnuggets.capable/post/684490…

Все коды конфигурации в этой статье
git ee.com/dusting-способность II…

Категории