webpack手动构建15步 | 8月更文挑战

262 阅读5分钟

1, webpack手动搭建15步

1,安装

 npm install -g webpack webpack-cli

2, 打包编译HTML文件

  npm install -D clean-webpack-plugin    清除dist文件
  npm install -D html-webpack-plugin     生成index.html

3, 热更新

 npm install -D webpack-dev-server

4,babel编译

  npm install babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
  npm install -S @babel/runtime
  npm install -S @babel/plugin-syntax-dynamic-import

5, CSS 处理

  npm install -D css-loader style-loader
  sass npm install -D node-sass sass-loader
  less npm install -D less less-loader
 
  postcss: 自动添加前缀,css模块化
  npm install -D postcss-loader
  npm install -D autoprefixer

6, 启用模块热替换

   hot:true
  安装 cross-env 添加一个环境变量,
  cnpm install cross-env -S
  环境分离,需要安装webpack-merge
  cnpm install webpack-merge -D

7, 环境变量

 通过webpack.DefinePlugin 插件获取变量的值

8, 开发环境配置查看源码

 devtool:'cheap-module-eval-source-map'

9, css提取

 cnpm install -D mini-css-extract-plugin 

10, 图片字体资源处理

   cnpm install -D file-loader url-loader
   在最外层index.html文件中引入图片的,需要添加插件html-witming-loader
   cnpm install -D html-withimg-loader
   编译之后:图片路径是相对路径,打包之后就会有问题

11, 提取公共代码

 配置:optimization中的 splitChunks

12, JS 和 CSS压缩

   cnpm install -D babili-webpack-plugin 
   cnpm install -D optimize-css-assets-webpack-plugin

13, 静态资源的相对路径改为绝对路径

   cnpm install -D copy-webpack-plugin
   css-loader开始模块化

14, Mock

   cnpm install -D mockjs
   cnpm install -D express
   cnpm install -D nodemon 

2, 完整的webpack手动构建代码

1,webpack.base.config

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const ENV_NODE = process.env.ENV_NODE
console.log(process.env.MOCK)
module.exports = {
  entry: {
    index: './src/index.js',
    about: './src/about.js',
  },
  output: {
    filename: ENV_NODE === 'production' ? '[name].[contenthash:7].js' : '[name].[hash:7].js',
    chunkFilename: ENV_NODE === 'production' ? '[name].[chunkhash:7].js' : '[name].[hash:7].js',
    path: path.resolve(__dirname, "dist"),
    publicPath: '/',
  },
  resolve: {
    alias: { // 配置别名
      '@': path.resolve(__dirname, 'src'),
    },
    extensions: ['.js', '.css', '.scss', '.less']
  },
  module: {
    rules: [
      {
        test: /\.(jsx|js)$/,
        use: {
          loader: "babel-loader",
        },
        exclude: /node_modules/,
      },
      {
        test: /\.(css|scss|sass)$/,
        use: [  // MiniCssExtractPlgin.loader替换 style-loader 为了提取CSS
          ENV_NODE === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
          {
            loader: 'css-loader',
            options:{
              modules: {
                localIdentName: '[path][name]-[local]-[hash:base64:5]'
              }
            }
          },
          'postcss-loader',
          'sass-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
            ENV_NODE === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
            {
              loader: 'css-loader',
              options:{
                modules:{
                  localIdentName: '[path][name]-[local]-[hash:base64:5]'
                }
              }
            },
            'postcss-loader',
            'less-loader'
        ]
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: [{
          loader: 'url-loader',
          options: {
            limit: 1024 * 10, // 10k以内的图片转Base64打包到js中
            name: '[name].[hash:7].[ext]', // 打包的文件名
            outputPath: 'images/',
            esModule: false
          }
        }]
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use:[{
          loader: 'url-loader',
          options: {
            limit: 1024 * 5, // 10k以内的打包到js中
            name: '[name].[hash:7].[ext]',
            outputPath: 'font/'
          }
        }]
      },
      {
        test: /\.(htm|html)$/,
        use: [{
          loader: 'html-withimg-loader',
        }]
      }
    ],
  },
  plugins: [
    new CleanWebpackPlugin(), // 清理dist 文件夹
    new HtmlWebpackPlugin({
      // 自动生成html
      template: "./index.html", //
      filename: "index.html",
      // script 标签位于哪里,默认true,在body里面, 还有head, false
      inject: true,
      hash: true, // 今天
      minify: {
        minifyJS: true,
        minifyCSS: true,
        removeComments: true,
        removeEmptyAttributes: true,
        collapseBooleanAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true,
        collapseInlineTagWhitespace: true,
        collapseWhitespace: true,
      },
      chunks: ['styles', 'vendor', 'common', 'manifest', 'index'],
    }),
    new HtmlWebpackPlugin({
      // 自动生成html
      template: "./about.html", //
      filename: "about.html",
      // script 标签位于哪里,默认true,在body里面, 还有head, false
      inject: true,
      hash: true, // 今天
      minify: {
        minifyJS: true,
        minifyCSS: true,
        removeComments: true,
        removeEmptyAttributes: true,
        collapseBooleanAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true,
        collapseInlineTagWhitespace: true,
        collapseWhitespace: true,
      },
      chunks: ['styles', 'vendor', 'common', 'manifest', 'about'],
    }),
    new CopyWebpackPlugin({
      patterns: [
        { from: path.join(__dirname, '/src/public'), to: 'public' }
      ]
    })
  ],
  // 提取公共代码
  optimization: {
    splitChunks: {
      chunks: 'all', // 表示从哪些chunks里面抽取代码 initial(入口文件), all(所有), async(异步模块, 默认)
      minSize: 30000, // 限制抽取出来的文件在压缩前的最小大小:默认30000
      maxSize: 0, // 0表示不限制最大值 默认是0
      minChunks: 1, // 表示被引入次数, 默认是1
      maxAsyncRequests: 5, // 最大异步请求数 默认是5
      maxInitialRequests: 3, // 最大的初始化加载次数 默认3
      automaticNameDelimiter: '~', // 抽取出来的文件的自动生成名字的分隔符, 默认~
      name: true, // 抽取出来文件的名字,true表示自动生成文件名字, 默认true
      // 上面的那么多参数,都是默认值,可以不用管,
      // cachGroups 才是我们配置的关键,它可以继承、覆盖上面splitChunks中所有的参数值,
      // 设置缓存组件用来抽取满足不同规则的chunk
      cacheGroups: {
       // 将所有的css文件打包为一个,将权重设置为最高
       sytles: {
         name: 'styles',
         test: /\.(css|scss|sass|less)$/,
         chunks: 'all',
         enforce: true, // 使用上层的配置minSize配置
         priority: 20, // 优先级, 越大越高
       },
       // 第三方库单独打包
       vendor: {
         test: /node_modules/,
         chunks: 'initial',
         name:'vendor',
         priority: 10,
         enforce:true
       },
       // 把所有引入超过1次的模块抽取为common
       common: {
         name: 'common',
         chunks: 'initial',
         priority: 2,
         minChunks: 2,
         enforce:true
       }
      }
    },
    runtimeChunk: { // 提取manifest, 管理模块之间的交互
      name: 'manifest' 
    }
  }
};

2,webpack.dev.config

const { merge } = require('webpack-merge')
const path = require('path')
const baseConfig = require('./webpack.base.config')
const webpack = require("webpack");
// const MiniCssExtractPlugin = require('mini-css-extract-plugin')

const devConfig = {
  mode: "development",
  devServer: {
    contentBase: path.join(__dirname, 'dist'), // 网站根目录
    host: "localhost",
    port: "9527",
    compress: true, // 设置服务端压缩
    hot: true  // 开启模块热替换
  },
  devtool: 'cheap-module-eval-source-map',
  plugins: [
    new webpack.NamedModulesPlugin(), // 当开启HMR的时候,使用该插件会显示模块的相对路径,
    new webpack.HotModuleReplacementPlugin(), // 开启HMR
    new webpack.DefinePlugin({ // 通过插件获取打包的环境变量
      "ENV_MOCK": process.env.MOCK
    }),
    // 提取CSS
    // new MiniCssExtractPlugin({
    //   filename: 'css/[name].[hash:7].css',
    //   chunkFilename: 'css/[id].[name].[hash:7].css'
    // })
  ],
};
module.exports = merge(baseConfig, devConfig)

3, webpack.prod.config.js

const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.base.config')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const BabiliPlugin = require('babili-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const proConfig = {
  mode: 'production',
  plugins: [
    // 提取CSS
    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash:7].css',
      chunkFilename: 'css/[name].[chunkhash:7].css'
    })
  ],
  optimization:{
    minimizer: [
      new BabiliPlugin(), // 基于Babel的压缩工具, 支持ES6的一些特性, 取代UglifyJS
      new OptimizeCSSAssetsPlugin() // 压缩CSS
    ]
  }
}

module.exports = merge(baseConfig, proConfig)

3, webpack总结

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程

  1. 初始化参数: 从配置文件 和命令行中读取与合并参数,初始化本次构建的配置参数,得出最终的参数;
  2. 开始编译: 用上一步得到的参数初始化 Compiler 对象,加载配置文件的所有 plugin,执行对象的 run 方法开始执行编译;
  3. 确定入口: 根据配置中的 entry 找出所有的入口文件,递归遍历所有的入口文件;
  4. 编译模块: 从入口文件出发,调用所有配置的 loader 对模块进行编译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
  5. 输出资源: 所有文件的编译及转化都已经完成,也就是最终输出的资源,其中包括即将输出的资源、代码块 Chunk 等等信息。

总结:用一句话概括本质:

webpack是一个打包模块化js的工具,可以通过loader转换文件,通过plugin扩展功能。