Когда я недавно работал над проектом, я столкнулся со сценарием: проект имеет несколько записей, а разные записи, маршрутизация, компоненты, ресурсы и т. д. имеют перекрывающиеся части, а также имеют разные части. Поскольку некоторые страницы маршрутизации в разных записях повторяются, я рассматриваю возможность использования конфигурации с несколькими записями Webpack для решения этого требования.
Еще раз, многие статьи, которые я нашел в Интернете, не соответствовали моим потребностям.Многие статьи лишь кратко представили конфигурацию в производственной среде, но не представили конфигурацию в среде разработки, а некоторые не объединяли несколько записей.vue-router,vuex,ElementUIМне нужно дождаться настройки, поэтому я продолжу исследовать яму, а затем запишу идеи и процесс настройки, оставлю себе на заметку, а также поделюсь со всеми, надеясь помочь студентам, у которых такие же потребности ~
1. Целевой анализ
- В проекте сохраняется несколько HTML-шаблонов, разные шаблоны имеют разные записи и собственные маршрутизаторы, хранилища и т. д.;
- Можно не только упаковывать различные HTML, но и легко отлаживать их во время разработки;
- Файлы разных записей могут ссылаться на один и тот же компонент, изображение и другие ресурсы или могут ссылаться на разные ресурсы;
Репозиторий кода:multi-entry-vue
Схема выглядит следующим образом:
2. Подготовка
Во-первых, мыvue init webpack multi-entry-vueиспользоватьvue-cliСоздайте элемент шаблона веб-пакета. Структура файла следующая:
.
├── build
├── config
├── src
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ ├── router
│ │ └── index.js
│ ├── App.vue
│ └── main.js
├── static
├── README.md
├── index.html
├── package-lock.json
└── package.json
Кстати, вот способы генерации деревьев каталогов под разные системы:
- Как создать дерево каталогов из командной строки системы Mac
tree -I node_modules --dirsfirst, эта команда означает, не отображатьnode_modulesпуть к файлу и создать дерево каталогов в порядке папок. Если сообщается об ошибке, что команда дерева не найдена, установите командную строку дереваbrew install treeВот и все.- Система Windows используется в целевом каталоге
tree /f 1.txtВы можете сгенерировать текущее дерево каталогов в новый файл1.txtсередина.
Прежде всего, давайте кратко представим соответствующие элементы конфигурации Webpack.Эти элементы конфигурации обычно хранятся в используемом шаблоне Webpack.webpack.config.jsилиwebpack.base.conf.jsсередина:
const path = require('path')
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'output-file.js',
publicPath: '/'
},
module: {}, // 文件的解析 loader 配置
plugins: [], // 插件,根据需要配置各种插件
devServer: {} // 配置 dev 服务功能
}
Эта конфигурация означает, что после выполнения Webpack он будет создан в каталоге выполнения команды.distкаталог (при необходимости) и упакуетsrcв каталогеmain.jsи его зависимости, генерироватьoutput-file.jsпомещатьdistв каталоге.
Ниже приводится небольшое объяснение соответствующих элементов конфигурации:
-
Вход:Элементы конфигурации файла ввода, которые могут быть строками, объектами или массивами.
Взяв приведенную выше форму объекта в качестве примера,
appимя записи, еслиoutput.filenameимеют[name], он будет заменен наapp. -
контекст:это базовый каталог при компиляции веб-пакета, используемый для синтаксического анализа
entryбазовый каталог опций (абсолютный путь),entryНачальная точка входа будет искаться относительно этого каталога, который эквивалентен общедоступному каталогу, и все последующие каталоги находятся в этом общедоступном каталоге. - выход:Элементы конфигурации для файлов экспорта.
-
выход/путь:Каталог, в который выводится упакованный файл, например, указанный выше.
distЗатем поместите выходной файл в текущий каталог.distПод папкой, если такой папки нет, создайте новую. можно настроить какpath.resolve(__dirname, './dist/${Date.now()}/')(Синтаксис md неудобен для преобразования в строку шаблона, пожалуйста, измените его самостоятельно) Он удобен для непрерывной интеграции. -
имя выходного файла:имя выходного файла,
[name]Это означает, что по имени входного файла он упакован в одноимённый, входов несколько, и несколько файлов могут быть упакованы. например входkeyдляapp, Упакованоapp.js, вход естьmy-entry, упаковать егоmy-entry.js. -
выход.publicPath:Публичный путь к статическим ресурсам можно запомнить по такой формуле:
静态资源最终访问路径 = output.publicPath + 资源loader或插件等配置路径. Например,publicPathнастроен как/dist/, изображениеurl-loaderЭлемент конфигурацииname: 'img/[name].[ext]', то путь к изображению в окончательном упакованном файле будетoutput.publicPath + 'img/[name].[ext]' = '/dist/img/[name].[ext]'.
Поскольку эта статья посвящена настройке входа и выхода, содержание в основном вращается вокругentry,outputи важный плагин для веб-пакетовhtml-webpack-plugin, этот подключаемый модуль тесно связан с упакованным файлом HTML и имеет следующие функции:
- Создание файлов HTML на основе шаблонов;
- Внедрение внешних ресурсов, таких как сгенерированные HTML-файлы.
link,scriptЖдать; - Измените хэш каждого импортированного внешнего файла, чтобы HTML не ссылался на устаревшие ресурсы в кеше;
Давайте шаг за шагом настроим проект с несколькими входами с нуля.
3. Начать настройку
3.1 Изменения структуры файла
существуетsrcкаталог будетmain.jsа такжеApp.vueСкопируйте два файла как разные записи, файловая структура станет следующей:
.
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── logo.png
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js # 主要配置目标
│ └── webpack.prod.conf.js # 主要配置目标
├── config
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── src
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ ├── router
│ │ └── index.js
│ ├── App.vue
│ ├── App2.vue # 新增的入口
│ ├── main.js
│ └── main2.js # 新增的入口
├── static
├── README.md
├── index.html
└── package.json
3.2 Простая конфигурация
Чтобы упаковать разные HTML из разных входов, мы можем изменить егоentryа такжеoutputдве конфигурации,
// build/webpack.prod.conf.js
module.exports = {
entry: {
entry1: './src/main.js',
entry2: './src/main2.js'
},
output: {
filename: '[name].js',
publicPath: '/'
},
plugins: [
new HtmlWebpackPlugin({
template: "index.html", // 要打包输出哪个文件,可以使用相对路径
filename: "index.html" // 打包输出后该html文件的名称
})
]
}
Согласно приведенному выше разделу мы знаем, что в конфигурации веб-пакетаoutput.filenameЕсли есть[name]Это означает, что по имени файла записи он упакован в JS-файл с соответствующим именем, поэтому теперь мы можем упаковать его по двум записям.entry.jsа такжеentry2.js.
Результат упаковки таков:
Текущий код:Github - multi-entry-vue1
Как показано выше, в это время мыnpm run buildУпакуйте ссылку на эти два файлаindex.html, то как упаковать разные файлы HTML и применить разные входные файлы JS соответственно, в это время нам нужно использовать помощьHtmlWebpackPluginэтот плагин.
HtmlWebpackPluginэтот плагин,newВо-первых, просто упакуйте HTML-страницу, так что мы вpluginsконфигурацияnewВо-вторых, вы можете упаковать две страницы.
3.3 Упакуйте разные HTML-страницы
Мы изменили файл конфигурации на следующий:
// build/webpack.prod.conf.js
module.exports = {
entry: {
entry: './src/main.js', // 打包输出的chunk名为entry
entry2: './src/main2.js' // 打包输出的chunk名为entry2
},
output: {
filename: '[name].js',
publicPath: '/'
},
plugins: [
new HtmlWebpackPlugin({
filename: 'entry.html', // 要打包输出的文件名
template: 'index.html', // 打包输出后该html文件的名称
chunks: ['manifest', 'vendor', 'entry'] // 输出的html文件引入的入口chunk
// 还有一些其他配置比如minify、chunksSortMode和本文无关就省略,详见github
}),
new HtmlWebpackPlugin({
filename: 'entry2.html',
template: 'index.html',
chunks: ['manifest', 'vendor', 'entry2']
})
]
}
Вышеупомянутая конфигурация должна обратить внимание наchunks, если нет конфигурации, то сгенерированный HTML будет импортировать все входные JS-файлы. В приведенном выше примере будут импортированы оба сгенерированных HTML-файлаentry.jsа такжеentry2.js, поэтому использоватьchunksНастройте, чтобы указать, какой файл JS должен быть включен в сгенерированный файл HTML. настроенchunksПосле этого можно ввести только соответствующий HTML для достижения различных HTML.chunksНазначение файла JS.
Вы можете видеть, что в дополнение к нашему упакованному сгенерированномуchunkдокументentry.jsа такжеentry2.jsКроме того, естьmanifestа такжеvendorЭти два, вот небольшое объяснение этих двухchunk:
-
vendorозначает, что экстракция включаетnode_modulesпубличные модули в; -
manifestправдаvendorКэш, сделанный модулем;
Упакованный результат выглядит следующим образом:
Структура файла:
Теперь упакованный стиль - это именно то, что нам нужно, в это время мы находимся вdistНачать в каталогеlive-server(Если вы не установили его, вы можете сначала установить егоnpm i -g live-server), вы можете увидеть эффект:
Текущий код:Github - multi-entry-vue2
На данный момент реализована простая конфигурация проекта с несколькими входами.
4. Улучшения конфигурации
4.1 Изменения структуры файла
В предыдущей статье мы настроили несколько записей. Чтобы создать новую запись, скопируйте несколько файлов и вручную измените соответствующую конфигурацию.
Но если разные файлы HTML под разнымиvue-router,vuexположить все этоsrcВ каталоге содержимое нескольких записей сгруппировано вместе, и каталог проекта станет грязным и неясным. Поэтому поместите файлы, относящиеся к нескольким записям, в отдельную папку. Если в будущем будет несколько записей, перейдите к этому обработанному в папке.
Давайте изменим файловую структуру ниже:
- Сначала мы создаем
entriesпапку, поместите разные записиrouter,store,main.jsВсе помещаются сюда, и каждая запись относится к отдельной папке; - существует
srcсоздать каталогcommonПапки, используемые для хранения компонентов, совместно используемых несколькими записями и т. д.;
Текущая структура каталогов:
.
├── build # 没有改动
├── config # 没有改动
├── entries # 存放不同入口的文件
│ ├── entry1
│ │ ├── router # entry1 的 router
│ │ │ └── index.js
│ │ ├── store # entry1 的 store
│ │ │ └── index.js
│ │ ├── App.vue # entry1 的根组件
│ │ ├── index.html # entry1 的页面模版
│ │ └── main.js # entry1 的入口
│ └── entry2
│ ├── router
│ │ └── index.js
│ ├── store
│ │ └── index.js
│ ├── App.vue
│ ├── index.html
│ └── main.js
├── src
│ ├── assets
│ │ └── logo.png
│ ├── common # 多入口通用组件
│ │ └── CommonTemplate.vue
│ └── components
│ ├── HelloWorld.vue
│ ├── test1.vue
│ └── test2.vue
├── static
├── README.md
├── index.html
├── package-lock.json
└── package.json
4.2 конфигурация веб-пакета
Тогда мыbuild/utilsДобавьте в файл две функции, которые используются для генерации веб-пакетов.entryнастроить иHtmlWebpackPluginКонфигурация плагина, так как вы хотите использоватьnode.jsчтобы прочитать структуру папок, поэтому вам нужно импортироватьfs,globи другие модули:
// build/utils
const fs = require('fs')
const glob = require('glob')
const merge = require('webpack-merge')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ENTRY_PATH = path.resolve(__dirname, '../entries')
// 多入口配置,这个函数从 entries 文件夹中读取入口文件,装配成webpack.entry配置
exports.entries = function() {
const entryFiles = glob.sync(ENTRY_PATH + '/*/*.js')
const map = {}
entryFiles.forEach(filePath => {
const filename = filePath.replace(/.*\/(\w+)\/\w+(\.html|\.js)$/, (rs, $1) => $1)
map[filename] = filePath
})
return map
}
// 多页面输出模版配置 HtmlWebpackPlugin,根据环境装配html模版配置
exports.htmlPlugin = function() {
let entryHtml = glob.sync(ENTRY_PATH + '/*/*.html')
let arr = []
entryHtml.forEach(filePath => {
let filename = filePath.replace(/.*\/(\w+)\/\w+(\.html|\.js)$/, (rs, $1) => $1)
let conf = {
template: filePath,
filename: filename + '.html',
chunks: [filename],
inject: true
}
// production 生产模式下配置
if (process.env.NODE_ENV === 'production') {
conf = merge(conf, {
chunks: ['manifest', 'vendor'],
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
chunksSortMode: 'dependency'
})
}
arr.push(new HtmlWebpackPlugin(conf))
})
return arr
}
Чтобы немного объяснить эти две функции:
-
exports.entriesфункция отentriesВ папке найдите файл JS во вторичном каталоге в качестве файла записи и используйте имя папки вторичного каталога какkey, который создает такой объект:{"entry1": "/multi-entry-vue/entries/entry1/main.js"}, в случае нескольких записей будет больше пар ключ-значение; -
exports.htmlPluginФункция аналогична предыдущей функции, но сборкаHtmlWebpackPluginКонфигурация плагина генерирует такой массив.Вы видите, что он в основном такой же, как и конфигурация, которую мы задали вручную, за исключением того, что теперь он генерируется в соответствии со структурой папок:// production 下 [ { template: "/multi-entry-vue/entries/entry1/index.html", chunks: ['manifest', 'vendor', 'entry1'], filename: "entry1.html", chunksSortMode: 'dependency' }, { ... } // 下一个入口的配置 ]
С этими двумя базамиentriesСтруктура папки для автоматического создания функции конфигурации веб-пакета, давайте изменим несколько файлов конфигурации, связанных с веб-пакетом:
// build/webpack.base.conf.js
module.exports = {
entry: utils.entries(), // 使用函数生成 entry 配置
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
}
}
// build/webpack.dev.conf.js
// const HtmlWebpackPlugin = require('html-webpack-plugin') // 不需要了
const devWebpackConfig = merge(baseWebpackConfig, {
devServer: {
historyApiFallback: {
rewrites: [ // 别忘了把 devserver 的默认路由改一下
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'entry1.html') },
],
}
},
plugins: [
// https://github.com/ampedandwired/html-webpack-plugin
// new HtmlWebpackPlugin({
// filename: 'index.html',
// template: 'index.html',
// inject: true
// }), // 注释掉原来的 HtmlWebpackPlugin 配置,使用生成的配置
].concat(utils.htmlPlugin())
})
// build/webpack.prod.conf.js
// const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpackConfig = merge(baseWebpackConfig, {
plugins: [
// new HtmlWebpackPlugin({
// ... 注释掉,不需要了
// }),
].concat(utils.htmlPlugin())
})
Теперь мыnpm run build, чтобы увидеть, как выглядит результирующий каталог:
В это время мыdistНачать в каталогеlive-serverПосмотрите, какой эффект:
Текущий код:Github - multi-entry-vue3
Большинство сообщений в Интернете имеют разную глубину и даже некоторые несоответствия. Следующие статьи являются кратким изложением процесса обучения. Если вы найдете какие-либо ошибки, пожалуйста, оставьте сообщение, чтобы указать ~
Ссылаться на:
PS: Всех приглашаю обратить внимание на мой публичный аккаунт [Front End Afternoon Tea], давайте работать вместе~
Кроме того, вы можете присоединиться к группе WeChat «Front-end Afternoon Tea Exchange Group», нажмите и удерживайте, чтобы определить QR-код ниже, чтобы добавить меня в друзья, обратите вниманиеДобавить группу, я заберу тебя в группу~