Как рендерить Svelte на стороне сервера?

Могу ли я каким-то образом создавать исходные файлы HTML с помощью Svelte?

Я использую Django, Webpack и Tailwindcss. Я хочу использовать Svelte со своим интерфейсом, но я не хочу отказываться от скорости, которая достигается при простом рендеринге на стороне сервера (шаблоны Django). Если то, что я представляю изначально, представляет собой загрузочную HTML-страницу, которая извлекает файл bundle.js, а Svelte строит DOM на стороне клиента, тогда браузеры начинают загружать изображения только после загрузки JS-файла.

Сравните это с изначально визуализированным HTML-кодом, который уже содержит ссылки на изображения, браузер начинает загружать их вместе с JS для более быстрой воспринимаемой загрузки страницы.

Я не хочу использовать Sapper в качестве сервера приложений, я хочу продолжать использовать Django.


person PaperTsar    schedule 23.07.2020    source источник
comment
Вы изучали возможности серверного рендеринга (SSR) Svelte? Вроде ответ положительный.   -  person Dmitry    schedule 16.01.2021


Ответы (3)


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

Но вся навигация будет загружать новый шаблон, вероятно, это больше не будет односторонним приложением. И вы, вероятно, напишете разные стройные приложения для разных шаблонов страниц (это может сделать rollup).

Я думаю, из-за этого вы сильно потеряете производительность, и в этом случае я бы предпочел angular, а не svelte (или react, или vue).

person Andreas Dolk    schedule 23.07.2020
comment
Это не отвечает на вопрос. - person Dmitry; 16.01.2021

Задача состоит в том, чтобы разделить состояние (реквизиты) между вашим приложением Django и компонентами Svelte.

Чтобы получить HTML-код из компонента:

require('svelte/register')
const MyComponent = require('./MyComponent.svelte').default

const { html } = MyComponent.render({ ...props... })

Если у компонентов нет реквизита, вы можете скомпилировать и кэшировать шаблоны HTML (возможно, даже до выполнения).

Если вы хотите отправлять реквизиты динамически, например, на основе данных в вашей базе данных, вам нужно сделать это во время выполнения. Это означает выполнение JS на стороне сервера. Производительность не будет плохой, если вы кешируете результат.

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

person joshnuss    schedule 23.07.2020

Согласно этому сообщению в блоге, вы выполните следующие инструкции:

В следующем посте я покажу, как использовать рендеринг на стороне сервера в Svelte.

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

SEO. На мой взгляд, основным вариантом использования серверного рендеринга является SEO. Выполнение первоначального рендеринга на сервере сделает ваш сайт более доступным для роботов. Сканеры обычно поддерживают выполнение некоторого кода JavaScript, но сложное приложение JavaScript вряд ли будет правильно проиндексировано.

Мой опыт показывает, что приложения, которые выполняют вызовы ajax для выборки данных, особенно сложно индексировать. Сканер может успешно отобразить начальную статическую часть приложения, но данные будут отсутствовать, поскольку он не будет выполнять необходимые вызовы ajax.

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

На стороне сервера или на стороне клиента Технически вы можете создать приложение Svelte без каких-либо компонентов на стороне клиента, но оно будет полностью статичным. В основном приложение будет вести себя так же, как старое серверное приложение PHP. Отлично подходит для поисковых роботов, но реальные пользователи обычно ожидают более богатого пользовательского опыта.

Здесь серверная сторона встречается со стороной клиента.

После того, как HTML-код, сгенерированный сервером, полностью отобразится в браузере, мы можем запустить клиентскую копию приложения. Версия на стороне клиента продолжается с того места, где остановилось приложение на стороне сервера.

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

Также важно понимать, что между клиентом и сервером нет общего состояния по умолчанию (например, свойства данных).

Карусель статей Я решил использовать серверную часть Svelte для создания карусели статей для целевой страницы своего блога. Идея состоит в том, чтобы использовать компонент Svelte для циклического просмотра статей в четырех из моих категорий статей.

Карусель должна загружаться мгновенно при загрузке страницы, поэтому я решил визуализировать начальное представление на сервере. После загрузки страницы я запускаю клиентский аналог компонента Svelte для динамического циклического просмотра статей.

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

Я начинаю с создания компонента Article, который будет использоваться для рендеринга статей в карусели. Компонент, вероятно, следует разделить на два компонента, но сохранить его как один для целей этого сообщения в блоге.

    <div class="row">
      <span class="slide-show-card col-sm-3">
        <h4>Angular</h4>
        <h5><a class="slide-show-link" href="{{angularUrl}}">{{angularTitle}}</a></h5>
        <div class="label label-success slide-show-count">Viewed {{angular.viewCount}} times</div>
        <div>{{angularIntro}}</div>
      </span>
      <span class="slide-show-card col-sm-3">
        <h4>JavaScript</h4>
        <h5><a class="slide-show-link" href="{{javascriptUrl}}">{{javascriptTitle}}</a></h5>
        <div class="label label-success slide-show-count">Viewed {{javascript.viewCount}} times</div>
        <div>{{javascriptIntro}}</div>
      </span>
      <span class="slide-show-card col-sm-3">
        <h4>Svelte</h4>
        <h5><a class="slide-show-link" href="{{svelteUrl}}">{{svelteTitle}}</a></h5>
        <div class="label label-success slide-show-count">Viewed {{svelte.viewCount}} times</div>
        <div>{{svelteIntro}}</div>
      </span>
      <span class="slide-show-card col-sm-3">
        <h4>React</h4>
        <h5><a class="slide-show-link" href="{{reactUrl}}">{{reactTitle}}</a></h5>
        <div class="label label-success slide-show-count">Viewed {{react.viewCount}} times</div>
        <div>{{reactIntro}}</div>
      </span>
     </div>
    <script>
    
    class ArticleService {
      constructor() {
        this.articles = [];
        this.index = {
          'angular': -1,
          'javascript': -1,
          'svelte': -1,
          'react': -1
        };
      }
    
      getArticles() {
        return fetch('/full-article-list-json')
               .then((data) => {
                  return data.json();
               })
               .then((articles) => {
                  this.articles = articles;
                  return articles;
               });
      }
    
      getNextArticle(key) {
        this.index[key] = (this.index[key] + 1) % this.articles[key].length;
        let a = this.articles[key][this.index[key]];
        return a;
      }
     }
    
      let articleService = new ArticleService();
    
       export default {
         onrender() {
           let update = () => {
                       this.set({angular: articleService.getNextArticle('angular')});
                       this.set({javascript: articleService.getNextArticle('javascript')});
                       this.set({svelte: articleService.getNextArticle('svelte')}); 
                       this.set({react: articleService.getNextArticle('react')}); 
          };
    
          articleService.getArticles()
                        .then((articles) => {
                           update();
                           if(typeof global === 'undefined') {
                             document.querySelector('#articles').innerHTML = '';
                             document.querySelector('#articles-client-side').style.display =
"block";
                           }
                        })
                        .then(() => {
                           setInterval(() => {
                             articleService.getArticles()
                                           .then((articles) => {
                                              update();
                                           });  
                           }, 5000);  
                        });
        },
    
         data () {
           return {}
         },
    
         computed: {
           angularTitle: angular => angular.title || '',
           angularIntro: angular => angular.intro || '',
           angularUrl: angular => `viewarticle/${angular.friendlyUrl}`,
           
           javascriptTitle: javascript => javascript.title || '',
           javascriptIntro: javascript => javascript.intro || '',
           javascriptUrl: javascript => `viewarticle/${javascript.friendlyUrl}`,
     
           svelteTitle: svelte => svelte.title || '',
           svelteIntro: svelte => svelte.intro || '',
           svelteUrl: svelte => `viewarticle/${svelte.friendlyUrl}`,
     
           reactTitle: react => react.title || '',
           reactIntro: react => react.intro || '',
           reactUrl: react => `viewarticle/${react.friendlyUrl}`,
         }
      }
    </script>

Сервер Давайте посмотрим на код сервера.

Первое, что нам нужно сделать, это активировать компилятор, потребовав svelte / ssr / register

require( 'svelte/ssr/register' );

Затем мы должны потребовать, чтобы HTML-файл компонента получил дескриптор компонента.

Затем мы вызываем метод рендеринга и передаем ему начальный объект данных. Объект данных - это стандартный объект данных Svelte.

app.get('/', function(req, res) {
      var component = require('./app/article-show/Articles.html');
      var articles = component.render({
                                  angular: articles.angular,
                                  svelte: articles.svelte,
                                  react: articles.react,
                                  javascript: articles.javascript
                              });
      res.render('index.html', {articles: articles});
});

После вызова render мы возвращаем полностью отрендеренный компонент. Теперь мы должны передать это механизму просмотра узлов.

В моем случае я использую Express с Mustache, поэтому я могу просто передать компонент как объект методу рендеринга. Затем на моей странице index.html я использую синтаксис представления Mustache с тройными фигурными скобками для визуализации компонента на странице таким образом.

{{{article}}} Клиент. То, что у нас есть, достаточно для визуализации начального представления моего компонента, но он не поддерживает циклический просмотр новых статей каждые несколько секунд.

Для этого мы должны запустить клиентскую версию компонента Article.

Клиентская версия загружается как стандартный клиентский компонент Svelte.

import articles from './articles';

var articlesSection = new articles({
  target: document.querySelector( 'main' ),
  data: {angular: {}, javascript: {}, svelte: {}, react: {}}
});

После активации клиентской версии у нас будет два компонента в DOM. Как только клиентская версия будет готова к работе, я уничтожу серверную версию.

Возможно, есть более элегантный способ сделать это, но я просто убираю созданный сервером элемент DOM и меняю стиль на клиентскую версию.

Навигация В дополнение к карусели статей я также построил свою основную навигацию как компонент на стороне сервера Svelte. Навигатор - это чисто серверный компонент, не имеющий аналогов на стороне клиента.

Навигация в основном статична, но поддерживает динамическое оформление активного элемента навигации. Вот код компонента навигации:

<div class="nav col-md-2 hidden-sm hidden-xs">
  <a class="navLink" href="/"><div class="navBox {{home}}">Home</div></a>
  <a class="navLink" href="/most-popular"><div class="navBox {{mostpopular}}">Most Popular</div></a>
  <a class="navLink" href="/most-recent"><div class="navBox {{mostrecent}}">Most Recent</div></a>
  <a class="navLink" href="/articleList/angular"><div class="navBox {{angular}}">Angular</div></a>
  <a class="navLink" href="/articleList/react"><div class="navBox {{react}}">React</div></a>
  <a class="navLink" href="/articleList/aurelia"><div class="navBox {{aurelia}}">Aurelia</div></a>
  <a class="navLink" href="/articleList/javascript"><div class="navBox {{javascript}}">JavaScript</div></a>
  <a class="navLink" href="/articleList/nodejs"><div class="navBox {{nodejs}}">NodeJS</div></a>
  <a class="navLink" href="/articleList/vue"><div class="navBox {{vue}}">Vue</div></a>
  <a class="navLink" href="/articleList/svelte"><div class="navBox {{svelte}}">Svelte</div></a>
  <a class="navLink" href="/articleList/mongodb"><div class="navBox {{mongodb}}">MongoDB</div></a>
  <a class="navLink" href="/articleList/unittesting"><div class="navBox {{unittesting}}">UnitTesting</div></a>
  <a class="navLink" href="/articleList/dotnet"><div class="navBox {{dotnet}}">.Net</div></a>
  <a class="navLink" href="/questions"><div class="navBox {{questions}}">Q&A</div></a>
  <a class="navLink" href="/full-article-list"><div class="navBox {{all}}">All</div></a>
</div>
<script>
  export default {
    data () {
      return {}
    },
  }
</script>

Поскольку навигация настолько статична, нет причин повторно создавать HTML-код на стороне сервера для каждого запроса. В качестве оптимизации я решил кэшировать различные варианты nav. Единственная разница между различными версиями навигации заключается в том, что стиль активных элементов навигации применяется к разным элементам.

Вот некоторая базовая логика кеширования, которая гарантирует, что мы создаем каждую версию навигации только один раз.

function getNavComponent(key) {
  key = key || 'home';
  key = key.replace('-', '');

  if(!navCache[key]) {
    navCache[key] = createNavComponent(key);
  }

  return navCache[key];
} 

function createNavComponent(key) {
  var nav = require('./app/article-show/Navigation.html');
  var data = {};
  data[key] = 'activeNav';
  return nav.render(data);
}

Демо Если вы хотите протестировать мой вид на стороне сервера и на стороне клиента, вы можете найти его здесь.

Я также загрузил свой сайт в инструменты веб-мастера Google, чтобы сравнить представление компонента сканером с представлением пользователя.

Как видно из скриншота, мой компонент выглядит неплохо для сканеров после добавления рендеринга на стороне сервера.

Левая сторона - это вид поискового робота, а правая сторона - вид пользователя.

введите описание изображения здесь

person Community    schedule 05.01.2021
comment
Почему вы просто копируете всю статью? Не могли бы вы извлечь из него важную информацию и ответить на вопрос? - person Dmitry; 16.01.2021