❤️ Если вам хорошо, ставьте лайк ❤️.Оригинальная ссылка
Поскольку веб-разработка становится все более сложной, нам нужны инструменты, которые помогут нам создавать современные веб-сайты. Это рабочий пример со сложной конфигурацией 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 похож на «черный ящик», и его ответ был кратким, но блестящим:
Он черный только в том случае, если вы его не открыли [Если вы не открыли этот так называемый «черный ящик», он никогда не будет известен. ]
Он прав, пора открывать «черный ящик».
Эта статья не расскажет вам все о вебпаке или даже о том, как его установить. Ниже вы найдете множество материалов на выбор. Вы можете выбрать способ, который считаете хорошим:
-
webpack — запутанные детали- Краткий обзор того, как работает webpack.
-
webpack documentation- Рекомендуется прочитать официальную документацию WebPack, если вы хотите узнать это хорошо
-
webpack fundamentals- обучающее видео по веб-пакету
Таких материалов еще много, вместо этого в этой статье будет настроен сложный полноценный рабочий пример с 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 Better package.json for the Frontend articleПодобный подход обсуждался в статье, чтобы заблокировать настройку между проектами для отдельных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.js
config, который содержит все настройки, которые мы используем для сборки проекта при его разработке, с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, который может анализировать ваш браузер.
Аналогично, для загрузчиков веб-пакетов они обрабатываются в обратном порядке:
-
postcss-loader- Загрузите и обработайте файл как PostCSS
-
resolve-url-loader- переписать все url() в css на относительный путь
-
css-loader- Разобрать все наши CSS @import и url()
-
MiniCssExtractPlugin.loader- Извлечь все 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-css
cookie, пользователь посещал наш веб-сайт за последние 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.