Laravel Blade、Mix 和 SASS 资源版本共享

Posted

技术标签:

【中文标题】Laravel Blade、Mix 和 SASS 资源版本共享【英文标题】:Laravel Blade, Mix and SASS resource versioning sharing 【发布时间】:2020-07-28 06:01:40 【问题描述】:

在我的项目中,我在 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"

它们被认为是两种不同的资源,这不是我所期望的......

可能的解决方法

我发现 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()(因为我需要将其他文件放在公共文件夹中以便在 Blade/HTML 中访问)。

我很确定我在某个地方弄错了,我在互联网上查找了在 Laravel 中使用 SASS 和 Blade 资源的正确方法,但我没有找到任何相关内容。 也许我应该考虑另一种文件结构?但是哪一个?

【问题讨论】:

我想发表自己的想法,同时我也面临同样的问题。 Laravel Mix 以不同的方式处理 .blade 和 CSS/JS 文件。您的徽标图像不会获得与使用 mix.version() 的 .blade 资产相同的哈希值,对于 CSS 文件,有自己的文件加载器和自己的哈希函数。它与文件结构没有任何共同之处。您提出的解决方法似乎是一个不错的选择,我认为这个问题没有本机解决方案。 【参考方案1】:

我发现 SASS 生成的 CSS 文件使用版本化 URL,即使我没有在 webpack.mix.js 中指定 version()。

在样式表中重写url() 是webpack feature,它将文件的计算MD5 哈希附加到URL。另一方面,mix.version() 生成不同的哈希值,这要归功于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 并定义自己的versionMD5() 方法,您可以从this extension 复制一些代码。

【讨论】:

以上是关于Laravel Blade、Mix 和 SASS 资源版本共享的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Laravel Mix 连接编译的 SASS 和组合 CSS?

Laravel mix.sass“prependData”不向 scss 文件提供数据

Laravel mix 不编译 sass

Laravel 5.4 Webpack Mix - 合并SCSS和CSS

Laravel Mix 未定义 JavaScript 函数

Laravel 5 & Webpack SASS - 如何添加另一个 CSS 文件