如何配置grunt-usemin以使用相对路径?

21

我有一个grunt项目,它由一个基于generator-webapp构建的yeoman-generator支持,如果有帮助的话,你可以在GitHub上找到它。

这个grunt项目使用了grunt-usemin任务。

我的项目涉及构建一个多语言网站,并为了保持整洁,我决定把所有用一种语言写的页面放在以该语言的2字代码命名的文件夹中。

| project/
|--dist/
|----en/
|------index.html
|------404.html
|------...
|----fr/
|------index.html
|------404.html
|------...

这些文件是使用handlebars模板制作的,并且使用assemble进行处理。在布局中,我有一些usemin的构建块。

<!-- build:css(.tmp) styles/main.css -->
<link rel="stylesheet" href="../styles/main.css">
<!-- endbuild -->
<!-- build:js scripts/vendor/modernizr.js -->
<script src="../bower_components/modernizr/modernizr.js"></script>
<!-- endbuild -->

在完美的世界里,这意味着

<link rel="stylesheet" href="../styles/main.css">
<script src="../scripts/vendor/modernizr.js"></script>

但相反展示

<link rel="stylesheet" href="styles/main.css">
<script src="scripts/vendor/modernizr.js"></script>

在我的情况下,这并不是理想的。相关的Gruntfile.js部分如下所示。


    useminPrepare: {
        options: {
            dest: '<%= yeoman.dist %>'
        },
        html: [
            '<%= yeoman.app %>/fr/{,*/}*.html',
            '<%= yeoman.app %>/en/{,*/}*.html'
        ]
    },
    usemin: {
        options: {
            dirs: ['<%= yeoman.dist %>']
        },
        html: [
            '<%= yeoman.dist %>/fr/{,*/}*.html',
            '<%= yeoman.dist %>/en/{,*/}*.html'
        ],
        css: ['<%= yeoman.dist %>/styles/{,*/}*.css']
    }

我尝试使用basedir选项, 将其设置为<%= yeoman.dist %>,并更改构建块。

<!-- build:css(.tmp) ../styles/main.css -->
<link rel="stylesheet" href="../styles/main.css">
<!-- endbuild -->
<!-- build:js ../scripts/vendor/modernizr.js -->
<script src="../bower_components/modernizr/modernizr.js"></script>
<!-- endbuild -->

但不幸的是,无法获得正确的输出。

更具体地说,第一个没有改变任何内容,第二个导致文件夹 scriptsstyles 的输出层级过高。

| project/
|--app/
|--dist/
|--styles/
|--scripts/

而不是

| project/
|--app/
|--dist/
|----styles/
|----scripts/

有人知道该怎么做吗?这似乎是一个相当简单的用例,但我在Google、GitHub或SO上找不到所需的帮助...


这个有进展了吗? - Jonathan Wilson
不,但自那以后我改变了我的工作流程,现在我使用gulp而不是grunt,并且我不依赖这种功能。有一个gulp-useref可以做grunt-usemin所做的事情,但我不确定它是否解决了这个特定的问题。由于我不依赖这个,我也没有计划提出自己的解决方案/实现。 - Jérémie Parker
这个问题有一个可行的修复方法: https://github.com/buddhike/grunt-usemin/commit/daefa9eb9ac498a17090008c48fb7392ab82d359#commitcomment-6062680,但不幸的是还没有人合并它。 - hugo der hungrige
9个回答

4
我相信你可以通过以下方式实现你需要的功能:
HTML文件:
    <!-- build:css styles/main.css -->
    <link href='../styles/css/style.css' rel='stylesheet' type='text/css'>
    <link href='../styles/css/responsive.css' rel='stylesheet' type='text/css'>

    <link href="../styles/css/skins/welld.css" rel='stylesheet' type='text/css' id="skin-file">
    <!-- endbuild -->

Gruntfile.js

        useminPrepare: {
            options: {
                dest: '<%= yeoman.dist %>/'
            },
            html: ['<%= yeoman.app %>/snippets/head.html','<%= yeoman.app %>/snippets/tail.html']
        },
        usemin: {
            options: {
                dirs: ['<%= yeoman.dist %>/'],
                blockReplacements: {
                    css: function (block) {
                        return '<link rel="stylesheet" href="../' + block.dest + '"/>';
                    },
                    js: function (block) {
                        return '<script src="../' + block.dest + '"></script>';
                    }
                }
            },
            html: ['<%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%= yeoman.dist %>/styles/{,*/}*.css']
        }

关键解决方案在usemin任务的“blockReplacements”选项中。基本上,该任务将把您的文件放置在“<%= yeoman.dist %>/styles/main.css”下,而您的HTML将位于“<%= yeoman.dist %>/en/somefileinEnglish.html”下,并且此文件中每个“styles/main.css”的实例都将被替换为“../styles/main.css”,添加正确的相对路径。
额外提示:如果您正在构建多语言网站,可以考虑使用grunt-i18n来在构建时翻译您的文件,这样您就不需要为每种语言维护不同的HTML文件。

3
我正在构建自己的Grunt脚手架,但遇到了这个问题让我感到沮丧。不过我成功地创建了一个解决方法,现在想与大家分享。
我想用自己的例子来说明这个问题的原因以及我是如何解决它的。假设我的目录结构如下:
| in/
+---- pages/
|    +---- login.html
+---- js/
|    +---- s1.js
|    +---- s2.js
+---- index.html
| out/
| Gruntfile.js

其中in/是我的所有源文件所在的位置,out/dest目录,所有输出文件都将存储在此处。假设我想将s1.js导入login.html,我可以这样写:

<!-- build:js ../js/login.js -->
<script src="../js/s1.js"></script>
<!-- endbuild -->

在这里,usemin块执行一个简单的字符串替换,因此输出路径应该正好是我想要链接输出文件的位置。问题出现在当login.js不是落在out/js/login.js,而是useminPrepare最终将其落在了js/login.js时。原因在于useminPrepare仅仅在dest(即out)和输出路径(即../js/login.js)之间执行路径连接,而它应该根据HTML文件所在的位置执行路径连接。
为了解决这个问题,需要注意一下,如果我将dest设置为out/pages,它会正常工作,并且会考虑到login.html所在的位置。但是请注意,如果index.html以类似的方式导入js/s2.js,那么这个也会出现问题。因此,为了解决这个问题,我们需要为index.html创建一个useminPrepare目标,destout,并为login.html创建另一个目标,destout/pages。因此,我的useminPrepare配置现在看起来像这样:
"useminPrepare": {
    t1: {
        dest: "out",
        files: [{src: ["in/*.html"]}],
        ...
    },
    t2: {
        dest: "out/pages",
        files: [{src: ["in/pages/*.html"]],
        ....
    }
}

所有目标都必须运行。现在你可能会说:如果我在其他子目录下有更多的HTML文件怎么办?这是否意味着我必须为每个包含HTML文件的目录创建一个目标?这很烦人和愚蠢。确实如此!我同意。因此,我编写了一个非常特殊的grunt任务来帮助解决问题。我称之为useminPreparePrepare。我故意给它取了一个愚蠢的名字,因为它是愚蠢的。我真的希望有一天可以摆脱这个解决方法,当usemin的开发人员解决这个问题时。正如其名称所示,useminPreparePrepare为useminPrepare准备配置文件。它自己的配置镜像useminPrepare(实际上,大多数配置只是复制过去),除了一个例外;您将不得不添加一个指向源根目录的属性,其中包含所有源文件,以便它可以计算出源根目录和HTML文件之间的相对路径。它将执行我上面提到的基本操作。另外,它也会为暂存目录执行相同的操作,因此暂存文件不会跳出暂存目录。您甚至可以在useminPreparePrepare中拥有多个目标,它将复制您运行的目标的选项。
为了使用这个解决方法,你首先需要导入useminPreparePrepare。我没有把它放在npm上,所以你只需要复制粘贴即可。然后将你的useminPrepare配置重命名为useminPreparePrepare,并添加src属性。对于上面的例子,src应该是"in"。接下来,你需要运行useminPreparePrepare,并选择你想要的目标,然后立即运行useminPrepare而不指定目标,以便所有目标都会运行。给定上面的例子,Grunt配置可能如下所示:
"useminPreparePrepare": {
    html: "in/**/*.html",   // All HTML files under in/ and its subdirectories.
    options: {
        src: "in",
        dest: "out",
        ....
    }
},
"copy": {
    html: { // Copies all HTML files from in/ to out/ preserving their relative paths.
        files: [{
                expand: true,
                cwd: "in",
                src: ["**/*.html"],
                dest: "out"
            }
        ]
    }
},
"usemin": {
    html: "out/**/*.html",  // Process all HTML files under out/ and its subdirectories.
    ...
}

你可以看到上面的配置足够简单,可以递归地包含源目录下的所有HTML文件。useminPreparePrepare会处理所有愚蠢的解决方法,而且看起来与useminPrepare一样。
我希望这有所帮助!

1
我使用了如下所示的多个目标:
 useminPrepare: {
        target1 : {
                src : ['target1/*.html'],
                options : {
                    dest: 'out'
                }
            },

            target2 : {
                src : ['target2/folder/*.html'],
                options : {
                    dest: 'out/subfolder'
                }
            }
    },

那么在HTML块中,您可以这样做:
<!-- build:js ../subfolder/script.js -->
<script src="../subfolder/scripts/main.js></script>
<!-- endbuild -->

1
我的解决方案是手动修改grunt-usemin插件。
在您喜欢的文本编辑器中打开node_modules/grunt-usemin/fileprocessor.js。在大约152行左右找到:
if (assetSearchPath && assetSearchPath.length !== 0) {
  this.file.searchPath = assetSearchPath;
}

将其替换为:

并用以下内容代替:

if (assetSearchPath && assetSearchPath.length !== 0) {
  this.file.searchPath.splice(0, 0, assetSearchPath);
}

默认情况下,this.file.searchPath是一个包含当前文件绝对路径的数组。覆盖它并没有意义,最好是在数组前面添加'dist'。这样,如果在'dist'目录中找不到某个项目,就可以得出结论,要么路径是相对的,要么是错误的。因此,我们使用searchPath数组的第二个值来查找文件,如果它是一个相对路径,我们就得到了想要的内容。

0

我看到很多关于这个问题的提问,但没有真正的答案。在我的项目中,我将“dist”文件夹映射到服务器端的“/static”。因此,在index.html中,我不需要计算相对路径。

但是,使用usemin仍然存在问题。

<!-- build:css(.tmp) static/css/main.css -->
<link rel="stylesheet" href="css/main.css">
<!-- endbuild -->

usemin 文件将被写在 dist/static/css/main.css

而 HTML 将显示错误的(但预期的)路径。

<link rel="stylesheet" href="static/css/main.css">

我找到的唯一解决方法是不要触碰usemin块,运行grunt并手动更新路径。


是的,我以前使用过这种行为并取得了成功,但在这里它确实很相关。手动更新路径也不是一个解决方案,因为最终我可能会有很多页面。我可以随时进行搜索/替换或使用任务来完成,但我真的对usemin无法处理如此微不足道的用例感到困惑。我相信这是可能的,如果不是,应该被实现。 - Jérémie Parker
同意这不是长期解决方案。目前正在阅读代码。它确切地在grunt-usemin / lib / htmlprocessor.js中。有一个变量来从process.cwd()解析相对路径。大多数grunt任务都有CWD参数,而usemin没有。 - mahery rafara

0
useminPrepare: {
        loyalty: {
            src: '<%= loyalty.app %>/Views/index.tpl',
            options: {
                dest: '.',
                flow: {
                    html: {
                        steps: {
                            js: ['concat', 'uglifyjs'],
                            css: ['cssmin']
                        },
                        post: {}
                    }
                }
            }
        }
    },
<!-- build:css /resources/v4/css/loyalty/index.css -->
<link rel="stylesheet" href="../Resources/css/main.css">
<link rel="stylesheet" href="../Resources/css/product.css">
<link rel="stylesheet" href="../Resources/css/use_code.css">
<!-- endbuild -->

在Gruntfile中使用'.'作为目标。


0

我将usemin块相对于模板进行了调整。

我的结构如下:

app/ - webroot source (devmode)
app/views/layouts - layouts for server-generated pages (also has usermin tags init)
app/js - source javascript

dist/ - production build dir

我在app/layouts/main.html中让我的HTML看起来像这样:

    <!-- build:js js/static.min.js -->
      <script src="../../js/controllers/StaticCtrl.js"></script>
      <script src="../../js/directives/backImg.js"></script>
      <script src="../../js/smooth-scroll.js"></script>
    <!-- endbuild -->

在开发过程中(没有使用usemin,只是提供文件),"../../"巧妙地解析为"/",因此仍然有效。这样,在开发过程中,您无需预处理(使用观察任务或其他方式)。


0
如果我理解你的问题正确,我认为你需要使用“root”选项:
{
  useminPrepare: {
    html: 'html/index.html',
    options: {
    root: 'app'
    dest: 'dist'
    }
  }
}

尽管index.html位于html/文件夹中,但文件查找将从app/而不是html/进行。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接