Skip to content

Instantly share code, notes, and snippets.

@andrew--r
Forked from mpj/templating_problems.MD
Last active October 24, 2019 17:55
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrew--r/1328f11e37112c37128929ac4ea5d554 to your computer and use it in GitHub Desktop.
Save andrew--r/1328f11e37112c37128929ac4ea5d554 to your computer and use it in GitHub Desktop.
Критика шаблонизаторов

Оригинальная заметка Матиаса Питера Йохансона, переведена с разрешения автора.


Размышления о языках шаблонов

Меня часто спрашивают, что я думаю о Vue.

Не буду оценивать Vue, так как я недостаточно хорошо знаком с ней, но я очень хорошо знаком с шаблонизаторами. Собственная система шаблонов Vue вместо JSX во многих статьях преподносится как причина, по которой вы должны выбрать Vue. Из-за этого я на стену лезу от негодования, потому что негативные стороны этого подхода даже не удостаиваются упоминания или обсуждения. JSX существует по достаточно веским причинам. Для меня JSX — большой шаг к упрощению и улучшению шаблонов.

Языки шаблонов часто продаются за счёт поверхностной элегантности. Недавно я наткнулся на очередную статью, восхваляющую Vue (и особенно её шаблоны). В ней был следующий пример JSX:

render() {
  let { items } = this.props
  
  let children
  if (items.length > 0) {
    children = (
      <ul>
        {items.map(item =>
          <li key={item.id}>{item.name}</li>)}
      </ul>
    )
  } else {
    children = <p>No items found.</p>
  }
  return (
    <div className="list-container">
      {children}
    </div>
  )
}

В статье этот пример осуждается за многословность, а затем приводится его реализация на Vue:

<template>
  <div class="list-container">
    <ul v-if="items.length">
      <li v-for="item in items">
        {{item.name}}
      </li>
    </ul>
    <p v-else>No items found.</p>
  </div>
</template>

Это выглядит как лукавство со стороны автора, потому что первый пример действительно написан слишком многословно. В более лаконичном виде он выглядел бы так:

let ListContainer = ({ items }) => {
  <div className="list-container">
    {items.length === 0
      ? <p>No items found</p>
      : <ul>
        {items.map(item => 
          <li key={item.id}>{item.name}</li>
        )}
      </ul>
    }
  </div> 
}

Я не знаю, специально ли автор вводит читателей в заблуждение, или он просто взял пример из какого-то руководства по React. Буду считать, что он взял пример из какой-то статьи. Если это действительно так, есть вероятность, что пример JSX был специально написан многословно для большей наглядности и очевидности происходящего: при изучении новых инструментов вам не нужна краткость, вам нужно простое и понятное объяснение того, как инструмент работает, без лишней магии.

Пример с Vue, однако, не особо даёт понять, КАК на самом деле он, чёрт возьми, РАБОТАЕТ. Я знаю, как работает Array.map, но что такое v-for и что за синтаксис используется внутри этого атрибута? Да, довольно легко понять ЧТО конкретно этот шаблон делает, потому что он читается почти как обычная речь на английском. Но когда вы на практике столкнётесь с подобными языками шаблонов, ваша продуктивность ощутимо упадёт, потому что вам придётся тратить время на изучение (и часто расширение) языка. Да, это небольшой язык, но это всё ещё язык, который придётся изучить. В случае с JSX вещей для изучения гораздо меньше, и в нём можно применять уже существующие знания и инструменты JavaScript.

Предположим для примера, что я хочу отфильтровать все элементы, которые неактивны. JSX — это просто JavaScript, поэтому я спокойно использую filter:

let ListContainer = ({ items }) => {
  <div className="list-container">
    {items.length === 0
      ? <p>No items found</p>
      : <ul>
        {items
          .filter(item => item.active) // <-- ДОБАВЛЕННАЯ СТРОКА
          .map(item => 
            <li key={item.id}>{item.name}</li>
          )
        }
      </ul>
    }
  </div> 
}

Как это сделать в шаблоне Vue? Честно, без понятия. Я хотел привести здесь пример, но спустя десять минут гуглинга я всё ещё не мог найти, как это делается. Конечно, можно найти, как это делается, но суть в том, что у меня УЖЕ ЕСТЬ Array.filter! И проблема возникла ТОЛЬКО из-за того, что мы изобрели новый язык вместо использования уже существующего.

PHP-сообщество поняло это 10 лет назад, когда большинство вменяемых PHP-программистов перестали использовать Smarty, поняв, что PHP — сам по себе замечательный язык шаблонов.

Нет никакой разницы и в случае с JavaScript — он замечательно подходит на роль языка шаблонов, так что не изобретайте ещё один узкоспециализированный язык.

@ivryb
Copy link

ivryb commented Apr 19, 2017

<div class="list-container">
  <ul v-if="items.length">
    <li v-for="item in items" v-if="item.active"> // <-- ДОБАВЛЕННЫЕ 20 БАЙТ
      {{item.name}}
    </li>
  </ul>
  <p v-else>No items found.</p>
</div>

@kana-sama
Copy link

kana-sama commented Apr 19, 2017

@ivryb чисто интуитивно мне кажется (пусть это не так), что этот if или скроет все li, если item.active (а item еще даже не существует и будет рантайм-ошибка), или покажет все. И не очень ясно, изменится ли что-нибудь, если еще и порядок изменить. Ангуляр хотя бы убирания недопонимания запрещает делать *ngFor и *ngIf вместе.

@vbifonixor
Copy link

Тем более никто не мешает использовать Array.filter внутри v-for

@ivryb
Copy link

ivryb commented Apr 19, 2017

@kana-sama в документации прямым текстом написано, что v-if имеет более низкий приоритет, чем v-for, что и позволяет делать так, как я написал — применять v-if конкретно для каждого элемента в цикле.

@ivryb
Copy link

ivryb commented Apr 19, 2017

@vbifonixor filter в v-for — ужас, не стоит так делать. Лучше подобную логику убирать из шаблонов.

@denkurbatov
Copy link

Интересно, что значит php сам по себе отличный язык шаблонов. У меня до сих пор, как вспомню, глаз дергается от всего этого "шаблонизаторства" в WordPress

@bucherok
Copy link

@ivryb выше описал удобный и понятный шаблон, который читается на порядок проще чем jsx.

@iwonz
Copy link

iwonz commented Apr 20, 2017

Я может не так понял, но фильтрация во Vue делается очень легко, причём как в v-for, так и можно отдельно вынести логику в функцию приложения.

http://011.vuejs.org/api/filters.html

P.S. Вся эта заметка дискредитирует себя в первом же предложении.

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

Если ты не знаешь Vue, зачем ты сравниваешь с Vue?

@andrew--r
Copy link
Author

andrew--r commented Apr 20, 2017

@iwonz не дискредитирует. Автор сказал, что не будет оценивать Vue в целом, а будет говорить только об одном её аспекте — о системе шаблонов. И сравнивает автор не Vue и React, а их системы шаблонов.

@AlexWayfer
Copy link

Это выглядит как лукавство со стороны автора, потому что первый пример действительно написан слишком многословно. В более лаконичном виде он выглядел бы так:

Многострочные тернарные условия — плохое решение, много где и кем критикуется.

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

Да, вёрстка в JS — совсем не магия, что вы. Всё очевидно.

Пример с Vue, однако, не особо даёт понять, КАК на самом деле он, чёрт возьми, РАБОТАЕТ.

А КАК на самом деле, чёрт возьми, РАБОТАЕТ Array.filter? Или new Date()? Это часто имеет значение?

Я знаю, как работает Array.map, но что такое v-for и что за синтаксис используется внутри этого атрибута?

А, то есть речь не про то, КАК что-то РАБОТАЕТ, а как с этим взаимодействовать! Разные вещи.

Ну, коль изучили Array.filter — при желании можно найти и v-for, там не 10 страниц документации для него.

Да, довольно легко понять ЧТО конкретно этот шаблон делает, потому что он читается почти как обычная речь на английском.

И это преимущество, которое обсуждается.

Но когда вы на практике столкнётесь с подобными языками шаблонов, ваша продуктивность ощутимо упадёт, потому что вам придётся тратить время на изучение (и часто расширение) языка.

Если меня посадить за ассемблер, или C#, или даже TypeScript, или JSX — моя продуктивность тоже ощутимо упадёт, потому что далее по тексту. Вывод: не беритесь на работе за то, чего не знаете, или предварительно это изучите.

Да, это небольшой язык, но это всё ещё язык, который придётся изучить.

Ну я не думаю, что это можно назвать "языком", скорее "синтаксис", "API".

В случае с JSX вещей для изучения гораздо меньше, и в нём можно применять уже существующие знания и инструменты JavaScript.

"Меньше вещей для изучения" — не всегда лучше. Иногда изучение стоит того. Тут нужен баланс, компромисс.

Я хотел привести здесь пример, но спустя десять минут гуглинга я всё ещё не мог найти, как это делается.

image

Две ссылки для двух разных версий. Здесь проблема явно не в Vue и не в Google.

Конечно, можно найти, как это делается

Конечно, если тыкнуть в первую или вторую ссылку выдачи, в зависимости от версии.

Нет никакой разницы и в случае с JavaScript — он замечательно подходит на роль языка шаблонов, так что не изобретайте ещё один узкоспециализированный язык.

Нет, JavaScript не особо имеет отношение к шаблонам, и в данном случае вы сравниваете с JSX, что всё же "немного" другая вещь.

@Xrymz
Copy link

Xrymz commented Apr 20, 2017

офф док фильтрация:
https://vuejs.org/v2/guide/list.html#Displaying-Filtered-Sorted-Results
если так сильно хочется, то vue отлично работает с jsx:
https://github.com/vuejs/babel-plugin-transform-vue-jsx

@Akiyamka
Copy link

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

@gbezyuk
Copy link

gbezyuk commented Apr 20, 2017

Таки да, заметка дискредитирует себя в первом и последнем предложениях. Во-первых "я не пытался разобраться, поэтому ну говно же!", во-вторых "PHP-программисты перестали использовать шаблонизаторы, ..." — лол, нет. Не знаю как там пхпшники живут сейчас, да и не суть, но вот правило того что в шаблоне не должно быть никакой сложной логики — это правило весьма универсальное. Собственно, vue-шаблоны его же придерживаются, и в 99% случаев — с большой пользой, а на оставшийся 1%... прикинь, можно даже этот любимый автором заметки JSX использовать напрямую.
З.Ы. <li v-for="element in list.filter((what) => ever)">
З.З.Ы. Ого, срачики докатились до gist. А где лайки ставить?

@JiLiZART
Copy link

JiLiZART commented Apr 20, 2017

Как это сделать в шаблоне Vue? Честно, без понятия. Я хотел привести здесь пример, но спустя десять минут гуглинга я всё ещё не мог найти, как это делается.

Вот это предложение полностью дискредитирует всю статью. Человек посмотрел только с одной точки зрения. Полностью предвзятая точка зрения.

PS. в PHP сейчас вместо Smarty правит Twig

@fatwebdev
Copy link

У фронтов 3 "каноничных" технологии. CSS, HTML, JS
Вместо того что бы выучить их, вы городите что то свое... поверх...

@pvolyntsev
Copy link

В PHP как и в других современных языках широко используется MVC и правило: в любом виде шаблонов они используются только для формирования интерфейса пользователя из заранее подготовленных данных. Вообще можно и Twig обойти так, чтобы сразу в базу дёрнуться, через аксессоры объектов и общий контейнер IoC.
Зачем это вообще упомянули?

IMHO Шаблонизация Vue нагляднее JSX

@gearmobile
Copy link

но что такое v-for и что за синтаксис используется внутри этого атрибута?

v-for - это директива, это не атрибут.

@jaxxreal
Copy link

А по моему, у автора акцент на другое - для того чтобы пользоваться JSX, даже доку по началу читать не надо - js + html-подобный синтаксис, в отличии от тех же vue/angular/ember и других абстракций над разметкой.

@bloadvenro
Copy link

bloadvenro commented May 23, 2017

С точки зрения фундаментальной логики, если взглянуть на VUE шаблонизацию (+ API всяких фильтров), то она по принципу действия не отличается от React ничем: передаем данные, используем свои или встроенные хелперы (aka фильтры), а также условия и циклы. VUE шаблоны может и получаются немного более разгруженными за счет маникакального вынесения логики в директивы и хелперы, а также отсутствия некрасивых скобок и тернарных операторов, но вы посчитайте, сколько символов в виде директив и всяких pile-like палочек для фильтров (с порой неочевидной логикой) нужно будет взамен писать. Все это не дает мне ощущения весомого преимущества (имхо). Мы лишь приобретаем дополнительный синтаксический слой для указания того, что мы хотим сделать в наших шаблонах.

Разобраться в JSX/VUE шаблонах - дело часа, может трех. Не вижу особого смысла тут рассуждать, ибо выбираешь ты не отдельно шаблонизатор, а целую экосистему инструентов, и, как правило, возникают проблемы посерьезнее синтаксиса шаблонов.

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

P.S. Зачем контейнеру логика фильтрации внутри, да еще и столь специфичная по .active значению? Что мешает еще короче записать?

file: ListContainer.jsx
-----------------------------

export default ({ items = [] }) => (
  <div className="list-container">
    {items.length
      ? <ul>{li(items)}</ul> // Уже отфильтрованные айтемы, li - переиспользуемый обертыватель.
      : <p>No items found</p>
    }
  </div> 
)

Согласитель, мало отличается от (вообще логику фильтрации тоже не мешало бы убрать)

<div class="list-container">
  <ul v-if="items.length">
    <li v-for="item in items" v-if="item.active"> // кстати, что если кто-то решит вставить более сложное условие?
      {{item.name}}
    </li>
  </ul>
  <p v-else>No items found.</p>
</div>

ListContainer можно в принципе сделать абстрактным контекстным компонентом в рамках приложения, если вынести из него всю специфичность.

@Piterden
Copy link

// кстати, что если кто-то решит вставить более сложное условие? @bloadvenro

<template>
  <div class="list-container">
    <ul v-if="items.length">
      <li 
        v-for="(item, key) in items"
        v-if="moreComplicatedCondition"
        :key="key"
      >
        {{ item.name }}
      </li>
    </ul>
    <p v-else>No items found.</p>
  </div>
</template>

<script>
export default {
  computed: {
    moreComplicatedCondition () {
      return this.whatEverYouWant
    },
  },
}
</script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment