Laravel Blade、Mix 和 SASS 资源版本共享
Laravel Blade, Mix and SASS resource versioning sharing
在我的项目中,我在 SASS 和 Blade 中都使用了一些资源(主要是图像)。另外,我有一些资源只在 SASS 中使用,有些只在 Blade 中使用。
例如,我可以在 Blade 文件中使用 mix('images/logo.png')
,在 SASS 文件中使用 background: url('../images/logo.png')
。
至于我的目录结构,我做了以下操作:
- resources
- js
- sass
- images // All images used by Blade, Sass, or both
- fonts
为了编译我的资源并将它们放在 public
文件夹中,我使用以下 webpack.mix.js
:
mix.copy('resources/images/**/*.*', 'public/images');
mix.copy('resources/fonts/**/*.*', 'public/fonts');
mix.version('public/images/**/*.*');
mix.version('public/fonts/**/*.*');
mix.js('resources/js/app.js', 'public/js')
.js('resources/js/vendor.js', 'public/js')
.scripts([ // Old not ES6 JS
'resources/js/tpl/core.min.js'
], 'public/js/core.min.js')
.sass('resources/sass/app.scss', 'public/css')
.sourceMaps()
.version();
结果,我在 app.css 中得到 URL :
background: url(/images/logo.png?0e567ce87146d0353fe7f19f17b18aca);
虽然我在渲染中得到另一个 HTML :
src="/images/logo.png?id=4d4e33eae039c367c8e9"
它们被视为 2 种不同的资源,这不是我所期望的...
可能的解决方法
我发现 SASS 生成的 CSS 文件使用版本化的 URL,即使我没有在 webpack.mix.js
中指定 version()
。所以我想知道也许我可以使用一些技巧,比如这个:
const sass = require('sass');
// Custom SASS function to get versioned file name
// Uses Mix version md5 hash
const functions = {
'versioned($uri)': function(uri, done) {
uri = uri && uri.getValue() || uri;
const version = File.find(path.join(Config.publicPath, uri)).version();
done(new sass.types.String(`${uri}?id=${version}`));
}
};
mix.sass('resources/sass/all.scss', 'public/css', {
sassOptions: {
functions
}
})
.options({ // Do not process URLs anymore
processCssUrls: false
});
然后像这样在 SASS 中使用它:
background-image: url(versioned('/images/logo.png'));
但是这个解决方案有很多缺点,我不得不每次都使用versioned
函数,如果没有webpack.mix.js
函数,我的源代码将无法在其他项目中轻松运行,并且我必须编辑资源文件夹中使用的每个文件才能使用该功能。
其他解决方案?
我认为问题的根源可能来自我构建文件的方式,我有一个 resources/images
文件夹,其中包含 SASS 使用的图像,也被 Blade 使用。
在 SASS 中使用的图像将被复制到 public/images
因为这是 SASS 使用 webpack 的方式,并且这些图像也会被复制第二次因为我使用了 mix.copy()
(因为我需要将其他文件放在 public 文件夹中,以便在 Blade/HTML 中访问。
我很确定我在某个地方弄错了,我在互联网上寻找使用 SASS 和 Laravel 中的 Blade 资源的正确方法,但我没有找到任何相关的东西.
也许我应该考虑另一种文件结构?但是哪一个?
I discovered that CSS files generated by SASS use a versioned URL even if I don't specify version() in webpack.mix.js.
在样式表中重写 url()
是 webpack feature, it appends the computed MD5 hash of the file to the URL. mix.version()
on the other hand generates a different hash thanks to those lines:
/**
* Read the file's contents.
*/
read() {
return fs.readFileSync(this.path(), 'utf8');
}
/**
* Calculate the proper version hash for the file.
*/
version() {
return md5(this.read()).substr(0, 20);
}
Laravel Mix 将文件作为字符串(而不是缓冲区)读取,对其进行哈希处理并仅提取前 20 个字符。我想不出一个简单的方法来覆盖这种行为,一个快速而肮脏的解决方法是重新定义 hash
函数:
const mix = require('laravel-mix');
let md5 = require('md5');
let fs = require('fs-extra');
Mix.manifest.hash = function (file) {
let f = new File(path.join(Config.publicPath, file));
let hash = md5(fs.readFileSync(f.path()));
let filePath = this.normalizePath(file);
this.manifest[filePath] = filePath + '?' + hash;
return this;
}
更好的方法是extend Laravel Mix and define your own versionMD5()
method, you might copy some code from this extension。
在我的项目中,我在 SASS 和 Blade 中都使用了一些资源(主要是图像)。另外,我有一些资源只在 SASS 中使用,有些只在 Blade 中使用。
例如,我可以在 Blade 文件中使用 mix('images/logo.png')
,在 SASS 文件中使用 background: url('../images/logo.png')
。
至于我的目录结构,我做了以下操作:
- resources
- js
- sass
- images // All images used by Blade, Sass, or both
- fonts
为了编译我的资源并将它们放在 public
文件夹中,我使用以下 webpack.mix.js
:
mix.copy('resources/images/**/*.*', 'public/images');
mix.copy('resources/fonts/**/*.*', 'public/fonts');
mix.version('public/images/**/*.*');
mix.version('public/fonts/**/*.*');
mix.js('resources/js/app.js', 'public/js')
.js('resources/js/vendor.js', 'public/js')
.scripts([ // Old not ES6 JS
'resources/js/tpl/core.min.js'
], 'public/js/core.min.js')
.sass('resources/sass/app.scss', 'public/css')
.sourceMaps()
.version();
结果,我在 app.css 中得到 URL :
background: url(/images/logo.png?0e567ce87146d0353fe7f19f17b18aca);
虽然我在渲染中得到另一个 HTML :
src="/images/logo.png?id=4d4e33eae039c367c8e9"
它们被视为 2 种不同的资源,这不是我所期望的...
可能的解决方法
我发现 SASS 生成的 CSS 文件使用版本化的 URL,即使我没有在 webpack.mix.js
中指定 version()
。所以我想知道也许我可以使用一些技巧,比如这个:
const sass = require('sass');
// Custom SASS function to get versioned file name
// Uses Mix version md5 hash
const functions = {
'versioned($uri)': function(uri, done) {
uri = uri && uri.getValue() || uri;
const version = File.find(path.join(Config.publicPath, uri)).version();
done(new sass.types.String(`${uri}?id=${version}`));
}
};
mix.sass('resources/sass/all.scss', 'public/css', {
sassOptions: {
functions
}
})
.options({ // Do not process URLs anymore
processCssUrls: false
});
然后像这样在 SASS 中使用它:
background-image: url(versioned('/images/logo.png'));
但是这个解决方案有很多缺点,我不得不每次都使用versioned
函数,如果没有webpack.mix.js
函数,我的源代码将无法在其他项目中轻松运行,并且我必须编辑资源文件夹中使用的每个文件才能使用该功能。
其他解决方案?
我认为问题的根源可能来自我构建文件的方式,我有一个 resources/images
文件夹,其中包含 SASS 使用的图像,也被 Blade 使用。
在 SASS 中使用的图像将被复制到 public/images
因为这是 SASS 使用 webpack 的方式,并且这些图像也会被复制第二次因为我使用了 mix.copy()
(因为我需要将其他文件放在 public 文件夹中,以便在 Blade/HTML 中访问。
我很确定我在某个地方弄错了,我在互联网上寻找使用 SASS 和 Laravel 中的 Blade 资源的正确方法,但我没有找到任何相关的东西.
也许我应该考虑另一种文件结构?但是哪一个?
I discovered that CSS files generated by SASS use a versioned URL even if I don't specify version() in webpack.mix.js.
在样式表中重写 url()
是 webpack feature, it appends the computed MD5 hash of the file to the URL. mix.version()
on the other hand generates a different hash thanks to those lines:
/**
* Read the file's contents.
*/
read() {
return fs.readFileSync(this.path(), 'utf8');
}
/**
* Calculate the proper version hash for the file.
*/
version() {
return md5(this.read()).substr(0, 20);
}
Laravel Mix 将文件作为字符串(而不是缓冲区)读取,对其进行哈希处理并仅提取前 20 个字符。我想不出一个简单的方法来覆盖这种行为,一个快速而肮脏的解决方法是重新定义 hash
函数:
const mix = require('laravel-mix');
let md5 = require('md5');
let fs = require('fs-extra');
Mix.manifest.hash = function (file) {
let f = new File(path.join(Config.publicPath, file));
let hash = md5(fs.readFileSync(f.path()));
let filePath = this.normalizePath(file);
this.manifest[filePath] = filePath + '?' + hash;
return this;
}
更好的方法是extend Laravel Mix and define your own versionMD5()
method, you might copy some code from this extension。