const path = require("path"); const glob = require("glob"); const toObject = require("./scripts/toObject"); const { VueLoaderPlugin } = require("vue-loader"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const FixStyleOnlyEntriesPlugin = require("webpack-fix-style-only-entries"); module.exports = (env, options) => { const jsSetting = { mode: "production", entry: { index: path.resolve(__dirname, "src/js/entry/index.js"), }, output: { path: path.resolve(__dirname, "apps/statics/dist/"), filename: "js/[name].js", }, target: ["web", "es5"], module: { rules: [ { test: /.m?js$/, include: path.resolve(__dirname, "src"), exclude: /(node_modules|bower_components)/, use: [ { loader: "babel-loader", options: { plugins: ["@babel/plugin-transform-runtime"], }, }, ], }, { test: /.vue$/, use: ["vue-loader"], }, { test: /.s?css$/i, use: ["vue-style-loader", "css-loader", "sass-loader"], }, { test: /\.(png|jpe?g|gif|svg)$/i, use: [ { loader: "url-loader", options: { name: "[path][name].[ext]", context: "src", fallback: require.resolve("file-loader"), limit: 8192, }, }, ], }, { test: /\.(woff|woff2|eot|ttf|otf|)$/, use: [ { loader: "file-loader", options: { name: "[path][name].[ext]", context: "src", }, }, ], }, ], }, plugins: [ new VueLoaderPlugin(), new MiniCssExtractPlugin({ filename: "style/[name].css", }), new CleanWebpackPlugin({ verbose: true }), ], resolve: { alias: { src: path.resolve(__dirname, "src/"), apps: path.resolve(__dirname, "apps/"), }, }, externals: { vue: "Vue", axios: "axios", jquery: "jQuery", }, }, const cssSetting = { mode: "production", entry: { ...toObject(glob.sync("apps/statics/scss/*/.scss")), }, output: { path: path.resolve(__dirname, ""), }, module: { rules: [ { test: /.s?css$/i, use: [ MiniCssExtractPlugin.loader, { loader: "css-loader", options: { url: false }, }, "postcss-loader", { loader: "sass-loader", options: { implementation: require("sass"), sassOptions: { outputStyle: "expanded", }, }, }, ], }, ], }, plugins: [new FixStyleOnlyEntriesPlugin(), new MiniCssExtractPlugin({})], resolve: { alias: { src: path.resolve(__dirname, "src/"), apps: path.resolve(__dirname, "apps/"), }, }, }, if (options.mode == "development") { jsSetting.devtool = "eval-source-map"; } return [cssSetting, jsSetting]; };为了简化内容,我已经移除了大部分的 entry 文件,并仅保留了说明所需的必要部分。在我们的项目中,我们首先构建 CSS,然后是 JavaScript。对于 css 文件,我们使用了一个名为 glob 的库来检测所有的.scss 文件。toObject 是一个自定义函数,用于生成如下的键值对:
{ "apps/statics/scss/index": "apps/statics/scss/index.scss", // ... }所以基本上,这些就是我们项目所需的内容:
const path = require("path"); const glob = require("glob"); const toObject = require("./scripts/toObject"); const { VueLoaderPlugin } = require("vue-loader"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const PreventOutputJSPlugin = require("./scripts/PreventOutputJSPlugin"); module.exports = (env, options) => { const isProd = options.mode === "production"; const cssSetting = { mode: "development", context: __dirname, devtool: false, entry: { ...toObject(glob.sync("apps/statics/scss/**/*.scss")), }, output: { path: path.resolve(__dirname, ""), }, module: { rules: [ { test: /\\.scss$/, use: ["postcss-loader", "sass-loader"], type: "css", }, ], }, plugins: [new PreventOutputJSPlugin()], resolve: { alias: { src: path.resolve(__dirname, "src/"), apps: path.resolve(__dirname, "apps/"), }, }, optimization: { minimize: false, }, }; const jsSetting = { context: __dirname, devtool: false, entry: { index: path.resolve(__dirname, "src/js/entry/index.js"), }, output: { path: path.resolve(__dirname, "apps/statics/dist/"), filename: "js/[name].js", }, target: ["web", "es5"], module: { rules: [ { test: /\\.vue$/, loader: "vue-loader", options: { experimentalInlineMatchResource: true, }, }, { test: /\\.scss$/, use: [ { loader: "style-loader", options: { esModule: false }, }, "css-loader", "postcss-loader", "sass-loader", ], type: "javascript/auto", }, { test: /\\.css$/, use: [ { loader: "style-loader", options: { esModule: false }, }, "css-loader", ], type: "javascript/auto", }, { test: /\\.(png|jpe?g|gif|svg)$/i, type: "asset/inline", }, { test: /\\.(woff|woff2|eot|ttf|otf|)$/, type: "asset/resource", }, ], }, plugins: [new VueLoaderPlugin()], resolve: { alias: { src: path.resolve(__dirname, "src/"), apps: path.resolve(__dirname, "apps/"), node_modules: path.resolve(__dirname, "node_modules/"), }, }, externals: { vue: "Vue", axios: "axios", jquery: "jQuery", }, optimization: { minimize: isProd, }, }; if (isProd) { jsSetting.plugins.push(new CleanWebpackPlugin({ verbose: true })); } return [cssSetting, jsSetting]; };
对于 CSS 文件,我们不需要 css-loader 或 MiniCssExtractPlugin 来构建.scss。对于 JavaScript 文件,大多数配置与之前相同。我们的项目大约有 40 个 JavaScript 文件和 90 个 CSS 文件需要构建,让我们看看构建性能:
开发服务器热构建时间:约 300 毫秒
{ loader: 'css-loader', options: { url: false } }而当前版本的 Rspack(v0.2.9)还不支持这一点。尽管它的构建速度仍然比 Webpack 快得多,但它也会在我们的终端上留下大量错误信息。在与我的团队成员讨论后,我们决定现在不替换它。我已经将其作为功能请求报告给 Rspack 的 Github 问题,并希望这个功能能尽快实现。