Конфигурация webpack4 для фронтенд-разработки [с комментариями]

Webpack
Конфигурация webpack4 для фронтенд-разработки [с комментариями]

❤️ Если вам хорошо, ставьте лайк ❤️.Оригинальная ссылка

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

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

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

Используйте правильные инструменты для работы

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

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

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

с помощью веб-пакета

Чуть больше года назад я опубликовал статью:A Gulp Workflow for Frontend Development Automation[рабочий процесс gulp для автоматизации интерфейса], объясняет, как использоватьgulpЗавершите то же самое. Однако в это время я более полезен.Vue-JSа такжеGraphQLТакие интерфейсные фреймворкиUsing VueJS + GraphQL to make Practical MagicЭта статья.

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

Есть другие варианты:

  • Laravel MixЭто слой инструментов сборки, основанный на веб-пакете, он очень прост, вы можете быстро настроить и запустить его, он может делать то, что вы хотите, в 90% случаев, но оставшиеся 10% все равно перейдут в веб-пакет, пока нет.Поддержите веб-пакет4.

  • Если вы просто используете интерфейсную структуру VueJS, используйтеvue-cliэто хороший вариант, он также основан на веб-пакете, работает большую часть времени и делает для вас некоторые неожиданные вещи. Но опять же, когда функциональность, которую он предоставляет, больше не соответствует вашим потребностям, вам все равно нужно использовать веб-пакет, и я использую не только VueJS.

  • Neutrino также основан на веб-пакете, мы можем следить за блогом:Neutrino: How I Learned to Stop Worrying and Love Webpack. Волшебный момент заключается в том, что он может настраивать веб-пакет, как блоки Lego, но стоимость обучения его использованию не сильно отличается от обучения веб-пакету.

Если вы выберете вышеуказанные инструменты (или другие инструменты), я не возлагаю на вас никакой ответственности: все они основаны на обертках веб-пакетов.

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

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

В какой-то момент я думаю, что имеет смысл понять, как работает такая важная вещь, как веб-пакет. Не так давно яSean Larkin(один из основных членов команды webpack) пожаловался, что webpack похож на «черный ящик», и его ответ был кратким, но блестящим:

Он черный только в том случае, если вы его не открыли [Если вы не открыли этот так называемый «черный ящик», он никогда не будет известен. ]

Он прав, пора открывать «черный ящик».

Эта статья не расскажет вам все о вебпаке или даже о том, как его установить. Ниже вы найдете множество материалов на выбор. Вы можете выбрать способ, который считаете хорошим:

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

WHAT WE GET OUT OF THE BOX

Когда я начал изучать веб-пакет, открыв «черный ящик», у меня был список технологий, от которых я зависел и которые я хотел сделать частью процесса сборки. Я также найду время, чтобы осмотреться и посмотреть, что еще я могу использовать в процессе.

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

Итак, вот что я хотел бы использовать для обработки веб-пакета, и методы, которые я хотел бы включить в процесс сборки:

  • Development / Production- В местной разработке я прохожуwebpack-dev-serverДелайте быстрые сборки для производственных сборок (обычно черезbuddy.worksстроить в контейнере Docker), я хочу максимально оптимизировать каждый пункт. Поэтому мы различаемdevа такжеprodконфигурация и сборка.

  • Hot Module Replacement—— Когда я изменяю js, css или страницы, я надеюсь, что страница может обновляться автоматически, что значительно повышает эффективность разработки: вам не нужно нажимать кнопку обновления браузера.

  • Dynamic Code Splitting- Я не хочу вручную определять фрагменты js в файле конфигурации, поэтому я позволяю веб-пакету делать это за меня.

  • Lazy Loading- Также известна как асинхронная загрузка динамического модуля, которая загружает требуемые ресурсы кода, когда это необходимо.

  • Modern & Legacy JS Bundles- Я хочу выпустить модуль es2015 + JavaScript для поддержки более 75% браузеров мира и предоставить пакет исправлений (включая все перекодирование и полифиллы) для браузеров более ранних версий.

  • Cache Busting via manifest.json- Позволяет нам настроить кэширование для статических ресурсов, обеспечивая при этом их автоматическое повторное кэширование при изменении.

  • Critical CSS- согласно статьеImplementing Critical CSS on your website, что может повысить скорость загрузки первой страницы.

  • Workbox Service Worker- Мы можем использовать проект Google Workbox, чтобы создать его для нас.Service Worker, чтобы понять все о нашем проекте [есть небольшая проблема с переводом этого предложения, вы можете прочитать исходный текст, чтобы понять]. ПВА, мы идем!

  • PostCSS- Я думаю, что это "бабель css", так как sass и scss построены поверх него, это дает вам доступ к будущим функциям css.

  • Image Optimization- В настоящее время изображения по-прежнему являются основным содержанием большинства веб-страниц, поэтому вы можетеmozjpeg,optipng,svgoНеобходимо использовать автоматизированные инструменты для сжатия и оптимизации ресурсов изображений.

  • Automatic .webp Creation- Chrome, Edge и FireFox поддерживают файлы .webp, которые меньше, чем jpeg, и экономят ресурсы.

  • VueJS—— VueJs — это интерфейсный фреймворк, который я использую на этот раз, я надеюсь, что смогу передать один файл.vueКомпоненты как часть процесса разработки.

  • Tailwind CSS— Tailwind — это служебный css, который я использую для быстрого прототипирования в локальной разработке, а затем для производства с помощью PurgeCss, уменьшая размер.

Ничего себе, это выглядит как довольно обширный список!

Есть много вещей, таких как автоматизация JavaScript, минимизация css и другие стандартные настройки для создания желаемой системы внешнего интерфейса.

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

Нельзя недооценивать важность ремонтопригодности и возможности повторного использования [обслуживаемость и повторное использование очень важны. ]

Фронтенд-фреймворк или технологический стек, который вы используете, могут отличаться от моих, но применяемые правила остаются теми же, так что читайте дальше, независимо от того, какой стек вы используете!

PROJECT TREE & ORGANIZATION

Чтобы дать вам представление об общей архитектуре программы, вот простое дерево проекта:

├── example.env
├── package.json
├── postcss.config.js
├── src
│   ├── css
│   │   ├── app.pcss
│   │   ├── components
│   │   │   ├── global.pcss
│   │   │   ├── typography.pcss
│   │   │   └── webfonts.pcss
│   │   ├── pages
│   │   │   └── homepage.pcss
│   │   └── vendor.pcss
│   ├── fonts
│   ├── img
│   │   └── favicon-src.png
│   ├── js
│   │   ├── app.js
│   │   └── workbox-catch-handler.js
│   └── vue
│       └── Confetti.vue
├── tailwind.config.js
├── templates
├── webpack.common.js
├── webpack.dev.js
├── webpack.prod.js
├── webpack.settings.js
└── yarn.lock

Полный код можно посмотреть по адресу:annotated-webpack-4-config

Основные методы профиля включают в себя:

  • .env-- настройки webpack-dev-server для разработчиков, которые не нужно проверять в git

  • webpack.settings.js- Файл настроек в формате JSON, единственный файл, который нам нужно редактировать между проектами.

  • webpack.common.js- Однотипные сборки помещаются в единый файл настроек

  • webpack.dev.js- Настройка локальной разработки для каждой сборки

  • webpack.prod.js- Настройка производственной среды для каждой сборки

Вот схема того, как вышеприведенная конфигурация может быть объединена:

Цель состоит в том, чтобы отредактировать только золотые закругленные области между элементами (.env&webpack.settings.js).

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

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

ANNOTATED PACKAGE.JSON

Начнем с изменения нашегоpackage.jsonНачать:

{
    "name": "example-project",
    "version": "1.0.0",
    "description": "Example Project brand website",
    "keywords": [
        "Example",
        "Keywords"
    ],
    "homepage": "https://github.com/example-developer/example-project",
    "bugs": {
        "email": "someone@example-developer.com",
        "url": "https://github.com/example-developer/example-project/issues"
    },
    "license": "SEE LICENSE IN LICENSE.md",
    "author": {
        "name": "Example Developer",
        "email": "someone@example-developer.com",
        "url": "https://example-developer.com"
    },
    "browser": "/web/index.php",
    "repository": {
        "type": "git",
        "url": "git+https://github.com/example-developer/example-project.git"
    },
    "private": true,

Здесь нет ничего интересного, просто содержит метаинформацию для нашего сайта, напримерpackage.jsonТехнические характеристикиописан в.

"scripts": {
    "dev": "webpack-dev-server --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js --progress --hide-modules"
},

Приведенный выше сценарий представляет собой два основных этапа сборки, которые мы предоставляем для проекта:

  • dev- Пока мы модифицируем код проекта, после запуска настройки он будет использовать webpack-dev-server для горячей замены модулей (HMR), компиляции памяти и прочих мелочей.

  • build— Когда мы развертываем продукт, он выполняет все причудливые и трудоемкие вещи, такие как критический CSS, минимизация JavaScript и т. д.

Нам просто нужно сделать следующее в командной строке: Если мы используемyarn,войтиyarn devилиyarn build; если используете npm, введитеnpm run devилиnpm run build. Это единственные две команды, которые вам нужно использовать.

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

Далее нашbrowserslistКонфигурация:

"browserslist": {
        "production": [
            "> 1%",
            "last 2 versions",
            "Firefox ESR"
        ],
        "legacyBrowsers": [
            "> 1%",
            "last 2 versions",
            "Firefox ESR"
        ],
        "modernBrowsers": [
            "last 2 Chrome versions",
            "not Chrome < 60",
            "last 2 Safari versions",
            "not Safari < 10.1",
            "last 2 iOS versions",
            "not iOS < 10.3",
            "last 2 Firefox versions",
            "not Firefox < 54",
            "last 2 Edge versions",
            "not Edge < 15"
        ]
    },

Это специфика, основанная на удобочитаемой конфигурациисписок браузера,PostCSS autoprefixerПо умолчанию используется вproductionВо время настройки мы будемlegacyBrowsersа такжеmodernBrowsersПерейти кBabelТрадиционное лечение для [прошедших] JS Современный и построенный пакет [выпускает обработку транскодирования и другой совместимой формулировкой ES6], будет подробно описан подробно.

С последующимdevDependencies, которые являются всеми пакетами npm, требуемыми системой сборки:

"devDependencies": {
        "@babel/core": "^7.1.0",
        "@babel/plugin-syntax-dynamic-import": "^7.0.0",
        "@babel/plugin-transform-runtime": "^7.1.0",
        "@babel/preset-env": "^7.1.0",
        "@babel/register": "^7.0.0",
        "@babel/runtime": "^7.0.0",
        "autoprefixer": "^9.1.5",
        "babel-loader": "^8.0.2",
        "clean-webpack-plugin": "^0.1.19",
        "copy-webpack-plugin": "^4.5.2",
        "create-symlink-webpack-plugin": "^1.0.0",
        "critical": "^1.3.4",
        "critical-css-webpack-plugin": "^0.2.0",
        "css-loader": "^1.0.0",
        "cssnano": "^4.1.0",
        "dotenv": "^6.1.0",
        "file-loader": "^2.0.0",
        "git-rev-sync": "^1.12.0",
        "glob-all": "^3.1.0",
        "html-webpack-plugin": "^3.2.0",
        "ignore-loader": "^0.1.2",
        "imagemin": "^6.0.0",
        "imagemin-gifsicle": "^5.2.0",
        "imagemin-mozjpeg": "^7.0.0",
        "imagemin-optipng": "^5.2.1",
        "imagemin-svgo": "^7.0.0",
        "imagemin-webp": "^4.1.0",
        "imagemin-webp-webpack-plugin": "^1.0.2",
        "img-loader": "^3.0.1",
        "mini-css-extract-plugin": "^0.4.3",
        "moment": "^2.22.2",
        "optimize-css-assets-webpack-plugin": "^5.0.1",
        "postcss": "^7.0.2",
        "postcss-extend": "^1.0.5",
        "postcss-hexrgba": "^1.0.1",
        "postcss-import": "^12.0.0",
        "postcss-loader": "^3.0.0",
        "postcss-nested": "^4.1.0",
        "postcss-nested-ancestors": "^2.0.0",
        "postcss-simple-vars": "^5.0.1",
        "purgecss-webpack-plugin": "^1.3.0",
        "purgecss-whitelister": "^2.2.0",
        "resolve-url-loader": "^3.0.0",
        "sane": "^4.0.1",
        "save-remote-file-webpack-plugin": "^1.0.0",
        "style-loader": "^0.23.0",
        "symlink-webpack-plugin": "^0.0.4",
        "terser-webpack-plugin": "^1.1.0",
        "vue-loader": "^15.4.2",
        "vue-style-loader": "^4.1.2",
        "vue-template-compiler": "^2.5.17",
        "webapp-webpack-plugin": "https://github.com/brunocodutra/webapp-webpack-plugin.git",
        "webpack": "^4.19.1",
        "webpack-bundle-analyzer": "^3.0.2",
        "webpack-cli": "^3.1.1",
        "webpack-dashboard": "^2.0.0",
        "webpack-dev-server": "^3.1.9",
        "webpack-manifest-plugin": "^2.0.4",
        "webpack-merge": "^4.1.4",
        "webpack-notifier": "^1.6.0",
        "workbox-webpack-plugin": "^3.6.2"
    },

Да, там много пакетов npm, но они нужны нам для всего, что делает наш процесс сборки.

наконец,dependenciesиспользование:

"dependencies": {
        "@babel/polyfill": "^7.0.0",
        "axios": "^0.18.0",
        "tailwindcss": "^0.6.6",
        "vue": "^2.5.17",
        "vue-confetti": "^0.4.2"
    }
}

Очевидно, что для реального веб-сайта или приложенияdependenciesВ будущем будет больше пакетов npm, но сейчас мы сосредоточимся на процессе сборки.

ANNOTATED WEBPACK.SETTINGS.JS

Я также использовал A Bet­ter package.json for the Fron­tend arti­cleПодобный подход обсуждался в статье, чтобы заблокировать настройку между проектами для отдельныхwebpack.settings.js, а саму конфигурацию webpack оставить без изменений.

Ключевая идея заключается в том, что единственный файл, который нам нужно редактировать от проекта к проекту, — это webpack.settings.js.

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

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

// webpack.settings.js - webpack settings config

// node modules
require('dotenv').config();

// Webpack settings exports
// noinspection WebpackConfigHighlighting
module.exports = {
    name: "Example Project",
    copyright: "Example Company, Inc.",
    paths: {
        src: {
            base: "./src/",
            css: "./src/css/",
            js: "./src/js/"
        },
        dist: {
            base: "./web/dist/",
            clean: [
                "./img",
                "./criticalcss",
                "./css",
                "./js"
            ]
        },
        templates: "./templates/"
    },
    urls: {
        live: "https://example.com/",
        local: "http://example.test/",
        critical: "http://example.test/",
        publicPath: "/dist/"
    },
    vars: {
        cssName: "styles"
    },
    entries: {
        "app": "app.js"
    },
    copyWebpackConfig: [
        {
            from: "./src/js/workbox-catch-handler.js",
            to: "js/[name].[ext]"
        }
    ],
    criticalCssConfig: {
        base: "./web/dist/criticalcss/",
        suffix: "_critical.min.css",
        criticalHeight: 1200,
        criticalWidth: 1200,
        ampPrefix: "amp_",
        ampCriticalHeight: 19200,
        ampCriticalWidth: 600,
        pages: [
            {
                url: "",
                template: "index"
            }
        ]
    },
    devServerConfig: {
        public: () => process.env.DEVSERVER_PUBLIC || "http://localhost:8080",
        host: () => process.env.DEVSERVER_HOST || "localhost",
        poll: () => process.env.DEVSERVER_POLL || false,
        port: () => process.env.DEVSERVER_PORT || 8080,
        https: () => process.env.DEVSERVER_HTTPS || false,
    },
    manifestConfig: {
        basePath: ""
    },
    purgeCssConfig: {
        paths: [
            "./templates/**/*.{twig,html}",
            "./src/vue/**/*.{vue,html}"
        ],
        whitelist: [
            "./src/css/components/**/*.{css,pcss}"
        ],
        whitelistPatterns: [],
        extensions: [
            "html",
            "js",
            "twig",
            "vue"
        ]
    },
    saveRemoteFileConfig: [
        {
            url: "https://www.google-analytics.com/analytics.js",
            filepath: "js/analytics.js"
        }
    ],
    createSymlinkConfig: [
        {
            origin: "img/favicons/favicon.ico",
            symlink: "../favicon.ico"
        }
    ],
    webappConfig: {
        logo: "./src/img/favicon-src.png",
        prefix: "img/favicons/"
    },
    workboxConfig: {
        swDest: "../sw.js",
        precacheManifestFilename: "js/precache-manifest.[manifestHash].js",
        importScripts: [
            "/dist/workbox-catch-handler.js"
        ],
        exclude: [
            /\.(png|jpe?g|gif|svg|webp)$/i,
            /\.map$/,
            /^manifest.*\\.js(?:on)?$/,
        ],
        globDirectory: "./web/",
        globPatterns: [
            "offline.html",
            "offline.svg"
        ],
        offlineGoogleAnalytics: true,
        runtimeCaching: [
            {
                urlPattern: /\.(?:png|jpg|jpeg|svg|webp)$/,
                handler: "cacheFirst",
                options: {
                    cacheName: "images",
                    expiration: {
                        maxEntries: 20
                    }
                }
            }
        ]
    }
};

Мы рассмотрим все в разделе конфигурации веб-пакета, здесь важно отметить, что мы переносили изменения из проекта в проект и добавляли их из нашего файла конфигурации веб-пакета и добавляли в отдельныйwebpack.settings.jsв файле.

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

COMMON CONVENTIONS FOR WEBPACK CONFIGS

Я настраиваю файлы для всех веб-пакетов (webpack.common.js,webpack.dev.jsа такжеwebpack.prod.js) принимает некоторые соглашения, чтобы они выглядели более последовательно.

Каждый файл конфигурации имеет две встроенные конфигурации:

  • legacyConfig- Конфигурации для устаревших сборок ES5.

  • modernConfig- Конфигурации, подходящие для сборки современных версий ES2015+

Мы делаем это, потому что у нас есть отдельные конфиги для создания совместимых старых и современных сборок, что делает их логически независимыми.webpack.common.jsесть также одинbaseConfig, чтобы обеспечить чистоту организации.

Думайте об этом как об объектно-ориентированном программировании, где различные элементы конфигурации наследуются,baseConfigкак корневой объект.

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

сделать это, потому что вwebpack.settings.jsНекоторые данные необходимо преобразовать перед использованием веб-пакета, и из-за прошлых/современных сборок нам необходимо возвращать разные конфигурации в зависимости от типа сборки.

Это также делает файлы конфигурации более читабельными.

В качестве общего концепции WebPack, знайте, что сам веб-пакет знает, как загрузить JavaScript и JSON. Чтобы загрузить что-то еще, вам нужно использовать соответствующийПогрузчик, мы будем использовать много разных загрузчиков в конфиге webpack.

ANNOTATED WEBPACK.COMMON.JS

Теперь давайте посмотримwebpack.common.jsконфигурационный файл, содержащийdevа такжеprodВся конфигурация разделяется между типами сборки.

// webpack.common.js - common webpack config
const LEGACY_CONFIG = 'legacy';
const MODERN_CONFIG = 'modern';

// node modules
const path = require('path');
const merge = require('webpack-merge');

// webpack plugins
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const WebpackNotifierPlugin = require('webpack-notifier');

// config files
const pkg = require('./package.json');
const settings = require('./webpack.settings.js');

В начале мы представили необходимый пакет узла и подключаемый модуль веб-пакета, который нам нужно было использовать. Затем мы импортируемwebpack.settings.jsтак какsettings, чтобы мы могли получить доступ к настройкам там, и поставитьpackage.jsonтак какpkgимпортировать, чтобы получить к нему доступ.

CONFIGURATION FUNCTIONS

ЭтоconfigureBabelLoader()параметр:

// Configure Babel loader
const configureBabelLoader = (browserList) => {
   return {
       test: /\.js$/,
       exclude: /node_modules/,
       use: {
           loader: 'babel-loader',
           options: {
               presets: [
                   [
                       '@babel/preset-env', {
                       modules: false,
                       useBuiltIns: 'entry',
                       targets: {
                           browsers: browserList,
                       },
                   }
                   ],
               ],
               plugins: [
                   '@babel/plugin-syntax-dynamic-import',
                   [
                       "@babel/plugin-transform-runtime", {
                       "regenerator": true
                   }
                   ]
               ],
           },
       },
   };
};

функцияconfigureBabelLoader()настроитьbabel-loaderобрабатывать всеjsсуффикс загрузки файла, он использует@babel/preset-envвместо.babelrcфайл, поэтому мы можем хранить все в файле конфигурации webpack.

Babel может компилировать современный ES2015+ (и многие другие языки, такие как TypeScript или CoffeeScript) в специфичный для браузера или стандартный JavaScript. мы будемbrowserListПередано в качестве аргумента, чтобы мы могли создавать современные модули ES2015+ для старых браузеров и использовать полифиллы для совместимости со старыми версиями ES5.

В нашем html мы делаем только это:

<!-- Browsers with ES module support load this file. -->
<script type="module" src="main.js"></script>

<!-- Older browsers load this file (and module-supporting -->
<!-- browsers know *not* to load this file). -->
<script nomodule src="main-legacy.js"></script>

Не используйте полифиллы, не удивляйтесь, старый браузер игнорируетtype="module"скрипт и получитеmain-legacy.js, загружается новый браузерmain.js,халатное отношениеnomodule, отлично выглядит, рад, что придумал! Чтобы не создавать у вас впечатления, что такой подход экстремальный,vue-cliЭта стратегия была принята в версии 3.

@ babel/plugin-syntax-dynamic-importПлагины могут быть реализованы даже в веб-браузерах.Динамический импорт ECMAScriptперед выполнением динамического импорта, который позволяет намЗагружайте наши модули JavaScript асинхронно и динамически по мере необходимости..

Так о чем ты говоришь? Это означает, что мы можем сделать что-то вроде этого:

// App main
const main = async () => {
   // Async load the vue module
   const Vue = await import(/* webpackChunkName: "vue" */ 'vue');
   // Create our vue instance
   const vm = new Vue.default({
       el: "#app",
       components: {
           'confetti': () => import(/* webpackChunkName: "confetti" */ '../vue/Confetti.vue'),
       },
   });
};
// Execute async function
main().then( (value) => {
});

Есть два момента:

1. Пройти/* webpackChunkName: "vue" */Мы сообщаем, что WebPack надеется, что этот разделительный блок динамического кода будет назван.

2. Поскольку мы используем асинхронную функцию («основную»)import(), функция ожидает результата динамически загружаемого импорта JavaScript, в то время как остальная часть кода продолжает свой путь.

Мы фактически сказали веб-пакету, что хотим, чтобы наши фрагменты были разделены через код, а не через конфигурацию, через@babel/plugin-syntax-dynamic-importВстроенная магия этого блока JavaScript может загружаться асинхронно по запросу.

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

Помимо использованияawait, мы также можемimport()Выполните наш код после возврата промиса:

// Async load the vue module
import(/* webpackChunkName: "vue" */ 'vue').then(Vue => {
   // Vue has loaded, do something with it
   // Create our vue instance
   const vm = new Vue.default({
       el: "#app",
       components: {
           'confetti': () => import(/* webpackChunkName: "confetti" */ '../vue/Confetti.vue'),
       },
   });
});

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

Если вы достаточно внимательны, то увидите, что мы эффективно разрешаем зависимости JavaScript с помощью промисов, отлично!

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

увидеть больше оModule Methods import()Информация.

Если вы хотите узнать больше о Babel, посетитеWorking with Babel 7 and WebpackЭта статья.

Далее у нас естьconfigureEntries():

// Configure Entries
const configureEntries = () => {
   let entries = {};
   for (const [key, value] of Object.entries(settings.entries)) {
       entries[key] = path.resolve(__dirname, settings.paths.src.js + value);
   }

   return entries;
};

Здесь мы проходимswttings.entriesотwebpack.settings.jsполучить веб-пакетentry,дляодностраничное приложение(СПА), есть только одна запись. Для более традиционного сайта у вас может быть несколько записей (возможно, одна запись на страницу шаблона).

В любом случае, поскольку мы ужеwebpack.settings.jsточки входа определены в , поэтому их легко настроить в файле, точки входа на самом деле просто<script src =“app.js”> </ script>тег, который вы добавите в свой HTML, чтобы добавить JavaScript.

Поскольку мы используем модули динамического импорта, обычно у нас есть только один на странице.<script></script>теги; остальная часть JavaScript загружается динамически по мере необходимости.

Далее у нас естьconfigureFontLoader()функция:

// Configure Font loader
const configureFontLoader = () => {
   return {
       test: /\.(ttf|eot|woff2?)$/i,
       use: [
           {
               loader: 'file-loader',
               options: {
                   name: 'fonts/[name].[ext]'
               }
           }
       ]
   };
};

devа такжеprodСоздание загрузки шрифтов аналогично, поэтому мы пишем это здесь, и для любых используемых нами локальных шрифтов мы можем указать веб-пакету загружать их в JavaScript:

import comicsans from '../fonts/ComicSans.woff2';

Далее у нас естьconfigureManifest()функция:

// Configure Manifest
const configureManifest = (fileName) => {
   return {
       fileName: fileName,
       basePath: settings.manifestConfig.basePath,
       map: (file) => {
           file.name = file.name.replace(/(\.[a-f0-9]{32})(\..*)$/, '$2');
           return file;
       },
   };
};

Это настраивает очистку кеша на основе имени файла.webpack-manifest-pluginкороче говоря, webpack знает все необходимые нам javascript, css и другие ресурсы, поэтому он может сгенерировать список ресурсов, указывающих на хэш с именем, например:

{
 "vendors~confetti~vue.js": "/dist/js/vendors~confetti~vue.03b9213ce186db5518ea.js",
 "vendors~confetti~vue.js.map": "/dist/js/vendors~confetti~vue.03b9213ce186db5518ea.js.map",
 "app.js": "/dist/js/app.30334b5124fa6e221464.js",
 "app.js.map": "/dist/js/app.30334b5124fa6e221464.js.map",
 "confetti.js": "/dist/js/confetti.1152197f8c58a1b40b34.js",
 "confetti.js.map": "/dist/js/confetti.1152197f8c58a1b40b34.js.map",
 "js/precache-manifest.js": "/dist/js/precache-manifest.f774c437974257fc8026ca1bc693655c.js",
 "../sw.js": "/dist/../sw.js"
}

Мы передаем имя файла, так как создаем современныйmonifest.jsonи для совместимыхmanifest-legacy.json, которые соответственно имеют точки входа для современных модулей ES2015+ и совместимых устаревших модулей ES5. Ключевые моменты в обоих файлах json одинаковы для ресурсов, созданных как для современных, так и для старых версий.

Дальше у нас довольно стандартныйconfigureVueLoader()Конфигурация:

// Configure Vue loader
const configureVueLoader = () => {
   return {
       test: /\.vue$/,
       loader: 'vue-loader'
   };
};

Эта конфигурация просто позволяет нам легко анализироватьКомпонент Vue с одним файлом, webpack позаботится о том, чтобы получить для вас правильный HTML, CSS и Javascript.

BASE CONFIG

baseConfigБудуmodernConfigа такжеlegacyConfigобъединить:

// The base webpack config
const baseConfig = {
   name: pkg.name,
   entry: configureEntries(),
   output: {
       path: path.resolve(__dirname, settings.paths.dist.base),
       publicPath: settings.urls.publicPath
   },
   resolve: {
       alias: {
           'vue$': 'vue/dist/vue.esm.js'
       }
   },
   module: {
       rules: [
           configureVueLoader(),
       ],
   },
   plugins: [
       new WebpackNotifierPlugin({title: 'Webpack', excludeWarnings: true, alwaysNotify: true}),
       new VueLoaderPlugin(),
   ]
};

Вся конфигурация здесь является довольно стандартной конфигурацией веб-пакета, но обратите внимание, что мыvue$направлениеvue/dist/vue.esm.js, чтобы мы могли получить модульную версию Vue ES2015.

Мы используемWebpackNotifierPluginПлагины интуитивно сообщают нам о статусе сборки.

LEGACY CONFIG

legacyConfigКонфигурация используется для обеспечения совместимости со старыми версиями ES5 с соответствующим полифиллом:

// Legacy webpack config
const legacyConfig = {
   module: {
       rules: [
           configureBabelLoader(Object.values(pkg.browserslist.legacyBrowsers)),
       ],
   },
   plugins: [
       new CopyWebpackPlugin(
           settings.copyWebpackConfig
       ),
       new ManifestPlugin(
           configureManifest('manifest-legacy.json')
       ),
   ]
};

Обратите внимание, что мы будемpkg.browserslist.legacyBrowsersПерейти кconfigureBabelLoader(),Будуmanifest-legacy.jsonПерейти кconfigureManifest().

Мы также включили в эту конфигурациюCopyWebpackPluginплагин, нам просто нужно скопироватьsettings.copyWebpackConfigопределяется в файле один раз.

MODERN CONFIG

modernConfigДля создания современных модулей Javascript ES2015, не прибегая ни к чему другому:

// Modern webpack config
const modernConfig = {
   module: {
       rules: [
           configureBabelLoader(Object.values(pkg.browserslist.modernBrowsers)),
       ],
   },
   plugins: [
       new ManifestPlugin(
           configureManifest('manifest.json')
       ),
   ]
};

Обратите внимание, что мы будемpkg.browserslist.modernBrowsersПерейти кconfigureBabelLoader(),Будуmanifest.jsonПерейти кconfigureManifest().

MODULE.EXPORTS

наконец,module.exportsиспользоватьwebpack-mergeПлагин объединяет предыдущую конфигурацию вместе и возвращаетwebpack.dev.jsа такжеwebpack.prod.jsобъект для использования.

// Common module exports
// noinspection WebpackConfigHighlighting
module.exports = {
   'legacyConfig': merge(
       legacyConfig,
       baseConfig,
   ),
   'modernConfig': merge(
       modernConfig,
       baseConfig,
   ),
};

ANNOTATED WEBPACK.DEV.JS

Теперь давайте посмотримwebpack.dev.jsconfig, который содержит все настройки, которые мы используем для сборки проекта при его разработке, сwebpack.common.jsНастройки в файлах объединяются, чтобы сформировать полную конфигурацию веб-пакета.

// webpack.dev.js - developmental builds
const LEGACY_CONFIG = 'legacy';
const MODERN_CONFIG = 'modern';

// node modules
const merge = require('webpack-merge');
const path = require('path');
const sane = require('sane');
const webpack = require('webpack');

// webpack plugins
const Dashboard = require('webpack-dashboard');
const DashboardPlugin = require('webpack-dashboard/plugin');
const dashboard = new Dashboard();

// config files
const common = require('./webpack.common.js');
const pkg = require('./package.json');
const settings = require('./webpack.settings.js');

В предисловии мы повторно представляем пакет узла, который нам нужно использовать, и подключаемый модуль веб-пакета, который мы используем, а затем представляемwebpack.settings.jsтак какsettings, чтобы мы могли там получить доступ к настройкам и импортироватьpackage.jsonтак какpkgчтобы получить доступ к некоторым настройкам там.

Мы также импортировалиwebpack.common.jsОбщая конфигурация веб-пакета будет объединена с нашей настройкой разработки.

CONFIGURATION FUNCTIONS

ЭтоconfigureDevServer()Конфигурация:

// Configure the webpack-dev-server
const configureDevServer = (buildType) => {
   return {
       public: settings.devServerConfig.public(),
       contentBase: path.resolve(__dirname, settings.paths.templates),
       host: settings.devServerConfig.host(),
       port: settings.devServerConfig.port(),
       https: !!parseInt(settings.devServerConfig.https()),
       quiet: true,
       hot: true,
       hotOnly: true,
       overlay: true,
       stats: 'errors-only',
       watchOptions: {
           poll: !!parseInt(settings.devServerConfig.poll()),
           ignored: /node_modules/,
       },
       headers: {
           'Access-Control-Allow-Origin': '*'
       },
       // Use sane to monitor all of the templates files and sub-directories
       before: (app, server) => {
           const watcher = sane(path.join(__dirname, settings.paths.templates), {
               glob: ['**/*'],
               poll: !!parseInt(settings.devServerConfig.poll()),
           });
           watcher.on('change', function(filePath, root, stat) {
               console.log('  File modified:', filePath);
               server.sockWrite(server.sockets, "content-changed");
           });
       },
   };
};

Когда мы делаем производственную сборку, webpack связывает и сохраняет все различные ресурсы в файловой системе, в отличие от того, когда мы разрабатываем в локальном проекте, мы передаемwebpack-dev-serverИспользуйте сборку разработки:

  • Запустите локальную сеть, которая обслуживает наши ресурсыexpressвеб сервер.

  • Для скорости структурируйте наши ресурсы в памяти, а не в файловой системе.

  • Восстанавливайте ресурсы, такие как компоненты JavaScript, CSS, Vue и т. д., используяГорячее обновление модуля (HMR), когда мы изменяем эти ресурсы, нам не нужно перезагружать интерфейс.

  • Страница будет перезагружена при изменении шаблона.

Это похоже на более сложныйBrowsersyncвариант, который значительно ускоряет разработку.

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

Уведомление,webpack-dev-serverКонфиг снова ссылаетсяwebpack.settings.jsфайл, значение по умолчанию, вероятно, подходит для большинства людей, но я используюLaravel HomesteadКак локальная разработка, как у нас в статьеLocal Development with Vagrant / HomesteadКак уже говорилось, это означает, что я запускаю все свои инструменты разработки на виртуальной машине Homestead.

следовательно,webpack.settings.jsдоступны из.envПрочитанный файл имеет определенныйdevServerконфиг, не мойweboack.settings.jsФайл в локальной среде разработки жесткого кода (как это может варьироваться):

// .env file DEVSERVER settings
# webpack example settings for Homestead/Vagrant
DEVSERVER_PUBLIC="http://192.168.10.10:8080"
DEVSERVER_HOST="0.0.0.0"
DEVSERVER_POLL=1
DEVSERVER_PORT=8080
DEVSERVER_HTTPS=0

Вы можете использовать различные конфигурации, поэтому вы можете использовать.envизменить настройки в файле,dotenvИдея заключается в том, что мы находимся в.envКонфигурация для конкретной среды определяется в файле, который не проверяется в репозитории git. если.envФайл не существует, все в порядке, используйте значение по умолчанию:

// webpack.settings.js devServerConfig defaults
devServerConfig: {
    public: () => process.env.DEVSERVER_PUBLIC || "http://localhost:8080",
    host: () => process.env.DEVSERVER_HOST || "localhost",
    poll: () => process.env.DEVSERVER_POLL || false,
    port: () => process.env.DEVSERVER_PORT || 8080,
    https: () => process.env.DEVSERVER_HTTPS || false,
},

ДалееconfigureImageLoader()Конфигурация:

// webpack.dev.js configureImageLoader()
// Configure Image loader
const configureImageLoader = (buildType) => {
    if (buildType === LEGACY_CONFIG) {
        return {
            test: /\.(png|jpe?g|gif|svg|webp)$/i,
            use: [
                {
                    loader: 'file-loader',
                    options: {
                        name: 'img/[name].[hash].[ext]'
                    }
                }
            ]
        };
    }
    if (buildType === MODERN_CONFIG) {
        return {
            test: /\.(png|jpe?g|gif|svg|webp)$/i,
            use: [
                {
                    loader: 'file-loader',
                    options: {
                        name: 'img/[name].[hash].[ext]'
                    }
                }
            ]
        };
    }
};

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

Стоит отметить, что это относится только к изображениям, включенным в наши сборки веб-пакета; многие другие изображения поступают из других источников (системы CMS, системы управления активами и т. д.).

Чтобы сообщить Webpack, что есть изображение, импортируйте его в свой файл JavaScript:

import Icon from './icon.png';

Для получения более подробной информации об этом, ознакомьтесь с документацией по веб-пакету"загрузить изображение"часть.

ДалееconfigurePostcssLoader()Конфигурация:

// Configure the Postcss loader
const configurePostcssLoader = (buildType) => {
    // Don't generate CSS for the legacy config in development
    if (buildType === LEGACY_CONFIG) {
        return {
            test: /\.(pcss|css)$/,
            loader: 'ignore-loader'
        };
    }
    if (buildType === MODERN_CONFIG) {
        return {
            test: /\.(pcss|css)$/,
            use: [
                {
                    loader: 'style-loader',
                },
                {
                    loader: 'vue-style-loader',
                },
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2,
                        sourceMap: true
                    }
                },
                {
                    loader: 'resolve-url-loader'
                },
                {
                    loader: 'postcss-loader',
                    options: {
                        sourceMap: true
                    }
                }
            ]
        };
    }
};

Мы используемPostCSSдля обработки всех css, включаяTailwind CSS. Я чувствую, что PostCSS — это Вавилон CSS, программирующий различные расширенные функции CSS в простой CSS, который браузеры могут анализировать.

Для загрузчиков веб-пакетов они обрабатываются в обратном порядке:

  • postcss-loader- Загрузите и обработайте файл как PostCSS

  • resolve-url-loader-- поместите все css вurl()переписать относительный путь

  • css-loader- Разобрать весь наш CSS@importа такжеurl()

  • vue-style-loader- Вставьте все css в один файл .vue.

  • style-loader- Внедрить весь CSS в

Нам не нужно извлекать все файлы CSS до самого маленького файла в процессе локальной разработки, наоборот, мы просто позволяемstyle-loaderВставьте его в нашу документацию.

webpack-dev-serverИспользуйте замену горячего модуля (HMR) для css, каждый раз, когда мы изменяем стили, он перестраивает css и автоматически внедряет его, это волшебство (что).

Мы сообщаем веб-пакету разрешить его импорт:

import styles from '../css/app.pcss';

в документации вебпакаLoading CSSРаздел обсуждается подробно.

мы начинаем сApp.jsТочка входа делает это, думайте об этом как о точке входа для PostCSS,app.pcssдокумент@importВсе CSS используемые в нашем проекте, о которых будет подробно рассказано позже.

MODULE.EXPORTS

наконец,module.exportsиспользоватьwebpack-mergeпакет будетwebpack.common.jsсерединаcommon.legacyConfigСлияние с совместимой с устаревшей конфигурацией нашей разработки и будетcommon.modernConfigОбъединено с современной конфигурацией среды разработки:

// Development module exports
module.exports = [
    merge(
        common.legacyConfig,
        {
            output: {
                filename: path.join('./js', '[name]-legacy.[hash].js'),
                publicPath: settings.devServerConfig.public() + '/',
            },
            mode: 'development',
            devtool: 'inline-source-map',
            devServer: configureDevServer(LEGACY_CONFIG),
            module: {
                rules: [
                    configurePostcssLoader(LEGACY_CONFIG),
                    configureImageLoader(LEGACY_CONFIG),
                ],
            },
            plugins: [
                new webpack.HotModuleReplacementPlugin(),
            ],
        }
    ),
    merge(
        common.modernConfig,
        {
            output: {
                filename: path.join('./js', '[name].[hash].js'),
                publicPath: settings.devServerConfig.public() + '/',
            },
            mode: 'development',
            devtool: 'inline-source-map',
            devServer: configureDevServer(MODERN_CONFIG),
            module: {
                rules: [
                    configurePostcssLoader(MODERN_CONFIG),
                    configureImageLoader(MODERN_CONFIG),
                ],
            },
            plugins: [
                new webpack.HotModuleReplacementPlugin(),
                new DashboardPlugin(dashboard.setData),
            ],
        }
    ),
];

пройти черезmodule.exportsВозвращая массив в , мы сообщаем webpack, что необходимо выполнить несколько компиляций: одну для устаревших сборок и одну для новых сборок.

Для устаревших сборок мы называем обработанный JavaScript как[name]-legacy.[hash].js, а новая сборка называется[name].[hash].js.

установивmodeдляdevelopment, который сообщает webpack, что это сборка для разработки.

БудуdevtoolУстановить какinline-source-map, мы требуем, чтобы CSS/JavsScript был.mapВстраивается в файл, хотя построенный проект будет большим, но это удобно для разработки и отладки.

пройти черезwebpack.HotModuleReplacementPluginПлагины, которые могут поддерживать горячую замену модулей Webpack (HMR).

DashboardPluginПлагин заставляет нас почувствовать себя космонавтом с крутой панелью:

я нашелDashboardPluginРазработка плагинаHUDБолее интуитивно понятный, чем стандартный дисплей прогресса веб-пакета.

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

ANNOTATED WEBPACK.PROD.JS

Теперь давайте посмотримwebpack.prod.jsФайл конфигурации, который содержит всю конфигурацию, которую мы используем для производственных сборок, пока мы работаем над проектом. это сwebpack.common.jsНастройки в объединены, чтобы сформировать полную конфигурацию веб-пакета.

// webpack.prod.js - production builds
const LEGACY_CONFIG = 'legacy';
const MODERN_CONFIG = 'modern';

// node modules
const git = require('git-rev-sync');
const glob = require('glob-all');
const merge = require('webpack-merge');
const moment = require('moment');
const path = require('path');
const webpack = require('webpack');

// webpack plugins
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CreateSymlinkPlugin = require('create-symlink-webpack-plugin');
const CriticalCssPlugin = require('critical-css-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ImageminWebpWebpackPlugin = require('imagemin-webp-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const PurgecssPlugin = require('purgecss-webpack-plugin');
const SaveRemoteFilePlugin = require('save-remote-file-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const WebappWebpackPlugin = require('webapp-webpack-plugin');
const WhitelisterPlugin = require('purgecss-whitelister');
const WorkboxPlugin = require('workbox-webpack-plugin');

// config files
const common = require('./webpack.common.js');
const pkg = require('./package.json');
const settings = require('./webpack.settings.js');

Мы повторно представили пакет node, упомянутый в преамбуле, и подключаемый модуль webpack, который мы использовали, а затемwebpack.settings.jsтак какsettingsимпорт иpackage.jsonтак какpkgИмпорт для быстрого доступа к необходимой конфигурации.

Мы также импортировалиwebpack.common.jsПубличный конфиг webpack мы объединим с настройками разработки.

TAILWIND EXTRACTOR

КлассTailwind CSSобычайPurgeCSSЭкстрактор, позволяющий использовать специальные символы в именах классов.

// Custom PurgeCSS extractor for Tailwind that allows special characters in
// class names.
//
// https://github.com/FullHuman/purgecss#extractor
class TailwindExtractor {
    static extract(content) {
        return content.match(/[A-Za-z0-9-_:\/]+/g) || [];
    }
}

Это взято из документации Tailwind CSSRemoving unused CSS with PurgeCSSэта часть. Подробнее о том, как этот экстрактор работает с purgecss, см. ниже, чтобы сделать ваш CSS намного чище.

CONFIGURATION FUNCTIONS

ЭтоconfigureBanner()функция:

// Configure file banner
const configureBanner = () => {
    return {
        banner: [
            '/*!',
            ' * @project        ' + settings.name,
            ' * @name           ' + '[filebase]',
            ' * @author         ' + pkg.author.name,
            ' * @build          ' + moment().format('llll') + ' ET',
            ' * @release        ' + git.long() + ' [' + git.branch() + ']',
            ' * @copyright      Copyright (c) ' + moment().format('YYYY') + ' ' + settings.copyright,
            ' *',
            ' */',
            ''
        ].join('\n'),
        raw: true
    };
};

Это просто добавляет баннер с названием проекта, именем файла, автором и информацией git для каждого файла, который мы генерируем.

с последующимconfigureBundleAnalyzer():

// webpack.prod.js configureBundleAnalyzer()
// Configure Bundle Analyzer
const configureBundleAnalyzer = (buildType) => {
    if (buildType === LEGACY_CONFIG) {
        return {
            analyzerMode: 'static',
            reportFilename: 'report-legacy.html',
        };
    }
    if (buildType === MODERN_CONFIG) {
        return {
            analyzerMode: 'static',
            reportFilename: 'report-modern.html',
        };
    }
};

использоватьWebpackBundleAnalyzerПлагин создает отчет для наших новых и старых сборок, а также отдельную интерактивную HTML-страницу, чтобы точно увидеть, что входит в комплект с webpack.

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

с последующимconfigureCriticalCss():

// webpack.prod.js configureCriticalCss()
// Configure Critical CSS
const configureCriticalCss = () => {
    return (settings.criticalCssConfig.pages.map((row) => {
            const criticalSrc = settings.urls.critical + row.url;
            const criticalDest = settings.criticalCssConfig.base + row.template + settings.criticalCssConfig.suffix;
            let criticalWidth = settings.criticalCssConfig.criticalWidth;
            let criticalHeight = settings.criticalCssConfig.criticalHeight;
            // Handle Google AMP templates
            if (row.template.indexOf(settings.criticalCssConfig.ampPrefix) !== -1) {
                criticalWidth = settings.criticalCssConfig.ampCriticalWidth;
                criticalHeight = settings.criticalCssConfig.ampCriticalHeight;
            }
            console.log("source: " + criticalSrc + " dest: " + criticalDest);
            return new CriticalCssPlugin({
                base: './',
                src: criticalSrc,
                dest: criticalDest,
                extract: false,
                inline: false,
                minify: true,
                width: criticalWidth,
                height: criticalHeight,
            })
        })
    );
};

использоватьCriticalCssPluginплагин черезwebpack.settings.jsсерединаsettings.criticalCssConfig.pagesЧанк для создания CriticalCSS для нашего сайта.

Следует отметить, что если имя входящей страницы содержитsettings.criticalCssConfig.ampPrefix, он сгенерирует CriticalCSS для всей страницы (а не только для свернутого содержимого выше), передав очень большую высоту.

CriticalCSS здесь подробно рассматриваться не будет, для получения дополнительной информации см.Implementing Critical CSS on your websiteЭта статья.

с последующимconfigureCleanWebpack():

// Configure Clean webpack
const configureCleanWebpack = () => {
    return {
        root: path.resolve(__dirname, settings.paths.dist.base),
        verbose: true,
        dry: false
    };
};

Это просто использованиеCleanWebpackPluginот нашегоwebpack.settings.jsудалятьsettings.paths.dist.baseКаталог сборки.

с последующимconfigureHtml():

// Configure Html webpack
const configureHtml = () => {
    return {
        templateContent: '',
        filename: 'webapp.html',
        inject: false,
    };
};

Это будет использоватьHtmlWebpackPluginа такжеWebappWebpackPlugin(См. ниже) Плагин генерирует HTML для наших фавиконов. Обратите внимание, что мыtemplateContentПередайте пустую строку, чтобы выходные данные были просто необработанными выходными данными WebappWebpackPlugin.

с последующимconfigureImageLoader():

// Configure Image loader
const configureImageLoader = (buildType) => {
    if (buildType === LEGACY_CONFIG) {
        return {
            test: /\.(png|jpe?g|gif|svg|webp)$/i,
            use: [
                {
                    loader: 'file-loader',
                    options: {
                        name: 'img/[name].[hash].[ext]'
                    }
                }
            ]
        };
    }
    if (buildType === MODERN_CONFIG) {
        return {
            test: /\.(png|jpe?g|gif|svg|webp)$/i,
            use: [
                {
                    loader: 'file-loader',
                    options: {
                        name: 'img/[name].[hash].[ext]'
                    }
                },
                {
                    loader: 'img-loader',
                    options: {
                        plugins: [
                            require('imagemin-gifsicle')({
                                interlaced: true,
                            }),
                            require('imagemin-mozjpeg')({
                                progressive: true,
                                arithmetic: false,
                            }),
                            require('imagemin-optipng')({
                                optimizationLevel: 5,
                            }),
                            require('imagemin-svgo')({
                                plugins: [
                                    {convertPathData: false},
                                ]
                            }),
                        ]
                    }
                }
            ]
        };
    }
};

мы проходим вbuildTypeпараметры, чтобы мы могли возвращать разные результаты в зависимости от того, является ли это новой или старой сборкой. Мы обрабатываем изображения через оптимизацию, черезimg-loaderДелайте новую сборку.

Делаем это только для новых сборок, потому что нет смысла тратить время на обработку изображений, оптимизированных для новой и старой версии (изображения одинаковы для обеих).

Обратите внимание, что это относится только к изображениям, включенным в нашу сборку веб-пакета, многие другие ресурсы изображений на самом деле взяты из других источников (система cms, система управления активами и т. д.).

Чтобы веб-пакет оптимизировал изображение, импортируйте его в JavaScript:

import Icon from './icon.png';

БолееLoading ImagesПодробности смотрите в соответствующем разделе документации webpack.

Тогда нашconfigureOptimization()Конфигурация:

// Configure optimization
const configureOptimization = (buildType) => {
    if (buildType === LEGACY_CONFIG) {
        return {
            splitChunks: {
                cacheGroups: {
                    default: false,
                    common: false,
                    styles: {
                        name: settings.vars.cssName,
                        test: /\.(pcss|css|vue)$/,
                        chunks: 'all',
                        enforce: true
                    }
                }
            },
            minimizer: [
                new TerserPlugin(
                    configureTerser()
                ),
                new OptimizeCSSAssetsPlugin({
                    cssProcessorOptions: {
                        map: {
                            inline: false,
                            annotation: true,
                        },
                        safe: true,
                        discardComments: true
                    },
                })
            ]
        };
    }
    if (buildType === MODERN_CONFIG) {
        return {
            minimizer: [
                new TerserPlugin(
                    configureTerser()
                ),
            ]
        };
    }
};

Этооптимизация производственной среды webpack, для старых сборок (делать это дважды не имеет смысла) мы используемMiniCssExtractPluginПлагин извлекает CSS, используемые в проекте в один файл CSS. Если вы использовали WebPack раньше, вы должны были использовать ExtractTextPlugin, чтобы сделать это раньше, но вам не нужно делать это сейчас.

Мы также использовалиOptimizeCSSAssetsPluginПлагин оптимизирует сгенерированный css, удаляя повторяющиеся правила иcssnanoСжать css.

Наконец, мы будем Javascriptminimizerустановлен вTerserPlugin, это из-за [UglifyJsPlugin] (GitHub.com/Webpack-con…) Больше не поддерживает минимизацию ES2015 + JavaScript. Поскольку мы генерируем новые пучки ES2015 +, нам это нужно.

с последующимconfigurePostcssLoader():

// Configure Postcss loader
const configurePostcssLoader = (buildType) => {
    if (buildType === LEGACY_CONFIG) {
        return {
            test: /\.(pcss|css)$/,
            use: [
                MiniCssExtractPlugin.loader,
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2,
                        sourceMap: true
                    }
                },
                {
                    loader: 'resolve-url-loader'
                },
                {
                    loader: 'postcss-loader',
                    options: {
                        sourceMap: true
                    }
                }
            ]
        };
    }
    // Don't generate CSS for the modern config in production
    if (buildType === MODERN_CONFIG) {
        return {
            test: /\.(pcss|css)$/,
            loader: 'ignore-loader'
        };
    }
};

Эта конфигурация очень похожа на версию разработкиconfigurePostcssLoader(), кроме финального загрузчика используемMiniCssExtractPlugin.loaderИзвлеките все css в один файл.

Мы делаем это только для устаревших совместимых сборок, потому что нет смысла делать это для каждой сборки (CSS одинаковый). Мы используемignore-loaderДелаем новую сборку, поэтому есть загрузчик для наших файлов .css и .pcss, но это ничего не делает.

Как упоминалось ранее, мы используемPostCSSобрабатывает все css, включаяTailwind CSS, я думаю, что это Babel для CSS, потому что он компилирует различные расширенные функции CSS в простой CSS, который может анализировать ваш браузер.

Аналогично, для загрузчиков веб-пакетов они обрабатываются в обратном порядке:

Поскольку это производственная сборка, мы используемMiniCssExtractPlugin.loaderИзвлеките все используемые css и сохраните в.cssв файле. CSS также сведен к минимуму и оптимизирован для производственных сред.

Мы сообщаем webpack, импортируя файл css:

import styles from '../css/app.pcss';

Это в документации веб-пакетаLoading CSSЕсть подробное введение.

Мы делаем это из точки входа App.js, думайте об этом как о точке входа для postCSS,app.pcssдокумент@importВесь CSS, используемый нашим проектом, будет подробно описан позже.

с последующимconfigurePurgeCss():

// Configure PurgeCSS
const configurePurgeCss = () => {
    let paths = [];
    // Configure whitelist paths
    for (const [key, value] of Object.entries(settings.purgeCssConfig.paths)) {
        paths.push(path.join(__dirname, value));
    }

    return {
        paths: glob.sync(paths),
        whitelist: WhitelisterPlugin(settings.purgeCssConfig.whitelist),
        whitelistPatterns: settings.purgeCssConfig.whitelistPatterns,
        extractors: [
            {
                extractor: TailwindExtractor,
                extensions: settings.purgeCssConfig.extensions
            }
        ]
    };
};

Tailwind CSS— это отличная CSS-инфраструктура, ориентированная на полезность, которая позволяет быстро создавать прототипы, поскольку при локальной разработке редко возникает необходимость в написании какого-либо CSS. Вместо этого вам просто нужно использовать предоставленные служебные классы CSS.

Недостатком является то, что сгенерированный CSS может быть немного большим, и вам нужно использовать его в данный момент.PurgeCSS, он проанализирует все файлы HTML/template/Vue/любые файлы и удалит неиспользуемый CSS.

Экономия места может быть огромной, а Tailwind CSS и PurgeCSS идеально подходят друг другу. мы в Tailwind CSS utility-first CSS with Adam WathanЭтот вопрос подробно обсуждается в блоге.

он пересекаетsettings.purgeCssConfig.pathsвсе пути вglobs, чтобы найти сохраняемые правила CSS, любые ненайденные правила CSS удаляются из созданной сборки CSS.

мы также использовалиWhitelisterPlugin,当我们知道不希望某些CSS 被剥离时,可以轻松地将整个文件或全局列入白名单。 И нашsettings.purgeCssConfig.whitelistПравила CSS во всех соответствующих файлах заносятся в белый список и никогда не удаляются из результирующей сборки.

ДалееconfigureTerser():

// Configure terser
const configureTerser = () => {
    return {
        cache: true,
        parallel: true,
        sourceMap: true
    };
};

Это просто настраивает [TerserPlugin] (GitHub.com/Webpack-con…) использует некоторые настройки, которые минимизируют наш устаревший и новый код JavaScript.

с последующимconfigureWebApp():

// Configure Webapp webpack
const configureWebapp = () => {
    return {
        logo: settings.webappConfig.logo,
        prefix: settings.webappConfig.prefix,
        cache: false,
        inject: 'force',
        favicons: {
            appName: pkg.name,
            appDescription: pkg.description,
            developerName: pkg.author.name,
            developerURL: pkg.author.url,
            path: settings.paths.dist.base,
        }
    };
};

использовать здесьwebappwebpackepulinГенерирует все фавиконы нашего веб-сайта, а также наши веб-приложения в бесчисленных форматах.manifest.jsonи другие детали PWA.

это сHtmlWebpackPluginПри совместном использовании вы также можете вывестиwebapp.htmlФайл, он содержит ссылки на все сгенерированные фавиконы и связанные файлы, которые будут включены в нашу HTML-страницу.<head></head>середина.

с последующимconfigureWorkbox():

// Configure Workbox service worker
const configureWorkbox = () => {
    let config = settings.workboxConfig;

    return config;
};

Мы используемWorkboxWebpackPluginсоздать один для веб-сайтаService Worker,объяснятьService WorkerЧто выходит за рамки данной статьи, но можно посмотретьGoing Offline: Service Workers with Jeremy KeithБлог как стартовый.

Все данные конфигурации берутся изwebpack.settings.jsсерединаsettings.workboxConfigобъект. В дополнение к предварительному кэшированию новых сборокminifest.jsonВ дополнение ко всем ресурсам, мы также включаемworkbox-catch-handler.jsнастроить его так, чтобыРеагируйте на все маршруты с запасными вариантами.

// fallback URLs
const FALLBACK_HTML_URL = '/offline.html';
const FALLBACK_IMAGE_URL = '/offline.svg';

// This "catch" handler is triggered when any of the other routes fail to
// generate a response.
// https://developers.google.com/web/tools/workbox/guides/advanced-recipes#provide_a_fallback_response_to_a_route
workbox.routing.setCatchHandler(({event, request, url}) => {
    // Use event, request, and url to figure out how to respond.
    // One approach would be to use request.destination, see
    // https://medium.com/dev-channel/service-worker-caching-strategies-based-on-request-types-57411dd7652c
    switch (request.destination) {
        case 'document':
            return caches.match(FALLBACK_HTML_URL);
            break;

        case 'image':
            return caches.match(FALLBACK_IMAGE_URL);
            break;

        default:
            // If we don't have a fallback, just return an error response.
            return Response.error();
    }
});

// Use a stale-while-revalidate strategy for all other requests.
workbox.routing.setDefaultHandler(
    workbox.strategies.staleWhileRevalidate()
);

MODULE.EXPORTS

наконец,module.exportиспользоватьwebpack-mergeБудуwebpack.commons.jsсерединаcommon.legacyConfigОбъедините с устаревшей конфигурацией нашей производственной среды и поместитеcommon.modernConfigДля слияния с нашей новой производственной конфигурацией:

// Production module exports
module.exports = [
    merge(
        common.legacyConfig,
        {
            output: {
                filename: path.join('./js', '[name]-legacy.[chunkhash].js'),
            },
            mode: 'production',
            devtool: 'source-map',
            optimization: configureOptimization(LEGACY_CONFIG),
            module: {
                rules: [
                    configurePostcssLoader(LEGACY_CONFIG),
                    configureImageLoader(LEGACY_CONFIG),
                ],
            },
            plugins: [
                new CleanWebpackPlugin(settings.paths.dist.clean,
                    configureCleanWebpack()
                ),
                new MiniCssExtractPlugin({
                    path: path.resolve(__dirname, settings.paths.dist.base),
                    filename: path.join('./css', '[name].[chunkhash].css'),
                }),
                new PurgecssPlugin(
                    configurePurgeCss()
                ),
                new webpack.BannerPlugin(
                    configureBanner()
                ),
                new HtmlWebpackPlugin(
                    configureHtml()
                ),
                new WebappWebpackPlugin(
                    configureWebapp()
                ),
                new CreateSymlinkPlugin(
                    settings.createSymlinkConfig,
                    true
                ),
                new SaveRemoteFilePlugin(
                    settings.saveRemoteFileConfig
                ),
                new BundleAnalyzerPlugin(
                    configureBundleAnalyzer(LEGACY_CONFIG),
                ),
            ].concat(
                configureCriticalCss()
            )
        }
    ),
    merge(
        common.modernConfig,
        {
            output: {
                filename: path.join('./js', '[name].[chunkhash].js'),
            },
            mode: 'production',
            devtool: 'source-map',
            optimization: configureOptimization(MODERN_CONFIG),
            module: {
                rules: [
                    configurePostcssLoader(MODERN_CONFIG),
                    configureImageLoader(MODERN_CONFIG),
                ],
            },
            plugins: [
                new webpack.optimize.ModuleConcatenationPlugin(),
                new webpack.BannerPlugin(
                    configureBanner()
                ),
                new ImageminWebpWebpackPlugin(),
                new WorkboxPlugin.GenerateSW(
                    configureWorkbox()
                ),
                new BundleAnalyzerPlugin(
                    configureBundleAnalyzer(MODERN_CONFIG),
                ),
            ]
        }
    ),
];

через нашmodule.exportsВозвращая массив в , мы сообщаем webpack, что необходимо выполнить несколько компиляций: одну для устаревших сборок и одну для более новых сборок.

Обратите внимание, что для устаревших совместимых сборок мы выводим обработанный JavaScript как[name]-legacy.[hash].js, в то время как новая сборка выводит его как[name].[hash].js.

поставивmodeУстановить какproduction, мы говорим веб-пакету, что этоСборка производственной среды, который включает множество удобных для производства настроек.

поставивdevtoolУстановить какsource-map, мы требуем, чтобы CSS/JavaScript былсоздать отдельный.mapдокумент, что упрощает отладку рабочих сайтов без добавления ресурсов к размеру файла.

Вот несколько плагинов для веб-пакетов, которые мы еще не рассмотрели:

  • CreateSymlinkPlugin- Это плагин, который я создал, который позволяет создавать символические ссылки в процессе сборки, использовать его для преобразования сгенерированногоfavicon.icoсимволическая ссылка на/favicon.ico, потому что многие веб-браузеры смотрят в корень веб-сайта.

  • SaveRemoteFilePlugin- Используется для загрузки удаленных файлов и их вывода как часть процесса сборки веб-пакета. Я использую его для загрузки и обслуживания Google Analytics.

  • ImageminWebpWebpackPlugin- Этот плагин создаст все файлы JPEG и PNG, импортированные проектом..webpВарианты.

До сих пор у нас была хорошая производственная сборка для проекта.

TAILWIND CSS & POSTCSS CONFIG

Для того, чтобы webpack правильно собрал Tailwind CSS и другие CSS, нам нужно выполнить некоторую настройку, спасибо моему приятелю Джонатану Мелвиллу за его работу по созданию этого, сначала нам нуженpostcss.config.jsдокумент:

module.exports = {
    plugins: [
        require('postcss-import'),
        require('postcss-extend'),
        require('postcss-simple-vars'),
        require('postcss-nested-ancestors'),
        require('postcss-nested'),
        require('postcss-hexrgba'),
        require('autoprefixer'),
        require('tailwindcss')('./tailwind.config.js')
    ]
};

Это можно сохранить в корне проекта, и PostCSS будет автоматически искать его в процессе сборки и применять указанный нами плагин PostCSS. Обратите внимание, что это наше введениеtailwind.config.jsРасположение файла, чтобы он стал частью процесса сборки.

Наконец, наша точка входа в CSSapp.pcssЭто выглядит так:

/**
 * app.css
 *
 * The entry point for the css.
 *
 */

/**
 * This injects Tailwind's base styles, which is a combination of
 * Normalize.css and some additional base styles.
 *
 * You can see the styles here:
 * https://github.com/tailwindcss/tailwindcss/blob/master/css/preflight.css
 */
 @import "tailwindcss/preflight";

/**
 * This injects any component classes registered by plugins.
 *
 */
@import 'tailwindcss/components';

/**
 * Here we add custom component classes; stuff we want loaded
 * *before* the utilities so that the utilities can still
 * override them.
 *
 */
@import './components/global.pcss';
@import './components/typography.pcss';
@import './components/webfonts.pcss';

/**
 * This injects all of Tailwind's utility classes, generated based on your
 * config file.
 *
 */
@import 'tailwindcss/utilities';

/**
 * Include styles for individual pages
 *
 */
@import './pages/homepage.pcss';

/**
 * Include vendor css.
 *
 */
 @import 'vendor.pcss';

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

POST-BUILD PROJECT TREE

Это структура нашего проекта после сборки:

├── example.env
├── package.json
├── postcss.config.js
├── src
│   ├── css
│   │   ├── app.pcss
│   │   ├── components
│   │   │   ├── global.pcss
│   │   │   ├── typography.pcss
│   │   │   └── webfonts.pcss
│   │   ├── pages
│   │   │   └── homepage.pcss
│   │   └── vendor.pcss
│   ├── fonts
│   ├── img
│   │   └── favicon-src.png
│   ├── js
│   │   ├── app.js
│   │   └── workbox-catch-handler.js
│   └── vue
│       └── Confetti.vue
├── tailwind.config.js
├── templates
├── web
│   ├── dist
│   │   ├── criticalcss
│   │   │   └── index_critical.min.css
│   │   ├── css
│   │   │   ├── styles.d833997e3e3f91af64e7.css
│   │   │   └── styles.d833997e3e3f91af64e7.css.map
│   │   ├── img
│   │   │   └── favicons
│   │   │       ├── android-chrome-144x144.png
│   │   │       ├── android-chrome-192x192.png
│   │   │       ├── android-chrome-256x256.png
│   │   │       ├── android-chrome-36x36.png
│   │   │       ├── android-chrome-384x384.png
│   │   │       ├── android-chrome-48x48.png
│   │   │       ├── android-chrome-512x512.png
│   │   │       ├── android-chrome-72x72.png
│   │   │       ├── android-chrome-96x96.png
│   │   │       ├── apple-touch-icon-114x114.png
│   │   │       ├── apple-touch-icon-120x120.png
│   │   │       ├── apple-touch-icon-144x144.png
│   │   │       ├── apple-touch-icon-152x152.png
│   │   │       ├── apple-touch-icon-167x167.png
│   │   │       ├── apple-touch-icon-180x180.png
│   │   │       ├── apple-touch-icon-57x57.png
│   │   │       ├── apple-touch-icon-60x60.png
│   │   │       ├── apple-touch-icon-72x72.png
│   │   │       ├── apple-touch-icon-76x76.png
│   │   │       ├── apple-touch-icon.png
│   │   │       ├── apple-touch-icon-precomposed.png
│   │   │       ├── apple-touch-startup-image-1182x2208.png
│   │   │       ├── apple-touch-startup-image-1242x2148.png
│   │   │       ├── apple-touch-startup-image-1496x2048.png
│   │   │       ├── apple-touch-startup-image-1536x2008.png
│   │   │       ├── apple-touch-startup-image-320x460.png
│   │   │       ├── apple-touch-startup-image-640x1096.png
│   │   │       ├── apple-touch-startup-image-640x920.png
│   │   │       ├── apple-touch-startup-image-748x1024.png
│   │   │       ├── apple-touch-startup-image-750x1294.png
│   │   │       ├── apple-touch-startup-image-768x1004.png
│   │   │       ├── browserconfig.xml
│   │   │       ├── coast-228x228.png
│   │   │       ├── favicon-16x16.png
│   │   │       ├── favicon-32x32.png
│   │   │       ├── favicon.ico
│   │   │       ├── firefox_app_128x128.png
│   │   │       ├── firefox_app_512x512.png
│   │   │       ├── firefox_app_60x60.png
│   │   │       ├── manifest.json
│   │   │       ├── manifest.webapp
│   │   │       ├── mstile-144x144.png
│   │   │       ├── mstile-150x150.png
│   │   │       ├── mstile-310x150.png
│   │   │       ├── mstile-310x310.png
│   │   │       ├── mstile-70x70.png
│   │   │       ├── yandex-browser-50x50.png
│   │   │       └── yandex-browser-manifest.json
│   │   ├── js
│   │   │   ├── analytics.45eff9ff7d6c7c1e3c3d4184fdbbed90.js
│   │   │   ├── app.30334b5124fa6e221464.js
│   │   │   ├── app.30334b5124fa6e221464.js.map
│   │   │   ├── app-legacy.560ef247e6649c0c24d0.js
│   │   │   ├── app-legacy.560ef247e6649c0c24d0.js.map
│   │   │   ├── confetti.1152197f8c58a1b40b34.js
│   │   │   ├── confetti.1152197f8c58a1b40b34.js.map
│   │   │   ├── confetti-legacy.8e9093b414ea8aed46e5.js
│   │   │   ├── confetti-legacy.8e9093b414ea8aed46e5.js.map
│   │   │   ├── precache-manifest.f774c437974257fc8026ca1bc693655c.js
│   │   │   ├── styles-legacy.d833997e3e3f91af64e7.js
│   │   │   ├── styles-legacy.d833997e3e3f91af64e7.js.map
│   │   │   ├── vendors~confetti~vue.03b9213ce186db5518ea.js
│   │   │   ├── vendors~confetti~vue.03b9213ce186db5518ea.js.map
│   │   │   ├── vendors~confetti~vue-legacy.e31223849ab7fea17bb8.js
│   │   │   ├── vendors~confetti~vue-legacy.e31223849ab7fea17bb8.js.map
│   │   │   └── workbox-catch-handler.js
│   │   ├── manifest.json
│   │   ├── manifest-legacy.json
│   │   ├── report-legacy.html
│   │   ├── report-modern.html
│   │   ├── webapp.html
│   │   └── workbox-catch-handler.js
│   ├── favicon.ico -> dist/img/favicons/favicon.ico
│   ├── index.php
│   ├── offline.html
│   ├── offline.svg
│   └── sw.js
├── webpack.common.js
├── webpack.dev.js
├── webpack.prod.js
├── webpack.settings.js
└── yarn.lock

INJECTING SCRIPT & CSS TAGS IN YOUR HTML

С показанной здесь конфигурацией веб-пакета<script>а также<style>не внедряется в HTML как часть производственной сборки, этот параметр используетCraft CMS, который имеет систему шаблонов, мы используемTwigpackПлагины внедряют теги.

Если вы не используете Craft CMS или систему с механизмом шаблонов и хотите внедрить эти теги в HTML, вам нужно использоватьHtmlWebpackPluginДля этого этот конфиг уже включен, вам просто нужно добавить конфиг, чтобы он вставил тег в HTML.

CRAFT CMS 3 INTEGRATION WITH THE TWIGPACK PLUGIN

если вы не используетеCraft CMS 3, вы можете пропустить этот раздел, он просто предоставляет некоторую полезную информацию об интеграции.

я написалTwigpackБесплатный плагин для простой интеграции нашей настройки сборки веб-пакета с Craft CMS 3.

он обрабатываетmanifest.jsonфайл для внедрения точек входа в шаблоны Twig и даже для обработки шаблонов, выполняющих внедрение устаревших/новых модулей, асинхронную загрузку css и многое другое.

Это сделает конфигурацию webpack4, описанную здесь, очень простой.

Чтобы включить CSS, я делаю это:

<!--# if expr="$HTTP_COOKIE=/critical\-css\=1/" -->
    {{ craft.twigpack.includeCssModule("styles.css", false) }}
<!--# else -->
    <script>
        Cookie.set("critical-css", '1', { expires: "7D", secure: true });
    </script>
    {{ craft.twigpack.includeCriticalCssTags() }}

    {{ craft.twigpack.includeCssModule("styles.css", true) }}
    {{ craft.twigpack.includeCssRelPreloadPolyfill() }}
<!--# endif -->

<!--#-->HTML-комментарииNginx Servier Side Includesинструкция, режим если установленcritical-csscookie, пользователь посещал наш веб-сайт за последние 7 дней, тогда в его браузере должен быть кэш css веб-сайта, мы просто обслуживаем css веб-сайта в обычном режиме.

Если не установленоcritical-cssкуки, которые мы используемTinyCookieУстановите файлы cookie, включите наш критический CSS и асинхронно загрузите CSS сайта. Для получения дополнительной информации о Critical CSS вы можете обратиться кImplementing Critical CSS on your websiteстатья.

Чтобы обслуживать наш javascript, мы просто делаем следующее:

{{ craft.twigpack.includeSafariNomoduleFix() }}
{{ craft.twigpack.includeJsModule("app.js", true) }}

второй параметрtrueСкажите ему загрузить модуль JavaScript асинхронно, чтобы результирующий HTML выглядел так:

<script>
!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()},!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();
</script>
<script type="module" src="http://example.test/dist/js/app.273e88e73566fecf20de.js"></script>
<script nomodule src="http://example.test/dist/js/app-legacy.95d36ead9190c0571578.js"></script>

Подробное введение см.Twigpackдокументация.

Это полный я используюconfig/twigpack.phpОбратите внимание, что у него есть мои локальные настройки, работающие внутри виртуальной машины Homestead, которые могут отличаться от ваших:

return [
    // Global settings
    '*' => [
        // If `devMode` is on, use webpack-dev-server to all for HMR (hot module reloading)
        'useDevServer' => false,
        // The JavaScript entry from the manifest.json to inject on Twig error pages
        'errorEntry' => '',
        // Manifest file names
        'manifest' => [
            'legacy' => 'manifest-legacy.json',
            'modern' => 'manifest.json',
        ],
        // Public server config
        'server' => [
            'manifestPath' => '/dist/',
            'publicPath' => '/',
        ],
        // webpack-dev-server config
        'devServer' => [
            'manifestPath' => 'http://localhost:8080/',
            'publicPath' => 'http://localhost:8080/',
        ],
        // Local files config
        'localFiles' => [
            'basePath' => '@webroot/',
            'criticalPrefix' => 'dist/criticalcss/',
            'criticalSuffix' => '_critical.min.css',
        ],
    ],
    // Live (production) environment
    'live' => [
    ],
    // Staging (pre-production) environment
    'staging' => [
    ],
    // Local (development) environment
    'local' => [
        // If `devMode` is on, use webpack-dev-server to all for HMR (hot module reloading)
        'useDevServer' => true,
        // The JavaScript entry from the manifest.json to inject on Twig error pages
        'errorEntry' => 'app.js',
        // webpack-dev-server config
        'devServer' => [
            'manifestPath' => 'http://localhost:8080/',
            'publicPath' => 'http://192.168.10.10:8080/',
        ],
    ],
];

WRAPPING UP!

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

Полный исходный код этой статьи см.annotated-webpack-4-configсклад.

Я надеюсь, что эта статья будет полезна для вас, медленно переварите ее и улучшите.

FURTHER READING

Если вы хотите получать уведомления о новых статьях, подписывайтесь на Twitter@nystudio107.