【问题标题】:MVC4 Less Bundle @import DirectoryMVC4 Less Bundle @import 目录
【发布时间】:2012-03-24 11:53:23
【问题描述】:

我正在尝试使用 MVC4 捆绑来对我的一些较少文件进行分组,但看起来我使用的导入路径已关闭。我的目录结构是:

static/
    less/
        mixins.less
        admin/
            user.less

在 user.less 中,我尝试使用以下方式导入 mixins.less:

@import "../mixins.less";

以前在使用 chirpy 和 dotless 时这对我有用,但现在我注意到 ELMAH 对我很生气,说:

System.IO.FileNotFoundException: 
    You are importing a file ending in .less that cannot be found.
File name: '../mixins.less'

我应该在 MVC4 中使用不同的 @import 吗?

一些附加信息

这是我用来尝试这个的 less 类和 global.asax.cs 代码:

LessMinify.cs

...
public class LessMinify : CssMinify
{
    public LessMinify() {}

    public override void Process(BundleContext context, BundleResponse response)
    {
        response.Content = Less.Parse(response.Content);
        base.Process(context, response);
    }
}
...

Global.asax.cs

...
DynamicFolderBundle lessFB = 
    new DynamicFolderBundle("less", new LessMinify(), "*.less");
    
BundleTable.Bundles.Add(lessFB);

Bundle AdminLess = new Bundle("~/AdminLessBundle", new LessMinify());
...
AdminLess.AddFile("~/static/less/admin/user.less");
BundleTable.Bundles.Add(AdminLess);
...

【问题讨论】:

  • 与您的问题无关,但您能分享一下您用于 Less Parsing 的内容吗?
  • @ShaneCourtrille 查看 nuget.org/packages?q=dotless 我相信我使用的是 dotless 1.3

标签: import asp.net-mvc-4 bundle less asp.net-optimization


【解决方案1】:

我写了一篇关于Using LESS CSS With MVC4 Web Optimization 的快速博客文章。

它基本上归结为使用 BundleTransformer.Less Nuget Package 并更改您的 BundleConfig.cs。

用引导程序测试。

编辑:应该提到我这么说的原因,是我也遇到了@import目录结构问题,这个库正确地处理了它。

【讨论】:

  • 谢谢你,我正悄悄地发疯,却找不到我需要的东西。不敢相信这篇文章没有被投票。我在您的解决方案中添加了一个带有附录的帖子。
  • 我不会说它简单也不优雅。如果您使用的是 LESS,BundleTransformer 是一个非常重的 Nuget 包。 (实际上是 5+ nuget 包,并且需要在您的 Web 服务器上安装 IE9+)。 Michael Baird 的回答要简单得多
  • Ben,我已按照您的博客文章的说明以及 BundleTransformer.Less 文档页面上给出的示例进行操作,但我仍然收到有关 dotless 无法找到引用的文件的错误通过@import 声明。我所有的文件都在同一个目录中,所以我只使用@import url("filename.less"); 我什至关注了这个帖子:geekswithblogs.net/ToStringTheory/archive/2012/11/30/… 任何想法可能发生什么? MVC4/.NET 4.5
  • @ps2goat 在黑暗中刺痛,但是您是否替换了安装 BundleTransformer nuget 包时它要求您替换的两个 web.config 部分?
  • 我认为所有的需求都不是通过 nuget 包自动下载的。我们最终选择了@MichaelBaird 的解决方案,因为正如其他人所说,它不需要 IE 9+ 或 Visual C++ 库(取决于您的 javascript 引擎切换器版本)。
【解决方案2】:

在 GitHub Gist 上发布了与 @import 和 dotLess 配合使用的代码:https://gist.github.com/2002958

我用Twitter Bootstrap 对其进行了测试,效果很好。

ImportedFilePathResolver.cs

public class ImportedFilePathResolver : IPathResolver
{
    private string currentFileDirectory;
    private string currentFilePath;

    /// <summary>
    /// Initializes a new instance of the <see cref="ImportedFilePathResolver"/> class.
    /// </summary>
    /// <param name="currentFilePath">The path to the currently processed file.</param>
    public ImportedFilePathResolver(string currentFilePath)
    {
        CurrentFilePath = currentFilePath;
    }

    /// <summary>
    /// Gets or sets the path to the currently processed file.
    /// </summary>
    public string CurrentFilePath
    {
        get { return currentFilePath; }
        set
        {
            currentFilePath = value;
            currentFileDirectory = Path.GetDirectoryName(value);
        }
    }

    /// <summary>
    /// Returns the absolute path for the specified improted file path.
    /// </summary>
    /// <param name="filePath">The imported file path.</param>
    public string GetFullPath(string filePath)
    {
        filePath = filePath.Replace('\\', '/').Trim();

        if(filePath.StartsWith("~"))
        {
            filePath = VirtualPathUtility.ToAbsolute(filePath);
        }

        if(filePath.StartsWith("/"))
        {
            filePath = HostingEnvironment.MapPath(filePath);
        }
        else if(!Path.IsPathRooted(filePath))
        {
            filePath = Path.Combine(currentFileDirectory, filePath);
        }

        return filePath;
    }
}

LessMinify.cs

public class LessMinify : IBundleTransform
{
    /// <summary>
    /// Processes the specified bundle of LESS files.
    /// </summary>
    /// <param name="bundle">The LESS bundle.</param>
    public void Process(BundleContext context, BundleResponse bundle)
    {
        if(bundle == null)
        {
            throw new ArgumentNullException("bundle");
        }

        context.HttpContext.Response.Cache.SetLastModifiedFromFileDependencies();

        var lessParser = new Parser();
        ILessEngine lessEngine = CreateLessEngine(lessParser);

        var content = new StringBuilder(bundle.Content.Length);

        foreach(FileInfo file in bundle.Files)
        {
            SetCurrentFilePath(lessParser, file.FullName);
            string source = File.ReadAllText(file.FullName);
            content.Append(lessEngine.TransformToCss(source, file.FullName));
            content.AppendLine();

            AddFileDependencies(lessParser);
        }

        bundle.Content = content.ToString();
        bundle.ContentType = "text/css";
        //base.Process(context, bundle);
    }

    /// <summary>
    /// Creates an instance of LESS engine.
    /// </summary>
    /// <param name="lessParser">The LESS parser.</param>
    private ILessEngine CreateLessEngine(Parser lessParser)
    {
        var logger = new AspNetTraceLogger(LogLevel.Debug, new Http());
        return new LessEngine(lessParser, logger, false);
    }

    /// <summary>
    /// Adds imported files to the collection of files on which the current response is dependent.
    /// </summary>
    /// <param name="lessParser">The LESS parser.</param>
    private void AddFileDependencies(Parser lessParser)
    {
        IPathResolver pathResolver = GetPathResolver(lessParser);

        foreach(string importedFilePath in lessParser.Importer.Imports)
        {
            string fullPath = pathResolver.GetFullPath(importedFilePath);
            HttpContext.Current.Response.AddFileDependency(fullPath);
        }

        lessParser.Importer.Imports.Clear();
    }

    /// <summary>
    /// Returns an <see cref="IPathResolver"/> instance used by the specified LESS lessParser.
    /// </summary>
    /// <param name="lessParser">The LESS prser.</param>
    private IPathResolver GetPathResolver(Parser lessParser)
    {
        var importer = lessParser.Importer as Importer;
        if(importer != null)
        {
            var fileReader = importer.FileReader as FileReader;
            if(fileReader != null)
            {
                return fileReader.PathResolver;
            }
        }

        return null;
    }

    /// <summary>
    /// Informs the LESS parser about the path to the currently processed file. 
    /// This is done by using custom <see cref="IPathResolver"/> implementation.
    /// </summary>
    /// <param name="lessParser">The LESS parser.</param>
    /// <param name="currentFilePath">The path to the currently processed file.</param>
    private void SetCurrentFilePath(Parser lessParser, string currentFilePath)
    {
        var importer = lessParser.Importer as Importer;
        if(importer != null)
        {
            var fileReader = importer.FileReader as FileReader;

            if(fileReader == null)
            {
                importer.FileReader = fileReader = new FileReader();
            }

            var pathResolver = fileReader.PathResolver as ImportedFilePathResolver;

            if(pathResolver != null)
            {
                pathResolver.CurrentFilePath = currentFilePath;
            }
            else
            {
               fileReader.PathResolver = new ImportedFilePathResolver(currentFilePath);
            }
        }
        else
        {
            throw new InvalidOperationException("Unexpected importer type on dotless parser");
        }


    }
}

【讨论】:

  • 当我尝试打开您的解决方案时出现错误。没有找到 nuget.targets。
  • 这正是我想要的。伟大的职位迈克尔!
  • 有一个稍微改进的版本声称也适用于 .net 4.5:github.com/dotless/dotless/issues/148bitbucket.org/mrcode/bundlingsandbox/changeset/…
  • .net 4.5 中可能发生了一些变化,但上面的代码没有正确缓存导入。为确保正确配置缓存依赖项,您需要在启用优化时将所有导入路径添加到 Bundle.Files 集合。我的工作代码 - gist.github.com/3924025
  • 查看 Ben Cull 的回答并投票。它是现代工作的一种,带有 BundleTransform.Less nuget 包。没有痛苦,只是有效。
【解决方案3】:

Ben Cull 回答的附录:

我知道这“应该是对 Ben Cull 帖子的评论”,但它添加了一些额外内容,这是无法在评论中添加的。因此,如果必须,请投票给我。或者关闭我。

Ben 的博客文章做到了这一切,只是它没有指定缩小。

所以按照 Ben 的建议安装 BundleTransformer.Less 包,然后,如果您想缩小 css,请执行以下操作(在 ~/App_Start/BundleConfig.cs 中):

var cssTransformer = new CssTransformer();
var jsTransformer = new JsTransformer();
var nullOrderer = new NullOrderer();

var css = new Bundle("~/bundles/css")
    .Include("~/Content/site.less");
css.Transforms.Add(cssTransformer);
css.Transforms.Add(new CssMinify());
css.Orderer = nullOrderer;

bundles.Add(css);

添加的行是:

css.Transforms.Add(new CssMinify());

CssMinifySystem.Web.Optimizations 中的位置

我很欣慰地解决了@import 问题,并且没有找到带有 .less 扩展名的结果文件,我不在乎谁投票给我。

相反,如果您有为这个答案投票的冲动,请投票给 Ben。

就这样。

【讨论】:

  • 这可行,但似乎导入的文件被内联多次(每次导入一次)。这违背了整个捆绑想法的目的,即减少文件大小......
【解决方案4】:

我发现一个非常有用的解决方法是在 LessMinify.Process() 中运行 Less.Parse 之前设置目录。这是我的做法:

public class LessTransform : IBundleTransform
    {
        private string _path;

        public LessTransform(string path)
        {
            _path = path;
        }

        public void Process(BundleContext context, BundleResponse response)
        {
            Directory.SetCurrentDirectory(_path);

            response.Content = Less.Parse(response.Content);
            response.ContentType = "text/css";
        }
    }

然后在创建 less 变换对象时传入路径,如下所示:

lessBundle.Transforms.Add(
    new LessTransform(HttpRuntime.AppDomainAppPath + "/Content/Less")
);

希望这会有所帮助。

【讨论】:

  • 我想知道当一个简单的解决方案就足够了时,为什么其他答案的复杂性如此之高。谢谢
  • 这对我有帮助,感谢发帖。这是一个简单的答案,效果很好。
【解决方案5】:

问题在于 DynamicFolderBundle 读取文件的所有内容并将合并的内容传递给 LessMinify。

因此,任何@imports 都不会引用文件的来源。

为了解决这个问题,我必须将所有“较少”文件放在一个位置。

那么你必须了解文件的顺序变得很重要。 因此,我开始用数字重命名文件(例如:“0 CONSTANTS.less”、“1 MIXIN.less”,这意味着它们在进入 LessMinify 之前被加载到组合输出的顶部。

如果您调试您的 LessMinify 并查看 response.Content,您将看到组合的 less 输出!

希望对你有帮助

【讨论】:

  • 这似乎没有帮助。我有 0colors.less 并且正在使用 bundle.AddDirectory 加载位于同一文件夹中的所有 less 文件。 @import "0colors.less" 抛出同样的错误。
  • 在 global.asax.cs 我有: DynamicFolderBundle lessFb = new DynamicFolderBundle("less", new LessMinify(), "*.less"); BundleTable.Bundles.Add(lessFb);然后使用路径 /static/less/admin/less (如上例)到达相对位置。
【解决方案6】:

这是我能想到的最简单的代码版本:

public class LessTransform : IBundleTransform
{
    public void Process(BundleContext context, BundleResponse bundle)
    {
        var pathResolver = new ImportedFilePathResolver(context.HttpContext.Server);
        var lessParser = new Parser();
        var lessEngine = new LessEngine(lessParser);
        (lessParser.Importer as Importer).FileReader = new FileReader(pathResolver);

        var content = new StringBuilder(bundle.Content.Length);
        foreach (var bundleFile in bundle.Files)
        {
            pathResolver.SetCurrentDirectory(bundleFile.IncludedVirtualPath);
            content.Append(lessEngine.TransformToCss((new StreamReader(bundleFile.VirtualFile.Open())).ReadToEnd(), bundleFile.IncludedVirtualPath));
            content.AppendLine();
        }

        bundle.ContentType = "text/css";
        bundle.Content = content.ToString();
    }
}

public class ImportedFilePathResolver : IPathResolver
{
    private HttpServerUtilityBase server { get; set; }
    private string currentDirectory { get; set; }

    public ImportedFilePathResolver(HttpServerUtilityBase server)
    {
        this.server = server;
    }

    public void SetCurrentDirectory(string fileLocation)
    {
        currentDirectory = Path.GetDirectoryName(fileLocation);
    }

    public string GetFullPath(string filePath)
    {
        var baseDirectory = server.MapPath(currentDirectory);
        return Path.GetFullPath(Path.Combine(baseDirectory, filePath));
    }
}

【讨论】:

  • 这段代码对我有用。无论如何,我在代码的第 14 行做了一些改动: using (var stream = new StreamReader(bundleFile.VirtualFile.Open())) { content.Append(lessEngine.TransformToCss(stream.ReadToEnd(), bundleFile.IncludedVirtualPath)) ; };否则在加载页面后我无法更改 .less 文件,因为“它正被另一个进程使用”
【解决方案7】:

这就是我所做的:

添加了 Twitter Bootstrap Nuget 模块。

将此添加到我的 _Layout.cshtml 文件中:

<link href="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Content/twitterbootstrap/less")" rel="stylesheet" type="text/css" />

请注意,我将“less”文件夹重命名为 twitterbootstrap 只是为了证明我可以

将所有较少的文件移动到名为“imports”的子文件夹except bootstrap.less 和(用于响应式设计)responsive.less

~/Content/twitterbootstrap/imports

在 web.config 中添加了一个配置:

<add key="TwitterBootstrapLessImportsFolder" value="imports" />

创建了两个类(对上面的类稍作修改):

using System.Configuration;
using System.IO;
using System.Web.Optimization;
using dotless.Core;
using dotless.Core.configuration;
using dotless.Core.Input;

namespace TwitterBootstrapLessMinify
{
    public class TwitterBootstrapLessMinify : CssMinify
    {
        public static string BundlePath { get; private set; }

        public override void Process(BundleContext context, BundleResponse response)
        {
            setBasePath(context);

            var config = new DotlessConfiguration(dotless.Core.configuration.DotlessConfiguration.GetDefault());
            config.LessSource = typeof(TwitterBootstrapLessMinifyBundleFileReader);

            response.Content = Less.Parse(response.Content, config);
            base.Process(context, response);
        }

        private void setBasePath(BundleContext context)
        {
            var importsFolder = ConfigurationManager.AppSettings["TwitterBootstrapLessImportsFolder"] ?? "imports";
            var path = context.BundleVirtualPath;

            path = path.Remove(path.LastIndexOf("/") + 1);

            BundlePath = context.HttpContext.Server.MapPath(path + importsFolder + "/");
        }
    }

    public class TwitterBootstrapLessMinifyBundleFileReader : IFileReader
    {
        public IPathResolver PathResolver { get; set; }
        private string basePath;

        public TwitterBootstrapLessMinifyBundleFileReader() : this(new RelativePathResolver())
        {
        }

        public TwitterBootstrapLessMinifyBundleFileReader(IPathResolver pathResolver)
        {
            PathResolver = pathResolver;
            basePath = TwitterBootstrapLessMinify.BundlePath;
        }

        public bool DoesFileExist(string fileName)
        {
            fileName = PathResolver.GetFullPath(basePath + fileName);

            return File.Exists(fileName);
        }

        public string GetFileContents(string fileName)
        {
            fileName = PathResolver.GetFullPath(basePath + fileName);

            return File.ReadAllText(fileName);
        }
    }
}

我的 IFileReader 实现着眼于 TwitterBootstrapLessMinify 类的静态成员 BundlePath。这允许我们注入一个基本路径供导入使用。我本来希望采用不同的方法(通过提供我的类的实例,但我不能)。

最后,我在 Global.asax 中添加了以下几行:

BundleTable.Bundles.EnableDefaultBundles();

var lessFB = new DynamicFolderBundle("less", new TwitterBootstrapLessMinify(), "*.less", false);
BundleTable.Bundles.Add(lessFB);

这有效地解决了导入不知道从哪里导入的问题。

【讨论】:

    【解决方案8】:

    截至 2013 年 2 月: Michael Baird 的出色解决方案被 Ben Cull 的帖子中提到的“BundleTransformer.Less Nuget Package”答案所取代。类似的答案在: http://blog.cdeutsch.com/2012/08/using-less-and-twitter-bootstrap-in.html

    Cdeutsch 的博客和 awrigley 的帖子添加缩小效果很好,但现在显然不是正确的方法。

    具有相同解决方案的其他人从 BundleTransformer 作者那里得到了一些答案: http://geekswithblogs.net/ToStringTheory/archive/2012/11/30/who-could-ask-for-more-with-less-css-part-2.aspx。请参阅底部的 cmets。

    总而言之,使用 BundleTransformer.MicrosoftAjax 而不是内置的内置压缩器。 例如 css.Transforms.Add(new CssMinify()); 替换为 css.Transforms.Add(new BundleTransformer.MicrosoftAjax());

    【讨论】:

      【解决方案9】:

      从下面的 RockResolve 开始,要使用 MicrosoftAjax 缩小器,请将其引用为 web.config 中的默认 CSS 缩小器,而不是将其作为参数传入。

      来自https://bundletransformer.codeplex.com/wikipage/?title=Bundle%20Transformer%201.7.0%20Beta%201#BundleTransformerMicrosoftAjax_Chapter

      要将 MicrosoftAjaxCssMinifier 设为默认 CSS-minifier 并将 MicrosoftAjaxJsMinifier 设为默认 JS-minifier,您需要更改 Web.config 文件。在 \configuration\bundleTransformer\core\css 元素的 defaultMinifier 属性中,必须将值设置为等于 MicrosoftAjaxCssMinifier,并且在 \configuration\bundleTransformer\core\js 元素的相同属性中 - MicrosoftAjaxJsMinifier。

      【讨论】:

        【解决方案10】:

        我遇到了同样的问题,看到同样的错误信息。在互联网上寻找解决方案把我带到了这里。我的问题如下:

        在一个 less 文件中,有时我的样式不正确,这给了我一个警告。无法解析 less 文件。我通过删除不正确的行来消除错误消息。

        我希望这对某人有所帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-06-02
          • 2012-07-18
          • 1970-01-01
          • 1970-01-01
          • 2012-08-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多