webpack单独提取css做cacheGroups无效?
是这样的:我想用张鑫旭老师说的 这个方法 实现网页换肤功能。
这个方法就是先将皮肤样式文件先用link标签预加载,然后用JS动态切换disabled得值来切换样式文件,实现换肤。
然后我就想,先编写好样式文件,然后在vue工程中的入口文件main.js中导入皮肤样式文件,如下:
import 'light.scss';
import 'dark.scss';
,在结合webpack,让webpack单独对这两个样式文件打包,然后dist/css文件夹下就会有light.css和dark.css文件,再通过配置webpack对link标签增加alternate属性,然后我就能在项目内动态通过JS来控制light.css和dark.css的加载了。
但是我在实现的过程中遇到了一些问题,我发现我没办法让webpack单独生成light.css和dark.css。我是写了一些webpack配置的,但是就是没法生成出来,以下是我的代码。
const { defineConfig } = require("@vue/cli-service");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const distName = () => {
return `dist_${Math.round(Math.random() * 100)}`;
};
module.exports = defineConfig({
outputDir: distName(),
configureWebpack: {
optimization: {
splitChunks: {
cacheGroups: {
lightStyle: {
priority: 20,
name: "lightStyle",
test(module) {
if (/light\.scss/.test(module.rawRequest)) {
return true
}
return false
},
enforce: true
},
darkStyle: {
priority: 20,
name: "darkStyle",
test(module) {
if (/dark\.scss/.test(module.rawRequest)) {
return true
}
return false
},
enforce: true
}
},
},
},
plugins: [
new MiniCssExtractPlugin({
filename: (params, p2) => {
//console.log("filename-----------", params, p2);
return "css/[name].[contenthash:8].css";
},
chunkFilename: (params) => {
//console.log("chunkFilename-----------", params);
return "css/[name].[contenthash:8].css";
},
}),
],
},
});
一开始我是想通过MiniCssExtractPlugin中的filename和chunkFileName来给皮肤样式文件生成固定的名称来实现的,但是我实践的过程中发现他们的传参params里面都是一些chunk信息,根本没法知道那个chunk是light.sccs和dark.scss。
然后我又想到了使用cacheGroups来做这件事,通过test函数我可以获取到module的信息,因为module里面的数据可以让我清楚知道那个module是light.scss和dark.scss。所以编码很顺利,但是当我执行npm run build之后,结果却是意料之外,dist文件夹下并没有light.css和dark.css文件,而是被打包进dist/css/app.122323as.css文件中,请问这是怎么回事?
我找到问题了,是因为我没有写chunks属性,我修改了下原来代码:
cacheGroups: {
lightStyle: {
priority: 20,
name: "lightStyle",
chunks: (chunks) => {
return chunks.name === 'app'
},
test(module) {
if (/light\.scss/.test(module.rawRequest)) {
return true
}
return false
},
enforce: true
},
darkStyle: {
priority: 20,
name: "darkStyle",
chunks: (chunks) => {
return chunks.name === 'app'
},
test(module) {
if (/dark\.scss/.test(module.rawRequest)) {
return true
}
return false
},
enforce: true
}
},
但是结果还是不是我想要的,我发现它生成的不是light.css和dark.css而是light.js和dark.js。要怎么样配置才能达到我想要的?
这个问题经过几天的研究,我已经解决了,我使用的方法是:“基于webpack打包入口提取CSS”,
这是我在官方文档中看到的一个办法。
以下是我的webpack代码:
const { defineConfig } = require("@vue/cli-service");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const htmlWebpackInjectAttributesPlugin = require("html-webpack-inject-attributes-plugin");
const path = require("path");
const HtmlWebpackExcludeAssetsPlugin = require("html-webpack-exclude-assets-plugin-webpack5");
const themeReg = /^(.*)((light|dark|default)\..+\.css)$/;
const themeRegJS = /^(.*)((light|dark|default).*\.js)$/;
const themeLinkJudge = (tagName, attributes) => {
return (
tagName === "link" && attributes.href && themeReg.test(attributes.href)
);
};
module.exports = defineConfig({
outputDir: "ThemeResearch",
publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
chainWebpack: (config) => {
// dev环境下vue使用的不是MiniCssExtractPlugin来提取css生成css文件,而是使用vue-style-loader来生成style标签,
// 因为我们需要生成文件,所以这里即使dev环境下也使用MiniCssExtractPlugin
config
.plugin("MiniCssExtractPlugin")
.use(MiniCssExtractPlugin, [
{
filename: () => {
return "css/[name].[contenthash:8].css";
},
chunkFilename: () => {
return "css/[name].[contenthash:8].css";
},
},
])
.end();
// 让normal这个module不去匹配我们的主题文件
config.module
.rule("scss")
.oneOf("normal")
.test(/^((?!light\.scss|dark\.scss|default\.scss).)*$/)
.end();
// 定义自己的module来使用我们自己的loader
const theme = config.module
.rule("scss")
.oneOf("ownTheme")
.test(/.*(light|dark|default)\.scss$/);
// 清除vue-cli写好的loader,改用自己写的
theme.uses.clear();
theme
.use(MiniCssExtractPlugin.loader)
.loader(MiniCssExtractPlugin.loader)
.end()
.use("css-loader")
.loader("css-loader")
.end()
.use("postcss-loader")
.loader("postcss-loader")
.end()
.use("sass-loader")
.loader("sass-loader")
.end();
// 增加自定义标签属性
config
.plugin("html")
.tap((args) => {
args[0].attributes = {
'data-theme': function (tag) {
const { attributes, tagName } = tag;
if (themeLinkJudge(tagName, attributes)) {
return 'true'
}
return false
},
rel: function (tag) {
const { attributes, tagName } = tag;
if (themeLinkJudge(tagName, attributes)) {
if (attributes.href.indexOf('default') > -1) {
return "stylesheet"
}
return "alternate stylesheet";
} else if (tagName === 'link') {
return "stylesheet"
}
return false;
},
title: function (tag) {
const { attributes, tagName } = tag;
if (themeLinkJudge(tagName, attributes)) {
if (attributes.href.indexOf('light') > -1) {
return 'light'
} else if (attributes.href.indexOf('dark') > -1) {
return 'dark'
} else if (attributes.href.indexOf('default') > -1) {
return 'default'
} else {
return false
}
}
}
};
args[0].excludeAssets = [themeRegJS];
return args;
})
.end();
// HtmlWebpackPlugin生成HTML文件时,这个插件能给生成的标签注入自己需要的属性
config
.plugin("htmlInjectAttribute")
.after("html")
.use(htmlWebpackInjectAttributesPlugin)
.end();
// 移除多入口生成的无用script标签和JS文件
config
.plugin("ExcludeAssets")
.after("htmlInjectAttribute")
.use(HtmlWebpackExcludeAssetsPlugin)
.end();
},
configureWebpack: {
entry: {
app: path.resolve(__dirname, "src/main.js"),
default: path.resolve(__dirname, "src/theme/default.js"),
light: path.resolve(__dirname, "src/theme/light"),
dark: path.resolve(__dirname, "src/theme/dark"),
},
},
});
- 经过验证的有效解决办法
- 自己的经验指引,对解决问题有帮助
- 遵循 Markdown 语法排版,代码语义正确
- 询问内容细节或回复楼层
- 与题目无关的内容
- “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容