3.8.4
https://github.com/iUUCoder/issues-demo__vue-cli
System:
OS: macOS 10.14.5
CPU: (8) x64 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
Binaries:
Node: 8.16.0 - ~/git/nvm/versions/node/v8.16.0/bin/node
Yarn: Not Found
npm: 6.4.1 - ~/git/nvm/versions/node/v8.16.0/bin/npm
Browsers:
Chrome: 75.0.3770.142
Firefox: Not Found
Safari: 12.1.1
npmGlobalPackages:
@vue/cli: 3.8.4
执行命令,构建应用:
npm run build
构建时,对 CSS 中用到的 url 进行处理。
vue.config.js 中的配置:
module.exports = {
pages: {
"pages/home/index": {
entry: "./src/pages/home/index.js",
template: "src/pages/home/index.html"
}
}
};
项目源码 src/pages/home/Index.vue 中,设置了样式:
.page {
color: red;
background-image: url("./res/image.png");
}
构建输出结构:
dist
├── css
│ └── pages
│ └── home
│ └── index.0a0cd7d2.css
├── img
│ └── image.6704b667.png
├── js
│ ├── chunk-vendors.2ce01813.js
│ ├── chunk-vendors.2ce01813.js.map
│ └── pages
│ └── home
│ ├── index.cc5a5f68.js
│ └── index.cc5a5f68.js.map
└── pages
└── home
└── index.html
构建后的 dist/css/pages/home/index.0a0cd7d2.css:
.page{color:red;background-image:url(../img/image.6704b667.png)}
问题:
构建结果,对 CSS 中的相对路径处理不正确,没有考虑到配置了多级目录的情况。
Vue Cli 配置了 mini-css-extract-plugin 的 publicPath ,对相对路径进行了特定处理,但却没有考虑多层级页面的情况。
源码:cli-service/lib/config/css.js:104行
我参考 cli-service/lib/config/css.js 的源码,对 Webpack 的配置进行了覆盖:
// 配置项 css.extract 的值
const cssExtract = undefined;
module.exports = {
css: {
extract: cssExtract
},
chainWebpack: config => {
/**
* 覆盖 Webpack 配置
*/
const isProd = process.env.NODE_ENV === 'production';
const extract = cssExtract || isProd;
const shadowMode = !!process.env.VUE_CLI_CSS_SHADOW_MODE;
const shouldExtract = extract !== false && !shadowMode;
if (shouldExtract) {
[
{ lang: 'css', test: /\.css$/ },
{ lang: 'postcss', test: /\.p(ost)?css$/ },
{ lang: 'scss', test: /\.scss$/ },
{ lang: 'sass', test: /\.sass$/ },
{ lang: 'less', test: /\.less$/ },
{ lang: 'stylus', test: /\.styl(us)?$/ },
].forEach(({ lang, test }) => {
const baseRule = config.module.rule(lang).test(test);
[
// rules for <style lang="module">
baseRule.oneOf('vue-modules').resourceQuery(/module/),
// rules for <style>
baseRule.oneOf('vue').resourceQuery(/\?vue/),
// rules for *.module.* files
baseRule.oneOf('normal-modules').test(/\.module\.\w+$/),
// rules for normal CSS imports
baseRule.oneOf('normal'),
].forEach(rule => {
rule.use('extract-css-loader')
.loader(require('mini-css-extract-plugin').loader)
.options({
hmr: !isProd,
});
});
});
}
}
};
[email protected] 可以通过配置解决这个问题
fix: 可配置
__use_default_css_public_path__环境变量, 让CSS使用默认的publicPath, 而不是使用相对路径
@iUUCoder 我找到一个简单的workround,查看vue-cli源码发现,mini-css-extract-plugin的publicPath参数是相对于extract.filename计算出来的:
// use relative publicPath in extracted CSS based on extract location
const cssPublicPath = process.env.VUE_CLI_BUILD_TARGET === 'lib'
// in lib mode, CSS is extracted to dist root.
? './'
: '../'.repeat(
extractOptions.filename
.replace(/^\.[\/\\]/, '')
.split(/[\/\\]/g)
.length - 1
)
vue-cli针对mini-css-extract-plugin插件生成的默认配置如下:
{
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].css'
}
请注意:filename和chunkFilename使用的是相对路径,所以在计算publicPath时,路径层级会出现少一层的情况,导致background-image加载失败。
目前发现最简单的Workround是:显示的在vue.config.js文件中配置extract.filename,使其相对于根路径(比默认配置多一层),问题就解决了,示例代码如下:
module.exports = {
publicPath: '/',
css: {
extract: process.env.NODE_ENV === 'development' ? false : {
filename: '/css/[name].[contenthash:8].css',
chunkFilename: '/css/[name].[contenthash:8].css'
},
},
...
}
@iUUCoder 我找到一个简单的workround,查看
vue-cli源码发现,mini-css-extract-plugin的publicPath参数是相对于extract.filename计算出来的:// use relative publicPath in extracted CSS based on extract location const cssPublicPath = process.env.VUE_CLI_BUILD_TARGET === 'lib' // in lib mode, CSS is extracted to dist root. ? './' : '../'.repeat( extractOptions.filename .replace(/^\.[\/\\]/, '') .split(/[\/\\]/g) .length - 1 )
vue-cli针对mini-css-extract-plugin插件生成的默认配置如下:{ filename: 'css/[name].[contenthash:8].css', chunkFilename: 'css/[name].[contenthash:8].css' }_请注意:_
filename和chunkFilename使用的是相对路径,所以在计算publicPath时,路径层级会出现少一层的情况,导致background-image加载失败。目前发现最简单的Workround是:显示的在
vue.config.js文件中配置extract.filename,使其相对于根路径(比默认配置多一层),问题就解决了,示例代码如下:module.exports = { publicPath: '/', css: { extract: process.env.NODE_ENV === 'development' ? false : { filename: '/css/[name].[contenthash:8].css', chunkFilename: '/css/[name].[contenthash:8].css' }, }, ... }
这个方法我也试过,但是要是路径复杂一点,或者路径变动,这块都得跟随着修改。
目前我觉得,还是覆盖配置比较有效,能够一劳永逸,就是比较冗长和麻烦。
期待官方给出更高效的方案。
have better methods for this problem?
have better methods for this problem?
我也这样设置了,但是还是不行,请问还有别的方法没,上面说的方法都试完了,我本地运行,样式表出不来了
我也遇见类似问题了,官方 没有什么 解决方案吗。。。
Hitting this exact same issue.. Spent quite a while trying to work around it. Might have to iterate through all the extract plugins and update the public path.
原因推测:
Vue Cli 配置了
mini-css-extract-plugin的publicPath,对相对路径进行了特定处理,但却没有考虑多层级页面的情况。源码:cli-service/lib/config/css.js:104行
解决:
我参考
cli-service/lib/config/css.js的源码,对Webpack的配置进行了覆盖:
- vue.config.js
// 配置项 css.extract 的值 const cssExtract = undefined; module.exports = { css: { extract: cssExtract }, chainWebpack: config => { /** * 覆盖 Webpack 配置 */ const isProd = process.env.NODE_ENV === 'production'; const extract = cssExtract || isProd; const shadowMode = !!process.env.VUE_CLI_CSS_SHADOW_MODE; const shouldExtract = extract !== false && !shadowMode; if (shouldExtract) { [ { lang: 'css', test: /\.css$/ }, { lang: 'postcss', test: /\.p(ost)?css$/ }, { lang: 'scss', test: /\.scss$/ }, { lang: 'sass', test: /\.sass$/ }, { lang: 'less', test: /\.less$/ }, { lang: 'stylus', test: /\.styl(us)?$/ }, ].forEach(({ lang, test }) => { const baseRule = config.module.rule(lang).test(test); [ // rules for <style lang="module"> baseRule.oneOf('vue-modules').resourceQuery(/module/), // rules for <style> baseRule.oneOf('vue').resourceQuery(/\?vue/), // rules for *.module.* files baseRule.oneOf('normal-modules').test(/\.module\.\w+$/), // rules for normal CSS imports baseRule.oneOf('normal'), ].forEach(rule => { rule.use('extract-css-loader') .loader(require('mini-css-extract-plugin').loader) .options({ hmr: !isProd, }); }); }); } } };
In my case, it works like this:
const styles = [
'css',
'postcss',
'scss',
'sass',
'less',
'stylus'
]
const modules = [
'vue-modules',
'vue',
'normal-modules',
'normal'
]
module.exports = {
// ...
chainWebpack: config => {
// ...
if (shouldExtract) {
styles.forEach(s => {
modules.forEach(m =>
config
.module
.rule(s)
.oneOf(m)
.use('extract-css-loader')
.tap(options => {
options.publicPath = '' // Set whatever you want as publicPath
return options
})
)
})
}
}
// ...
};
If you set the publicPath as 'https://example.com/', it will generate an image reference like url(https://example.com/your-image.png).
Check mini-css-extract-plugin for other loader options you can set.
Most helpful comment
@iUUCoder 我找到一个简单的workround,查看
vue-cli源码发现,mini-css-extract-plugin的publicPath参数是相对于extract.filename计算出来的:vue-cli针对mini-css-extract-plugin插件生成的默认配置如下:请注意:
filename和chunkFilename使用的是相对路径,所以在计算publicPath时,路径层级会出现少一层的情况,导致background-image加载失败。目前发现最简单的Workround是:显示的在
vue.config.js文件中配置extract.filename,使其相对于根路径(比默认配置多一层),问题就解决了,示例代码如下: