Обзор Vue.js 2.6

в 12:47, , рубрики: javascript, vue.js, vuejs, Блог компании Constanta, Программирование

Привет!

Совсем скоро должна выйти новая версия Vue.js — 2.6. Под катом вы найдете обзор новых фич следующей версии, включая новый синтаксис слотов, Vue.observable() и много чего еще!

Обзор Vue.js 2.6 - 1

1. Новый синтаксис для scoped slots

Это одно из самых значимых изменений. Оно включает в себя:

  • Новую директиву v-slot, объединяющую slot и slot-scope
  • Сокращение для использования именованных scoped slots

Проще всего это понять на примере:

Как использовались scoped slots в версии Vue@2.5.22:

<template>
  <TestComponent>
    <!-- Дефолтный scoped slot -->
    <div slot-scope="{ message }">
      {{ `Default slot with message: ${message}` }}
    </div>

    <!-- Именованный scoped slot -->
    <div slot="text" slot-scope="{ text }">
      {{ `Named slot with text: ${text}` }}
    </div>
  </TestComponent>
</template>

Как можно теперь:

<template>
  <TestComponent>
    <!-- Дефолтный scoped slot -->
    <template v-slot="{ message }">
      <div>
        {{ `Default slot with message: ${message}` }}
      </div>
    </template>

    <!-- Именованный scoped slot -->
    <template v-slot:text="{ text }">
      <div>
        {{ `Named slot with text: ${text}` }}
      </div>
    </template>
  </TestComponent>
</template>

Для дефолтного слота можно применить специальный синтаксис, если не используются именованные слоты:

<template>
  <!-- v-slot используется прямо на родителе -->
  <TestComponent v-slot="{ message }">
    <div>
      {{ `Default slot with message: ${message}` }}
    </div>
  </TestComponent>
</template>

И вот сокращение для именованных слотов:

<template>
  <TestComponent>
    <!-- # - это сокращение для v-slot: -->
    <template #text="{ text }">
      <div>
        {{ `Named slot with text: ${text}` }}
      </div>
    </template>
  </TestComponent>
</template>

Новую директиву можно использовать и без каких-либо scope-переменных, но тогда слот все-равно попадет в $scopedSlots родителя.

Ссылки:

  1. Новый синтаксис v-slot

  2. Сокращение для v-slot

2. Динамический аргумент директивы

Если вы хотите динамический аргумент для v-bind или v-on, то во Vue@2.5.22 у вас есть только один вариант:

<div v-bind="{ [key]: value }"></div>
<div v-on="{ [event]: handler }"></div>

Но у него есть пара недостатков:

  • Не все знают о возможности использования v-bind/v-on на объектах и о динамических названиях переменных
  • vue-template-compier генерирует неэффективный код
  • v-slot не имеет похожего синтаксиса для объектов

Чтобы избавиться от них, Vue@2.6.0 представляет новый синтаксис:

<div v-bind:[key]="value"></div>
<div v-on:[event]="handler"></div>
 

Примеры использования:

<template>
  <div>
    <!-- v-bind с динамическим ключом -->
    <div v-bind:[key]="value"></div>

    <!-- сокращение v-bind с динамическим ключом  -->
    <div :[key]="value"></div>

    <!-- v-on с динамическим событием -->
    <div v-on:[event]="handler"></div>

    <!-- сокращение v-on с динамическим событием -->
    <div @[event]="handler"></div>

    <!-- v-slot с динамическим именем -->
    <TestComponent>
      <template v-slot:[name]>
        Hello
      </template>
    </TestComponent>

    <!-- сокращение v-slot с динамическим именем -->
    <TestComponent>
      <template #[name]>
        Cool slot
      </template>
    </TestComponent>
  </div>
</template>

Ссылки:

3. Создание реактивных объектов с помощью Vue.observable()

Раньше, чтобы создать реактивный объект, нужно было засунуть его внутрь инстанса vue-компонента. Теперь у нас есть отдельный метод, который делает объект реактивным — Vue.observable().

Реактивный объект можно спокойно использовать в render- и computed-функциях.

Пример использования:

import Vue from vue;

const state = Vue.observable({
  counter: 0,
});

export default {
  render() {
    return (
      <div>
        {state.counter}

        <button v-on:click={() => { state.counter++; }}>
          Increment counter
        </button>
      </div>
    );
  },
};

4. Загрузка данных на сервере

В новом обновлении vue-server-renderer изменил стратегию загрузки данных для SSR.

Раньше нам советовали вызывать методы asyncData() у компонентов, полученных через router.getMatchedComponents().

В новой версии появился специальный метод у компонентов — serverPrefetch(). vue-server-renderer вызовет его у каждого компонента и дождется решения возвращенных промисов:

<template>
  <div v-if="item">
    {{ item.name }}
  </div>
</template>

<script>
export default {
  // Вызовется на сервере
  async serverPrefetch() {
    await this.fetchItem();
  },

  computed: {
    item() {
      return this.$store.state.item;
    },
  },

  // Вызовется на клиенте
  mounted() {
    if (!this.item) {
      this.fetchItem();
    }
  },

  methods: {
    async fetchItem() {
      await this.$store.dispatch('fetchItem');
    },
  },
};
</script>

Чтобы узнать, когда завершилось ожидание всех serverPrefetch() и приложение завершило свой рендеринг, в контексте функции серверного рендера появилась возможность добавить хук rendered():

/* Упрощенный entry-server.js */

import { createApp } from './app';

export default context => new Promise((resolve, reject) => {
  const { app, router, store } = createApp();
  const { url } = context;

  router.push(url);

  router.onReady(() => {
    context.rendered = () => {
      // Передаем состояние хранилища после завершения всех serverPrefetch()
      context.state = store.state;
    };

    resolve(app);
  }, reject);
});

5. Улучшенный вывод ошибок компилятора

При компиляции html в render-функцию vue-template-compiler может выдать ошибки. Раньше Vue выводил описание ошибки без ее местоположения, теперь новая версия будет показывать, где она находится.

Пример:

<template>
  <div>
    <template key="test-key">
      {{ message }}
    </template>
  </div>
</template>

Ошибка vue-template-compiler@2.5.22:

  Error compiling template:

  <div>
    <template key="test-key">
      {{ message }}
    </template>
  </div>

  - <template> cannot be keyed. Place the key on real elements instead.

Новый вывод ошибки vue-template-compiler@2.6.0:

  Errors compiling template:

  <template> cannot be keyed. Place the key on real elements instead.

  1  |
  2  |  <div>
  3  |    <template key="test-key">
     |              ^^^^^^^^^^^^^^
  4  |      {{ message }}
  5  |    </template>

6. Отлов асинхронных ошибок

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

Пример:

/* TestComponent.vue */

<template>
  <div @click="doSomething()">
    Some message
  </div>
</template>

<script>
export default {
  methods: {
    async doSomething() {
      await this.$nextTick();

      throw new Error('Another Error');
    },
  },

  async mounted() {
    await this.$nextTick();

    throw new Error('Some Error');
  },
};
</script>

Ошибка после маунта:

[Vue warn]: Error in mounted hook (Promise/async): "Error: Some Error"

Ошибка после клика:

[Vue warn]: Error in v-on handler (Promise/async): "Error: Another Error"

7. Новая сборка для ESM браузеров

В новой версии добавилась еще одна сборка Vue — vue.esm.browser.js. Она предназначена для браузеров, поддерживающих ES6 Modules.

Ее особенности:

  • Содержит компилятор HTML в render-функцию
  • Использует синтаксис ES6 Modules
  • Содержит нетранспилированный код

Пример использования:

<html lang="en">
  <head>
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      {{ message }}
    </div>

    <script type="module">
      // Раньше приходилось использовать vue.esm.js, 
      // который содержал транспилированный код,
      // весил чуть больше и работал чуть медленнее
      import Vue from 'path/to/vue.esm.browser.js';

      new Vue({
        el: '#app',

        data() {
          return {
            message: 'Hello World!',
          };
        },
      });
    </script>
  </body>
</html>

Если честно, мне бы хотелось видеть еще одну сборку — такую же, как vue.esm.browser.js, но без компилятора HTML. Тогда я бы смог подвозить браузерам с ES6 Modules более свежий код, когда компилирую шаблоны при сборке.

8. Сокращение для v-bind.prop

У директивы v-bind есть специальный модификатор — .prop. Посмотреть, что он делает, можно вот тут в документации. Сам я ни разу его не использовал и не представляю себе случай, когда его стоит применить.

Для него теперь есть специальное сокращение: вместо записи v-bind:someProperty.prop=«foo» можно писать .someProperty=«foo».

Пример:

Как было во Vue@2.5.22:

<template>
  <div>
    <div v-bind:textContent.prop="'Important text content'" />

    <!-- Или сокращенный вариант -->
    <div :textContent.prop="'Important text content'" />
  </div>
</template>

Как можно во Vue@2.6.0:

<template>
  <div .textContent="'Important text content'" />
</template>

9. Поддержка кастомного toString()

Тут все просто: если вы переопределите метод toString() у объекта, то Vue при отображении станет использовать его вместо JSON.stringify().

Пример:

/* TestComponent.vue */

<template>
  <div>
    {{ message }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: {
        value: 'qwerty',

        toString() {
          return 'Hello Habr!';
        },
      },
    };
  },
};
</script>

В версии Vue@2.5.22 мы увидим на экране:

{ "value": "qwerty" }

В версии Vue@2.6.0:

Hello Habr!

10. Работа v-for с итерируемыми объектами

В новой версии v-for может работать с любыми объектами, которые реализуют iterable protocol, например Map или Set. Правда, для Map и Set в версии 2.X не будет поддерживаться реактивность.

Пример:

/* TestComponent.vue */

<template>
  <div>
    <div 
      v-for="item in items"
      :key="item"
    >
      {{ item }}
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: new Set([4, 2, 6]),
    };
  },
};
</script>

Посмотреть все новые фишки в деле можно прямо сейчас, установив бета-версию Vue:

npm i vue@2.6.0-beta.2

Спасибо, что дочитали до конца!

Автор: Андрей Аникин

Источник


* - обязательные к заполнению поля