likes
comments
collection
share

webpack5 多页脚手架搭建

作者站长头像
站长
· 阅读数 9

webpack 作为目前最流行的构建工具,几乎成为了前端开发中必备的工具之一,如果想前端进阶的话,懂得 webpack 配置也是必不可少的一部分。然而,也是由于 webpack 的配置多样化,导致劝退了很多想学习入门 webpack 的童鞋。写这个文章,主要是把平时使用 webpack 的一些经验和心得分享给大家,同时也是为了巩固自己的知识。

观看该文章时,默认已经对 node.js 和 webpack 有一定了解。

基础依赖

想要搭建 webpack 脚手架,我们需要先安装 webpackwebpack-servewebpack-cli,这三个是必不可少的。

以下我们都默认使用 pnpm 进行依赖安装, node 版本为 16.0.0

pnpm i webpack webpack-cli webpack-serve -D

安装完成即可在控制台查看对应版本号。安装命令如果没有添加对应版本号,则默认取的是最新的稳定版本。

devDependencies:
+ webpack 5.70.0
+ webpack-cli 4.9.2
+ webpack-serve 4.7.4
依赖版本作用
webpack5.70.0核心库,提供了很多 API
webpack-cli4.9.2webpack 命令行工具,通过命令行输入命令来使用 webpack
webpack-serve4.7.4webpack 提供用来开发调试的服务器,可以通过 url 打开页面来调试

公用配置

一般来说,一个完整的项目构建,是需要区分 开发环境生产环境 的,那这两个环境必然是存在一些公用部分,如公用的 loaderplugin,所以这里我们按照实际情况来进行配置抽离。

首先在根目录下创建 build 目录,并在其中创建 webpack.base.config.js 文件,用于存在一些公用配置项。

webpack5 多页脚手架搭建

安装以下依赖

pnpm i copy-webpack-plugin -D
依赖版本作用
copy-webpack-plugin10.2.4用于复制一些静态资源文件,如 public ,不然在 html 中引入 favicon.ico 会 404

创建完成之后,在 webpack.base.config.js 中新增以下配置。

// 引入 path 模块
const path = require('path')

// 引入静态资源复制插件
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
  // 打包入口地址
  entry: {
    // 由于可能是多页,所以采用对象的形式
    index: ['./src/views/index/index.js']
  },
  
  // 模块resolve的规则
  resolve: {
    //自动的扩展后缀,比如一个js文件,则引用时书写可不要写.js
    extensions: ['.js', '.json', '.css', '.less'],
    // 路径别名
    alias: {
      '@': path.join(__dirname, '../src')
    }
  },
  
  // 构建目标
  target: ['web', 'es5'],
  
  // context 是 webpack entry 的上下文,是入口文件所处的目录的绝对路径,默认情况下是当前根目录。
  // 由于我们 webpack 配置文件放于 build 目录下,所以需要重新设置下 context ,使其指向根目录。
  context: path.resolve(__dirname, '../'),
  
  plugins: [
    // 把public的一些静态文件复制到指定位置,排除 html 文件
    new CopyWebpackPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, '../public'),
          globOptions: {
            dot: true,
            gitignore: true,
            ignore: ['**/*.html']
          }
        }
      ]
    })
  ]
}

path 模块为 node.js 自带,主要用于帮助我们方便的解决了路径设置问题

开发环境配置

公用配置先告一段落,接下来,我们要进行 开发环境 的配置,先把项目给跑起来。

首先,在 build 目录下创建 webpack.dev.config.js 文件。

接着安装以下依赖

pnpm i webpack-merge html-webpack-plugin -D
依赖版本作用
webpack-merge5.8.0用于合并webpack配置项
html-webpack-plugin5.5.0用来打包入口 html 文件,entry 配置的入口是 js 文件,webpack 以 js 文件为入口,遇到 import, 用配置的 loader 加载引入文件,但作为浏览器打开的入口 html, 是引用入口 js 的文件,它在整个编译过程的外面, 所以,我们需要 html-webpack-plugin 来打包作为入口的 html 文件

安装完成之后,在 webpack.dev.config.js 中新增以下配置。

// 引入合并对象插件
const { merge } = require('webpack-merge')

// 引入 path 模块
const path = require('path')

// 引入打包html插件
const HtmlWebpackPlugin = require('html-webpack-plugin')

// 引入基础配置文件
const baseWebpackConfig = require('./webpack.base.config')

const devWebpackConfig = merge(baseWebpackConfig, {
  // 模式,必填项
  mode: 'development',
  
  // 开启持久化缓存
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename]
    }
  },

  output: {
    // 输出文件目录
    path: path.resolve(__dirname, '../dist'),
    // 输出文件名
    filename: 'js/[name].js',
  },
  
  // 源码映射
  devtool: 'eval-cheap-module-source-map',

  // 开发服务配置
  devServer: {
    // 服务器 host,默认为 localhost
    host: '0.0.0.0',
    // 服务器端口号,默认 8080
    port: 7001,
    // 静态资源属性
    static: {
      // 挂载到服务器中间件的可访问虚拟地址
      // 例如设置为 /static,在访问服务器静态文件时,就需要使用 /static 前缀
      // 相当于webpack-dev-server@3.X的 contentBasePublicPath 属性
      publicPath: './',
      // 告诉服务器从哪里提供内容
      directory: path.join(__dirname, '../public')
    },
    // 需要监听的文件,由于是多页应用,无法实现热更新,所以都只能刷新页面
    watchFiles: ['src/**/*']
  },

  // 插件
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/views/index/index.html'favicon: path.resolve(__dirname, '../public/favicon.ico')
    })
  ]
})

module.exports = devWebpackConfig

接着在根目录下创建 public 目录,用于存放一些静态资源,如 favicon.ico

然后在根目录下创建 src/views/index 目录,用于存放相关业务代码 ,并在 index 目录内新建 index.htmlindex.js

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title></title>
  </head>
  <body>
    <div>index test</div>
  </body>
</html>

index.js

console.log('index.js')

开发环境到这边,就算简单的配置完成了,然后我们在 package.json 中的 scripts 添加运行命令

"serve": "webpack serve --progress --no-stats --config build/webpack.dev.config.js",

控制台运行命令

pnpm run serve

当控制台出现以下信息则代表启动成功,打开浏览器输入 http://localhost:7001/index.html ,发现页面和控制台多出现相应的信息,这样一个最简单的开发环境就配置好了。

webpack5 多页脚手架搭建

配置loader

启动运行只有,我们会发现,不管是引入样式文件,还是使用图片,控制台都会报错,这是因为,webpack 只能识别 .js 类型文件,所以,我们需要配置一些 loader 来是脚手架的功能更加完善。

因为 loader 配置,不管是在 开发环境 还是 生产环境,都是需要配置的,所以我们把这些配置项放到 webpack.base.config.js 中。

样式资源配置

首先安装以下插件

pnpm i style-loader css-loader less-loader postcss-loader postcss autoprefixer mini-css-extract-plugin cross-env -D
依赖版本作用
css-loader6.7.1解析 css 文件
style-loader3.3.1将 css-loader 解析好的通过 style 标签的形式添加到页面上,建议开发环境使用
less4.1.2less 核心库
less-loader10.2.0解析 less 文件
postcss8.4.12postcss 核心库
postcss-loader6.2.1要使用 autoprefixer ,就需要使用到 postcss-loader
postcss-preset-env7.4.3帮 postcss 找到 browserslist 里面的配置,通过配置加载指定的 css 兼容性样式
mini-css-extract-plugin2.6.0分离样式文件,与 style-loader 不共存,建议生产环境使用
cross-env2.6.0运行跨平台设置和使用环境变量的脚本,主要为了区分当前环境,用于判断是使用 style-loader 还是 mini-css-extract-plugin

安装完成之后,在 webpack.base.config.js 中添加以下代码


// ...

+ // 判断当前环境是否是生产环境
+ const isProduction = process.env.NODE_ENV === 'production'

+ // 样式单独分离到一个文件中
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  // ...
  context: path.resolve(__dirname, '../'),

  // 不同类型模块的处理规则
+  module: {
+    rules: [
+      // 处理 css、less 文件
+      {
+        test: /\.(css|less)$/,
+        use: [
+          isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
+          {
+            loader: 'css-loader',
+            options: {
+              // 是否使用source-map
+              sourceMap: !isProduction,
+              // 兼容IE11
+              esModule: false
+            }
+          },
+          {
+            loader: 'postcss-loader',
+            options: {
+              // 是否使用source-map
+              sourceMap: !isProduction
+            }
+          },
+          'less-loader'
+        ]
+      }
+    ]
+  }

  // ...
}

然后在根目录下创建 .browserslistrcpostcss.config.js 文件

.browserslistrc 用于告诉 postcss 的浏览器作用范围

defaults
not ie < 11
last 2 versions
> 1%
iOS 7
last 3 iOS versions

postcss.config.js 配置 postcss 配置项

module.exports = {
  plugins: [
    'postcss-preset-env'
  ]
}

然后重新修改 package.json 中的 serve 命令,然后重新运行,就会发现不管是在 js 中引入 .css|.less 文件,都可以作用到 html 上了,并且也给样式加上了对应的浏览器兼容前缀。

- "serve": "webpack serve --progress --no-stats --config ./build/webpack.dev.config.js",
+ "serve": "cross-env NODE_ENV=development webpack serve --progress --no-stats --config ./build/webpack.dev.config.js",

图片资源配置

安装 html-loader,用于解析 html 中的图片路径

pnpm i html-loader -D

然后在 webpack.base.config.js 中新增以下配置


// ...
module.exports = {
  // ...
  
  // 不同类型模块的处理规则
  module: {
    rules: [
      // ...
+      // 解析 html 中的 src 路径
+      {
+        test: /\.html$/,
+        use: 'html-loader'
+      },
+      // 对图片资源文件进行处理,webpack5已经废弃了url-loader,改为type
+      {
+        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+        type: 'asset',
+        exclude: [path.resolve(__dirname, 'src/assets/imgs')],
+        generator: {
+          filename: 'imgs/[name].[contenthash][ext]'
+        }
+      },
       // ...
    ]
  }

  // ...
}

配置完之后,重新运行项目,我们会发现,不管是何种方式的图片使用,都可以准确的显示在页面中了。

配置字体图标

为了让目录结构更加清晰美观,我们单独把字体图标提取到一个目录中,还是在 webpack.base.config.js 中添加以下代码


// ...
module.exports = {
  // ...
  
  // 不同类型模块的处理规则
  module: {
    rules: [
      // ...
+      // 对字体资源文件进行处理,webpack5已经废弃了url-loader,改为type
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        type: 'asset',
+        generator: {
+          filename: 'fonts/[name].[contenthash][ext]'
+        }
+      },
       // ...
    ]
  }

  // ...
}

这样我们字体图标在打包的时候,就可以输出到 fonts 这个目录下了。

配置音频资源

同理,对于音频的处理,我们也是采用上述的方式,在 webpack.base.config.js 中添加以下代码


// ...
module.exports = {
  // ...
  
  // 不同类型模块的处理规则
  module: {
    rules: [
      // ...
+      // 对音频资源文件进行处理,webpack5已经废弃了url-loader,改为type
+      {
+        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
+        type: 'asset',
+        exclude: [path.resolve(__dirname, 'src/assets/medias')],
+        generator: {
+          filename: 'medias/[name].[contenthash][ext]'
+        }
+      },
       // ...
    ]
  }

  // ...
}

音频资源在 html 中的引入方式也是跟图片的引入方式一样。

配置javaScript语法转换

当我们会用 ES6 语法的时候,在 IE11 浏览器上,我们会发现控制台报错了,这是因为语法的不兼容,所以我们进行一层语法的转换。

由于市场上的浏览器众多,每个浏览器的语法支持程度都不一样,所以为了能够让浏览器兼容我们写的 javaScript 代码,我们需要对 ES6 以上语法进行兼容处理,也就是降级为 ES5,保证每个浏览器的语法兼容问题,这里我们以 IE11 浏览器为例。

安装以下插件

pnpm i babel-loader @babel/core @babel/preset-env core-js -D
依赖版本作用
babel-loader8.2.4将 ES2015+ 代码转换为 ES5
@babel/core7.17.8babel 核心包
@babel/preset-env7.16.11Babel 编译的预设
core-js3.21.1新功能的 es 'api' 转换为大部分现代浏览器都可以支持
运行的一个 'api' 补丁包集合

webpack.base.config.js 中添加以下代码


// ...
module.exports = {
  // ...
  
  // 不同类型模块的处理规则
  module: {
    rules: [
      // ...
 +    {
 +       test: /\.(m|j)s$/,
 +       exclude: /node_modules/,
 +       use: [
 +         {
 +           loader: 'babel-loader',
 +           options: {
 +             cacheDirectory: true
 +           }
 +         }
 +       ]
 +     }
       // ...
    ]
  }

  // ...
}

在根目录下创建 babel.config.json 文件,并且添加以下内容

{
  "presets": [
    [
      "@babel/preset-env",
      {
        // useBuiltIns: false 默认值,无视浏览器兼容配置,引入所有 polyfill
        // useBuiltIns: entry 根据配置的浏览器兼容,引入浏览器不兼容的 polyfill
        // useBuiltIns: usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加
        "useBuiltIns": "usage",
        "corejs": {
          "version": 3
        },
        "modules": false
      }
    ]
  ]
}

配置好之后,重新项目,就可以愉快的使用 ES5 以上的语法了,并且在 IE11 上完美展示。

webpack.base.config.jswebpack.dev.config.js 到这里就算配置完成了,最后我们还需要把项目打包成一个静态项目,方便部署到生产服务器上。

生产环境配置

在生产环境中,我们会针对性的进行一些项目优化,方便后续如果项目庞大起来,在构建速度上也不会有太大的时间差。

在根目录下创建 webpack.pro.config.js,并添加以下代码

// 引入合并对象插件
const { merge } = require('webpack-merge')

// 引入 path 模块
const path = require('path')

// 引入打包html插件
const HtmlWebpackPlugin = require('html-webpack-plugin')

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

// 引入基础配置文件
const baseWebpackConfig = require('./webpack.base.config')

const proWebpackConfig = merge(baseWebpackConfig, {
  // 模式,必填项
  mode: 'production',

  output: {
    // 输出文件目录
    path: path.resolve(__dirname, '../dist'),
    // 输出文件名
    filename: 'js/[name].[contenthash].js',
    // 清空目录
    clean: true
  },

  // 源码映射
  devtool: false,

  // 插件
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/views/index/index.html',
      filename: 'html/index.html',
      favicon: path.resolve(__dirname, '../public/favicon.ico')
    }),

    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash].css'
    })
  ]
})

module.exports = proWebpackConfig

package.json 中添加打包命令

"build": "cross-env NODE_ENV=production webpack --config ./build/webpack.pro.config.js",

这样一个最基本的打包配置就完成了,可以运行 pnpm run build 查看效果,可以发现在 主流浏览器 上和 IE11 与开发环境展示效果保持一致。

webpack5 多页脚手架搭建

接着,我们来对打包进行一些优化

安装 css-minimizer-webpack-plugincss 进行优化

pnpm i css-minimizer-webpack-plugin -D

然后在 webpack.pro.config.js 中添加新代码

// ...

+ // 压缩css
+ const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')

+ const TerserPlugin = require('terser-webpack-plugin')

const proWebpackConfig = merge(baseWebpackConfig, {
  // ...
+  optimization: {
+    minimize: true,
+    minimizer: [
+      new CssMinimizerPlugin(),
+      new TerserPlugin({
+        terserOptions: {
+          // 生产环境自动删除console
+          compress: {
+            drop_debugger: true,
+            drop_console: true,
+            pure_funcs: ['console.log']
+          }
+        },
+        parallel: true,
+        exclude: /[\\/]node_modules[\\/]/
+      })
+    ],
+    splitChunks: {
+      cacheGroups: {
+        // 配置提取模块的方案
+        default: false,
+        styles: {
+          name: 'styles',
+          test: /\.(css|less)$/,
+          chunks: 'all',
+          enforce: true,
+          priority: 10
+        },
+        vendors: {
+          name: 'chunk-vendors',
+          test: /[\\/]node_modules[\\/]/,
+          chunks: 'all',
+          priority: 2,
+          enforce: true,
+          reuseExistingChunk: true
+        }
+      }
+    }
  }
})

module.exports = proWebpackConfig

到这里,我们 生产环境 的优化就基本完成了。

总结

由于项目比较小,所以做过多的优化反而会加重构建负担,所以本文章就大概了做一些基础优化,也是为了巩固下 webpack 的配置,毕竟 webpack 配置多而杂,难免会有忘记的时候。

文章项目Demo

感兴趣的可以拉取代码运行看看~