MVC4 Less Bundle @import目录

Jes*_*ing 51 import bundle less asp.net-mvc-4 asp.net-optimization

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

static/
    less/
        mixins.less
        admin/
            user.less
Run Code Online (Sandbox Code Playgroud)

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

@import "../mixins.less";
Run Code Online (Sandbox Code Playgroud)

这曾经在我使用chirpy和无点之前为我工作,但现在我注意到ELMAH对我生气,说:

System.IO.FileNotFoundException: 
    You are importing a file ending in .less that cannot be found.
File name: '../mixins.less'
Run Code Online (Sandbox Code Playgroud)

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

一些额外的信息

这是我用来尝试这个的较少的类和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);
    }
}
...
Run Code Online (Sandbox Code Playgroud)

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);
...
Run Code Online (Sandbox Code Playgroud)

Ben*_*ull 41

我写了一篇关于在MVC4 Web优化中使用LESS CSS的快速博客文章.

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

用bootstrap测试.

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

  • 我不会称之为简单而优雅.BundleTransformer是一个非常重的Nuget包,如果您使用的只是LESS.(实际上有5个以上的nuget包,需要在你的网络服务器上安装IE9 +).Michael Baird的答案要简单得多 (6认同)
  • 谢谢你,我悄悄地疯了,没找到我需要的东西.不敢相信这篇文章没有被投票.我在你的解决方案中添加了一个附有附录的帖子. (5认同)

Mic*_*ird 26

在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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");
        }


    }
}
Run Code Online (Sandbox Code Playgroud)

  • .net 4.5中的某些内容可能已更改,但上述代码未正确缓存导入.要确保正确配置缓存依赖项,您需要在启用优化时将所有导入路径添加到`Bundle.Files`集合.我的工作代码 - https://gist.github.com/3924025 (6认同)

awr*_*ley 21

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);
Run Code Online (Sandbox Code Playgroud)

增加的线是:

css.Transforms.Add(new CssMinify());
Run Code Online (Sandbox Code Playgroud)

CssMinifySystem.Web.Optimizations

我很放心绕过@import问题和带有.less扩展名的结果文件没有发现我不在乎谁投票给我.

相反,如果您觉得有投票支持这个答案的冲动,请将您的投票给Ben.

所以那里.


小智 17

我发现一个非常有用的方法是在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";
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

lessBundle.Transforms.Add(
    new LessTransform(HttpRuntime.AppDomainAppPath + "/Content/Less")
);
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.

  • 我想知道为什么当一个简单的解决方案足够时,其他答案中的复杂程度如此之高.谢谢 (5认同)

The*_*ire 4

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

因此,任何@imports 都没有引用文件来自的位置。

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

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

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

希望这可以帮助