Understand the Webpack multi-entry configuration in one article

Understand the Webpack multi-entry configuration in one article

Once again, the Internet to find many articles do not fit my needs, many articles are simply describes the configuration, there is no production environment configuration introduces the development environment, and some do not combine multiple entry vue-router, vuex, ElementUIand other configurable Therefore, I will continue to explore the pit, and then record the ideas and configuration process, leave it as a note for myself, and share it with everyone, hoping to help students with the same needs~

1. Target analysis

  1. There are multiple HTML templates saved in a project, and different templates have different entrances, and each has its own router, store, etc.;
  2. Not only can you package out different HTML, but you can also debug smoothly during development;
  3. Documents of different entries can refer to the same components, pictures and other resources, or they can refer to different resources;

Code repository: multi-entry-vue

The schematic diagram is as follows:

2. Preparation

First we vue init webpack multi-entry-vueuse vue-clito create a template webpack items. The file structure is as follows:

.
├── 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

By the way, here is how to generate a directory tree under different systems:

  1. mac method of generating a system command line tree tree-I node_modules--dirsfirst, this means that the command does not display node_modulesthe file path to the folder and is sorted first generation tree. If the report does not find fault tree command, the command-line installation tree brew install treecan be.
  2. windows system used in the target directory tree/f1.txtto the current directory tree to generate a new file 1.txtin.

1. we briefly explain the relevant configuration items Webpack of these configuration items, depending on Webpack templates used in the general store webpack.config.jsor webpack.base.conf.jsin:

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: {},//File parsing loader configuration
  plugins: [],//Plug-ins, configure various plug-ins as needed
  devServer: {}//Configure dev service function
}

This configuration means that, after Webpack, will create a new directory in the execution command of distthe directory (if necessary), and the package srcunder the directory main.jsand its dependencies, generate output-file.jsin distthe directory.

Here is a little explanation of the relevant configuration items:

  1. entry: Entry file configuration item, which can be a string, object, or array. The above objects in the form of, for example, appa name entry, if output.filenamethere are [name], it would be replaced app.
  2. context: is the base directory webpack compile time, used to resolve entrythe underlying directory (absolute path) option, entrythe entrance will be relative to this directory to find the starting point, the equivalent of a public directory, all of the following directories are in the public directory.
  3. output: configuration items of the export file.
  4. output/path: Package catalog file output, such as the above dist, it will output the file in the current directory at the same level directory distfolder, this folder does not create a new one. It can be configured as path.resolve(__dirname,'./dist/${Date.now()}/')(md syntax is inconvenient to change to a template string, please modify it yourself) to facilitate continuous integration.
  5. output.filename: The name of [name]the output file, which means that it is packaged into the same name according to the name of the entry file. If there are several entries, several files can be packaged. For example, the entrance keyis app, it is packaged app.js, entrance is my-entry, it is packaged my-entry.js.
  6. output.publicPath: common path static resources, you can remember this formula: 静态资源最终访问路径=output.publicPath+资源loader或插件等配置路径. For example, publicPathconfigure /dist/, pictures of url-loaderconfiguration items is name:'img/[name].[ext]', then the final package out of the picture file reference path output.publicPath+'img/[name].[ext]'='/dist/img/[name].[ext]'.

Because this article is related to the inlet and outlet configurations, so mainly around entry, outputand an important webpack plug-html-webpack-plugin, this plugin is closely associated with the package out of the HTML file, the main are the following functions:

  1. Generate HTML files according to the template;
  2. Introduce external resources such as link, scriptetc. to the generated HTML file ;
  3. Change the Hash of each imported external file to prevent HTML from referencing outdated resources in the cache;

Let's configure a multi-entry project step by step from the beginning.

3. Start configuration

3.1 File structure changes

In the srcdirectory will main.jsand App.vuetwo files for each copy it as a different entrance, the file structure becomes:

.
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── logo.png
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js # Main configuration goals
│ └── webpack.prod.conf.js # Main configuration target
├── config
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── src
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ ├── router
│ │ └── index.js
│ ├── App.vue
│ ├── App2.vue # New entrance
│ ├── main.js
│ └── main2.js # New entry
├── static
├── README.md
├── index.html
└── package.json

3.2 Simple configuration

Order from a different entrance, packaging different HTML, we can change the look entryandoutput two configurations,

//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",//Which file to package and output, you can use a relative path
            filename: "index.html"//The name of the html file after packaging and output
        })
    ]
}

According to the above one measure we know, webpack configuration where output.filenameif there are [name]means based on the name of the file entry, the name of the JS packaged into the corresponding file, so now we can pack out according to two entrances entry.jsand entry2.js.

The results of the packaging are as follows:

Current code: Github-multi-entry-vue1

As shown above, at this time we npm run buildpacked a reference to these two documents index.html, then how to package different HTML files, each using different entrance JS file, then we need the help of HtmlWebpackPluginthis plugin.

HtmlWebpackPluginThis plug-in, newa, packed up an HTML page, so we pluginsconfigured in newtwo, will be able to pack two pages.

3.3 Package out different HTML pages

We change the configuration file to the following:

//build/webpack.prod.conf.js

module.exports = {
  entry: {
    entry:'./src/main.js',//The packaged output chunk is named entry
    entry2:'./src/main2.js'//The packaged output chunk is named entry2
  },
  output: {
    filename:'[name].js',
    publicPath:'/'
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename:'entry.html',//file name to be packaged and output
      template:'index.html',//The name of the html file after packaging and output
      chunks: ['manifest','vendor','entry']//Entry chunks introduced by the output html file
     //There are some other configurations such as minify, chunksSortMode, which are irrelevant to this article and are omitted, see github
    }),
    new HtmlWebpackPlugin({
      filename:'entry2.html',
      template:'index.html',
      chunks: ['manifest','vendor','entry2']
    })
  ]
}

A configuration of the above should be noted that chunks, if not configured, then the generated HTML will introduce all entrances JS files, in the above example is to generate two HTML files are introduced entry.jsand entry2.js, so use a chunksconfiguration file to specify the generated HTML should Which JS file to import. Configure the chunksfollowing in order to achieve different HTML introduced only correspondencechunks purposes JS files.

We can see that in addition to our pack-generated chunkdocuments entry.jsand entry2.jsoutside, as well manifest, and vendorthese two, a little here to explain these two chunk:

  1. vendorExtracting means relates to node_modulesthe common module;
  2. manifestIs a vendormodule to do caching;

The packaged results are as follows:

File structure:

Now pack out of style is exactly what we need, when we diststart the next directory live-server(if you do not have to install it can be installed npm i-g live-server), you can see the results came out:

Current code: Github-multi-entry-vue2

So far, a simple multi-entry project configuration has been realized.

4. Configuration improvements

4.1 File structure changes

We have configured multiple entries in the previous article. If you want to create a new entry, copy multiple files, and then manually change the corresponding configuration.

But if different HTML files different vue-router, vuexare placed in srcthe directory, the contents of multiple entrance tiled together, not messy project directory will become clear, so the next multi-entry related files into a separate folder In the future, if there are multiple entries in the future, it will be processed in this folder.

Below we carry out the transformation of the file structure:

  1. First we created in the root directory of a entriesfolder, to a different entrance router, store, main.jswe are put here, each entry associated placed in a separate folder;
  2. In srcestablishing a directory commonfolder used to store multiple components of the common entrance, and the like;

The current directory structure:

.
├── build # No changes
├── config # No changes
├── entries # Store files of different entries
│ ├── entry1
│ │ ├── router # router of entry1
│ │ │ └── index.js
│ │ ├── store # entry1 store
│ │ │ └── index.js
│ │ ├── App.vue # The root component of entry1
│ │ ├── index.html # entry1 page template
│ │ └── main.js # entry1 entry
│ └── entry2
│ ├── router
│ │ └── index.js
│ ├── store
│ │ └── index.js
│ ├── App.vue
│ ├── index.html
│ └── main.js
├── src
│ ├── assets
│ │ └── logo.png
│ ├── common # Multi-entry common components
│ │ └── CommonTemplate.vue
│ └── components
│ ├── HelloWorld.vue
│ ├── test1.vue
│ └── test2.vue
├── static
├── README.md
├── index.html
├── package-lock.json
└── package.json

4.2 webpack configuration

Then we build/utilsadded two file functions, it is used to generate the webpack entryconfiguration and HtmlWebpackPluginplug configuration, due to the use of node.jsa folder structure to read the file, and therefore need to be introduced fs, globand other modules:

//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')

//Multi-entry configuration, this function reads the entry file from the entries folder and assembles it into webpack.entry configuration
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
}

//Configure HtmlWebpackPlugin for multi-page output template, and assemble html template configuration according to the environment
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
    }

   //configuration in production mode
    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
}

Explain a little bit about these two functions:

  1. exports.entriesFrom function entriesto find the JS file folder in the secondary directory entry as the file, and the file folder directory name as two key, generating such an object: {"entry1":"/multi-entry-vue/entries/entry1/main.js"}, where the plurality of inlets have more value pairs;
  2. exports.htmlPluginFunction and principle before the function is similar, but the assembly is HtmlWebpackPluginconfigured widget generate such an array, we can see and configure manually set essentially the same, but now is based on the folder structure generated:
//under production
[
  {
    template: "/multi-entry-vue/entries/entry1/index.html",
    chunks: ['manifest','vendor','entry1'],
    filename: "entry1.html",
    chunksSortMode:'dependency'
  },
  {...}//Configuration of the next entry
]

With these two according entriesto the configuration of automatically generating function webpack folder structure, the following related to the change about webpack several configuration files:

//build/webpack.base.conf.js

module.exports = {
  entry: utils.entries(),//Use function to generate entry configuration
  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')//no need

const devWebpackConfig = merge(baseWebpackConfig, {
  devServer: {
    historyApiFallback: {
      rewrites: [//Don’t forget to change the default route of 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
   //}),//Comment out the original HtmlWebpackPlugin configuration and use the generated configuration
  ].concat(utils.htmlPlugin())
})
//build/webpack.prod.conf.js

//const HtmlWebpackPlugin = require('html-webpack-plugin')

const webpackConfig = merge(baseWebpackConfig, {
  plugins: [
   //new HtmlWebpackPlugin({
   //... comment out, no need
   //}),
  ].concat(utils.htmlPlugin())
})

Now let's npm run buildtake a look at what the generated directory looks like:

At this point we diststart the directory live-serverto see what effects:

Current code: Github-multi-entry-vue3

Most of the posts on the Internet are of different depths, and even some are inconsistent. The articles below are summaries of the learning process. If you find errors, please leave a message to point out~

reference:

  1. Webpack puzzles: multi-entry file packaging strategy
  2. webpack configuration file: entry and exit, multi-entry, multi-outlet configuration
  3. Advanced configuration and optimization of webpack that you can understand at a glance
Reference: https://cloud.tencent.com/developer/article/1534366 an article to understand Webpack multi-entry configuration-Cloud + Community-Tencent Cloud