Подтвердить что ты не робот

Что такое чистый шаблон для хранения всего JavaScript в нижней части моей страницы?

У нас есть вложенный макет для наших разных страниц. Например:

Master.cshtml

<!DOCTYPE html>
<html>
   <head>...</head>
   <body>@RenderBody()<body>
</html>

Question.cshtml

<div>
  ... lot of stuff ...
  @Html.Partial("Voting", Model.Votes)
</div>
<script type="text/javascript">
  ... some javascript ..
</script>

Voting.cshtml

<div>
  ... lot of stuff ...
</div>
<script type="text/javascript">
  ... some javascript ..
</script>

Все это работает отлично, но я хотел бы нажать все блоки JavaScript, которые будут отображаться в нижнем колонтитуле страницы, после всего содержимого.

Есть ли способ определить магическую директиву в вложенных элементарных элементах, которая может привести к тому, что различные теги script будут отображаться в порядке внизу страницы?

Например, могу ли я создать волшебный помощник, который захватывает все блоки js, а затем получит макет верхнего уровня для его отображения:

Voting.cshtml

<div>
  ... lot of stuff ...
</div>
@appendJSToFooter{
   <script type="text/javascript">
     ... some javascript ..
   </script>
}
4b9b3361

Ответ 1

Я придумал относительно простое решение этой проблемы около года назад, создав помощник для регистрации скриптов в ViewContext.TempData. Моя первоначальная реализация (которую я в процессе переосмысления) просто выводит ссылки на различные ссылочные сценарии. Не идеальный, но вот прохождение моей текущей реализации.

На частичном я регистрирую связанный файл script по имени:

@Html.RegisterScript("BnjMatchyMatchy")

На главной странице я вызываю метод для итерации зарегистрированных скриптов:

@Html.RenderRegisteredScripts()

Это текущий помощник:

public static class JavaScriptHelper
{
    private const string JAVASCRIPTKEY = "js";

    public static void RegisterScript(this HtmlHelper helper, string script)
    {
        var jScripts = helper.ViewContext.TempData[JAVASCRIPTKEY]
            as IList<string>; // TODO should probably be an IOrderedEnumerable

        if (jScripts == null)
        {
            jScripts = new List<string>();
        }

        if (!jScripts.Contains(script))
        {
            jScripts.Add(script);
        }

        helper.ViewContext.TempData[JAVASCRIPTKEY] = jScripts;
    }

    public static MvcHtmlString RenderRegisteredScripts(this HtmlHelper helper)
    {
        var jScripts = helper.ViewContext.TempData[JAVASCRIPTKEY]
            as IEnumerable<string>;

        var result = String.Empty;

        if (jScripts != null)
        {
            var root = UrlHelper.GenerateContentUrl("~/scripts/partials/",
                    helper.ViewContext.HttpContext);

            result = jScripts.Aggregate("", (acc, fileName) =>
                String.Format("<script src=\"{0}{1}.js\" " +
                    "type=\"text/javascript\"></script>\r\n", root, fileName));
        }

        return MvcHtmlString.Create(result);
    }
}

Как указано моим TODO (я должен обойти это), вы можете легко изменить это, чтобы использовать IOrderedEnumerable, чтобы гарантировать порядок.

Как я уже сказал, это не идеально, а вывод кучи тегов script src, безусловно, создает некоторые проблемы. Я скрывался, так как ваша дискуссия о налоге jQuery разыгралась с помощью Steve Souders, Stack Overflow, Twitter и ваш блог. Во всяком случае, это вдохновило меня на переделку этого помощника, чтобы прочитать содержимое файлов script, а затем сбросить их на отображаемую страницу в свои собственные теги script, а не теги ссылок. Это изменение должно помочь ускорить рендеринг страницы.

Ответ 2

Можете ли вы определить раздел в нижней части элемента body в вашем файле Master.cshtml следующим образом:

@RenderSection("Footer", required: false)

Затем в отдельных файлах .cshtml вы можете использовать следующее:

@section Footer {
    <script type="text/javascript">
    ...
    </script>
}

Ответ 3

Вместо того, чтобы создавать некоторую серверную инфраструктуру для решения проблемы на стороне клиента, посмотрите на мой ответ на другой вопрос: fooobar.com/questions/287357/....

С RequireJS, http://requirejs.org, ваши скрипты не обязательно будут внизу страницы, но они будут загружаться асинхронно, что очень поможет в производительности. При выполнении сценариев рендеринг страниц не будет остановлен. Это также способствует написанию Javascript как множеству небольших модулей, а затем предоставляет инструмент развертывания для объединения и минимизации их при публикации сайта.

Ответ 4

Это немного взломано, но если ваша цель - повлиять на минимальные изменения в существующих представлениях (помимо перемещения визуализированных скриптов), то, как я это делал в прошлом (особенно в Web Forms, но также применим к MVC ) было переопределить TextWriter тем, что подталкивает скрипты к нижней.

В основном вы просто пишете реализацию TextWriter и подключаете ее к своей базовой странице MVC, которая ищет <script src=" и фиксирует имя файла во внутреннем Queue, а затем, когда он начинает получать вызовы Write для </body> он отображает все, что было создано в Queue. Это можно сделать с помощью регулярного выражения, но, вероятно, довольно медленно. В этой статье показан пример TextWriter для перемещения ViewState, но должен применяться тот же принцип.

Чтобы переопределить для зависимостей, я определил файлы script и зависимые файлы script в моем web.config, аналогично этому в ситуациях, когда мне нужно было переопределить порядок:

<scriptWriter>
    <add file="~/scripts/jquery.ui.js">
        <add dependency="~/scripts/jquery.js" />
    </add>
</scriptWriter>

Отказ от ответственности Как я уже сказал, это хакерство, лучшим решением было бы использовать какой-то синтаксис CommonJS/AMD в ваших partials (@Script.Require("~/scripts/jquery-ui.js")), в основном вы могли бы написать функцию, которая если страница master/layout указывает на ее регистрацию регистрации script, она может прослушивать все дочерние регистрации, иначе она может просто выводить встроенную строку, поэтому не повредит ее просто использовать. Конечно, это может сломать intellisense.

Итак, предполагая, что какой-то код в вашем хозяине нравится:

@using(Script.Capture()) {
    @RenderBody()

    @Html.Partial("Partial")
}

И частично:

@Script.Require("~/scripts/jquery-ui.js")

Тогда вы можете просто скопировать что-то вроде этого, чтобы обработать его:

public class ScriptHelper : IDisposable
{
    bool _capturing = false;
    Queue<string> _list = new Queue<string>();
    readonly ViewContext _ctx;


    public ScriptHelper Capture()
    {
        _capturing = true;
        return this;
    }

    public IHtmlString Require(string scriptFile)
    {
        _list.Enqueue(scriptFile);

        if (!_capturing)
        {
            return Render();
        }

        return new HtmlString(String.Empty);
    }

    public IHtmlString Render()
    {
        IHtmlString scriptTags;

        //TODO: handle dependencies, order scripts, remove duplicates

        _list.Clear();

        return scriptTags;
    }

    public void Dispose()
    {
        _capturing = false;

        _ctx.Writer.Write(Render().ToHtmlString());
    }
}

Вам может понадобиться сделать Queue ThreadStatic или использовать HttpContext или что-то еще, но я думаю, что это дает общую идею.

Ответ 5

Если у вас есть ситуация, когда вы не можете улучшить существующую структуру (то есть у вас есть как страницы MVC, так и старые WebForms, теги javascript включены повсюду и т.д.), вы могли бы взглянуть на некоторые работа, которую я выполняю на модуле HTTP, который выполняет постпроцессию на выходе, так же, как mod_pagespeed в Apache. Тем не менее, в ранние дни.

https://github.com/Teun/ResourceCombinator

Если вы все равно можете включить свои сценарии в предопределенный способ, это, вероятно, лучше.

Изменить: проект, упомянутый Сэмом (см. комментарии), действительно был в основном перекрывающимся и более зрелым. Я удалил проект ResourceCombinator из GitHub.

Ответ 6

Я понимаю, что этот корабль довольно много плавает, но поскольку у меня есть какое-то решение (хотя и не идеальное), я думал, что поделюсь им.

Я написал сообщение в блоге о подходах к рендерингу script, которые я использую. В этом я упомянул библиотеку, написанную Майклом Дж. Райаном, которую я изменил для своих целей.

Используя это, можно сделать script в любом месте, используя что-то вроде этого:

@Html.AddClientScriptBlock("A script", @"

    $(function() {
        alert('Here');
    });

")

И затем запустите его вывод на странице макета, используя этот вызов непосредственно перед закрывающим тегом body:

@Html.ClientScriptBlocks()

Вы не получаете intellisense в Visual Studio, используя этот подход. Если честно, я не советую использовать эту технику; Я бы предпочел иметь отдельные файлы JS для точек отладки и использовать атрибуты данных HTML для управления любым динамическим поведением. Но в случае, если это полезно, я думал, что поделюсь им. Caveat emptor и т.д.

Вот список релевантных ссылок:

Ответ 7

Я бы предположил, что вы не должны размещать скрипты непосредственно на своей странице.

Вместо этого потяните JavaScript в отдельный .js файл, а затем ссылайтесь на него в заголовке.

Я знаю, что это не совсем то, о чем вы просите, но я предполагаю, что вы делаете это для целей SEO, и это должно иметь тот же эффект, что и размещение script в нижней части страницы.

Ответ 8

Мы используем метод ScriptRegistrar из библиотеки Asp.Net MVC Telerik (GPL open source).

SImply включает ваш javascript на любые виды/частичные страницы и т.д., как показано ниже. Библиотека Telerik обрабатывает все это в нижней части конечной выводимой страницы.

<% 
Html.Telerik().ScriptRegistrar()
             .Scripts(scripts => scripts.AddSharedGroup('lightbox')
             .OnDocumentReady(() => { %>
   <%-- LIGHTBOX  --%>
   $('.images a').lightBox();
<% });%>

Он также смотрит, как группировка нескольких css и js включается вместе в одиночные запросы.

http://www.telerik.com/products/aspnet-mvc.aspx

Ответ 9

Мой скромный вариант - оставить такие вещи профессионалам:-) Посмотрите ControlJS, lib для загрузки ваши внешние файлы javascript, не прерывая обработку страницы (async, defer, по запросу и т.д.). С ControlJS он не возражает против "where" вы поместите код загрузки, все они будут загружаться в конце или по требованию.

Эта презентация автора книги Стива Соудерса дает хороший обзор проблемы (и решения).

Ответ 10

Сэм,

Я написал Portal именно по этой причине: http://nuget.org/packages/Portal В основном вы помещаете

@Html.PortalOut() 

в вашем представлении макета/макета и HTML-код в виде из представлений или частичных представлений, подобных этому

@Html.PortalIn(@<text> $(function() { alert('Hi'); }); </text>)

Вы можете использовать несколько "порталов", указав ключ ввода и вывода. Прямо сейчас он добавляет код в том порядке, в котором он входит (частичное рендеринг сначала, от "сверху донизу" ). Если вы находитесь в одном и том же представлении, вам приходится иметь дело с ограничением сверху вниз, то есть вы не можете иметь "внешний" портал перед "внутри", но это не проблема при отправке из представлений в макет. В файле Portal.cs есть больше примеров.

Ответ 11

Как насчет фильтра ответов html, который изменяет ваш окончательный html. Найти все теги script до определенного места и вставить их в конец тела. Он также работает для улучшения рендеринга dataTables, сначала помечая их скрытыми и создавая js для их отображения.

Никаких вмешательств или изменений в действительном коде не требуется.