webpack多页面配置

之前都是使用脚手架直接生成的webpack配置,然后根据需求适当调整便使用了。最近空闲下来,然后就想从零开始写一个webpack配置,在此记录一下配置过程以及遇到的一些问题吧。

  1. node环境、npm或者yarn包管理器
  2. 创建一个新文件夹,然后使用npm或者yarn初始化
  3. 添加webpack依赖,创建一个webpack.config.js文件。顺便创建文件目录等
  4. 根据需要安装插件,目前我所用的到插件有
{
  "name": "interview",
  "version": "1.0.0",
  "description": "面试题记录",
  "main": "index.js",
  "scripts": {
    "dev": "webpack serve --mode development",
    "build": "webpack --mode production",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/core": "^7.14.3",
    "@babel/plugin-transform-runtime": "^7.14.3",
    "@babel/preset-env": "^7.14.4",
    "autoprefixer": "^10.2.6",
    "babel-loader": "^8.2.2",
    "babel-polyfill": "^6.26.0",
    "css-loader": "^5.2.6",
    "file-loader": "^6.2.0",
    "glob": "^7.1.7",
    "html-loader": "^2.1.2",
    "node-sass": "^6.0.0",
    "postcss": "^8.3.0",
    "postcss-loader": "^5.3.0",
    "postcss-px2rem": "^0.3.0",
    "sass": "^1.34.0",
    "sass-loader": "^11.1.1",
    "style-loader": "^2.0.0",
    "url-loader": "^4.1.1",
    "webpack-dev-server": "^3.11.2"
  },
  "devDependencies": {
    "clean-webpack-plugin": "^4.0.0-alpha.0",
    "copy-webpack-plugin": "^9.0.0",
    "extract-text-webpack-plugin": "^3.0.2",
    "html-webpack-plugin": "^5.3.1",
    "mini-css-extract-plugin": "^1.6.0",
    "optimize-css-assets-webpack-plugin": "^6.0.0",
    "uglifyjs-webpack-plugin": "^2.2.0",
    "webpack": "^5.38.1",
    "webpack-cli": "^4.7.0"
  }
}

  1. 其中一些loader,例如css-loader、style-loader等都是针对于css处理的,因为extract-text-webpack-plugin在webpack4中使用存在一些问题,而且官方建议使用mini-css-extract-plugin插件,所以我这里就使用官方推荐了。
  2. @babel/core、@babel/preset-env等是针对编译js所引入的包处理
  3. webpack-dev-server开发使用的本地服务,可以配置端口号、热更新等服务

遇到的问题:

  • px转rem插件无法处理sass改用webpack-px-to-rem
  • html无法进行热更新(将热更新关闭,使用页面自动刷新,比较耗费性能)

webpack.config.js文件内容

/*
 * @Author: PrendsMoi
 * @GitHub: https://github.com/PrendsMoi
 * @Blog: https://liuhgxu.com
 * @Description: webpack配置
 * @FilePath: /interview/webpack.config.js
 * @Date: 2021-05-30 11:27:51
 * @LastEditors: PrendsMoi
 * @LastEditTime: 2021-06-13 12:03:35
 */
const webpack = require('webpack')
const path = require('path')
const glob = require('glob')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')

// 获取多页面
const getPages = (argv) => {
  const isDev = argv.mode === 'development'
  const views = glob.sync('src/views/**/*.html')
  const viewJs = glob.sync('src/views/**/*.js')
  const config = {
    hash: true,
    inject: true,
    minify: {
      removeComments: !isDev,
      collapseWhitespace: !isDev
    }
  }
  const entries = {}
  const htmls = []
  views.forEach(item => {
    const fileInfo = path.parse(item)
    console.log(viewJs.includes(`${fileInfo.dir}/${fileInfo.name}.js`))
    if (viewJs.includes(`${fileInfo.dir}/${fileInfo.name}.js`)) {
      entries[fileInfo.dir.split('/').slice(-1)[0]] = `./${item}`.replace('.html', '.js')
    }
    htmls.push(new HtmlWebpackPlugin({
      ...config,
      filename:  `${fileInfo.name}.html`,
      template: `./${fileInfo.dir}/${fileInfo.name}.html`,
      chunks: [ 'vendor', 'commons', fileInfo.name]
    }))
  })
  const entry = Object.assign({'babel-polyfill': ['babel-polyfill'],commons: './src/app.js'}, entries)
  return {
    entries: entry,
    htmls
  }
}
module.exports = (env, argv) => ({
  entry:  getPages(argv).entries,
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: (argv.mode === 'development') ? './js/[name].js' : './js/[name].[contenthash].js',
    chunkFilename: (argv.mode === 'development') ? './js/[name].js' : './js/[name].[contenthash].js',
    publicPath: (argv.mode === 'development') ? '' : './'
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          (argv.mode === 'development') ? 'style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: { importLoaders: 2 }
          },
          {
            loader: 'sass-loader',
            options: {
              outputStyle: 'expanded'
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [
                require('autoprefixer')({
                  browsers: ['last 10 versions', 'ie >= 9'],
                  flexbox:true,
                  remove: false
                }),
                // require('postcss-px2rem')({remUnit: 75})
              ]
            }
          }
        ]
      },
      {
        test: /\.(html)$/,
        use: {
          loader: 'html-loader',
          options: {
            sources: false
          }
        }
      },
      {
        test: /\.js$/,
        include: [
          path.resolve(__dirname, 'src')
        ],
        use: 'babel-loader'
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader'
          }
        ]
      },
      {
        test: /\.(woff2|woff|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: 'static/fonts/[name].[hash:7].[ext]'
        }
      }
    ]
  },
  resolve: {//解析模块可选项
  },
  plugins: [
    ...getPages(argv).htmls,
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({filename:(argv.mode === 'development') ? "css/[name].css" : 'css/[name].[hash].css', chunkFilename: '[id].css'}),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, './src/assets'),
          to: 'assets',
          noErrorOnMissing: true
        }
      ],
      options: {}
    }),
    new webpack.LoaderOptionsPlugin({
      options: {
        postcss: [
          require('postcss-px2rem')({remUnit: 37.5})
        ]
      }
    })
  ],
  devtool: (argv.mode === 'development') ? "eval" : "nosources-source-map",
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          chunks: 'async',
          name: 'commons',
          minChunks: 2,
          maxInitialRequests: 5,
          // minSize: 0
        },
        vendor: {
          test:/node_modules/,
          chunks:'all',
          name:'vendor',
          priority:10
        }
      }
    },
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: (argv.mode === 'development') ? true : false
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,  //是否启用gzip
    host: 'localhost',
    port: 9000,
    hot: true, // 热更新
    open: true,
    overlay: true,
    inline: true,
    watchContentBase: true
  }
})

.babelrc文件

{
	"presets": ["@babel/preset-env"],
	"plugins": ["@babel/plugin-transform-runtime"]
}