ASP.NET приложение MVC4 не удается скомпилировать Bootstrap.Меньше на производстве, пока он работает на dev
Я чувствую себя немного застрял прямо сейчас. Сначала я использовал nuget для
установка-пакет Bootstrap.меньше
а также
установить-пакет dotless
тогда, как показано в Рик Андерсонс Blogpost о связывании и минификации in asp.net mvc, я создал класс LessTransform. Я установил 2 почти пустым .меньше файлов и создан новый пакет упаковывать их...
var lessBundle = new Bundle("~/MyLess").IncludeDirectory("~/Content/MyLess", "*.less", true);
lessBundle.Transforms.Add(new LessTransformer());
lessBundle.Transforms.Add(new CssMinify());
bundles.Add(lessBundle);
что действовать лучше. Затем я добавил новый StyleBundle к основному бутстрапу.меньше файла (который в основном использует @import для включения всех других .меньше файлов, что Bootstrap.меньше кораблей)...
bundles.Add(new StyleBundle("~/Bootstrap").Include("~/Content/Bootstrap/less/bootstrap.less"));
и ScriptBundle для загрузочных JavaScripts...
bundles.Add(new ScriptBundle("~/bundles/Bootstrap").Include("~/Scripts/bootstrap/js/bootstrap-*"));
чтобы включить все отправленные bootstrap -*.файлы js и TADAA все работали нормально. CSS был скомпилирован, включая все импортированные файлы JavaScript были правильно загружены.
но ... все, что только работало режим разработки с
<compilation debug="true" targetFramework="4.5"/>
как только я отключу отладку, чтобы увидеть, работает ли пакетирование в один файл и минимизация, я сталкиваюсь с проблемой.
процесс связывания, похоже, не в состоянии импортировать все это .меньше файлов, импортированных в bootstrap.меньше
/* Minification failed. Returning unminified contents.
(11,1): run-time error CSS1019: Unexpected token, found '/'
(11,2): run-time error CSS1019: Unexpected token, found '/'
(12,1): run-time error CSS1031: Expected selector, found '@import'
(12,1): run-time error CSS1025: Expected comma or open brace, found '@import'
(12,27): run-time error CSS1019: Unexpected token, found '/'
(12,28): run-time error CSS1019: Unexpected token, found '/'
... here go many many lines like these
(60,25): run-time error CSS1019: Unexpected token, found ';'
(62,1): run-time error CSS1019: Unexpected token, found '/'
(62,2): run-time error CSS1019: Unexpected token, found '/'
(63,1): run-time error CSS1031: Expected selector, found '@import'
(63,1): run-time error CSS1025: Expected comma or open brace, found '@import'
(63,27): run-time error CSS1019: Unexpected token, found '/'
(63,28): run-time error CSS1019: Unexpected token, found '/'
: run-time error CSS1067: Unexpected end of file encountered
*/
/*!
* Bootstrap v2.3.1
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Designed and built with all the love in the world @twitter by @mdo and @fat.
*/
// Core variables and mixins
@import "variables.less"; // Modify this for custom colors, font-sizes, etc
@import "mixins.less";
... and the rest of the original bootstrap.less... no style definitions
взглянув на minified bootstrap.JavaScript bundle также поражает меня. в dev не было проблем после загрузки страницы, теперь после начальной загрузки.javascript был в комплекте и minified в Google состояния консоли JavaScript
Uncaught TypeError: Cannot read property 'Constructor' of undefined
Я просмотрел несколько тем, которые казались тесно связанными с моей проблемой, и я попробовал несколько вещей, но до сих пор безуспешно.
заранее большое спасибо всем, кто может пролить свет на мою ситуацию и кто укажет, что я упускаю или делаю неправильно. С наилучшими пожеланиями, Инго
4 ответов
если вы хотите использовать bootstrap как less-files и, кроме того, хотите перестать беспокоиться о связывании и минимизации на вашей машине разработки, а также на вашей производственной машине, вы можете рассмотреть возможность использования следующего подхода.
Примечание: вам не нужно все это, если вы играете только с меньшими файлами во время отладки; но как только вы хотите, чтобы ваше приложение работало на рабочем сервере, таком как Windows Azure, и все еще хотите просто изменить свой меньше файлов без необходимости заботиться о процедурах связывания и минимизации... что ж... тогда этот подход будет работать
поэтому, чтобы решить проблему, я чувствовал себя немного застрявшим, я должен был подойти к проблеме по-другому и должен был изменить (см. модификацию 2 ниже по сообщению) "BundleSource", который я думал, что хотел бы иметь.
поэтому не забудьте прочитать 2-ю модификацию / предупреждение близко к нижней части этого отвечай!
Модификация 1)
таким образом, первая и большая часть работы заключается в том, чтобы получить связывание файлов без начальной загрузки. Для этого я взял на себя смелость раскошелиться на кусок кода, который я нашел в интернете, который (если вам нужен только один пакет с меньшим количеством файлов) сам решает мою проблему... если вы не хотите использовать или иметь возможность использовать несколько меньших пакетов с несколькими базовыми каталогами... Так вот где я на самом деле нашел подход, который мне очень помог ...
... поэтому я благодарю Кристоф Клаас для своего блога-запись "использование ASP.NET связывание и минимизация с меньшим количеством файлов", на которые я случайно и с удовольствием наткнулся.
как и я, он пытался использовать LessMinify.cs, который Скотт Хансельман показывал в своих выступлениях, чтобы работать с 1 LESS-file вместо того, чтобы просто связывать каждый файл в 1 каталоге, полном LESS-files.
но ему пришлось немного расширить всю процедуру связывания, как он показывает в своей записи в блоге. Таким образом, решение, которое он предлагает, может объединить 1 меньше файла, который использует импорт для загрузки других меньших файлов. Но как он статически реализует путь, который добавляется в исходный каталог, в котором можно найти меньше файлов... какой бы меньший пакет вы ни определили, он должен выбрать меньший файл в том же каталоге...
вот где я взял на себя смелость расширить его решение немного дальше. Я создал файл LessBundling.cs со следующим содержанием:
using dotless.Core.configuration;
using dotless.Core.Input;
using MvcApplication2.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Web;
using System.Web.Hosting;
using System.Web.Optimization;
namespace MvcApplication2.Extensions
{
// create Less-Minifier (use Type to define source directory of less files [see below at BootstrapFileReader])
public class LessMinify<TFileReader> : CssMinify
where TFileReader : IFileReader
{
public LessMinify() {}
public override void Process(BundleContext context, BundleResponse response)
{
var config = new DotlessConfiguration()
{
MinifyOutput = true,
ImportAllFilesAsLess = true,
CacheEnabled = false,
LessSource = typeof(TFileReader)
};
response.Content = dotless.Core.Less.Parse(response.Content, config);
base.Process(context, response);
}
}
// create a LessStyleBundler to allow initializing LessBundle with a single less file that uses imports
public class LessStyleBundle<TFileReader> : Bundle
where TFileReader : IFileReader
{
public LessStyleBundle(string virtualPath)
: base(virtualPath, new LessMinify<TFileReader>()) {}
public LessStyleBundle(string virtualPath, string cdnPath)
: base(virtualPath, cdnPath, new LessMinify<TFileReader>()) { }
}
// create abstract VirtualFileReader from dotless-IFileReader as a Base for localized
internal abstract class VirtualFileReader : IFileReader
{
public byte[] GetBinaryFileContents(string fileName)
{
fileName = GetFullPath(fileName);
return File.ReadAllBytes(fileName);
}
public string GetFileContents(string fileName)
{
fileName = GetFullPath(fileName);
return File.ReadAllText(fileName);
}
public bool DoesFileExist(string fileName)
{
fileName = GetFullPath(fileName);
return File.Exists(fileName);
}
public string GetFullPath(string path)
{
return HostingEnvironment.MapPath(SourceDirectory + path);
}
public abstract string SourceDirectory {get;}
// implement to return Path to location of less files
// e. g. return "~/Content/bootstrap/less/";
}
// create BootstrapFileReader overwriting the Path where to find the Bootstrap-Less-Files
internal sealed class BootstrapFileReader : VirtualFileReader
{
public override string SourceDirectory
{
get { return "~/Content/bootstrap/less/"; }
}
}
}
Итак, что же это на самом деле?
- LessMinify расширяет класс CssMinify и поэтому приносит все необходимое для минимизации файлов css
- важным отличием от" обычного " объединения является создание новой конфигурации без точек с источником LessSource, определенным как typeof (TFileReader) ...
- С помощью
можно определить класс, который будет содержать исходный каталог, в котором bundler / minifier будет искать меньше файлов, которые будут приняты во внимание
- LessStyleBundle расширяет пакет и поэтому приносит все необходимое для связывания файлов
- в этом классе я снова использую TFileReader, так как здесь будет создан экземпляр LessMinify(er)
- VirtualFileReader реализует IFileReader, который является интерфейсом без точек, определяющим все методы, необходимые для анализа меньше файлы и дать информацию где найти файлы для импорта
- чтобы расширить решение Кристофа на проблему, я добавил абстрактное свойство SourceDirectory... требуя от меня также сделать абстрактный класс VirtualFileReader
теперь с этой настройкой вы можете создать столько LessFileReaders, сколько хотите. Вам просто нужно расширить абстрактный VirtualFileReader, как видно в
- BootstrapFileReader расширяется VirtualFileReader
- единственная цель BootstrapFileReader-иметь свойство-геттер для SourceDirectory, в котором bundler / minifier найдет меньше файлов, которые должны быть импортированы
Ну в моем случае Bootstraps меньше-файлов, где лежит в ~ / Content / bootstrap / less, который должен быть местоположением по умолчанию, если вы установите " twitter.загрузчик.меньше",-самородок.
если бы у вас был другой каталог в вашем приложении, который содержал меньше файла, который снова имеет несколько импорта, вы просто создаете новый класс, расширяющий VirtualFileReader и определяете свойство-геттер для SourceDirectory, чтобы вернуть соответствующий путь
если вы хотите использовать этот метод связывания, чтобы фактически связывать и минимизировать меньше файлов в рабочей среде, вы просто добавляете lessstylebundle-instantion в BundlerConfig.cs:
bundles.Add(new LessStyleBundle<BootstrapFileReader>("~/bundles/BootstrapCSS")
.Include("~/Content/bootstrap/less/bootstrap.less"));
и, конечно, ваш файл _Layout.cshtml также должен знать о готовая связка
@Styles.Render("~/bundles/BootstrapCSS")
Модификация 2)
теперь небольшая модификация, которую я также должен был добавить, чтобы получить эту работу
в моей первой попытке связать bootstrap.меньше я использовал это
bundles.Add(new LessStyleBundle<BootstrapFileReader>("~/Content/BootstrapCSS")
.Include("~/Content/bootstrap/less/bootstrap.less"));
Я думал, что буду использовать контент в маршрутах для CSS / Less и пакеты в маршрутах для Javascript.
но это не работает из коробки. ASP.net не допускает создания Пакет, который начинается с ~/контент. Вы получите сбой авторизации 403. Поэтому самое простое решение для этого-использовать ~/пачки вместо:
bundles.Add(new LessStyleBundle<BootstrapFileReader>("~/bundles/BootstrapCSS")
.Include("~/Content/bootstrap/less/bootstrap.less"));
поскольку существует не так много реальных решений этой проблемы, я надеюсь, что это поможет хотя бы некоторым из вас, если вы планируете интегрировать Twitter bootstrap в свой asp.net применение mvc4.
С наилучшими пожеланиями, Инго!--6-->
Я изменил обходной путь Ingo, чтобы избавиться от пользовательских классов для каждого каталога. Кроме того, я добавил правильный вывод исключений (потому что в противном случае все исключения молчали, и вы просто получили пустой файл меньше в случае ошибки).
public class LessTransform : IItemTransform
{
[ThreadStatic]
internal static string CurrentParsedFileDirectory;
public string Process (string includedVirtualPath, string input)
{
CurrentParsedFileDirectory = Path.GetDirectoryName (includedVirtualPath);
var config = new DotlessConfiguration
{
MinifyOutput = false,
CacheEnabled = false,
MapPathsToWeb = true,
ImportAllFilesAsLess = true,
LessSource = typeof (VirtualFileReader),
Logger = typeof (ThrowExceptionLogger)
};
return Less.Parse (input, config);
}
}
internal class VirtualFileReader : IFileReader
{
public bool UseCacheDependencies
{
get { return false; }
}
public byte[] GetBinaryFileContents (string fileName)
{
return File.ReadAllBytes (GetFullPath (fileName));
}
public string GetFileContents (string fileName)
{
return File.ReadAllText (GetFullPath (fileName));
}
public bool DoesFileExist (string fileName)
{
return File.Exists (GetFullPath (fileName));
}
public string GetFullPath (string path)
{
if (string.IsNullOrEmpty (path))
return string.Empty;
return HostingEnvironment.MapPath (path[0] != '~' && path[0] != '/'
? Path.Combine (LessTransform.CurrentParsedFileDirectory, path)
: path);
}
}
public class ThrowExceptionLogger : Logger
{
public ThrowExceptionLogger (LogLevel level) : base (level)
{
}
protected override void Log (string message)
{
if (string.IsNullOrEmpty (message))
return;
if (message.Length > 100)
message = message.Substring (0, 100) + "...";
throw new LessTransformException (message);
}
}
[Serializable]
public sealed class LessTransformException : Exception
{
public LessTransformException (string message) : base (message)
{
}
}
использование:
bundles.Add (new StyleBundle ("~/styles-bundle/common")
.Include ("~/content/bootstrap/bootstrap.less", new LessTransform ()));
у меня была та же проблема сегодня, я нашел работу, но я хотел бы получить лучшее решение. Я также пытался использовать dotless и пользовательское преобразование, подобное тому, что у вас есть.
решение:
событие перед построением:
"$(SolutionDir)packages\dotless.1.3.1.0\tool\dotless.compiler.exe" "$(ProjectDir)Content\less\bootstrap.less"
это создаст bootstrap.css
файл, который вы можете включить как обычный CSS
вместо LESS
.
это решение не идеально, так как вам придется обновлять событие сборки каждый раз при обновлении dotless, и иметь ручку пачки оно более чисто также.
Я очень, очень рекомендую установить WebEssentials 2012 вместо.
Он будет генерировать css-файл и уменьшенный css-файл из вашего.меньше, и вы можете ссылаться на css вместо этого. Он будет автоматически обновлять css каждый раз, когда вы вносите изменения в свой .меньше, поэтому нет необходимости запоминать какие-либо предварительные шаги или что-либо еще...
при установке WebEssentials вы также получите другие сладкие функции, такие как предварительный просмотр CoffeeScript, TypeScript и меньше. JSHint, автоматическое minification и много и много больше "вкусностей"!