likes
comments
collection
share

深度解析webpack5以及打包实践攻略,看完这篇带你玩转高级自定义打包

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

1.webpack5对比webpack4做了哪些优化

Webpack 5 对比Webpack 4 存在一些重要的优化。Webpack 5 在性能、构建速度、Tree Shaking 等方面都有所改进:

  1. 性能改进

    • Webpack 5 在构建速度和性能方面有所提升。这主要是通过改进缓存策略、优化构建算法以及增强的持久化缓存等方式实现的。这意味着更快的构建时间和更高的整体性能。
  2. 支持Tree Shaking

    • 在 Webpack 5 中,Tree Shaking(树摇)机制得到了改进,这意味着它更有效地识别和删除未使用的代码,以减小最终构建的文件大小。
  3. 对ES6模块和JSON模块的优化

    • Webpack 5 对于 ES6 模块和 JSON 模块的处理更加智能和优化,从而提高了构建速度。
  4. 默认持久化缓存

    • Webpack 5 默认启用了持久化缓存,这意味着构建过程中的中间结果会被缓存,以便在后续的构建中重复使用,进而加快构建速度。
  5. 改进的 Tree Shaking 算法

    • Webpack 5 中对 Tree Shaking 算法进行了改进,使其更加精确和高效,从而能够更好地识别和删除未使用的代码。
  6. 内置的模块类型优化

    • Webpack 5 提供了一种新的模块类型——module.type,可以通过配置文件进行设置,以便更好地与不同的目标环境进行兼容和优化。

2.webpack5的核心原理

Webpack 5 的核心原理涉及了模块化、依赖分析、打包优化等多个方面。下面是Webpack 5 的核心原理的概述:

  1. 模块化

    • Webpack 5 将项目中的各个文件视为模块,这些模块可以是 JavaScript 文件、CSS 文件、图片、字体等各种资源。它使用模块化的思想来管理这些文件,使得项目的各个部分可以相互依赖、独立开发和测试。
  2. 依赖分析

    • 在构建过程中,Webpack 5 会对项目中的模块进行依赖分析,即分析模块之间的依赖关系。通过识别模块之间的 import、require 等语句,Webpack 能够确定模块之间的依赖关系,从而构建出一个依赖关系图,这也是 Webpack 5 能够正确地打包项目中所有依赖的基础。
  3. 打包优化

    • Webpack 5 会根据依赖关系图进行打包优化。这包括但不限于:
      • Tree Shaking:删除项目中未使用的代码,以减小打包后的文件大小。
      • 代码分割:将项目代码拆分为多个块,按需加载,从而提高页面加载速度。
      • 持久化缓存:通过缓存构建过程中的中间结果,减少重复工作,提高构建速度。
      • 多线程构建:利用多线程或者多进程来并行处理模块,加快构建速度。
      • 模块类型优化:根据目标环境,优化模块类型,提高兼容性和执行效率。
      • 自动刷新:在开发过程中,Webpack 5 会监视文件的变化,并实时更新构建结果,提供热更新功能。
  4. 插件系统

    • Webpack 5 提供了丰富的插件系统,允许开发者通过插件来扩展和定制 Webpack 的功能。通过插件系统,开发者可以在打包过程的各个阶段介入,实现自定义的功能,比如代码压缩、资源优化、文件拷贝等。
  5. 编译器和解析器

    • Webpack 5 内部包含了编译器和解析器,用于将项目中的源代码转换为可执行的 JavaScript 代码。编译器负责将各种类型的模块转换为标准的 JavaScript 代码,解析器负责解析 JavaScript 代码中的语法,构建 AST(抽象语法树),以便后续的依赖分析和打包优化。

    Webpack 5 的核心原理包括模块化、依赖分析、打包优化、插件系统以及编译器和解析器等多个方面,它通过这些机制来实现对项目代码的打包和优化,从而提供了高效、灵活的前端构建解决方案。

3.webpack5实践-常用API

在webpack配置文件webpack.config.js中,下面介绍一些必需要掌握的api:

1. entry(入口)

Webpack 5 的入口配置指定了打包的起始点。可以是一个单一的入口或多个入口。下面举例单一入口代码:

module.exports = {
  entry: './src/index.js'
};

搞懂了后,后面我再继续探索多入口,由浅入深,举一反三,加油!!!

2. output(输出)

output 配置项指定了打包后的文件输出的位置和文件名等信息。

module.exports = {
  output: {
    filename: 'bundle.js', //打包后生成js文件名
    path: path.resolve(__dirname, 'dist')。 //js文件在dist目录里头
  }
};

3. devServer(开发服务器)

devServer 配置项用于配置开发服务器,提供了许多有用的功能,如自动刷新、热模块替换等。

module.exports = {
  devServer: {
    contentBase: './dist', //项目打包后生成文件的目录名
    port: 8080, //本地运行的端口号
    hot: true//是否开启热更新
  }
};

4. mode(模式)

mode 配置项指定了当前构建的环境,可选值有 'development':开发模式、'production' :生产环境和 'none' 。

module.exports = {
  mode: 'development' //
};

5. module(模块)

module 配置项用于配置不同类型模块的处理规则,例如配置 loader。

module.exports = {
  module: {
    rules: [
        {
          //.js文件loader
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
              cacheCompression: false
            }
          }
        },
        {
          //.vue文件loader
          test: /\.vue$/,
          use: 'vue-loader'
        },
        {
          //.css文件loader
          test: /\.css$/,
          use: [isProduction ? MiniCssExtractPlugin.loader : 'vue-style-loader', 'css-loader']
        },
        { //图片
          test: /\.(png|svg|jpg|jpeg|gif|bmp|ico)$/,
          type: 'asset/resource',
          generator:{ 
              filename: 'image/[contenthash:10].[ext]',
          }, 
        },
        { //视频或者文件在这里配置
          test: /\.(mp4|ttf)$/,
          use: {
            loader: 'file-loader',
            options: {
              name: 'video/[contenthash:10].[ext]',
            },
          },
        },
      ]
  }
};

6. resolve(解析)

resolve 配置项用于配置模块解析的规则。

module.exports = {
  resolve: {
    extensions: ['.js', '.jsx','vue'],
    alias: {
      '@': path.resolve(__dirname, 'src/')
    }
  }
};

7. optimization(优化)

optimization 配置项用于配置打包优化相关的选项,可以用第三方插件对js文件进行分包和压缩,减少包的体积。

const ESBuildPlugin = require('esbuild-webpack-plugin').default;

module.exports = {
  optimization: {
      minimizer: [new ESBuildPlugin()],
      splitChunks: {
        cacheGroups: {
          defaultVendors: {
            name: 'chunk-vendors',
            test: /[\\/]node_modules[\\/]/,
            priority: -10,
            chunks: 'initial'
          },
          common: {
            name: 'chunk-common',
            minChunks: 2,
            priority: -20,
            chunks: 'initial',
            reuseExistingChunk: true
          }
        }
      }
    },
};

8. plugins(插件)

plugins 配置项用于配置各种 webpack 插件。

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const {VueLoaderPlugin} = require('vue-loader');
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const path = require('path');
const isProduction = process.env.NODE_ENV === 'production';
module.exports = {
  plugins: [
      new VueLoaderPlugin(), //在 webpack 中,需要使用 vue-loader 来处理 Vue.js 单文件组件。VueLoaderPlugin 插件的作用是向 webpack 中添加必要的 loader 配置,以确保 vue-loader 能够正确地解析和加载 Vue.js 单文件组件。同理,用react框架的也可以用同样的方式配置react的loader
      new HtmlWebpackPlugin({  //  webpack 中生成 HTML 文件的插件
        template: 'index.html',//自动创建一个 HTML 文件
        minify: {
          collapseWhitespace: true, //去掉空格
          removeComments: true, //删除注释
          removeAttributeQuotes: true, //删除双引号
          removeEmptyAttributes: true, //删除声明了但是没赋值的属性 let a;
          minifyCSS: true, //压缩css
          minifyJS: true, //压缩js
          minifyURLs: true,//是否对 HTML 文件中的 URL 进行最小化处理
          removeTagWhitespace: true,//是否移除 HTML 标签之间的空白字符
      },
        favicon: path.resolve('favicon.ico') //配置icon图标
      }),
      isProduction ?new MiniCssExtractPlugin({
        filename: 'css/[name].[contenthash:8].css',   //生成css文件路径和名字
        chunkFilename: 'css/[name].[contenthash:8].chunk.css', //生成css的chunkFilename				文件路径和名
      }): null,//将 CSS 提取为独立文件的插件
      new webpack.DefinePlugin({
        __VUE_OPTIONS_API__: false,
        __VUE_PROD_DEVTOOLS__: false,
      }),//定义全局常量的插件。它允许你在编译时创建全局常量,这些常量在编译过程中会被替换为实际的值。这在配置 webpack 打包过程中经常用于传递环境变量或者其他常量。
      new CleanWebpackPlugin(), //每次打包前清理旧的编译文件
  ].filter(Boolean)
};

以上是 Webpack 5 中一些常用配置项的用法,它们可以组合使用项目的需求,搞懂以后,基本上一些中小型项目的框架搭建和打包可以轻松玩转。

4.高级自定义玩转webpack5

如果,简单的webpack5框架无法满足你项目的业务需求,下面我们可以配置一些高级玩法,比如, 配置多入口文件,搭载多页面应用,多页面应用利于SEO,在上述代码文件webpack.config.js的基础上,新增文件webpack.util.js配置自定义入口,

const glob = require('glob');
const HtmlWebpackPlugin = require('html-webpack-plugin');

function setEntry() {
  const files = glob.sync('./src/pages/**/index.js') //多文件入口的路径
  const entry = {}
  files.forEach(file => {
    const ret = file.match(/^\.\/src\/pages\/(\S*)\/index\.js$/)
    if (ret) {
      entry[ret[1]] = {
        import: file,
        dependOn: 'vue_vendors',
      }
    }
  })
   // 拆分vue依赖
   entry['vue_vendors'] = {
    import: ['vue'],
    filename: 'commom/[name].js'
  }
  return entry
}
function getTemplate() {
    const files = glob.sync(`./src/index.html`)
    return files[0]
}
  
function setHtmlPlugin() {
    const files = glob.sync('./src/pages/**/index.js')
    const options = []
    files.forEach(file => {
      const ret = file.match(/^\.\/src\/pages\/(\S*)\/index\.js$/)
      if (ret) {
        const name = ret[1]
        if(name === 'home'){
          options.push(new HtmlWebpackPlugin({
            filename: 'index.html',
            template: getTemplate(),
            title: name,
            minify: {
              collapseWhitespace: false,
              removeComments: true, 
            },
            chunks: ['vue_vendors',name]
          }))
        }
        options.push(new HtmlWebpackPlugin({
          filename: `${name}.html`,
          template: getTemplate(),
          title: name,
          minify: {
            collapseWhitespace: false,
            removeComments: true, 
          },
          chunks: ['vue_vendors',name]
        }))
      }
    })
    return options
}
module.exports = {
  setEntry,
  setHtmlPlugin
}

然后再webpack.config.js文件中引入webpack.util.js文件的方法

const { setEntry,setHtmlPlugin } = require('./webpack.util.js')
module.exports = () => {
	return {
	...
	entry: setEntry,//引入自定义入口文件的方法
	plugins: [
      ...setHtmlPlugin(), //引入自定义设置html的方法
      //plugins中的剩余代码同上
    ].filter(Boolean)
    }
}

此外,如果需要自定义区分生产环境和开发环境,也可以新增两个不同的webpack配置文件,区分两个不同的环境,这种情况一般在大型项目中比较常用,举个简单的例子:

  • 在开发环境webpack.dev.js
const config = require('./webpack.config.js')
module.exports = Object.assign({}, config, {
    mode: "development",
    devtool: 'inline-source-map',
    devServer: {
        open: true, 
        hot: true, 
        port: 8000, 
        static: "./public", 
        historyApiFallback: true,
        //proxy: {
          // '/': {
          //   target: 'http://127.0.0.1',
          //   pathRewrite:{'^/':'/'},
          //   secure: false,
          //   changeOrigin: true,
          // },
        //}
    },
    ...
})
  • 在生产环境webpack.prod.js中配置
const config = require('./webpack.config.js')
module.exports = Object.assign({}, config, {
    mode: "production",
    devtool: false,
    ...
})

最后,在package.json文件中分别设置两个不同环境的启动方式:


{
 "name": "webpack5",
 "version": "1.0.0",
 "description": "搭建webpack5框架",
 "main": "webpack.config.js",
 "scripts": {
    "build": "webpack --config webpack.prod.js --mode production", //生产环境
    "start": "webpack serve --config ./webpack.dev.js --mode development", //开发环境
    "test": "echo \"Error: no test specified\" && exit 1" //暂无测试环境,可根据自己的业务配置
  },
  "keywords": [],
  "author": "高级架构师",
  "license": "ISC",
  "dependencies": {
    "clean-webpack-plugin": "^4.0.0",
    "element-plus": "^2.4.2",
    "esbuild-webpack-plugin": "^1.1.0",
    "vue": "3.2.26",
  },
  "devDependencies": {
    "@babel/core": "^7.23.3",
    "@babel/plugin-transform-runtime": "^7.23.4",
    "@babel/preset-env": "^7.23.3",
    "babel-loader": "^9.1.3",
    "html-webpack-plugin": "^5.5.3",
    "mini-css-extract-plugin": "^2.7.6",
    "vue-loader": "^17.3.1",
    "vue-style-loader": "^4.1.3",
    "webpack": "^5.89.0",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^4.15.1"
  }
}

我们想启动开发环境:npm run dev,线上环境:npm run build

最后也是本文最重要的,码字不易,我会持续输出前端技术干货,欢迎点赞关注加收藏,你的鼓励是我持之以恒码字的动力,感谢!!!