Непосредственная практика конфигурации Webpack с несколькими входами

Vue.js Webpack
Непосредственная практика конфигурации Webpack с несколькими входами

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

Еще раз, многие статьи, которые я нашел в Интернете, не соответствовали моим потребностям.Многие статьи лишь кратко представили конфигурацию в производственной среде, но не представили конфигурацию в среде разработки, а некоторые не объединяли несколько записей.vue-router,vuex,ElementUIМне нужно дождаться настройки, поэтому я продолжу исследовать яму, а затем запишу идеи и процесс настройки, оставлю себе на заметку, а также поделюсь со всеми, надеясь помочь студентам, у которых такие же потребности ~

1. Целевой анализ

  1. В проекте сохраняется несколько HTML-шаблонов, разные шаблоны имеют разные записи и собственные маршрутизаторы, хранилища и т. д.;
  2. Можно не только упаковывать различные HTML, но и легко отлаживать их во время разработки;
  3. Файлы разных записей могут ссылаться на один и тот же компонент, изображение и другие ресурсы или могут ссылаться на разные ресурсы;

Репозиторий кода: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

Кстати, вот способы генерации деревьев каталогов под разные системы:

  1. Как создать дерево каталогов из командной строки системы Mactree -I node_modules --dirsfirst, эта команда означает, не отображатьnode_modulesпуть к файлу и создать дерево каталогов в порядке папок. Если сообщается об ошибке, что команда дерева не найдена, установите командную строку дереваbrew install treeВот и все.
  2. Система 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в каталоге.

Ниже приводится небольшое объяснение соответствующих элементов конфигурации:

  1. Вход:Элементы конфигурации файла ввода, которые могут быть строками, объектами или массивами. Взяв приведенную выше форму объекта в качестве примера,appимя записи, еслиoutput.filenameимеют[name], он будет заменен наapp.
  2. контекст:это базовый каталог при компиляции веб-пакета, используемый для синтаксического анализаentryбазовый каталог опций (абсолютный путь),entryНачальная точка входа будет искаться относительно этого каталога, который эквивалентен общедоступному каталогу, и все последующие каталоги находятся в этом общедоступном каталоге.
  3. выход:Элементы конфигурации для файлов экспорта.
  4. выход/путь:Каталог, в который выводится упакованный файл, например, указанный выше.distЗатем поместите выходной файл в текущий каталог.distПод папкой, если такой папки нет, создайте новую. можно настроить какpath.resolve(__dirname, './dist/${Date.now()}/')(Синтаксис md неудобен для преобразования в строку шаблона, пожалуйста, измените его самостоятельно) Он удобен для непрерывной интеграции.
  5. имя выходного файла:имя выходного файла,[name]Это означает, что по имени входного файла он упакован в одноимённый, входов несколько, и несколько файлов могут быть упакованы. например входkeyдляapp, Упакованоapp.js, вход естьmy-entry, упаковать егоmy-entry.js.
  6. выход.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 и имеет следующие функции:

  1. Создание файлов HTML на основе шаблонов;
  2. Внедрение внешних ресурсов, таких как сгенерированные HTML-файлы.link,scriptЖдать;
  3. Измените хэш каждого импортированного внешнего файла, чтобы 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:

  1. vendorозначает, что экстракция включаетnode_modulesпубличные модули в;
  2. manifestправдаvendorКэш, сделанный модулем;

Упакованный результат выглядит следующим образом:

Структура файла:

Теперь упакованный стиль - это именно то, что нам нужно, в это время мы находимся вdistНачать в каталогеlive-server(Если вы не установили его, вы можете сначала установить егоnpm i -g live-server), вы можете увидеть эффект:

Текущий код:Github - multi-entry-vue2

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

4. Улучшения конфигурации

4.1 Изменения структуры файла

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

Но если разные файлы HTML под разнымиvue-router,vuexположить все этоsrcВ каталоге содержимое нескольких записей сгруппировано вместе, и каталог проекта станет грязным и неясным. Поэтому поместите файлы, относящиеся к нескольким записям, в отдельную папку. Если в будущем будет несколько записей, перейдите к этому обработанному в папке.

Давайте изменим файловую структуру ниже:

  1. Сначала мы создаемentriesпапку, поместите разные записиrouter,store,main.jsВсе помещаются сюда, и каждая запись относится к отдельной папке;
  2. существует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
}

Чтобы немного объяснить эти две функции:

  1. exports.entriesфункция отentriesВ папке найдите файл JS во вторичном каталоге в качестве файла записи и используйте имя папки вторичного каталога какkey, который создает такой объект:{"entry1": "/multi-entry-vue/entries/entry1/main.js"}, в случае нескольких записей будет больше пар ключ-значение;

  2. 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


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

Ссылаться на:

  1. Головоломки Webpack: стратегия упаковки файлов с несколькими входами
  2. файл конфигурации webpack: вход и выход, многовходовая, многовыходная конфигурация
  3. Расширенная настройка и оптимизация веб-пакета с первого взгляда

PS: Всех приглашаю обратить внимание на мой публичный аккаунт [Front End Afternoon Tea], давайте работать вместе~

Кроме того, вы можете присоединиться к группе WeChat «Front-end Afternoon Tea Exchange Group», нажмите и удерживайте, чтобы определить QR-код ниже, чтобы добавить меня в друзья, обратите вниманиеДобавить группу, я заберу тебя в группу~