Привет!
Совсем скоро должна выйти новая версия Vue.js — 2.6. Под катом вы найдете обзор новых фич следующей версии, включая новый синтаксис слотов, Vue.observable() и много чего еще!
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 родителя.
Ссылки:
- Новый синтаксис v-slot
- Сокращение для 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
Спасибо, что дочитали до конца!
Автор: Андрей Аникин