В октябре 2019 года наконец-то была выпущена пре-альфа Vue 3.0. Так что использовать Vue 3.0 в рабочей среде - вопрос времени.
Чтобы подготовиться к этому, я хотел бы рассказать, как перейти с Vue 2.0 на Vue 3.0. Поскольку в наши дни большое количество пользователей Vue уже представили TypeScript, в этой статье также используется TypeScript.
Есть 3 шага по миграции, как показано ниже.
- Внедрение Vue 3.0 в Vue 2.0
- Заменить коды Vue 2.0 на Vue 3.0
- Создайте глобальный магазин с API композиции Vue 3.0
Поскольку тема немного длинная, я разделил ее на 3 статьи. Эта статья является продолжением первого шага. Если вы хотите начать с конкретной части, не стесняйтесь переходить на страницу.
Заменить коды Vue 2.0 на Vue 3.0
Вы уже настраивали API композиции Vue 3.0 в предыдущей статье! Итак, давайте постепенно заменим код Vue 2.0 на Vue 3.0!
Используйте createComponent и setup () вместо Vue.extend
В Vue 2.0 «Vue.extend» используется, чтобы позволить TypeScript правильно определять типы внутри компонента Vue. Вместо «Vue.extend» вы должны использовать createComponent в Vue 3.0 для вывода типа. Кроме того, внутри createComponent вы должны использовать функцию настройки, которая является точкой входа, где вызываются функции композиции.
// src/List.vue <script lang="ts"> import { createComponent } from "@vue/composition-api"; export default createComponent({ setup(){ // DO SOMETHING HERE } })
Но когда вы заменяете Vue.extend на createComponent, в вашем редакторе возникает много ошибок, потому что createComponent не знает this
в Vue 2.0. У вас много ошибок компиляции в терминале.
Но не волнуйтесь. Это ошибка от Vetur. Все нормально работает.
Замените «данные» Vue 2.0 на реактивный
Следующим шагом будет замена локального состояния Vue 2.0, data
. Это очень просто, используйте реактивную функцию внутри функции настройки, как показано ниже.
// src/List.vue <script lang="ts"> import { FilterByEnum, Data, FilterBy } from "./types/List"; import { createComponent, reactive } from "@vue/composition-api"; export default createComponent({ setup(){ const todoState = reactive<Data>({ todos: [], newTodo: "", filterBy: FilterByEnum.ALL }); } })
reactive
является эквивалентом текущего Vue.observable () в Vue 2.0. Возвращаемое значение из реактивного - это реактивный объект, который вам нравится.
На самом деле у вас есть другой API для создания реактивных переменных, называемый ref()
. Но сама функция очень похожа на reactive()
. Пожалуйста, проверьте официальную документацию.
Замените «методы» Vue 2.0!
Методы в Vue2.0 превращаются в простые функции внутри функции настройки. Коды легче увидеть. Давайте сначала займемся задачами.
// src/List.vue ... import todos from "../utils/todos"; export default createComponent({ setup(){ const todoState = reactive<Data>({ ... const init = function(): void { getTodos(); }; const getTodos = function(): void { setTimeout(() => { todoState.todos = [...todos]; }, 1000); } })
Поскольку вам не нужно использовать this
для доступа к локальному штату, это проще.
Пока что у вас есть функция инициализации задач. Но когда вы это называете? Да, вы хотите сделать это, когда экземпляр Vue «установлен».
Конечно, у Vue 3.0 есть хуки жизненного цикла. Вы должны использовать его, как показано ниже.
// src/List.vue ... export default createComponent({ setup(){ const todoState = reactive<Data>({ ... onMounted(() => { init(); }); const init = function(): void { getTodos(); }; const getTodos = function(): void { setTimeout(() => { todoState.todos = [...todos]; }, 1000); } })
Остальные методы можно переместить во внутреннюю функцию настройки. После перемещения всего метода List.vue
становится таким, как показано ниже.
// src/List.vue <script lang="ts"> import Vue from "vue"; import todos from "../utils/todos"; import { FilterByEnum, Data, FilterBy } from "./types/List"; import { createComponent, reactive, onMounted } from "@vue/composition-api"; export default createComponent({ setup(props, context) { const todoState = reactive<Data>({ todos: [], newTodo: "", filterBy: FilterByEnum.ALL }); onMounted(() => { init(); }); const init = function(): void { getTodos(); }; const getTodos = function(): void { setTimeout(() => { todoState.todos = [...todos]; }, 1000); }; const addTodo = function(): void { const newTodo = { name: todoState.newTodo, completed: false }; todoState.todos = [...todoState.todos, newTodo]; todoState.newTodo = ""; }; const deleteTodo = function(index: number): void { todoState.todos = todoState.todos.filter((todo, i) => i !== index); }; const completeTodo = function(index: number): void { todoState.todos[index].completed = true; }; const handleClickFilterBy = function(filterBy: FilterBy): void { todoState.filterBy = filterBy; }; const goEditTodo = function(index: number): void { context.root.$router.push(`/todos/${index}/edit`); }; },
Одна вещь, которую вы еще не знаете, - это два аргумента настройки, свойства и контекст. Реквизит понять несложно. Вы можете получить реквизит внутри функции настройки. В контексте вы можете проверить многие вещи, которые можно увидеть в this
Vue 2.0, например, slot
, parent
и root
.
Итак, если вы хотите использовать $router
, вы можете получить доступ с context.root
.
Заменить «вычисленный» в Vue 2.0
Вы уже заметили, что у вас есть коды Vue2.0 только в вычисленных. Но его также очень легко заменить.
// src/List.vue ... import { createComponent, reactive, onMounted, computed } from "@vue/composition-api"; export default createComponent({ setup(props, context) { const todoState = reactive<Data>({ todos: [], newTodo: "", filterBy: FilterByEnum.ALL }); const filteredTodos = computed(function() { return todoState.todos.filter(todo => { if (todoState.filterBy === FilterByEnum.WORKING) { return !todo.completed; } if (todoState.filterBy === FilterByEnum.DONE) { return todo.completed; } return todo; }); const numOfTodos = computed(function(): number { return todoState.todos.filter(todo => !todo.completed).length; }); ...
Вы можете импортировать вычисленное из @vue/composition-api
. И так же, как методы, вы можете объявить имя вычисляемого и передать вычисляемую логику аргументу вычисляемого. Вот и все.
Если вы хотите использовать переменные, методы в шаблонах
Пока вы заменили все коды Vue 2.0 на код Vue 3.0. Это приложение todo использует множество переменных и методов внутри шаблона, как показано ниже.
- Состояние
- задачи
- newTodo
- filterBy - Вычислено
- filterTodos
- numOfTodos - методы
- addTodo
- deleteTodo
- completeTodo
- handleClickFilterBy
- goEditTodo
Но если вы хотите использовать их внутри шаблона, что вам следует делать? Это действительно просто. Просто верните то, что вам нужно, внутри функции настройки.
// src/List.vue // Inside of template, nothing changed. ... import { createComponent, reactive, onMounted, computed, toRefs } from "@vue/composition-api"; export default createComponent({ setup(props, context) { ... return { ...toRefs(todoState), filteredTodos, numOfTodos, addTodo, handleClickFilterBy, completeTodo, goEditTodo, deleteTodo }; } })
Вы не знаете, что toRefs
. Это потому, что когда вы используете reactive
без reRefs
, вы должны использовать их внутри храма, например todoState.todos
, todoState.filterBy
. Но это не СУХОЕ! С toRefs()
и деструктуризацией вы можете использовать их так же, как и раньше.
Итак, теперь все работает правильно!
Сделайте это компонуемым
Пока что Vue 3.0 отлично работает. Но вы можете провести рефакторинг. Основная цель API композиции Vue 3.0 - сделать код компонуемым. Это значит, что код нужно разделять на мелкие части и заставлять их использовать снова и снова.
Итак, давайте составим задачи.
- Сделайте функцию с именем useTodos за пределами экспорта по умолчанию.
// src/List.vue ... import { createComponent, reactive, onMounted, computed, toRefs } from "@vue/composition-api"; export default createComponent({ setup(props, context) { ... } }) const useTodos = () => {}
2. Скопируйте и вставьте функции и состояние внутри настройки в useTodos
Поскольку goEditTodo
немного отличается, вам не нужно его перемещать.
// src/List.vue ... import { createComponent, reactive, onMounted, computed, toRefs } from "@vue/composition-api"; export default createComponent({ setup(props, context) { const goEditTodo = function(index: number): void { context.root.$router.push(`/todos/${index}/edit`); }; return { goEditTodo }; } }) const useTodos = () => { const todoState = reactive<Data>({ todos: [], newTodo: "", filterBy: FilterByEnum.ALL }); ... return { ...toRefs(todoState), filteredTodos, numOfTodos, addTodo, handleClickFilterBy, completeTodo, deleteTodo } }
Эта useTodos
функция называется функцией композиции.
3. Вызовите useTodos
внутри программы установки и используйте ее.
// src/List.vue ... import { createComponent, reactive, onMounted, computed, toRefs } from "@vue/composition-api"; export default createComponent({ setup(props, context) { const goEditTodo = function(index: number): void { context.root.$router.push(`/todos/${index}/edit`); }; return { ...useTodos(), goEditTodo }; } }) const useTodos = () => { ... return { ...toRefs(todoState), filteredTodos, numOfTodos, addTodo, handleClickFilterBy, completeTodo, deleteTodo } }
Теперь работает отлично!
4. Отделите функцию композиции от другого файла.
Поскольку функция композиции - это просто функция, вы можете легко разделить ее. Из демонстрационного репозитория Линуса Борга эти функции композиции сохраняются в src/composables/
. И каждая функция композиции названа как useHoge
, как показано ниже. Фактически, типовой файл также перемещается в раздел composables.
// composables/useTodos.ts import todos from "../utils/todos"; import { FilterByEnum, Data, FilterBy } from "./types/UseTodos"; import { reactive, onMounted, computed, toRefs } from "@vue/composition-api"; export const useTodos = () => { ... };
И импортируйте его в List.vue
.
// src/List.vue <script lang="ts"> import Vue from "vue"; import { createComponent } from "@vue/composition-api"; import { useTodos } from "../composables/useTodos"; export default createComponent({ setup(props, context) { const goEditTodo = function(index: number): void { context.root.$router.push(`/todos/${index}/edit`); }; return { ...useTodos(), goEditTodo }; } }); </script>
Теперь List.vue
супер чистый. С помощью Compostion API вы можете отделить логику от компонента vue, а компонент vue может больше сосредоточиться на представлении. Это одна из главных ценностей Vue 3.0.
Следующий уровень
Пока что функция композиции вызывается в компоненте List.vue. Что, если вы вызовете ту же функцию композиции в Edit.vue? Распределены ли состояния и функции между компонентами?
Ответ - нет". Поскольку функция композиции - это просто функция, вы должны использовать что-то, чтобы поделиться ими во всем мире. Функция композиции - это не управление состоянием, как Vuex.
Но если вы используете Provide и Inject of Vue, вы можете создать глобальное хранилище. В следующей статье объясняется, как создать глобальное хранилище с помощью Vue 3.0.
В этой статье я объяснил, как заменить Vue 2.0 на Vue 3.0. Но в этой статье пока не говорится о global store
Vue. Я хотел бы поделиться возможностью глобального магазина Vue 3.0 и попытаться сделать это.