Frontend Development 20 min read

Webpack Multi-Page Project Build Practice

The article walks through configuring Webpack to build a multi‑page Vue application—showing why the default CLI SPA setup falls short, how to generate dynamic entry points, set output paths, apply loaders for JS, CSS, images, and use plugins such as MiniCssExtractPlugin, CopyWebpackPlugin and HtmlWebpackPlugin to produce separate bundles and HTML files for each page.

37 Interactive Technology Team
37 Interactive Technology Team
37 Interactive Technology Team
Webpack Multi-Page Project Build Practice

This article shares practical experience of using Webpack to bundle a multi‑page Vue project. It starts with a brief introduction, explains why the default Vue CLI single‑page setup does not meet the needs of a project that contains several independent pages (login, game, payment, etc.), and then describes how to adjust the Webpack configuration to support multiple entry points and corresponding HTML templates.

Directory structure

project
├───bin
│   └───vb.js
├───build
│   ├───dev.js
│   ├───release.js
│   ├───webpack.config.base.js
│   ├───webpack.config.build.js
│   └───webpack.config.dev.js
├───src
│   ├───components
│   │   ├───count.vue
│   │   ├───dialog.vue
│   │   └───errortips.vue
│   ├───game
│   │   ├───game.htm
│   │   ├───game.js
│   │   └───game.vue
│   ├───login
│   │   ├───login.htm
│   │   ├───login.js
│   │   └───login.vue
│   ├───pay
│   │   ├───pay_result.htm
│   │   ├───pay_result.js
│   │   ├───pay_result.vue
│   │   ├───pay.htm
│   │   ├───pay.js
│   │   └───pay.vue
│   └───…

The core concepts that need to be configured are the four Webpack fundamentals: entry , output , loader , and plugins .

1. Entry configuration

const config = {
  entry: {
    game: './src/game/game.js',
    login: './src/login/login.js',
    pay: './src/pay/pay.js',
    pay_result: './src/pay/pay_result.js'
  }
};

Because the number of pages may change, a helper getEntry() function is introduced to scan the src folder and automatically generate the entry object only for pages that have a matching .htm template.

const fs = require('fs');
const glob = require('glob');
function getEntry() {
  const entry = {};
  glob.sync('./src/*/*.js') // find all js files under src/*
    .forEach(function (filePath) {
      var name = filePath.match(/\/src\/(.+)\/.*\.js/)[1];
      if (!fs.existsSync('./src/' + name + '.htm')) { return; }
      entry[name] = filePath;
    });
  return entry;
}
module.exports = {
  entry: getEntry()
};

2. Output configuration

const config = {
  output: {
    path: path.join(__projectDir, __setting.distJs),
    publicPath: __setting.domainJs, // public URL for static assets
    filename: '[name][hash].js'
  }
};

The publicPath determines how the bundles are referenced in the browser. It can be a CDN URL, e.g. publicPath: "https://cdn.example.com/assets/" , which results in script tags like <script src="https://cdn.example.com/assets/bundle.js"></script> . The special variable __webpack_public_path__ can be set at the top of an entry file to override the global publicPath at runtime.

3. Loader configuration

Loaders enable Webpack to process non‑JavaScript files.

// JavaScript (babel)
module: {
  rules: [{
    test: /\.js$/,
    include: [path.resolve(__projectDir, 'src')],
    exclude: /node_modules/,
    loader: "babel-loader"
  }]
}

For CSS and pre‑processors:

{
  test: /\.css$/,
  use: ['style-loader', 'css-loader']
}

{ // SCSS / SASS
  test: /\.(sc|sa)ss$/,
  use: [
    { loader: 'vue-style-loader' },
    { loader: 'css-loader', options: { sourceMap: true } },
    { loader: 'postcss-loader', options: { sourceMap: true } },
    { loader: 'sass-loader', options: { sourceMap: true } },
    { loader: 'sass-resources-loader', options: { sourceMap: true, resources: [path.resolve('./src/public/css/common.scss')] } }
  ]
}

Image handling can be done with file-loader or url-loader (the latter inlines small files as Base64):

{
  test: /\.(gif|png|jpe?g)$/,
  loader: 'file-loader'
}

{ // inline if < 10KB
  test: /\.(png|jpg|jpeg|svg|gif)$/,
  use: [{
    loader: 'url-loader',
    options: { limit: 10240, name: 'image/[name][hash].[ext]' }
  }]
}

For legacy libraries that expose a global variable (e.g., Zepto), script-loader together with exports-loader can be used:

{
  test: require.resolve('zepto'),
  use: ['exports-loader?window.Zepto', 'script-loader']
}

4. Plugins

Commonly used plugins are demonstrated:

// Extract CSS into separate files
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  module: {
    rules: [{
      test: /\.css$/,
      use: [MiniCssExtractPlugin.loader, 'css-loader']
    }]
  },
  plugins: [
    new MiniCssExtractPlugin({ filename: 'css/[hash].css' })
  ]
};
// Copy static assets
const CopyWebpackPlugin = require('copy-webpack-plugin');
plugins.push(
  new CopyWebpackPlugin([
    { from: { glob: './src/public/*.htm', dot: true },
      to: path.join(__setting.distTpl, 'public', '[name].htm') }
  ], { copyUnmodified: true })
);
// Generate HTML for each entry
const HtmlWebpackPlugin = require('html-webpack-plugin');
const htmlPluginArray = [];
function getEntry() { /* same as before */ }
// after building entry object, push a plugin per page
htmlPluginArray.push(new HtmlWebpackPlugin({
  filename: `${__setting.distTpl}/${name}.htm`,
  template: `./src/${name}.htm`,
  inject: 'body',
  minify: { removeComments: true, collapseWhitespace: true },
  chunks: [name],
  inlineSource: '.(js|css)'
}));
module.exports = {
  plugins: [new MiniCssExtractPlugin({ filename: 'css/[hash].css' })]
    .concat(htmlPluginArray)
};

Additional plugins such as html-webpack-inline-source-plugin and a custom scriptInlineHtml plugin are shown to inline assets directly into the generated HTML.

5. Other configurations

Resolve aliases simplify imports, e.g. alias: { '@': path.resolve(__projectDir, 'src') } , allowing let img = require('@/public/image/common/ico-back.png').default;

The development server is configured with hot module replacement (HMR) and a custom publicPath :

devServer: {
  contentBase: __projectDir,
  publicPath: '/',
  port: 8080,
  host: '127.0.0.1',
  open: true,
  hot: true
}

Finally, the article concludes that many more topics (environment separation, caching, additional plugins, etc.) can be explored, but the presented configuration already covers the essential steps for building a multi‑page Vue application with Webpack.

frontendconfigurationVueWebpackbuildloaderMulti‑Page
37 Interactive Technology Team
Written by

37 Interactive Technology Team

37 Interactive Technology Center

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.