# Сборка для миграции

# Обзор

@vue/compat (она же «сборка для миграции») — специальная сборка Vue 3, поведение которой можно настраивать для совместимости с Vue 2.

Сборка для миграции по умолчанию работает в режиме Vue 2 — большая часть публичных API ведёт себя точно также, как и во Vue 2, за несколькими исключениями. Использование возможностей, которые претерпели изменения или объявлены устаревшими во Vue 3, будут выбрасывать предупреждения во время выполнения. Также есть возможность включать и выключать на уровне компонента совместимость с определёнными возможностями.

# Предполагаемые сценарии использования

  • Обновление приложения Vue 2 на Vue 3 (с некоторыми ограничениями)
  • Миграция библиотеки для поддержки Vue 3
  • Для опытных разработчиков Vue 2, которые ещё не пробовали Vue 3, сборка для миграции может помочь в изучении различий между версиями.

# Известные ограничения

Несмотря на то, что сборка для миграции реализовывалась максимально похожей поведением на Vue 2, есть некоторые ограничения, которые могут препятствовать обновлению приложения:

# Ожидания

Обратите внимание, сборка для миграции нацелена лишь на публично документированные API и поведение Vue 2. Если приложение не будет работать со сборкой для миграции из-за использования недокументированного поведения, маловероятно, что изменим сборку для миграции, чтобы удовлетворить подобный конкретный случай. Вместо этого рассмотрите возможность рефакторинга, чтобы избавиться от данного нестандартного поведения.

Предостережение: для большого и сложного приложения миграция, скорее всего, будет сложной даже со сборкой для миграции. Если приложение не подходит для обновления, обратите внимание, что планируется портировать Composition API и некоторые другие функции Vue 3 в релизе 2.7 (предположительно в конце третьего квартала 2021).

Если приложение на сборке для миграции удалось запустить, то его можно опубликовать в production до завершения процесса миграции. Несмотря на небольшие накладные расходы по производительности/размеру, это не должно заметно повлиять на UX в production. Такое может потребоваться, если есть зависимости, которые зависят от поведения Vue 2, и не могут быть обновлены/заменены.

Сборка для миграции выпускается, начиная с версии 3.1, и будет продолжать обновляться вместе с релизами 3.2. Публикация обновлений сборки для миграции будет прекращена в одной из будущих минорных версий (не ранее конца 2021 года), поэтому до этого времени стоит запланировать переход на стандартную сборку.

# Процесс обновления

Следующий процесс обновления описывает шаги по миграции фактического приложения на Vue 2 (Vue HackerNews 2.0) до Vue 3. Полный список коммитов можно найти здесь (opens new window). Обратите внимание, что для собственного проекта фактические шаги могут отличаться. Описанную здесь последовательность следует рассматривать как общее руководство к действию, а не как строгую инструкцию.

# Подготовка

# Установка

  1. Обновите инструментарий, при возможности.

  2. В файле package.json, обновите vue до 3.1. Установите @vue/compat такой же версии и замените vue-template-compiler (если присутствует) на @vue/compiler-sfc:

    "dependencies": {
    -  "vue": "^2.6.12",
    +  "vue": "^3.1.0",
    +  "@vue/compat": "^3.1.0"
        ...
    },
    "devDependencies": {
    -  "vue-template-compiler": "^2.6.12"
    +  "@vue/compiler-sfc": "^3.1.0"
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    Пример коммита (opens new window)

  3. В конфигурации сборки установите псевдоним для vue на @vue/compat и включите режим совместимости сборки для миграции через опции компилятора Vue.

    Примеры конфигураций

    vue-cli
    // vue.config.js
    module.exports = {
      chainWebpack: config => {
        config.resolve.alias.set('vue', '@vue/compat')
    
        config.module
          .rule('vue')
          .use('vue-loader')
          .tap(options => {
            return {
              ...options,
              compilerOptions: {
                compatConfig: {
                  MODE: 2
                }
              }
            }
          })
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Чистый webpack
    // webpack.config.js
    module.exports = {
      resolve: {
        alias: {
          vue: '@vue/compat'
        }
      },
      module: {
        rules: [
          {
            test: /\.vue$/,
            loader: 'vue-loader',
            options: {
              compilerOptions: {
                compatConfig: {
                  MODE: 2
                }
              }
            }
          }
        ]
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    Vite
    // vite.config.js
    export default {
      resolve: {
        alias: {
          vue: '@vue/compat'
        }
      },
      plugins: [
        vue({
          template: {
            compilerOptions: {
              compatConfig: {
                MODE: 2
              }
            }
          }
        })
      ]
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  4. При использовании TypeScript нужно изменить типизацию vue для объявления экспорта по умолчанию (которого больше нет во Vue 3), добавив *.d.ts следующего вида:

    declare module 'vue' {
      import { CompatVue } from '@vue/runtime-dom'
      const Vue: CompatVue
      export default Vue
      export * from '@vue/runtime-dom'
    }
    
    1
    2
    3
    4
    5
    6
  5. На данном этапе приложение может столкнуться с ошибками / предупреждениями на этапе компиляции (например, при использовании фильтров). Исправьте их в первую очередь. После того как все предупреждения компилятора будут исправлены, можно переключить компилятор в режим Vue 3.

    Пример коммита (opens new window)

  6. После исправления ошибок приложение должно запускаться, если его не затрагивают известные ограничения, перечисленные выше.

    Скорее всего увидите МНОЖЕСТВО предупреждений, как в командной строке, так и в консоли браузера. Вот несколько общих советов:

    • Можно отфильтровать конкретные предупреждения в консоли браузера. Хорошая идея — воспользоваться фильтром и сосредоточиться на исправлении одного типа ошибки за раз. Также можно использовать отрицание в фильтрах, например -GLOBAL_MOUNT.

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

    • Некоторые предупреждения могут быть вызваны используемой зависимостью (например, vue-router). Проверить это можно по трассировке предупреждения компонента или трассировке стека (разворачивается по клику). Для начала лучше сосредоточиться на исправлении предупреждений в собственном исходным коде.

    • Если используете vue-router, обратите внимание, что <transition> и <keep-alive> не будут работать с <router-view> пока не обновите vue-router до версии v4.

  7. Обновите имена классов <transition>. Это единственная функция, которая не имеет предупреждения во время выполнения. Можно выполнить поиск по всему проекту для имён CSS-классов .*-enter и .*-leave.

    Пример коммита (opens new window)

  8. Обновите точку входа приложения на использование нового глобального API монтирования.

    Пример коммита (opens new window)

  9. Обновите vuex до версии v4 (opens new window).

    Пример коммита (opens new window)

  10. Обновите vue-router до версии v4 (opens new window). Если также используется vuex-router-sync, то можно его заменить на геттер.

    После обновления, для использования <transition> и <keep-alive> совместно с <router-view> необходимо воспользоваться новым синтаксисом слотов с ограниченной областью видимости (opens new window).

    Пример коммита (opens new window)

  11. Выбирайте отдельные предупреждения и исправляйте их. Обратите внимание, что некоторые функции имеют несовместимое поведение между Vue 2 и Vue 3 — например, API render-функций или изменения функциональных компонентов vs. асинхронных компонентов. Чтобы выполнять миграцию на API Vue 3, не затрагивая остальные части приложения, можно определять поведение Vue 3 для конкретного компонента через опцию compatConfig.

    Пример коммита (opens new window)

  12. После исправления всех предупреждений можно удалить сборку для миграции и перейти на обычную сборку Vue 3. Обратите внимание, что это нельзя будет сделать, если есть зависимости, которые полагаются на поведение Vue 2.

    Пример коммита (opens new window)

# Конфигурация совместимости

# Глобальная конфигурация

Совместимость с возможностями можно отключать по-отдельности:

import { configureCompat } from 'vue'

// отключение совместимости для отдельных возможностей
configureCompat({
  FEATURE_ID_A: false,
  FEATURE_ID_B: false
})
1
2
3
4
5
6
7

В качестве альтернативы, всё приложение может по умолчанию использовать поведение Vue 3, включая совместимость лишь для определённых возможностей:

import { configureCompat } from 'vue'

// по умолчанию всё поведение как во Vue 3, и только
// включение совместимости для определённых возможностей
configureCompat({
  MODE: 3,
  FEATURE_ID_A: true,
  FEATURE_ID_B: true
})
1
2
3
4
5
6
7
8
9

# Конфигурация для компонента

В компоненте можно использовать опцию compatConfig, которая ожидает такие же параметры, что и глобальный метод configureCompat:

export default {
  compatConfig: {
    MODE: 3, // выбор поведения Vue 3 для этого компонента
    FEATURE_ID_A: true // возможностями можно управлять на уровне компонента
  }
  // ...
}
1
2
3
4
5
6
7

# Конфигурация компиляции

Возможности, названные с COMPILER_, специфичны для компилятора: при использовании полной сборки (с компиляцией шаблонов в браузере), они могут быть настроены во время выполнения. Однако при использовании шага сборки их нужно настраивать через опцию compilerOptions в конфигурации сборки (см. примеры конфигураций выше).

# Перечень возможностей

# Типы совместимости

  • ✔ Полная совместимость
  • ◐ Частичная совместимость (с ограничениями)
  • ⨂ Нет совместимости (только предупреждения)
  • ⭘ Только для совместимости (нет предупреждений)

# Нет совместимости

Требуется исправить заранее или может приводить к ошибкам

ID Тип Описание Документация
GLOBAL_MOUNT_CONTAINER Монтируемое приложение не заменяет элемент, к которому монтируется ссылка
CONFIG_DEVTOOLS devtools в production теперь определяется флагом при сборке ссылка (opens new window)
COMPILER_V_IF_V_FOR_PRECEDENCE приоритет v-if и v-for при использовании на одном и том же элементе изменился ссылка
COMPILER_V_IF_SAME_KEY ветви v-if больше не могут иметь одинаковый key ссылка
COMPILER_V_FOR_TEMPLATE_KEY_PLACEMENT для <template v-for> указывать key теперь нужно на <template> ссылка
COMPILER_SFC_FUNCTIONAL <template functional> больше не поддерживается в однофайловых компонентах ссылка

# Частичная совместимость (с ограничениями)

ID Тип Описание Документация
CONFIG_IGNORED_ELEMENTS config.ignoredElements теперь является config.compilerOptions.isCustomElement (только в сборке с компилятором в браузере). При использовании шага сборки, isCustomElement должен передаваться через конфигурацию сборки. ссылка
COMPILER_INLINE_TEMPLATE inline-template удалён (поддерживается только совместимость в сборке с компилятором шаблонов) ссылка
PROPS_DEFAULT_THIS фабрика значения по умолчанию входного параметра больше не имеет доступа к this (в режиме совместимости this не является реальным экземпляром — он только предоставляет доступ к входным параметрам, $options и внедрённым свойствам) ссылка
INSTANCE_DESTROY удалён метод экземпляра $destroy (в режиме совместимости поддерживается только для корневого экземпляра)
GLOBAL_PRIVATE_UTIL Vue.util является приватным и больше недоступно
CONFIG_PRODUCTION_TIP config.productionTip больше не нужен ссылка
CONFIG_SILENT config.silent удалён

# Только для совместимости (нет предупреждений)

ID Тип Описание Документация
TRANSITION_CLASSES Изменены классы переходов enter/leave ссылка

# Полная совместимость

ID Тип Описание Документация
GLOBAL_MOUNT new Vue() -> createApp ссылка
GLOBAL_EXTEND Vue.extend удалён (используйте defineComponent или опцию extends) ссылка
GLOBAL_PROTOTYPE Vue.prototype -> app.config.globalProperties ссылка
GLOBAL_SET Vue.set удалён (больше не нужен)
GLOBAL_DELETE Vue.delete удалён (больше не нужен)
GLOBAL_OBSERVABLE Vue.observable удалён (используйте reactive) ссылка
CONFIG_KEY_CODES config.keyCodes удалён ссылка
CONFIG_WHITESPACE Во Vue 3 пробельные символы по умолчанию "condense"
INSTANCE_SET vm.$set удалён (больше не нужен)
INSTANCE_DELETE vm.$delete удалён (больше не нужен)
INSTANCE_EVENT_EMITTER vm.$on, vm.$off, vm.$once удалены ссылка
INSTANCE_EVENT_HOOKS Экземпляр больше не генерирует события hook:x ссылка
INSTANCE_CHILDREN vm.$children удалён ссылка
INSTANCE_LISTENERS vm.$listeners удалён ссылка
INSTANCE_SCOPED_SLOTS vm.$scopedSlots удалён; vm.$slots теперь предоставляет доступ к функциям ссылка
INSTANCE_ATTRS_CLASS_STYLE $attrs теперь содержит class и style ссылка
OPTIONS_DATA_FN data должна быть функцией во всех случаях ссылка
OPTIONS_DATA_MERGE data из примеси или расширения теперь неглубоко объединяются ссылка
OPTIONS_BEFORE_DESTROY beforeDestroy -> beforeUnmount
OPTIONS_DESTROYED destroyed -> unmounted
WATCH_ARRAY Отслеживание массива больше не срабатывает при мутации без deep ссылка
V_FOR_REF ref внутри v-for не регистрирует массив refs ссылка
V_ON_KEYCODE_MODIFIER v-on больше не поддерживает модификаторы keyCode ссылка
CUSTOM_DIR Имена хуков пользовательских директив изменены ссылка
ATTR_FALSE_VALUE Атрибут больше не удаляется при привязке булево значения false ссылка
ATTR_ENUMERATED_COERCION Перечисляемые атрибуты больше не являются особым случаем ссылка
TRANSITION_GROUP_ROOT <transition-group> не отрисовывает корневой элемент по умолчанию ссылка
COMPONENT_ASYNC API асинхронных компонентов изменено (теперь требуется defineAsyncComponent) ссылка
COMPONENT_FUNCTIONAL API функциональных компонентов изменено (должны быть обычными функциями) ссылка
COMPONENT_V_MODEL Переработана v-model на компонентах ссылка
RENDER_FUNCTION API render-функций изменено ссылка
FILTERS Фильтры удалены (эта опция влияет только на API фильтров во время выполнения) ссылка
COMPILER_IS_ON_ELEMENT Использование is теперь ограничено только <component> ссылка
COMPILER_V_BIND_SYNC v-bind.sync заменяется v-model с аргументами ссылка
COMPILER_V_BIND_PROP Удалён модификатор v-bind.prop
COMPILER_V_BIND_OBJECT_ORDER v-bind="object" is now order sensitive ссылка
COMPILER_V_ON_NATIVE Удалён модификатор v-on.native ссылка
COMPILER_V_FOR_REF ref в v-for (поддержка компилятора)
COMPILER_NATIVE_TEMPLATE <template> без специальных директив теперь отрисовывается как нативный элемент
COMPILER_FILTERS Фильтры (поддержка компилятора)