webpack 打包配置及优化


大前端重要知识点
58 篇文章42 订阅
订阅专栏
这个文章就是为了教大家怎么升级到webpack5 以及 怎么去最大程度的优化配置,使得构建速度得到提升。项目用的就是【章节1】和【章节2】的例子。
【章节4】原项目升级到webpack5并进行配置优化
【章节5】创建项目的一些想法
流程比较长,这是个通用流程,有不懂的地方可以加我微信,能帮则帮。

一、前言
可以升了wepack5 再 做commit 预处理的方案,也可以先commit 预处理 再来 升webpack5,我这是后者。
基础工程:https://gitee.com/huqinggui/webpack5_base.git
clone 下来后需要切换到webpack4-d2分支

git clone https://gitee.com/huqinggui/webpack5_base.git -b webpack4
cd webpack5_base
npm install
1
2
3
项目体积越大越能看见差异,最好是编译几分钟以上的项目,如果是1分钟以内的就没必要升级webpack5了(需要用到webpack5联邦模块的例外),我这个工程我大概构建速度20s,代码量几乎没有,我硬装了6个ui组件库进去,采用的vue-cli的形式的,默认时优化了的,内置的webpack4。本次示例将升级到webpack5,并且我门手动配置webpack5,看下他能达到什么程度。

看我用了这6个ui组件,就将几乎0代码量的工程整到了20s启动和构建,可见效率是很低的。

二、添加webpack5相关依赖 和建立webpack配置目录
注意:我会固定版本,是为了避免出现版本不兼容情况,你们可以不带版本,如果报错换成对应版本即可,当然版本越高越好!以前的webpack的配置也要注意下,因为存在一些特殊配置在src里用了,所以我们对应的需要在webpack里做相应的配置!

"webpack": "^5.83.1", // webpack5
"webpack-cli": "^5.1.1",// webpack5 版本的 cli
"webpack-dev-server": "^4.15.0", // 开法环境server服务
"webpack-merge": "^5.8.0",// 合并配置用
"webpackbar": "^5.0.2" // 进度条
1
2
3
4
5
执行:

npm install -D webpack@5.83.1 webpack-cli@5.1.1 webpack-dev-server@4.15.0 webpack-merge@5.8.0 webpackbar@5.0.2
1
项目根目录下新建webpack目录,这里存放webpack配置文件。
目录结构如图:


// 目录结构
webpack
    config // 环境配置
        config.js
        index.js
    lib // webpack工具
        alias.js
        cdn.js
        console.js // 没有实际作用为了在终端打印好看东西
        rules.js
        utils.js
    dev-server.js // 启动服务
    webpack.config.analyzer.js // 包分析配置
    webpack.config.common.js // 启动 和 build 公用配置
    webpack.config.dev.js // 启动配置
    webpack.config.prod.js // build 配置
    webpack.config.speed.js // 速度分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
三、package.json脚本更改
"dev": "cross-env NODE_ENV=dev node ./webpack/dev-server.js",
"sit": "cross-env NODE_ENV=sit node ./webpack/dev-server.js",
"pre": "cross-env NODE_ENV=pre node ./webpack/dev-server.js",
"prod": "cross-env NODE_ENV=prod node ./webpack/dev-server.js",
"build": "cross-env NODE_ENV=production webpack --config ./webpack/webpack.config.prod.js",
"build:analyzer": "cross-env NODE_ENV=production webpack --config ./webpack/webpack.config.analyzer.js",
"build:speed": "cross-env NODE_ENV=production webpack --config ./webpack/webpack.config.speed.js",
1
2
3
4
5
6
7
从脚本上看:

cross-env设置了dev(开发)、sit(测试)、pre(预生产)、prod(生产)四个环境的启动环境变量
cross-env设置了一个production(生产)构建变量
build的入口文件是./webpack/webpack.config.prod.js,其他的是./webpack/dev-server.js
添加了包分析脚本(build:analyzer) 和速 度分析脚本(build:speed)
npm i -D cross-env@7.0.3
1
四、补充对应文件里的代码内容—启动
dev-server.js
const Webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const getWebpackConfig = require('./webpack.config.dev.js'); //开发配置

const localIPv4 = WebpackDevServer.internalIPSync('v4'); // 获取ipv4地址

const compiler = Webpack(getWebpackConfig(localIPv4));
const devServerOptions = { ...getWebpackConfig(localIPv4).devServer };
const devServer = new WebpackDevServer(devServerOptions, compiler);

devServer.startCallback(() => {}); // 启动
1
2
3
4
5
6
7
8
9
10
11
config/config.js
const getConfig = (env) => {
  return {
    dev: {
      envName: '开发环境',
      proxy: 'http://xxxxx',
      PORT: 8088,
    },
    sit: {
      envName: '测试环境',
      proxy: 'http://xxxx',
      PORT: 8089,
    },
    pre: {
      envName: '预生产环境',
      proxy: 'http://xxxxx',
      PORT: 8090,
    },
    prod: {// 不到万不得已 禁止使用!!!
      envName: '生产环境',
      proxy: 'xxxxx',
      PORT: 8091,
    },
    // build
    production: {
      envName: '生产环境',
    },
  }[env];
};
module.exports = (env) => getConfig(env);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
可见:

启动环境的 四个环境名和接口代理地址 及端口号
生产环境的环境名
config/index.js
"use strict";
const path = require("path");
const env = process.env.NODE_ENV;
const envConfig = require("./config")(env); // 当前启动环境

module.exports = {
  // 构建配置
  build: {
    index: path.resolve(__dirname, "../../dist/index.html"),
    assetsRoot: path.resolve(__dirname, "../../dist"),
    assetsSubDirectory: "static",
    assetsPublicPath: "/", // 对应router 的 base
    productionSourceMap: false,
    productionGzip: true,
    productionGzipExtensions: ["js", "css"],
    bundleAnalyzerReport: process.env.npm_config_report,
  },
  // 启动配置
  dev: {
    envToStart: envConfig.envName, // 启动环境
    port: envConfig.PORT,
    autoOpenBrowser: true,
    assetsSubDirectory: "static",
    assetsPublicPath: "/",
    devtool: "eval-cheap-module-source-map",
    proxyTable: {
      "/ins/web": {
        target: envConfig.proxy,
        // ws: true,        //如果要代理 websockets,配置这个参数
        // secure: false,  // 如果是https接口,需要配置这个参数
        logLevel: "silent", // 不显示终端提示
        changeOrigin: true, //是否跨域
        pathRewrite: {
          "^/ins/web/": "/ins/web/",
        },
      },
    },
  },
};

console.log(
  "\x1b[36m%s\x1b[0m",
  `【${envConfig.envName}】webpack正在努力为您构建中................`
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
可见:

从config里拿了代理地址和环境名
区分了构建时的配置和启动时的配置
webpack.config.dev.js
const { merge } = require("webpack-merge");
const FriendlyErrorsPlugin = require("@soda/friendly-errors-webpack-plugin");
const colors = require("ansicolors");

const { resolvePath, assetsPath } = require("./lib/utils.js");

const config = require("./config/index");
const rules = require("./lib/rules.js");
const common = require("./webpack.config.common.js");

// 忽略不安全警告. https需要
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";

// webpack-dev-server 配置
const devServer = {
  https: false, // 是否https启动
  hot: true, // 热更新
  host: "localhost",
  port: config.dev.port,
  compress: true, //采用gzip压缩
  open: config.dev.autoOpenBrowser, // 自动打开
  client: {
    logging: "error",
    progress: false, // 自带进度显示
    overlay: false, // 编译错误浏览器全屏覆盖
    progress: false, // 浏览器中显示编译进度
  },
  historyApiFallback: true, // 解决前端路由刷新404问题
  static: resolvePath("./dist/"),
  proxy: config.dev.proxyTable, // 代理配置
};

// webpack 配置
const getDevWebpackConfig = (ipv4) => {
  return merge(common, {
    mode: "development",
    devtool: config.dev.devtool,
    stats: "errors-only",
    output: {
      filename: "js/[name].js", // 本地无需hash
      publicPath: config.dev.assetsPublicPath,
      assetModuleFilename: assetsPath("images/[name][ext]"),
    },
    optimization: {
      nodeEnv: process.env.NODE_ENV,
      runtimeChunk: "single",
    },
    module: { rules: rules() },
    devServer: devServer,
    plugins: [
      new FriendlyErrorsPlugin({
        // 自定义控制台输出
        compilationSuccessInfo: {
          messages: [
            `【${config.dev.envToStart}】App running at:`,
            `- Local:    ` +
              colors.blue(
                `http${devServer.https ? "s" : ""}://${devServer.host}:${
                  config.dev.port
                }`
              ),
            `- Network:  ` +
              colors.blue(
                `http${devServer.https ? "s" : ""}://${ipv4}:${config.dev.port}`
              ),
          ],
        },
        clearConsole: true, // 清除控制台
        additionalFormatters: [
          (errors) => ({
            title: "Compiled failed that affect everything",
            log: `${errors.map((e) => e.message).join("\n")}`,
          }),
        ],
        additionalTransformers: [(error) => error],
      }),
    ],
  });
};

module.exports = (ipv4) => getDevWebpackConfig(ipv4);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
引入了三个包:

webpack-merge:合并webpack5配置
@soda/friendly-errors-webpack-plugin:自定义控制台输出
ansicolors:终端颜色控制
npm install -D webpack-merge@5.8.0 @soda/friendly-errors-webpack-plugin@1.8.1 ansicolors@0.3.2
1
引入了webpack公用方法resolvePath
引入了构建规则lib/rules.js
引入了公共配置文件webpack.config.common.js

webpack.config.common.js
const { ProvidePlugin } = require("webpack");
const { VueLoaderPlugin } = require("vue-loader");

const HtmlPlugin = require("html-webpack-plugin"); // 将打包出的静态文件插入到 HTML 模板中
const CopyWebpackPlugin = require("copy-webpack-plugin"); // 复制文件到指定目录
const WebpackBar = require("webpackbar");
const { resolvePath } = require("./lib/utils.js");
const config = require("./config/index");

const configType = process.env.NODE_ENV === "production" ? "build" : "dev";

const alias = require("./lib/alias.js");

module.exports = {
  entry: ["./src/main.js"],
  output: {
    clean: true,
    path: resolvePath("./dist/"),
  },
  resolve: {
    alias,
    extensions: [".js", ".ts", ".vue", ".json"],
  },
  plugins: [
    new HtmlPlugin({
      title: "webpack5-demo",
      inject: "head",
      filename: "index.html",
      template: resolvePath("./public/index.html"),
    }),
    // 解决 static 下的资源找不到问题
    new CopyWebpackPlugin({
      patterns: [
        {
          from: resolvePath("./static/"),
          to: config[configType].assetsSubDirectory,
        },
      ],
    }),
    new ProvidePlugin({ process: require.resolve("process/browser") }),
    new WebpackBar({
      // color: "#85d", // 默认green,进度条颜色支持HEX
      basic: false, // 默认true,启用一个简单的日志报告器
      profile: false, // 默认false,启用探查器。
    }),
    new VueLoaderPlugin(),
  ],
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
npm i -D vue-loader@15.9.8 html-webpack-plugin@5.5.0 copy-webpack-plugin@6.4.1
1
lib/alias.js
const { resolvePath } = require("./utils.js");

module.exports = {
  'vue$': 'vue/dist/vue.esm.js',// 解决el-table无法渲染数据
  "@": resolvePath("./src"),
  "@api": resolvePath("./src/api-new"),
  "@assets": resolvePath("./src/assets"),
  "@static": resolvePath("../static"),
  "@components": resolvePath("./src/components"),
  "@router": resolvePath("./src/router"),
  "@lib": resolvePath("./src/lib"),
  "@views": resolvePath("./src/views"),
};
1
2
3
4
5
6
7
8
9
10
11
12
13
lib/utils.js
const path = require("path");
const config = require("../config/index");

const resolvePath = (_path) => {
  return path.resolve(process.cwd(), _path);
};

const assetsPath = (_path) => {
  const assetsSubDirectory =
    process.env.NODE_ENV === "production"
      ? config.build.assetsSubDirectory
      : config.dev.assetsSubDirectory;
  return path.posix.join(assetsSubDirectory, _path);
};

module.exports = {
  resolvePath,
  assetsPath,
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
lib/rule.js
const { assetsPath } = require("./utils.js");

const common = [
  {
    test: /\.vue$/,
    loader: "vue-loader",
  },
  {
    test: /\.js$/,
    exclude: /node_modules/,
    use: [
      {
        loader: "babel-loader",
        options: {},
      },
    ],
  },
  {
    test: /\.txt$/,
    type: "asset/source",
  },
  {
    test: /\.(png|svg|gif|jpe?g|webp)$/,
    type: "asset/resource",
    parser: { dataUrlCondition: { maxSize: 5 * 1024 } },
  },
  {
    test: /\.(woff|woff2|eot|ttf|otf)$/,
    type: "asset/resource",
    generator: { filename: assetsPath("font/[name]-[contenthash:8][ext]") },
  },
];

const dev = [
  {
    test: /\.css$/i,
    use: ["style-loader", "css-loader"],
  },
  {
    test: /\.s[ac]ss$/i,
    use: ["style-loader", "css-loader", "sass-loader"],
  },
  {
    test: /\.less$/i,
    use: ["style-loader", "css-loader", "less-loader"],
  },
];

const prod = [
  {
    test: /\.css$/i,
    use: ["css-loader"],
  },
  {
    test: /\.s[ac]ss$/i,
    use: ["css-loader", "sass-loader"],
  },
  {
    test: /\.less$/i,
    use: ["css-loader", "less-loader"],
  },
];

module.exports = (env) => common.concat(env === "prod" ? prod : dev);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
这里用了几个包:

babel-loader :js编译用的
style-loader、css-loader、less、less-loader:css编译用的
npm i -D babel-loader@8.2.4 style-loader@3.3.1 css-loader@6.7.1 less@4.1.3 less-loader@5.0.0
1
如过项目里用了sass请自行 安装 sass 和sass-loader。安装需对应node版本。

通过以上配置我们已经可以启动项目了。

五、验证项目启动


验证结果:
开发 测试 预生产 生产 都可以启动!

六、补充对应文件里的代码内容—build构建
webpack.config.prod.js
const { merge } = require("webpack-merge");

const rules = require("./lib/rules.js");
const config = require("./config/index");
const { assetsPath } = require("./lib/utils.js");
const common = require("./webpack.config.common.js");

module.exports = merge(common, {
  mode: "production",
  devtool: false,
  stats: "errors-only", // 只显示错误信息
  output: {
    path: config.build.assetsRoot,
    filename: assetsPath("js/[name]-[contenthash:8].js"),
    assetModuleFilename: assetsPath("images/[name]-[contenthash:8][ext]"),
    publicPath: config.build.assetsPublicPath,
    chunkFilename: assetsPath("chunk/[name]-[contenthash:8].js"),
  },
  module: { rules: rules("prod") },
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

升级以后构建速度为29s,对比vuecli创建的webpack4项目来说确实确实慢些,毕竟我们还没做任何的优化处理,接下来我们看看怎么优化!

七、包分析plugin 和 速度分析plugin
npm i -D webpack-bundle-analyzer@4.5.0 speed-measure-webpack-plugin@1.5.0
1
webpack.config.analyzer.js
const { merge } = require("webpack-merge");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");

// 生产配置
const prod = require("./webpack.config.prod.js");

const result = merge(prod, {
  plugins: [
    new BundleAnalyzerPlugin({ openAnalyzer: true }),
  ],
});

module.exports = result;
1
2
3
4
5
6
7
8
9
10
11
12
13
webpack.config.speed.js
// 使用此功能需要关闭webpack5缓存

const { merge } = require('webpack-merge');
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');

// 生产配置
const prod = require('./webpack.config.prod.js');

const result = merge(prod, {
  plugins: [new SpeedMeasurePlugin()],
});

module.exports = result;
1
2
3
4
5
6
7
8
9
10
11
12
13
npm run build:analyzer
npm run build:speed
1
2


这么一来webpack5 的项目就非常完善了,接着呢我们就要做点配置来优化一下项目!

八、优化 npm run build速度和 产出文件大小
优化思路很重要,我们从什么角度优化呢,产出文件大小决定着用户加载资源的大小,所以这个是肯定要解决的,那怎噩梦解决呢?

压缩
compression-webpack-plugin

npm i -D compression-webpack-plugin@10.0.0
1
webpack.config.prod.js
const CompressionWebpackPlugin = require("compression-webpack-plugin"); // 为输出文件启用 Gzip 压缩并生成相应的 .gz 文件
optimization: {
    minimize: true,
    minimizer: [
      // 开启gzip压缩
      new CompressionWebpackPlugin({
        filename: "[path][base].gz",
        algorithm: "gzip",
        test: /\.js$|\.css$|\.html$/,
        threshold: 10240,
        minRatio: 0.8,
        deleteOriginalAssets: false, // 是否删除源文件
      }),
    ],
  },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
添加完后我们npm run build试试

惊人的5s就构建好了,再看看代码体积


.gz局势压缩后的文件,1.3m,秀儿!

分包
我们来试试分包

webpack.config.prod.js
optimization: {
    nodeEnv: "production",
    runtimeChunk: "single",
    splitChunks: {
      chunks: "all", // 同时分割同步和异步代码,推荐。
      cacheGroups: {
        // 公用模块抽离
        common: {
          chunks: "initial",
          minSize: 0, // 大于0个字节
          minChunks: 2, // 抽离公共代码时,这个代码块最小被引用的次数
        },
        element: {
          test: /[\\/]node_modules[\\/](element-ui)[\\/]/,
          name: "element", // 将这个缓存组命名为 element
          chunks: "all",
        },
        // 第三方库抽离
        vendor: {
          priority: 1, // 权重
          test: /node_modules/,
          chunks: "initial",
          minSize: 0, // 大于0个字节
          minChunks: 2, // 在分割之前,这个代码块最小应该被引用的次数
        },
      },
    },
  },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
npm run build试试,速度略有提升

再看看包体积


包的体积也有减小,cacheGroups 可以自定义选择抽离哪些代码组成一个文件。我这里抽出了elementui,同时他还会根据模块自己抽离出来。

babel-loader 是不是 可以被替换?(esbuild)
可以!现在有了esbuilder 可以替代babel-loader更高速度的编译js,底层是go,速度杠杠的,vite的底层就是他

npm i -D esbuild-loader@3.0.1
1
lib/rules.js
- {
  - test: /\.js$/,
  - exclude: /node_modules/,
  -  use: [
  -    {
  -      loader: "babel-loader",
  -      options: {},
  -    },
  -  ],
- },

+ {
+   test: /\.(js|jsx|ts|tsx)$/,
+   exclude: /node_modules/,
+   use: [
+     {
+       loader: 'esbuild-loader', // 代替 babel-loader 提升速度
+       options: {
+         target: 'es2015', // 目标版本.
+         loader: 'jsx', // 指定loader例如 jsx/ts/tsx
+       },
+     },
+   ],
+ },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
webpack.config.prod
+ const { EsbuildPlugin } = require("esbuild-loader"); // 替代 terser-webpack-plugin 和 css-minimizer-webpack-plugin 压缩代码

optimization: {
  minimizer: [
+     // js css处理
+     new EsbuildPlugin({
+       target: "es2015",
+       css: true,
+       minify: true,
+       minifyWhitespace: true, // 去掉空格
+       minifyIdentifiers: true, // 缩短标识符
+       minifySyntax: true, // 缩短语法
+       legalComments: "none", // 去掉注释
+       exclude: /node_modules/,
+     }),
  ],
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

看着好像提升很小,实际上是因为代码量太少了,如果是正常的项目是非常巨大的提升的!

产出的js也被压缩了,那么他的大小?

也变小了!!!

将css从js提出来
尽管做了上面这些,但是呢我发现js里有css,那么肯定是不好的

所以我们把它提取出来。

npm i -D mini-css-extract-plugin@2.7.6
1
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 将 CSS 从 JavaScript 中提取出来作为单独的 CSS 文件
optimization: {
  minimizer: [
+     new MiniCssExtractPlugin({
+       filename: assetsPath("style/[name]-[contenthash:8].css"),
+     }), // 样式内容将打包成一个或多个单独的CSS文件
  ],
},
1
2
3
4
5
6
7
8
lib/rules.js
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin');

- const prod = [
-  {
-    test: /\.css$/i,
-    use: ["css-loader"],
-  },
-  {
-    test: /\.s[ac]ss$/i,
-    use: ["css-loader", "sass-loader"],
-  },
-  {
-    test: /\.less$/i,
-    use: ["css-loader", "less-loader"],
-  },
- ];

+ const prod = [
+   {
+     test: /\.css$/i,
+     use: [MiniCssExtractPlugin.loader, "css-loader"],
+   },
+   {
+     test: /\.s[ac]ss$/i,
+     use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
+   },
+   {
+     test: /\.less$/i,
+     use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
+   },
+ ];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
npm run build看看

css都被i提取到了style目录下,js文件也变小了,构建速度没什么变化

多线程构建
有些babel处理的东西非常大,就会非常耗时,thread-loader 将这些耗时的任务放到 worker 池中,利用多线程并发执行任务,从而提高构建速度。
注意:thread-loader用了如果变慢了,说明你的代码量太少了,编译时间还没启动一个线程花的时间多。
本文只是例子,对于正常的项目,强烈推荐使用多线程!

npm i -D thread-loader@4.0.2
1
lib/rules.js
{
  test: /\.vue$/,
- loader: "vue-loader",
+ use: [
+    "thread-loader", // 多线程
+    {
+      loader: 'vue-loader',
+      options: {
+        compilerOptions: {
+          cacheDirectory: true,
+          preserveWhitespace: false,
+       },
+     },
+    },
+  ],
},
{
  test: /\.(js|jsx|ts|tsx)$/,
  exclude: /node_modules/,
  use: [
+   'thread-loader', // 多线程
    {
      loader: "esbuild-loader", // 代替 babel-loader 提升速度
      options: {
        target: "es2015", // 目标版本.
        loader: "jsx", // 指定loader例如 jsx/ts/tsx
      },
    },
  ],
},

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
oneOf
当我们使用多个 loader 时,Webpack 需要对每个文件都执行所有的 loader,这会导致编译速度变慢。为了提高编译速度,我们可以使用 oneOf 配置项来优化 loader 的执行顺序。 oneOf 配置项允许我们在多个 loader 中选择一个匹配的 loader,并只对该文件执行该 loader,从而减少不必要的 loader 执行,提高编译速度。

部分包放cdn
lib/cdn.js
module.exports = [
    {
      name: "vue",
      library: "Vue",
      js: "https://file.tuns.com.cn/npm/vue@2.7.14/dist/vue.min.js",
      css: "",
    },
    {
      name: "vue-router",
      library: "VueRouter",
      js: "https://file.tuns.com.cn/npm/vue-router@3.6.5/dist/vue-router.min.js",
      css: "",
    },
    {
      name: "vuex",
      library: "Vuex",
      js: "https://file.tuns.com.cn/npm/vuex@3.6.2/dist/vuex.min.js",
      css: "",
    },
    {
      name: "axios",
      library: "axios",
      js: "https://file.tuns.com.cn/npm/axios@0.17.1/dist/axios.min.js",
      css: "",
    },
    {
      name: "element-ui",
      library: "ELEMENT",
      js: "https://file.tuns.com.cn/npm/element-ui@2.15.13/lib/index.js",
      css: "https://file.tuns.com.cn/npm/element-ui@2.15.13/lib/theme-chalk/index.css",
    },
    {
      name: "echarts",
      library: "echarts",
      js: "https://file.tuns.com.cn/npm/echarts@3.8.5/dist/echarts.min.js",
      css: "",
    },
  ];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
lib/utils.js
+ const cdn = require("./cdn");
// 组装externals
const getExternals = () => {
  let externals = {};
  for (const {name, library} of cdn) {
    // externals[name] = [js || css, library];
    // 将 ${name} 转换为 ${library}
    externals[name] = `${library}`;
  }
  return externals;
};

// 组装index.html 的 css 和 js
const getCdnConfig = () => {
  let cdnConfig = {
    js: [],
    css: [],
  };
  cdn.forEach((config) => {
    if (config.js) cdnConfig.js.push(config.js);
    if (config.css) cdnConfig.css.push(config.css);
  });
  return cdnConfig;
};

module.exports = {
  resolvePath,
  assetsPath,
+  getExternals,
+  getCdnConfig,
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
webpack.config.common.js
- const { resolvePath } = require("./lib/utils.js");
+ const {
+  resolvePath,
+  getExternals,
+  getCdnConfig,
+ } = require("./lib/utils.js");

module.exports = {
...
+   externals: getExternals(),
  plugins: [
   new HtmlPlugin({
     title: "webpack5-demo",
     inject: "head",
     filename: "index.html",
     template: resolvePath("./public/index.html"),
+    cdnConfig: getCdnConfig(), // 自动将cdn 注入到index.html
   }),
 ],
...
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
根目录/public/index.html
<!DOCTYPE html>
<html lang="">
  <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">
    <link rel="icon" href="/favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
    <!-- 使用CDN的CSS文件 -->
    <% for (var i in htmlWebpackPlugin.options.cdnConfig && htmlWebpackPlugin.options.cdnConfig.css) { %>
    <link href="<%= htmlWebpackPlugin.options.cdnConfig.css[i] %>" rel="external nofollow preload" as="style" />
    <link href="<%= htmlWebpackPlugin.options.cdnConfig.css[i] %>" rel="external nofollow stylesheet" />
    <% } %> 
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
  <!-- 使用CDN的JS文件 -->
  <% for (var i in htmlWebpackPlugin.options.cdnConfig && htmlWebpackPlugin.options.cdnConfig.js) { %>
    <script src="<%= htmlWebpackPlugin.options.cdnConfig.js[i] %>" rel="external nofollow preload" as="script"></script>
  <% } %>
</html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Webpack打包优化具体配置如下: 1. 缩小打包体积:可以通过以下方式缩小打包体积。 - Tree Shaking:在Webpack 4中,可以通过设置mode为production自动启用Tree Shaking,去除无用代码。也可以通过设置optimization.usedExports为true手动启用。 - Code Splitting:通过使用Webpack的Code Splitting功能,将一个大文件拆分成多个小文件,只按需加载需要的模块。 - 动态导入:通过使用import()或require.ensure()等语法,实现按需加载模块。 2. 加快打包速度:可以通过以下方式加快打包速度。 - 缓存:使用babel-loader时,可以开启缓存,避免相同的文件重复编译。 - 多线程:使用HappyPack或thread-loader等插件,将loader的执行过程多线程并行处理,提高打包速度。 - DllPlugin:使用DllPlugin将第三方库单独打包成一个文件,减少重复打包,提高打包速度。 3. 优化代码质量:可以通过以下方式优化代码质量。 - ESLint:使用ESLint进行代码风格检查和错误检查,提高代码质量。 - Prettier:使用Prettier进行代码格式化,统一代码格式,提高代码可读性。 - TypeScript:使用TypeScript进行类型检查,提高代码质量和可维护性。 4. 优化图片资源:可以通过以下方式优化图片资源。 - 压缩:使用imagemin-webpack-plugin等插件,对图片进行压缩。 - 转换:使用url-loader或file-loader等插件,将图片转换为DataURL或Base64格式,减少网络请求。 - 懒加载:使用Intersection Observer API等技术,实现图片懒加载,提高页面加载速度。 5. 拆分第三方库:可以通过以下方式拆分第三方库。 - externals:通过配置externals属性,将第三方库从Webpack打包文件中排除,使用CDN等方式加载。 - DllPlugin:使用DllPlugin将第三方库单独打包成一个文件,通过CDN等方式加载,减少打包体积和页面加载时间。 6. 优化Webpack配置:可以通过以下方式优化Webpack配置。 - resolve.alias:通过配置resolve.alias属性,指定模块的别名,加快模块查找速度。 - noParse:通过配置noParse属性,排除不需要解析的模块,加快打包速度。 以上是Webpack打包优化的具体配置,具体实现需要根据具体项目情况进行分析和决策。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值