предисловие
Недавно был запущен новый многостраничный проект, и webpack4 ранее не использовался, так что я готов приступить к работе. Эта статья в основном посвящена некоторым введениям в настройку.Для студентов, которые готовятся к использованию webpack4, вы можете сделать справку.
По сравнению с предыдущими 2 и 3, webpack4 сильно изменился. Суть в том, что многие настройки уже встроены, поэтому webpack работает «из коробки». Конечно, это стандартное приложение не может удовлетворить все ситуации, но можно использовать многие предыдущие конфигурации. Например, раньше для сжатия запутанного кода нужно было увеличитьuglify
Плагины, объем хостинга необходимо добавитьModuleConcatenationPlugin
. В webpack4 вам нужно только установитьmode
дляproduction
Вот и все. Конечно, если эти плагины добавлены принудительно, об ошибке не будет сообщено.
Поэтому я предлагаю, если вы хотите перейти на webpack4, вам следует начать с 0 и выполнить добавление, обратиться к истории и создать новую конфигурацию. Вместо того, чтобы резать и резать из исторической конфигурации, а затем обновляться до webpack4. Таким образом, конфигурация webpack4 будет выглядеть более упорядоченной.
Оптимизация упаковки
Оптимизация упаковки в основном заключается в разумной упаковке всех зависимостей загрузки страниц при создании многостраничного приложения. В отрасли существует множество практик, включая webpack4, и представлено множество статей. Добавлю еще несколько мелких деталей, которые не так просто заметить. Некоторые моменты подробно описывать не буду.Студенты не знакомые с настройкой webpack могут не понять.Можно поискать по соответствующим ключевым словам.В интернете должны быть очень подробные статьи.
Прежде всего, при сборке многостраничных приложений часто извлекаются следующие пакеты фрагментов:
-
common
: поместить пакеты зависимостей, на которые ссылаются несколько страниц, в общий фрагмент. Большинство учебных пособий в Интернете дважды вводятся в общий доступ. Я предлагаю вам настроить его в соответствии с количеством ваших собственных страниц.В моем проекте я установил общий пакет для импорта только тогда, когда количество импортов превышает 1/3 от количества страниц. -
dll
: извлеките зависимые пакеты, на которые ссылается каждая страница и которые в основном не изменятся, такие как react/react-dom и т. д., чтобы изменения в других модулях не загрязняли хэш-кэш библиотеки dll. -
manifest
: код времени выполнения веб-пакета. Всякий раз, когда изменяется пакет зависимостей, исполняемый код веб-пакета также будет меняться.Если эта часть не извлечена, вероятность изменения хеш-значения общего пакета будет увеличена. - соответствующий файлу входа страницы
page.js
Затем мы внедрим contentHash в имя пакета чанка, чтобы добиться максимального эффекта кэширования. В процессе разделения фрагментов одна из наиболее важных идей состоит в том, чтобы свести к минимуму изменение хеш-значения фрагмента для каждого выпуска итерации. В отрасли также существует множество практик, таких как эта статья:GitHub.com/Ass Miserable/блог…
Но в webpack4 нам не нужно добавлять так много плагинов, одна конфигурация оптимизации может сделать все это.
Сначала я вставлю конфигурацию оптимизации моего веб-пакета, а затем расскажу о ней, чтобы углубить ваше впечатление.
const commonOptions = {
chunks: 'all',
reuseExistingChunk: true
}
export default {
namedChunks: true,
moduleIds: 'hashed',
runtimeChunk: {
name: 'manifest'
},
splitChunks: {
maxInitialRequests: 5,
cacheGroups: {
polyfill: {
test: /[\\/]node_modules[\\/](core-js|raf|@babel|babel)[\\/]/,
name: 'polyfill',
priority: 2,
...commonOptions
},
dll: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'dll',
priority: 1,
...commonOptions
},
commons: {
name: 'commons',
minChunks: Math.ceil(pages.length / 3), // 至少被1/3页面的引入才打入common包
...commonOptions
}
}
}
}
runtimeChunk
Перед webpack4 для извлечения манифеста вам нужно использовать CommonsChunkPlugin для настройки фрагмента с указанным атрибутом имени как «манифест». В webpack4 нет необходимости вручную вводить плагины, достаточно настроить runtimeChunk.
splitChunks
Данная конфигурация позволяет извлекать нужный пакет по определенным правилам.Мы можем извлечь несколько пакетов, например verdor + common, поэтому в splitChunks предусмотрено поле cacheGroups.Каждый дополнительный ключ в cacheGroups эквивалентен еще одному правилу извлечения пакета.
Во многих учебниках в Интернете dll часто специально настраивается с помощью веб-пакета, используйте DllPlugin для создания библиотеки dll, а затем используйте DllReferencePlugin в веб-пакете вашего собственного проекта для сопоставления библиотеки dll. Хотя скорость сборки будет намного выше, но это действительно раздражает...
Я очень раздражающий человек, я бы предпочел использовать splitChunks в webpack4, настроить правила, а затем извлечь соответствующий пакет dll. Конечно, каждый может выбрать план в соответствии с реальной ситуацией.
В дополнение к dll и общим чанкам я также добавил полифилл. Это связано с тем, что некоторые новые библиотеки, которые мы используем или используют некоторый синтаксис ES6+ (например, async/await), требуют прокладок времени выполнения. Например, я использую в своем проекте react16 и мне нужно добавитьMap
/Set
/requestAnimationFrame
(реагировать JS.org/docs/Java удаляет…). Затем я должен добавить полифилл перед загрузкой библиотеки dll, поэтому я помещаю все пакеты, представленные core-js и babel, в полифилл, чтобы гарантировать, что впоследствии загруженные фрагменты могут быть выполнены.priority
Поле используется для настройки приоритета импорта чанков, общий проект должен быть polyfill > dll > common > page.
Элементы конфигурации в splitChunksmaxInitialRequests
Указывает максимальное количество начальных фрагментов запроса в записи (исключая загружаемые по запросу, то есть фрагменты, введенные сценарием в dom). Значение по умолчанию — 3. Теперь у меня есть три в cacheGroups, и поскольку runtimeChunk настроен, манифест будет отображаться, поэтому всего имеется 4 пакета фрагментов, что превышает значение по умолчанию, равное 3, поэтому значение необходимо перенастроить.
moduleIds
Учащиеся, которые немного разбираются в механизме работы webpack, знают, что webpack назначит moduleId модулю, загруженному в проект проекта, и сопоставит соответствующий модуль. Проблема, вызванная этим, заключается в том, что после добавления или удаления модулей в проекте или изменения порядка, moduleId изменится, что может повлиять на хеш-значение содержимого всех фрагментов. Просто потому, что moduleId изменяется, кеш становится недействительным, а это определенно не то, что нам нужно.
До webpack4 черезHashedModuleIdsPlugin
Для плагинов мы можем сопоставить путь модуля с хеш-значением вместо moduleId, потому что путь к модулю в основном не меняется, поэтому хеш-значение в основном не меняется.
А вот в webpack4 просто нужноoptimization
устанавливается в элементе конфигурацииmoduleIds
дляhashed
Вот и все.
namedChunks
В дополнение к moduleId мы знаем, что у выделенного фрагмента также есть свой chunkId. Аналогично, у chunkId также есть проблема с аннулированием кеша из-за изменения chunkId. из-заmanifest
И набранный пакет чанка имеетchunkId
связанные данные, поэтому, как только такие операции, как «добавление и удаление страниц», вызывают изменение chunkId, это может повлиять на недействительность кэша фрагментов.
Перед webpack4, добавивNamedChunksPlugin
, используйте chunkName для замены chunkId, реализуйте возможность закрепить chunkId и поддерживать кеш. В webpack4 просто добавьтеoptimization
устанавливается в элементе конфигурацииnamedChunks
дляtrue
Вот и все.
css связанные
Перед webpack4 используйтеextract-text-webpack-plugin
Плагин отделяет пакет css от пакета js и упаковывает его отдельно. В веб-пакете вам нужно заменить его наMiniCssExtractPlugin
. А в производственной среде или когда требуется HMR (горячая замена модуля), используйтеMiniCssExtractPlugin.loader
заменятьstyle-loader
.
Обратите внимание, что здесь есть яма. Из-за среды разработки мы настроим горячее обновление, горячее обновление css в настоящее времяMiniCssExtractPlugin.loader
Его еще нужно поддерживать самому, поэтому его нужно увеличивать.css-hot-loader
.Запомнить,css-hot-loader
Его нельзя использовать в производственной среде. В противном случае значения contentHash всех пакетов фрагментов js в каждом процессе сборки будут несогласованными, что приведет к аннулированию всех кэшей js.Поскольку эта конфигурация добавляется в производственную среду, ошибок не будет, и страница может быть построена нормально, поэтому ее легко игнорировать.
Упрощенный входной файл для многостраничных приложений
использоватьreact
/vue
Другие студенты фреймворка знают, что нам вообще нужен входindex.js
, так:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './app'
ReactDOM.render(<App />, document.getElementById('root'))
Если вам все еще нужно использоватьdva
, или добавление функции макета на все страницы реагирования, это может выглядеть так:
import React from 'react'
import dva from 'dva'
import Model from './model'
import Layout from '~@/layout'
import App from './app'
const app = dva()
app.router(() => (
<Layout>
<App />
</Layout>
))
app.model(Model)
app.start(document.getElementById('root'))
Если каждая страница такая, это немного неудобно, потому что программисты больше всего боятся писать повторяющиеся вещи. Но он должен быть, нет возможности выделить его в отдельный файл. Поскольку это входной файл и многостраничный проект, каждая страница должна иметь свой собственный входной файл, даже если они выглядят совершенно одинаково. Итак, наш каталог ресурсов будет выглядеть так:
- src
- layout.js
- pages
- pageA
- index.js
- app.js
- model.js
- pageB
- index.js
- app.js
- model.js
Поскольку все индексы одинаковы, файл входа для моей идеальной страницы долженapp.js
Именно так:
- src
- layout.js
- pages
- pageA
- app.js
- model.js
- pageB
- app.js
- model.js
Как фронтенд-разработчик,Node
Для нас это должен быть инструмент, который умело используется, а не просто множество инструментов, которые были упакованы другими.
В этой задаче мы могли бы передать webpack перед сборкойNode
файловая система (File System
), соответствующие каждой из наших страниц, создайте несколько временных файлов записей с помощью одного и того же шаблона файлов записей:
- src
- .entires
- pageA.js
- pageB.js
- layout.js
- pages
Затем используйте эти временные файлы в качестве входной конфигурации webpack. код показывает, как показано ниже:
const path = require('path')
const fs = require('fs')
const glob = require('glob')
const rimraf = require('rimraf')
const entriesDir = path.resolve(process.cwd(), './src/.entries')
const srcDir = path.resolve(process.cwd(), './src')
// 返回webpack entry配置
module.exports = function() {
if (fs.existsSync(entriesDir)) {
rimraf.sync(entriesDir)
}
fs.mkdirSync(entriesDir)
return buildEntries(srcDir)
}
function buildEntries(srcDir) {
return getPages(srcDir).reduce((acc, current) => {
acc[current.pageName] = buildEntry(current)
return acc
}, {})
}
// 获取页面数据,只考虑一级目录
function getPages(srcDir) {
const pagesDir = `${srcDir}/pages`
const pages = glob.sync(`${pagesDir}/**/app.js`)
return pages.map(pagePath => {
return {
pageName: path.relative(pagesDir, p).replace('/app.js', ''), // 取出page文件夹名
pagePath: pagePath
}
})
}
// 构建临时入口文件
function buildEntry({ pageName, pagePath }) {
const fileContent = buildFileContent(pagePath)
const entryPath = `${entriesDir}/${pageName}.js`
fs.writeFileSync(entryPath, fileContent)
return entryPath
}
// 替换模板中的 App 模块地址,返回临时入口文件内容
function buildFileContent(pagePath) {
return `
import React from 'react'
import dva from 'dva'
import Model from './model'
import Layout from '~@/layout'
import App from 'PAGE_APP_PATH'
const app = dva()
app.router(() => (
<Layout>
<App />
</Layout>
))
app.model(Model)
app.start(document.getElementById('root'))
`.replace(PAGE_APP_PATH, pagePath)
}
Таким образом, мы просто удалили повторяющиеся файлы ввода и добавили функцию макета. Это всего лишь простой код.Реальный проект может иметь несколько уровней каталогов, несколько моделей и т. д., и вам нужно настроить его самостоятельно.
webpack4
Прошло много времени с момента выхода, и написание статьи немного запаздывает, поэтому я не стал подробно писать о многих местах, которые, я думаю, должны быть понятны каждому. Если у вас есть вопросы, пишите в комментарии~