Webpack5 基本使用 - 3(完结)

环境区分

可以定义多个配置文件,通过 webpack-merge 合并配置文件。
安装 webpack-merge

yarn add webpack-merge

公共配置

// webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    entry: path.join(__dirname, 'index'),
    module: {
        rules: [
            {
                test: /\.js$/,
                use: ['babel-loader'],
                include: path.join(__dirname, 'src'),
                exclude: /node_modules/
            },
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.join(__dirname, 'src/index.html'),
            filename: 'index.html'
        })
    ]
};

开发环境配置

// webpack.dev.js
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const webpackCommonConf = require('./webpack.common.js');

module.exports = merge(webpackCommonConf, {
    mode: 'development',
    plugins: [
    	// 定义环境变量
        new webpack.DefinePlugin({
       		// window.ENV = 'production'
            ENV: JSON.stringify('development')
        })
    ],
    devServer: {
        port: 8080, // 修改端口号
        hot: true, // 开启 HMR:开启后 index.html 不会自动刷新
        static: {
            watch: true, // 自动刷新浏览器
            staticOptions: {
                progress: true, // 显示打包的进度条
                contentBase: path.join(__dirname, 'dist') // 指定运行代码的目录,输出在内存中,看不到
            }
        },
        open: true, // 自动打开浏览器
        compress: true, // 启动 gzip 压缩
        // 设置代理
        proxy: {
            // 将本地 /api/xxx 代理到 localhost:3000/api/xxx
            '/api': 'http://localhost:3000',

            // 将本地 /api2/xxx 代理到 localhost:3000/xxx
            '/api2': {
                target: 'http://localhost:3000',
                pathRewrite: {
                    '/api2': ''
                }
            }
        }
    }
});
// package.json
{
  "name": "webpack5-zql",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack-dev-server --config webpack.dev.js",
  },
}

生产环境配置

// webpack.prod.js
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const webpackCommonConf = require('./webpack.common.js');

module.exports = merge(webpackCommonConf, {
    mode: 'production',
    output: {
         filename: 'bundle.[contenthash:8].js', // 输出文件名,打包代码时加上 hash 戳可以命中缓存
        path: path.join(__dirname, 'dist'), // 输出的文件目录
        clean: true, //
        // publicPath: 'http://cdn.abc.com'  // 修改所有静态文件 url 的前缀(如 cdn 域名),这里暂时用不到
    },
    plugins: [
        new webpack.DefinePlugin({
            // window.ENV = 'production'
            ENV: JSON.stringify('production')
        })
    ]
});

// package.json
{
  "name": "webpack5-zql",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config webpack.prod.js"
  },
}

提升开发体验

  • SourceMap(源代码映射):用来生成源代码与构建后代码一一映射的文件。
  • 它会生成一个 xxx.map 文件,里面包含换代码和构建后代码每一行、每一列的映射关系。当构建后代码出错了,会通过 xxx.map 文件,从构建后代码出错的位置找到映射后源代码出错的位置,帮助我们更快的找到错误根源。

通过 webpack.devtool 配置源代码映射。

开发环境

  • 开发环境:cheap-module-source-map
  • 打包编译速度快,只包含行映射,没有列映射。
module.exports = {
	mode: 'development',
	devtool: 'cheap-module-source-map'
};

生产环境

  • 生产环境:source-map
  • 包含行、列映射,打包速度编译慢
module.exports = {
	mode: 'production',
	devtool: 'source-map'
};

优化打包构建速度

HMR(仅开发环境)

  • 开发时,我们修改了其中一个模块代码,webpack 默认会将所有模块全部重新打包,速度很慢,所以我们需要做到修改某个模块代码,就只有这个模块代码需要重新打包编译,其他的模块不变,这样打包速度就可以加快。
  • Hot Module Replacement (热模块替换):在程序运行中,替换、添加或删除模块,而无序重新打包整个模块。
module.exports = {
	mode: 'development',
	devServer: {
		hot: true // 开启 HMR,默认是 true
	}
};
  • 开发环境下,css 文件默认支持 HMR,是因为 style-loader 做了支持,如果使用 MiniCssExtractPlugin.loadercssHMR 会失效,所有开发环境下请使用 style-loader ,生产环境下使用 MiniCssExtractPlugin.loader

  • js 文件默认是不支持 HMR 的,修改 js 文件依然会全部代码重新打包,如果想让某个 js 文件支持 HMR,那么需要在入口文件中写上如下代码:

if (module.hot) {
    // 首先判断是否支持 HMR 功能,因为有些低版本浏览器是不支持的
    module.hot.accept('./common/utils/add.js'); // 接收 add.js,一旦 add.js 发生变化,就只加载这个文件
    // 由于开发项目这样写起来很麻烦,所以可以采用 loader 自动实现
    // 开发 vue 项目可以使用 vue-loader
    // 开发 react 项目可以使用 react-hot-loader
}

HMR 原理

webpack-dev-server 会创建两个服务:express 服务(提供静态资源) 和 socket 服务(服务器可以主动发送文件到客户端)。

  • express server 负责直接提供静态资源的服务:打包后的资源直接被浏览器请求和解析。
  • HMR Socket Server,是一个 socket 的长连接,建立连接后双方可以通信,服务器可以直接发送文件到客户端。而 http 请求必须要浏览器主动发起请求。

当服务器监听到对应的模块发生变化时,会生成两个文件。 manifest.json 文件记录更新的位置信息等配置信息,update chunk .js 文件记录实际更新的具体内容。通过长连接,可以直接将这两个文件主动发送给浏览器,浏览器拿到两个新的文件后,通过 HMR runtime 机制(webpack 在打包的时候提供),加载这两个文件,并且针对修改的模块进行更新。

oneOf

优化生产环境构建打包速度。

正常来讲,一种文件只能被一个 loader 处理。当一种文件要被多个 loader 处理,那么一定要指定 loader 执行的先后顺序:比如处理 js 时,先执行 eslint 再执行 babel

假如有 10loader,打包一种文件时就会轮询 10loader
如果用了oneOf,只要匹配到了这个loader就不会再往后面继续轮询。但是oneOf里面不能有两个配置处理同一种类型文件,相同的话需要抽出一个放在oneOf 外面,然后指定优先执行。

module.exports = {
	module: {
		rules: [
			{
				test: /\.(js|jsx)/,
				loader: 'eslint-loader',
				enforce: 'pre', // 指定优先执行
			},
			{
				oneOf: [
			   		// oneOf 里的 loader 只会执行一个
					{
						test: /\.(js|jsx)/,
						loader: 'babel-loader',
						exclude: /node_modules/
					},
					{
						test: /\.css/,
						use: [
							'style-css',
							'css-loader'
						]
					}
				]
			}
		]
	}
};

include / exclude

开发时我们需要使用第三方库或插件,所有文件都下载到 mode_modules 中了,而这些文件是不需要编译可以直接使用的。
所以我们在对 js 文件处理时,需要排查 node_modules 下面的文件。
include:包含,只处理 xxx 文件
exclude:排除,除了 xxx 文件以外的其他文件都要处理
includeexclude 只能写一个,要们包含,要么排除。

module.exports = {
	module: {
		rules: [
			{
				test: /\.js$/,
				loader: 'babel-loader',
				exclude: "mode_modules" // 默认值 mode_modules
			}
		]
	}
};
module.exports = {
	module: {
		rules: [
			{
				test: /\.js$/,
				loader: 'babel-loader',
				include: path.join(__dirname, 'src')
			}
		]
	}
};

cache

每次打包时 js 文件都要经过 eslint 检查和 babel 编译,速度比较慢。
我们可以缓存之前的 eslint 检查和 babel 编译结果,这样第二次打包时速度就会更快了。
cache - 会对 eslint 检查和 babel 编译结果进行缓存。

module.exports = {
	module: {
		rules: [
			{
                test: /\.js$/,
                use: 'babel-loader',
                include: path.resolve(__dirname, 'src'),
                options: {
                    cacheDirectory: true, // 开启 babel 缓存,会默认缓存到 node_modules/.cache 目录下
                    cacheCompression: false // 关闭缓存的压缩
                }
            },
		]
	},
	plugins: [
		 new EslintPlugin({
            context: path.resolve(__dirname, 'src'), // 需要检测的目录
            extensions: ['js', 'jsx', 'json'], // 需要检查的文件类型
            fix: true, // 自动修复
            cache: true, // 开球 eslint 缓存
            cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache') // 指定缓存的位置
        })
	]
};

Thread

当项目越来越大时,打包速度越来越慢。
我们想要继续提升打包速度,其实就是要提升 js 的打包速度,因为其他文件都比较少。
而对 js 文件的处理主要是 eslint、babel、Terser 三个工具,所以我们要提升他们的运行速度。我们可以开启多进程同时处理 js 文件,这样速度就比之前的单进程打包更快了。

  • 多进程打包:开启电脑的多个进程同时干一件事,速度更快。

  • 注意:仅在特别耗时的操作中使用,因为每个进程启动就大约有 600ms 左右的开销。

  • 启动进程的数量就是我们 cpu 的核数

// 由于每个电脑获取 cpu 核数方式都不一样
// 所以使用 nodejs 核心模块来直接使用
const os = require('os'); // 返回 cpu 的一些信息
const threads = os.cpus().length; // cpu 核数

安装 thread-loader

yarn add thread-loader
  • 一般在 babel-loader 进行打包的时候使用,因为处理语法转换很耗时。
  • 一般放在需要处理的那个 loader 之后调用。
const os = require('os');

const threads = os.cpus().length;

module.exports = {
	module: {
		rules: [
			{
				test: /\.js$/,
				exclude: /mode_modules/,
				use: [
					{
						loader: 'thread-loader',
						options: {
							workers: threads // 开启多进程和设置进程数量
						}
					},
					{
						loader: 'babel-loader',
						options: {
		                    cacheDirectory: true, // 开启 babel 缓存,会默认缓存到 node_modules/.cache 目录下
		                    cacheCompression: false // 关闭缓存的压缩
		                }
					}
				]
			}
		]
	},
	plugins: [
		new EslintPlugin({
            context: path.resolve(__dirname, 'src'), // 需要检测的目录
            extensions: ['js', 'jsx', 'json'], // 需要检查的文件类型
            fix: true, // 自动修复
            cache: true, // 开球 eslint 环迅
            cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 指定缓存的位置
            threads // 开启多进程和设置进程数量
        })
	],
	optimization: {
        minimize: true,
        minimizer: [
            new TerserWebpackPlugin({
                parallel: threads // 开启多进程和设置进程数量
            })
        ]
    }
};

减少代码体积

Tree Shaking

开发时我们定义了一些工具函数库,或者引用第三方工具函数库或组件库。如果诶呦特殊处理的话,我们打包时会引入整个库,但是实际上我们可能只用上极小部分的功能,这样把真个库都打包进来,题久就太大了。

  • Tree Shaking:通常用于描述移除 js 中没有使用上的代码。注意:它依赖 es module
  • weboack 生产模式下已经默认开启了这个功能,无需其他配置。

减小 babel 体积

babel 为编译的每个文件都插入了辅助代码,使体积过大。
babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。默认情况下会被添加到每一个需要它的文件中。你可以将这些辅助代码作为一个独立的模块,来避免重复引入。
@babel/plugin-transform-runtime:禁用了 babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用。
安装 @babel/plugin-transform-runtime

yarn add  @babel/plugin-transform-runtime
module.exports = {
	module: {
		rules: [
			{
				test: /\.js$/,
				exclude: /mode_modules/,
				use: [
					{
						loader: 'babel-loader',
						options: {
		                    cacheDirectory: true, // 开启 babel 缓存,会默认缓存到 node_modules/.cache 目录下
		                    cacheCompression: false, // 关闭缓存的压缩
		                    plugins: ['@babel/plugin-transform-runtime'] // 较少代码体积
		                }
					}
				]
			}
		]
	},
};
// 第二种配置
// .babelrc
{
    "presets": [
        "@babel/preset-env"
    ],
    "plugins": [
        "@babel/plugin-transform-runtime"
    ]
}

压缩图片

安装 image-minimizer-webpack-plugin、imagemin

yarn add image-minimizer-webpack-plugin imagemin -D

无损压缩:下载 imagemin-gifsicle、 imagemin-jpegtran、 imagemin-optipng、 imagemin-svgo

yarn add  imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D

有损压缩:下载 imagemin-gifsicle、 imagemin-mozjpeg、 imagemin-pngquant、 imagemin-svgo

yarn add imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D
module.exports = {
	 optimization: {
        minimize: true,
        minimizer: [
            // 压缩图片
            new ImageMinimizerWebpackPlugin({
                minimizer: {
                    implementation: ImageMinimizerWebpackPlugin.imageminGenerate,
                    options: {
                        plugins: [
                            ['gifsicle', { interlaced: true }],
                            ['jpegtran', { progressive: true }],
                            ['optipng', { optimizationLevel: 5 }],
                            [
                                'svgo',
                                {
                                    plugins: [
                                        'preset-default',
                                        'prefixIds',
                                        {
                                            name: 'sortAttrs',
                                            params: {
                                                xmlnsOrder: 'alphabetical'
                                            }
                                        }
                                    ]
                                }
                            ]
                        ]
                    }
                }
            })
        ]
    }
};

优化代码运行性能

code split

打包代码时会将所有 js 文件打包到一个文件中,体积太大了。我们如果只要渲染首页,就应该只加载首页的 js 文件,其他文件不应该加载。
所以我们需要将打包生成的文件进行代码分割,生成多个 js 文件,渲染哪个页面就只加载某个 js 文件,这样加载的资源就少,速度就更快。

code split:主要做了两件事。

  • 分割文件:将打包生成的文件进行分割,生成多个 js 文件
  • 按需加载:需要哪个文件就加载哪个文件

多入口提取文件

正常情况下,有几个入口就会输出几个 bundle,如果 AB 都引用了模块 C,那么输出后的 bundleAbundleB,模块 C 就会分别打包两次。
那么我们可以将 C 单独输出成一个 bundleC,然后bundleAbundleB 去复用。

// add.js
const add = (a, b) => a + b;

export default add;
// index.js
import add from '@utils/add';

console.log(add(1, 6));
// other.js
import add from '@utils/add';

console.log(add(1, 4));
const path = require('path');

module.exports = {
    mode: 'production',
    entry: {
        app: './src/index.js',
        main: './src/other.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
    optimization: {
        splitChunks: {
            chunks: 'all', // 对所有模块都进行分割
            // 以下是默认值
            // minSize: 20000, // 分割代码最小的大小
            // minRemainingSize: 0, // 类似于 minSize 最后取保提取的文件大小不能为0
            // minChunks: 1, // 至少被引用的次数,满足条件才会代码分割
            // mazAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量
            // maxInitialRequests: 30, // 入口 js 文件最大并行请求数量
            // enforceSizeThreshold: 50000, // 超过 50kb 一定会单独打包(此时会忽略 minRemainingSize、mazAsyncRequests、maxInitialRequests)
            // cacheGroups: { // 分组,哪些模块要打包到一个组
            //     defaultVendors: { // 组名
            //         test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
            //         priority: -10, // 权重(越大越高)
            //         reuseExistingChunk: true // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块
            //     },
            //     default: { // 其他没有写的配置会使用默认值
            //         minChunks: 2, // 这里的 minChunks 权重更大
            //         priority: -20,
            //         reuseExistingChunk: true
            //     }
            // },
            // 修改配置
            cacheGroups: {
                default: { // 其他没有写的配置会使用默认值
                    minSize: 0, // 我们定义的文件体积太小了,所以要改打包的最小文件体积
                    minChunks: 2, // 这里的 minChunks 权重更大
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }
};

在这里插入图片描述

多入口按需加载

实现按需加载,动态导入模块。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 class="title">hello webpack5</h1>
    <button id="btn">按钮</button>
</body>
</html>
// add.js
const add = (a, b) => a + b;

export default add;
// count.js
export const count = (a, b) => a - b;
// index.js
import add from './add';

console.log(add(1, 6));
// other.js
document.getElementById('btn').onclick = function () {
    // 动态引入 -> 实现按需加载
    // 即使只被引用了一次,也会代码分割
    import('./count.js').then(({ count }) => {
        alert(count(1, 4));
    });
};
const path = require('path'),
    HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'production',
    entry: {
        app: './src/index.js',
        main: './src/other.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'src/index.html'),
            filename: 'index.html'
        })
    ],
    optimization: {
        splitChunks: {
            chunks: 'all', // 对所有模块都进行分割
            // 修改配置
            cacheGroups: {
                default: { // 其他没有写的配置会使用默认值
                    minSize: 0, // 我们定义的文件体积太小了,所以要改打包的最小文件体积
                    minChunks: 2, // 这里的 minChunks 权重更大
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }
};

单入口提取文件按需加载

// count.js
export const count = (a, b) => a - b;
// index.js
document.getElementById('btn').onclick = function () {
    // 动态引入 -> 实现按需加载
    // 即使只被引用了一次,也会代码分割
    import('./count.js').then(({ count }) => {
        alert(count(1, 4));
    });
};
const path = require('path'),
    HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'src/index.html'),
            filename: 'index.html'
        })
    ],
    optimization: {
        splitChunks: {
            chunks: 'all', // 对所有模块都进行分割
        }
    }
};
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 class="title">hello webpack5</h1>
    <button id="btn">按钮</button>
</body>
</html>

给模块重命名

// index.js
document.getElementById('btn').onclick = function () {
    // 动态引入 -> 实现按需加载
    // 即使只被引用了一次,也会代码分割
    // 通过注释 webpackChunkName 命名
    import(/* webpackChunkName: 'math' */'./count.js').then(({ count }) => {
        alert(count(1, 4));
    });
};
  • chunkFilename 使用 [name] (对应 webpackChunkName
const path = require('path'),
    HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.[name].js',
        chunkFilename: 'chunk/[name].chunk.js', // 给打包输出的其他文件命名
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'src/index.html'),
            filename: 'index.html'
        })
    ],
    optimization: {
        splitChunks: {
            chunks: 'all', // 对所有模块都进行分割
        }
    }
};

Preload / Prefetch

使用 code split 可以做代码分割,同时会使用 import 动态导入的语法来进行代码按需加载(懒加载)。但是加载速度还不够好,比如,用户是点击那个按钮才下载资源的,如果资源体积较大,那么用户会感觉到明显的卡顿效果。
我们想在浏览器空闲时间,加载后续需要使用的资源,就需要用上 PreloadPrefetch 技术。

  • Preload:告诉浏览器立即加载资源

  • Prefetch:告诉浏览器在空闲时才开始加载资源

  • Preload加载优先级高,Prefetch优先级低;

  • Preload只加载当前页面需要使用的资源,Prefetch可以加载当前页面资源,也可以下载一个页面需要使用的资源。

  • 都会加载资源,并不执行;都有缓存;兼容性比较差。

安装 @vue/preload-webpack-plugin

yarn add @vue/preload-webpack-plugin -D
const path = require('path'),
    HtmlWebpackPlugin = require('html-webpack-plugin'),
    PreloadPlugin = require('@vue/preload-webpack-plugin'); 

module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.[name].js',
        chunkFilename: 'chunk/[name].chunk.js', // 给打包输出的其他文件命名
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'src/index.html'),
            filename: 'index.html'
        }),
        // new PreloadPlugin({
        //     rel: 'prefetch'
        // }),
        new PreloadPlugin({
            rel: 'preload',
            as: 'script'
        })
    ],
    optimization: {
        splitChunks: {
            chunks: 'all', // 对所有模块都进行分割
        }
    }
};

文件资源缓存

当缓存的资源发生变化时,希望重新加载这个资源;当有一个文件发生变化,只让这一个文件的缓存失效,不影响其他文件缓存。

hash

  • 给打包好的文件名添加哈希值,哈希值不变就不会重新打包。

存在问题:因为 jscss 同时使用一个hash值,如果修改了js,重新打包,会导致所有缓存失效,css 也会重新打包。

// 给文件名添加hash

module.exports = {
    output: { 
		path: path.resolve(__dirname, '/build'),
		// 输出文件带 8 位哈希值,每次重新打包都会生成一个唯一的 hash 值
		filename: 'js/built.[hash:8].js'
    }
};

chunkhash

  • 根据chunk生成hash值,如果打包来源于同一个chunk,那么hash值一样。

存在问题:jscsshash值还是一样的。因为css是在js中被引入的,所以同属一个chunk

contenthash

  • contenthash 是根据文件的内容生成 hash 值,不同文件 hash 值一定不一样。

存在问题:如果 index.js 引入了 count.js,打包出来的 index.bundle.jscount.chunk.js,如果 修改了 count.js 的内容,那么 index.js 的缓存也会失效。

module.exports = {
    output: { 
		path: path.resolve(__dirname, '/build'),
		// 输出文件带 10 位哈希值,每次重新打包都会生成一个唯一的 hash 值
		filename: 'js/built.[contenthash:10].js',
		chunkFilename: 'js/chunk/[name].[contenthash:8].chunk.js', // 给打包输出的其他文件命名
    }
};

runtime

使用 runtime 就可以避免 contenthash 的问题。

  • runtimeChunk:它会把文件之间依赖的 hash 值,单独提取出来打包成一个文件去保管。
  • A 引用 BB 发生变化,只有 Bruntime会发生变化,不会影响到 A
const path = require('path'),
    HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].[contenthash:8].bundle.js',
        chunkFilename: 'js/chunk/[name].[contenthash:8].chunk.js', // 给打包输出的其他文件命名
        clean: true
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'src/index.html'),
            filename: 'index.html'
        })
    ],
    optimization: {
        splitChunks: {
            chunks: 'all', // 对所有模块都进行分割
        },
        // 把文件之间依赖的 `hash` 值,单独提取出来打包成一个文件去保管
        runtimeChunk: {
        	name: entrypoint => `runtime-${entrypoint.name}.js`
        }
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/347845.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

牛客——都别吵吵了,我才是签到(质因数分解和统计质因数次数)

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 陶陶刚上一年级&#xff0c;今天数学课上老师教了乘法和除法&#xff0c;老师留了一道课后习题&#xff0c;陶陶很快地写完了&#xff0c;现在想请你帮助他检查一下是否和答案一致。…

Ubuntu 申请 SSL证书并搭建邮件服务器

文章目录 Log 一、域名连接到泰坦&#xff08;Titan&#xff09;电子邮件二、NameSilo Hosting 避坑三、Ubuntu 搭建邮件服务器1. 环境准备2. 域名配置3. 配置 Postfix 和 Dovecot① 安装 Nginx② 安装 Tomcat③ 申请 SSL 证书&#xff08;Lets Encrypt&#xff09;④ 配置 pos…

ORA-12528: TNS: 监听程序: 所有适用例程都无法建立新连

用了网上的办法&#xff1a; 1、修改listener.ora的参数,把动态的参数设置为静态的参数,红色标注部分 位置D:\oracle\product\10.2.0\db_1\NETWORK\ADMIN SID_LIST_LISTENER (SID_LIST (SID_DESC (SID_NAME PLSExtProc) (ORACLE_HOME D:\oracle\produ…

C++练习题1-9

文章目录 NO1、选出妃子、宫女和嬷嬷No2、根据数字判断月份No3、循环计数No4、循环选数No5、玩转字符No6、计算字符串长度No7、显示字符串中的字符No8、字符串反转No9、二维数组的应用 NO1、选出妃子、宫女和嬷嬷 其他要求&#xff1a; 超女用结构体表示不要嵌套if输入所有数据…

博物馆环境监控系统的需求是怎么来的???

一、博物馆环境基本调研和识别需求 在环境监测软件的需求中&#xff0c;首要任务是进行深入的基本调研。这包括把握已有的环境监测技术、标准与法规&#xff0c;以及用户的实际操作过程和困惑。积极与环保局、科研院所、公司等沟通&#xff0c;可搜集很多原始记录&#xff0c;…

MySQL的SQL分类与数据类型

MySQL是一款广泛使用的关系型数据库管理系统&#xff0c;开源、免费且跨平台&#xff0c;常用于存储、管理和检索结构化数据&#xff0c;并通过SQL语言支持高效的数据操作与管理。 文章目录 何为SQLSQL分类DDLDMLDCLTCLDQL MySQL的数据类型数值型日期型字符串型二进制型其他类型…

网安培训第一期——sql注入+文件

文章目录 sql inject报错注入time盲注联合查询万能密码拦截和过滤ascii注入流程base64查询的列名为mysql保留关键字key 文件上传ffuf脚本要做的三件事网络端口进程用户权限文件文件包含文件下载XSS跨站请求攻击csrf跨站请求伪造 sql inject 判断输入字段是字符串还是数字 方法…

【GitHub项目推荐--开源小游戏】【转载】

01 回合制生存游戏 Cataclysm-DDA 是一款回合制生存游戏&#xff0c;背景设置在后世界末日的世界中。虽然有些人将其描述为“僵尸游戏”&#xff0c;但《大灾变》远不止这些。努力在一个严酷、持久、程序生成的世界中生存。 为食物、设备寻找一个死去的文明的残余物。或者&am…

arcgis 面要素shp数据处理

面要素是工作中用到最多的&#xff0c;那么面要素是如何形成的呢&#xff0c;主要还是由闭合的线要素转换而成。在面要素数据中常用的有以下几点&#xff1a; 一、 线转面&#xff08;要素转面&#xff09; 通过上一篇得到了点转线的要素&#xff0c;那么根据上节的线要素&am…

大模型学习笔记一:大模型应用开发基础

文章目录 一、大模型一些概念介绍 一、大模型一些概念介绍 1&#xff09;产品和大模型的区别&#xff08;产品通过调用大模型来具备的能力&#xff09; 2&#xff09;AGI定义 概念&#xff1a;一切问题可以用AI解决 3&#xff09;大模型通俗原理 根据上文&#xff0c;猜测下…

1174:长整数排序(指针专题)

题目描述 长整数排序。输入n 然后输入n个位数不超过100位的大整数&#xff0c;输入的整数可能含有前导0。将这n个长整数排序后输出&#xff0c;输出不含前导0。int greater(char *s1, char *s2){若s1指向的整数大于s2指向的整数&#xff0c;返回一个正整数;若s1指向的整数小于s…

重生之C++王者归来DAY1

c的概述 c的编程思想&#xff1a;面向对象、泛型编程。 1.第一个c程序 本文用的是QT&#xff0c;VS之类的也可 2.c面向对象的三大特性&#xff08;重要&#xff09; 封装:将相同属性的数据和方法封装在一起&#xff0c;加权限区分&#xff0c;用户只能借助公共方法操作 私有…

PCL 高斯投影正算:大地坐标转高斯投影坐标(C++详细过程版)

目录 一、算法原理二、代码实现三、结果展示四、测试数据PCL 高斯投影正算:大地坐标转高斯投影坐标(C++详细过程版)由CSDN点云侠原创。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 二、代码实现 头文件及读取保存函数见:

SAP同步异常2:SAP删除获利能力特征字段后VF02发货过帐报错。

测试环境VF02过帐报错&#xff0c; 原因是之前删除已经激并使用的获利能力特征字段后&#xff0c;只处理了数据库&#xff0c;没有处理程序。 处理方案&#xff1a; 1、 KEA0 维护经营关注点&#xff1a; 这里WW291已经删除&#xff0c;但没有激活程序。 退出后&#xff…

web安全学习笔记【09】——算法2

基础[1] 入门-算法逆向&散列对称非对称&JS源码逆向&AES&DES&RSA&SHA #知识点&#xff1a; 1、Web常规-系统&中间件&数据库&源码等 2、Web其他-前后端&软件&Docker&分配站等 3、Web拓展-CDN&WAF&OSS&反向&负载…

2.数据结构 顺序表(自留笔记)

文章目录 一.静态顺序表&#xff1a;长度固定二.动态顺序表1.下面证明原地扩容和异地扩容代码如下&#xff1a;2.下面是写一段Print&#xff0c;打印数字看看&#xff1a;3.头插4.尾删5.头删6.越界一定会报错吗7.下标插入8.下标删除9.查找数字10.应用&#xff1a;利用顺序表写一…

跨平台同步 Shell 历史记录,无缝切换会话 | 开源日报 No.154

atuinsh/atuin Stars: 14.3k License: MIT Atuin 是一个用 SQLite 数据库替换现有 shell 历史记录的工具&#xff0c;可以记录命令的额外上下文&#xff0c;并提供可选且完全加密的历史同步功能。其主要功能和核心优势包括&#xff1a; 重新绑定 ctrl-r 和 up (可配置) 到全屏…

安装宝塔面板后k8s所在节点pod无法正常工作解决方法,kubernetes k8s 与宝塔面板冲突解决方法

在实际项目过程中我们使用了k8s 在生产环境中运行管理服务。 但是对服务器的状态管理我们使用了宝塔面板进行 K8s 版本1.2.8 宝塔面板 版本 8.05 操作步骤是这样的。 1.完成1.2.8 k8s的节点安装&#xff0c;并正常运行服务。 过程略 2.安装宝塔面板 ​ yum install -y …

不要在细节上雕花

前段时间在网上看到一张趣图,有人在社交网络分享学习编程的笔记,一行行手抄代码,字迹清晰,排版工整,霎是认真。 这可能只是个梗,但它让我想起我的学生年代。许多年前我还在念书的时候,班上有不少非常认真的同学,热衷于把课堂笔记做得非常漂亮、工整,有些甚至要用尺子对…

vue —— h函数的学习与使用

文章目录 一、h函数是什么&#xff1f;二、h函数格式说明及使用示例1&#xff1a;简单创建一个VNode&#xff08;vue3&#xff09;示例2&#xff1a;vue2中h函数用法示例3&#xff1a;vue3中h函数的用法vue2和vue3中h函数的区别&#xff1f; 三、h函数实现原理四、h函数常用场景…
最新文章