25

Web プロジェクトに LESS ファイルを含めようとしており、MVC 4 バンドル機能を dotLess ライブラリに呼び出して LESS を CSS に変換し、結果を縮小してブラウザに渡します。

ASP.NET サイト( LESS、CoffeeScript、SCSS、Sass Bundlingという見出しの下) で例を見つけました。これによりLessTransform、次のようなクラスが得られました。

public class LessTransform : IBundleTransform
{
    public void Process(BundleContext context, BundleResponse response)
    {
        response.Content = dotless.Core.Less.Parse(response.Content);
        response.ContentType = "text/css";
    }
}

BundleConfigそして私のクラスのこの行:

bundles.Add(new Bundle(
    "~/Content/lessTest", 
    new LessTransform(), 
    new CssMinify()).Include("~/Content/less/test.less"));

最後に、_Layout.cshtml に次の行があります<head>

@Styles.Render("~/Content/lessTest")

サイトがデバッグ モードの場合、これはブラウザーにレンダリングされます。

<link href="/Content/less/test.less" rel="stylesheet"/>

.less ファイルのルールが適用され、そのリンクをたどると、LESS が正しく CSS に変換されていることがわかります。

ただし、サイトをリリース モードにすると、次のように表示されます。

<link href="/Content/less?v=lEs-HID6XUz3s2qkJ35Lvnwwq677wTaIiry6fuX8gz01" rel="stylesheet"/>

リンクをたどると IIS から 404 エラーが発生するため、.less ファイルのルールは適用されません。

そのため、バンドルに問題があるようです。これをリリースモードで動作させるにはどうすればよいですか、または何が問題なのかを正確に知るにはどうすればよいですか?

4

5 に答える 5

12

2019 年 12 月 8 日編集 ASP.NET には何年にもわたって大きな変更が加えられてきたため、これはこの問題に対する受け入れ可能な回答ではなくなりました。このコードを変更したり、この問題を解決するのに役立つ他の回答を提供したりする他の回答がさらに下にあります。

dotless エンジンは、@import パスを解決するために、現在処理されているバンドル ファイルのパスを知る必要があるようです。上記のプロセス コードを実行すると、解析中の .less ファイルに他の less ファイルがインポートされている場合、dotless.Core.Less.Parse() の結果は空の文字列になります。

ここでの Ben Foster の応答は、インポートされたファイルを最初に読み取ることでそれを修正します。

ファイルと DotLess のインポート

LessTransform ファイルを次のように変更します。

public class LessTransform : IBundleTransform
{
    public void Process(BundleContext context, BundleResponse bundle)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

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

        var bundleFiles = new List<FileInfo>();

        foreach (var bundleFile in bundle.Files)
        {
            bundleFiles.Add(bundleFile);

            SetCurrentFilePath(lessParser, bundleFile.FullName);
            string source = File.ReadAllText(bundleFile.FullName);
            content.Append(lessEngine.TransformToCss(source, bundleFile.FullName));
            content.AppendLine();

            bundleFiles.AddRange(GetFileDependencies(lessParser));
        }

        if (BundleTable.EnableOptimizations)
        {
            // include imports in bundle files to register cache dependencies
            bundle.Files = bundleFiles.Distinct();
        }

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

    /// <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, true, false);
    }

    /// <summary>
    /// Gets the file dependencies (@imports) of the LESS file being parsed.
    /// </summary>
    /// <param name="lessParser">The LESS parser.</param>
    /// <returns>An array of file references to the dependent file references.</returns>
    private IEnumerable<FileInfo> GetFileDependencies(Parser lessParser)
    {
        IPathResolver pathResolver = GetPathResolver(lessParser);

        foreach (var importPath in lessParser.Importer.Imports)
        {
            yield return new FileInfo(pathResolver.GetFullPath(importPath));
        }

        lessParser.Importer.Imports.Clear();
    }

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

        return fileReader.PathResolver;
    }

    /// <summary>
    /// Informs the LESS parser about the path to the currently processed file. 
    /// This is done by using a 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)
            throw new InvalidOperationException("Unexpected dotless importer type.");

        var fileReader = importer.FileReader as FileReader;

        if (fileReader == null || !(fileReader.PathResolver is ImportedFilePathResolver))
        {
            fileReader = new FileReader(new ImportedFilePathResolver(currentFilePath));
            importer.FileReader = fileReader;
        }
    }
}

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

    public ImportedFilePathResolver(string currentFilePath)
    {
        if (string.IsNullOrEmpty(currentFilePath))
        {
            throw new ArgumentNullException("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)
    {
        if (filePath.StartsWith("~"))
        {
            filePath = VirtualPathUtility.ToAbsolute(filePath);
        }

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

        return filePath;
    }
}
于 2013-03-07T13:15:10.987 に答える
2

これは機能するように見えます-Processファイルコレクションを反復処理するようにメソッドを変更しました:

public void Process(BundleContext context, BundleResponse response)
{
    var builder = new StringBuilder();
    foreach (var fileInfo in response.Files)
    {
        using (var reader = fileInfo.OpenText())
        {
            builder.Append(dotless.Core.Less.Parse(reader.ReadToEnd()));
        }
    }

    response.Content = builder.ToString();
    response.ContentType = "text/css";
}

ただし、少ないファイルにステートメントがある場合、これは壊れ@importます。この場合、次のようにもう少し作業を行う必要があります: https://gist.github.com/chrisortman/2002958

于 2013-03-06T18:58:51.527 に答える
1

lessすでにいくつかの素晴らしい答えがあります。これは、ファイルに関する MVC バンドルを追加しようとしたときに自分で見つけた非常に簡単な解決策です。

lessファイル (たとえば、 )を作成した後test.less、それを右クリックし、[Web コンパイラ (ここで取得)] オプションで、[ Compile File.

これにより、結果のcssファイルlessとその縮小版が生成されます。(test.cssおよびtest.min.css)。

バンドルでは、生成されたcssファイルを参照するだけです

style = new StyleBundle("~/bundles/myLess-styles")
    .Include("~/Content/css/test.css", new CssRewriteUrlTransform());

bundles.Add(style);

ビューで、そのバンドルを参照します。

@Styles.Render("~/bundles/myLess-styles")

それはうまくいくはずです。

于 2016-01-08T16:02:08.980 に答える