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 — он замечательно подходит на роль языка шаблонов, так что не изобретайте ещё один узкоспециализированный язык.

@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