Хабрахабр

Обзор Vue.js 2.6

Привет, Хабр!

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

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

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

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

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

5. Как использовались scoped slots в версии Vue@2. 22:

<template> <TestComponent> <!-- Дефолтный scoped slot --> <div slot-scope=""> {{ `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 не имеет похожего синтаксиса для объектов

6. Чтобы избавиться от них, Vue@2. 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().

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

<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>

5. Ошибка vue-template-compiler@2. 22:

Error compiling template: <div> <template key="test-key"> {{ message }} </template> </div> - <template> cannot be keyed. Place the key on real elements instead.

6. Новый вывод ошибки vue-template-compiler@2. 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>

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

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

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

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

Пример:

5. Как было во Vue@2. 22:

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

6. Как можно во Vue@2. 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>

5. В версии Vue@2. 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

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть