我正在尝试使用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
?
这是我尝试使用的较少的类和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);
...
答案 0 :(得分:41)
我写了一篇关于Using LESS CSS With MVC4 Web Optimization的快速博文。
它基本上归结为使用BundleTransformer.Less Nuget Package并更改BundleConfig.cs。
使用bootstrap进行测试。
编辑:应该提一下我说这个的原因,我是否也遇到了@import目录结构问题,这个库正确处理它。
答案 1 :(得分: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;
}
}
<强> 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");
}
}
}
答案 2 :(得分:21)
我知道这“应该是对Ben Cull的帖子的评论”,但它增加了一些额外的内容,无法在评论中添加。如果必须的话,请投票给我。或者关闭我。
Ben的博客文章全部完成,除了它没有指定缩小。
所以安装BundleTransformer.Less包就像Ben建议的那样,如果你想缩小你的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());
CssMinify
位于System.Web.Optimizations
我很放心解决@import问题,并且带有.less扩展名的结果文件没有发现我不在乎谁投票给我。
相反,如果你有投票支持这个答案的冲动,请将你的投票给Ben。
所以那里。
答案 3 :(得分: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";
}
}
然后在创建less变换对象时传入路径,如下所示:
lessBundle.Transforms.Add(
new LessTransform(HttpRuntime.AppDomainAppPath + "/Content/Less")
);
希望这有帮助。
答案 4 :(得分:4)
问题是DynamicFolderBundle读取文件的所有内容并将组合内容传递给LessMinify。
因此,任何@imports都没有引用文件来自的位置。
要解决此问题,我必须将所有“较少”的文件放在一个位置。
然后你必须了解文件的顺序变得重要。 因此我开始用一个数字重命名该文件(例如:“0 CONSTANTS.less”,“1 MIXIN.less”,这意味着它们在进入LessMinify之前被加载到组合输出的顶部。
如果您调试LessMinify并查看response.Content,您将看到合并较少的输出!
希望这有帮助
答案 5 :(得分:3)
这是我能想出的最简单的代码版本:
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));
}
}
答案 6 :(得分:2)
这就是我的所作所为:
添加了Twitter Bootstrap Nuget模块。
将此添加到我的_Layout.cshtml文件中:
<link href="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Content/twitterbootstrap/less")" rel="stylesheet" type="text/css" />
请注意,我将“less”文件夹重命名为twitterbootstrap,以证明我可以
将所有较少的文件移动到名为“imports”的子文件夹中,除 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);
这有效地解决了导入不知道从何处导入的问题。
答案 7 :(得分:1)
截至2013年2月: 迈克尔贝尔德的伟大解决方案被Ben Cull的帖子中提到的“BundleTransformer.Less Nuget Package”所取代。类似的答案: http://blog.cdeutsch.com/2012/08/using-less-and-twitter-bootstrap-in.html
Cdeutsch的博客&amp; awrigley的帖子添加缩小是好的,但显然现在不是正确的方法。
使用相同解决方案的其他人从BundleTransformer作者那里得到了一些答案: http://geekswithblogs.net/ToStringTheory/archive/2012/11/30/who-could-ask-for-more-with-less-css-part-2.aspx。请参阅底部的评论。
总结一下,使用BundleTransformer.MicrosoftAjax而不是内置的内置缩小器。 例如 css.Transforms.Add(new CssMinify()); 换成了 css.Transforms.Add(new BundleTransformer.MicrosoftAjax());
答案 8 :(得分:1)
继下面的RockResolve之后,要使用MicrosoftAjax minifier,请将其作为web.config中的默认CSS minifier引用,而不是将其作为参数传递。
要使MicrosoftAjaxCssMinifier成为默认的CSS-minifier,而将MicrosoftAjaxJsMinifier作为默认的JS-minifier,您需要对Web.config文件进行更改。在
答案 9 :(得分:0)
检查我的图书馆https://www.nuget.org/packages/LessMVCFour 希望它有所帮助。
答案 10 :(得分:-1)
我遇到了同样的问题,看到了同样的错误信息。在互联网上寻找解决方案让我来到这里。我的问题如下:
在一个较少的文件中,我在某个时候有一个不正确的风格,它给了我一个警告。无法解析较少的文件。我通过删除错误的行来消除错误消息。
我希望这有助于某人。