diff --git a/src/.vuepress/config.js b/src/.vuepress/config.js index 3c3a6ecfc3..2f73bf125e 100644 --- a/src/.vuepress/config.js +++ b/src/.vuepress/config.js @@ -172,6 +172,7 @@ const sidebar = { ], migration: [ '/guide/migration/introduction', + '/guide/migration/migration-build', { title: 'Details', collapsable: false, diff --git a/src/guide/migration/array-refs.md b/src/guide/migration/array-refs.md index d55f8ecd62..0c41ef0c82 100644 --- a/src/guide/migration/array-refs.md +++ b/src/guide/migration/array-refs.md @@ -70,3 +70,10 @@ Note that: - `itemRefs` doesn't have to be an array: it can also be an object where the refs are set by their iteration keys. - This also allows `itemRefs` to be made reactive and watched, if needed. + +## Migration Strategy + +[Migration build flags:](migration-build.html#compat-configuration) + +- `V_FOR_REF` +- `COMPILER_V_FOR_REF` diff --git a/src/guide/migration/async-components.md b/src/guide/migration/async-components.md index f9032ad105..fcdee250bf 100644 --- a/src/guide/migration/async-components.md +++ b/src/guide/migration/async-components.md @@ -95,3 +95,4 @@ const asyncComponent = defineAsyncComponent( For more information on the usage of async components, see: - [Guide: Dynamic & Async Components](/guide/component-dynamic-async.html#dynamic-components-with-keep-alive) +- [Migration build flag: `COMPONENT_ASYNC`](migration-build.html#compat-configuration) diff --git a/src/guide/migration/attribute-coercion.md b/src/guide/migration/attribute-coercion.md index a2800190f9..b9dbf6a962 100644 --- a/src/guide/migration/attribute-coercion.md +++ b/src/guide/migration/attribute-coercion.md @@ -137,3 +137,8 @@ In 3.x, `null` or `undefined` should be used to explicitly remove an attribute. + +[Migration build flags:](migration-build.html#compat-configuration) + +- `ATTR_FALSE_VALUE` +- `ATTR_ENUMERATED_COERSION` diff --git a/src/guide/migration/attrs-includes-class-style.md b/src/guide/migration/attrs-includes-class-style.md index dbb6a5ce52..27755f6c7a 100644 --- a/src/guide/migration/attrs-includes-class-style.md +++ b/src/guide/migration/attrs-includes-class-style.md @@ -60,6 +60,8 @@ when used like this: In components that use `inheritAttrs: false`, make sure that styling still works as intended. If you previously relied on the special behavior of `class` and `style`, some visuals might be broken as these attributes might now be applied to another element. +[Migration build flag: `INSTANCE_ATTRS_CLASS_STYLE`](migration-build.html#compat-configuration) + ## See also - [Relevant RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0031-attr-fallthrough.md) diff --git a/src/guide/migration/children.md b/src/guide/migration/children.md index 087a6ee969..39464fae20 100644 --- a/src/guide/migration/children.md +++ b/src/guide/migration/children.md @@ -38,3 +38,7 @@ export default { ## 3.x Update In 3.x, the `$children` property is removed and no longer supported. Instead, if you need to access a child component instance, we recommend using [$refs](/guide/component-template-refs.html#template-refs). + +## Migration Strategy + +[Migration build flag: `INSTANCE_CHILDREN`](migration-build.html#compat-configuration) diff --git a/src/guide/migration/custom-directives.md b/src/guide/migration/custom-directives.md index aca9148ab7..6b5a3656c7 100644 --- a/src/guide/migration/custom-directives.md +++ b/src/guide/migration/custom-directives.md @@ -103,3 +103,7 @@ mounted(el, binding, vnode) { :::warning With [fragments](/guide/migration/fragments.html#overview) support, components can potentially have more than one root node. When applied to a multi-root component, a custom directive will be ignored and a warning will be logged. ::: + +## Migration Strategy + +[Migration build flag: `CUSTOM_DIR`](migration-build.html#compat-configuration) diff --git a/src/guide/migration/custom-elements-interop.md b/src/guide/migration/custom-elements-interop.md index 1135dc87ab..4f138d46ed 100644 --- a/src/guide/migration/custom-elements-interop.md +++ b/src/guide/migration/custom-elements-interop.md @@ -96,6 +96,8 @@ In 3.0, we are limiting Vue's special treatment of the `is` prop to the ` Note: this section only affects cases where Vue templates are directly written in the page's HTML. diff --git a/src/guide/migration/data-option.md b/src/guide/migration/data-option.md index 3d914909cf..ca8f41ce3e 100644 --- a/src/guide/migration/data-option.md +++ b/src/guide/migration/data-option.md @@ -111,6 +111,8 @@ In 3.0, the result will be: } ``` +[Migration build flag: `OPTIONS_DATA_FN`](migration-build.html#compat-configuration) + ## Migration Strategy For users relying on the object declaration, we recommend: @@ -119,3 +121,8 @@ For users relying on the object declaration, we recommend: - Rewrite references to the shared data to point to a new shared object For users relying on the deep merge behavior from mixins, we recommend refactoring your code to avoid such reliance altogether, since deep merges from mixins are very implicit and can make the code logic more difficult to understand and debug. + +[Migration build flags:](migration-build.html#compat-configuration) + +- `OPTIONS_DATA_FN` +- `OPTIONS_DATA_MERGE` diff --git a/src/guide/migration/events-api.md b/src/guide/migration/events-api.md index 0e22aeb377..fd3afa994f 100644 --- a/src/guide/migration/events-api.md +++ b/src/guide/migration/events-api.md @@ -79,3 +79,5 @@ export default { This provides the same event emitter API as in Vue 2. These methods may also be supported in a future compatibility build of Vue 3. + +[Migration build flag: `INSTANCE_EVENT_EMITTER`](migration-build.html#compat-configuration) diff --git a/src/guide/migration/filters.md b/src/guide/migration/filters.md index cc58cbb572..b2541e1dca 100644 --- a/src/guide/migration/filters.md +++ b/src/guide/migration/filters.md @@ -73,6 +73,11 @@ Using the example above, here is one example of how it could be implemented. Instead of using filters, we recommend replacing them with computed properties or methods. +[Migration build flags:](migration-build.html#compat-configuration) + +- `FILTERS` +- `COMPILER_FILTERS` + ### Global Filters If you are using filters that were globally registered and then used throughout your app, it's likely not convenient to replace them with computed properties or methods in each individual component. diff --git a/src/guide/migration/functional-components.md b/src/guide/migration/functional-components.md index 876195dcde..f84aa94b20 100644 --- a/src/guide/migration/functional-components.md +++ b/src/guide/migration/functional-components.md @@ -117,3 +117,4 @@ For more information on the usage of the new functional components and the chang - [Migration: Render Functions](/guide/migration/render-function-api.html) - [Guide: Render Functions](/guide/render-function.html) +- [Migration build flag: `COMPONENT_FUNCTIONAL`](migration-build.html#compat-configuration) diff --git a/src/guide/migration/global-api.md b/src/guide/migration/global-api.md index 7174a2bec4..cb21ad555d 100644 --- a/src/guide/migration/global-api.md +++ b/src/guide/migration/global-api.md @@ -85,6 +85,7 @@ An app instance exposes a subset of the Vue 2 global APIs. The rule of thumb is | Vue.mixin | app.mixin | | Vue.use | app.use ([see below](#a-note-for-plugin-authors)) | | Vue.prototype | app.config.globalProperties ([see below](#vue-prototype-replaced-by-config-globalproperties)) | +| Vue.extend | _removed_ ([see below](#vue-extend-replaced-by-definecomponent)) | All other global APIs that do not globally mutate behavior are now named exports, as documented in [Global API Treeshaking](./global-api-treeshaking.html). @@ -94,6 +95,8 @@ In Vue 3.x, the "use production build" tip will only show up when using the "dev For ES modules builds, since they are used with bundlers, and in most cases a CLI or boilerplate would have configured the production env properly, this tip will no longer show up. +[Migration build flag: `CONFIG_PRODUCTION_TIP`](migration-build.html#compat-configuration) + ### `config.ignoredElements` Is Now `config.isCustomElement` This config option was introduced with the intention to support native custom elements, so the renaming better conveys what it does. The new option also expects a function which provides more flexibility than the old string / RegExp approach: @@ -115,6 +118,8 @@ In Vue 3, the check of whether an element is a component or not has been moved t - This will be a new top-level option in the Vue CLI config. ::: +[Migration build flag: `CONFIG_IGNORED_ELEMENTS`](migration-build.html#compat-configuration) + ### `Vue.prototype` Replaced by `config.globalProperties` In Vue 2, `Vue.prototype` was commonly used to add properties that would be accessible in all components. @@ -134,6 +139,58 @@ app.config.globalProperties.$http = () => {} Using `provide` (discussed [below](#provide-inject)) should also be considered as an alternative to `globalProperties`. +[Migration build flag: `GLOBAL_PROTOTYPE`](migration-build.html#compat-configuration) + +### `Vue.extend` Removed + +In Vue 2.x, `Vue.extend` was used to create a "subclass" of the base Vue constructor with the argument that should be an object containing component options. In Vue 3.x, we don't have the concept of component constructors anymore. Mounting a component should always use the `createApp` global API: + +```js +// before - Vue 2 + +// create constructor +const Profile = Vue.extend({ + template: '

{{firstName}} {{lastName}} aka {{alias}}

', + data() { + return { + firstName: 'Walter', + lastName: 'White', + alias: 'Heisenberg' + } + } +}) +// create an instance of Profile and mount it on an element +new Profile().$mount('#mount-point') +``` + +```js +// after - Vue 3 +const Profile = { + template: '

{{firstName}} {{lastName}} aka {{alias}}

', + data() { + return { + firstName: 'Walter', + lastName: 'White', + alias: 'Heisenberg' + } + } +} + +Vue.createApp(Profile).mount('#mount-point') +``` + +#### Type Inference + +In Vue 2, `Vue.extend` was also used for providing TypeScript type inference for the component options. In Vue 3, the `defineComponent` global API can be used in place of `Vue.extend` for the same purpose. + +Note that although the return type of `defineComponent` is a constructor-like type, it is only used for TSX inference. At runtime `defineComponent` is largely a noop and will return the options object as-is. + +#### Component Inheritance + +In Vue 3, we strongly recommend favoring composition via [Composition API](/api/composition-api.html) over inheritance and mixins. If for some reason you still need component inheritance, you can use the [`extends` option](/api/options-composition.html#extends) instead of `Vue.extend`. + +[Migration build flag: `GLOBAL_EXTEND`](migration-build.html#compat-configuration) + ### A Note for Plugin Authors It is a common practice for plugin authors to install the plugins automatically in their UMD builds using `Vue.use`. For instance, this is how the official `vue-router` plugin installs itself in a browser environment: @@ -187,6 +244,8 @@ app.directive('focus', { app.mount('#app') ``` +[Migration build flag: `GLOBAL_MOUNT`](migration-build.html#compat-configuration) + ## Provide / Inject Similar to using the `provide` option in a 2.x root instance, a Vue 3 app instance can also provide dependencies that can be injected by any component inside the app: diff --git a/src/guide/migration/inline-template-attribute.md b/src/guide/migration/inline-template-attribute.md index faa79067e4..05cf6f126c 100644 --- a/src/guide/migration/inline-template-attribute.md +++ b/src/guide/migration/inline-template-attribute.md @@ -30,6 +30,8 @@ This feature will no longer be supported. Most of the use cases for `inline-template` assumes a no-build-tool setup, where all templates are written directly inside the HTML page. +[Migration build flag: `COMPILER_INLINE_TEMPLATE`](migration-build.html#compat-configuration) + ### Option #1: Use `` - In-browser playground on [Codepen](https://codepen.io/yyx990803/pen/OJNoaZL) - In-browser Sandbox on [CodeSandbox](https://v3.vue.new) @@ -37,6 +40,10 @@ Start learning Vue 3 at [Vue Mastery](https://www.vuemastery.com/courses-path/vu # select vue 3 preset ``` +## Migration Build + +If you have an existing Vue 2 project or library that you intend to upgrade to Vue 3, we provide a build of Vue 3 that offers Vue 2 compatible APIs. Check out the [Migration Build](./migration-build.html) page for more details. + ## Notable New Features Some of the new features to keep an eye on in Vue 3 include: @@ -53,10 +60,6 @@ Some of the new features to keep an eye on in Vue 3 include: ## Breaking Changes -::: info INFO -We are still working on a dedicated Migration Build of Vue 3 with Vue 2 compatible behavior and runtime warnings of incompatible usage. If you are planning to migrate a non-trivial Vue 2 app, we strongly recommend waiting for the Migration Build for a smoother experience. -::: - The following consists a list of breaking changes from 2.x: ### Global API diff --git a/src/guide/migration/keycode-modifiers.md b/src/guide/migration/keycode-modifiers.md index 7d2131423e..e7ceed4831 100644 --- a/src/guide/migration/keycode-modifiers.md +++ b/src/guide/migration/keycode-modifiers.md @@ -54,3 +54,8 @@ As a result, this means that `config.keyCodes` is now also deprecated and will n ## Migration Strategy For those using `keyCode` in their codebase, we recommend converting them to their kebab-cased named equivalents. + +[Migration build flags:](migration-build.html#compat-configuration) + +- `CONFIG_KEY_CODES` +- `V_ON_KEYCODE_MODIFIER` diff --git a/src/guide/migration/listeners-removed.md b/src/guide/migration/listeners-removed.md index f6c650c5b0..12dd8c6cee 100644 --- a/src/guide/migration/listeners-removed.md +++ b/src/guide/migration/listeners-removed.md @@ -65,6 +65,8 @@ If this component received an `id` attribute and a `v-on:close` listener, the `$ Remove all usages of `$listeners`. +[Migration build flag: `INSTANCE_LISTENERS`](migration-build.html#compat-configuration) + ## See also - [Relevant RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0031-attr-fallthrough.md) diff --git a/src/guide/migration/migration-build.md b/src/guide/migration/migration-build.md new file mode 100644 index 0000000000..9856ba7860 --- /dev/null +++ b/src/guide/migration/migration-build.md @@ -0,0 +1,330 @@ +# Migration Build + +## Overview + +`@vue/compat` (aka "the migration build") is a build of Vue 3 that provides configurable Vue 2 compatible behavior. + +The migration build runs in Vue 2 mode by default - most public APIs behave exactly like Vue 2, with only a few exceptions. Usage of features that have changed or been deprecated in Vue 3 will emit runtime warnings. A feature's compatibility can also be enabled/disabled on a per-component basis. + +### Intended Use Cases + +- Upgrading a Vue 2 application to Vue 3 (with [limitations](#known-limitations)) +- Migrating a library to support Vue 3 +- For experienced Vue 2 developers who have not tried Vue 3 yet, the migration build can be used in place of Vue 3 to help learn the difference between versions. + +### Known Limitations + +While we've tried hard to make the migration build mimic Vue 2 behavior as much as possible, there are some limitations that may prevent your app from being eligible for upgrading: + +- Dependencies that rely on Vue 2 internal APIs or undocumented behavior. The most common case is usage of private properties on `VNodes`. If your project relies on component libraries like [Vuetify](https://vuetifyjs.com/en/), [Quasar](https://quasar.dev/) or [ElementUI](https://element.eleme.io/#/en-US), it is best to wait for their Vue 3 compatible versions. + +- Internet Explorer 11 support: [Vue 3 has officially dropped the plan for IE11 support](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0038-vue3-ie11-support.md). If you still need to support IE11 or below, you will have to stay on Vue 2. + +- Server-side rendering: the migration build can be used for SSR, but migrating a custom SSR setup is much more involved. The general idea is replacing `vue-server-renderer` with [`@vue/server-renderer`](https://github.com/vuejs/vue-next/tree/master/packages/server-renderer). Vue 3 no longer provides a bundle renderer and it is recommended to use Vue 3 SSR with [Vite](https://vitejs.dev/guide/ssr.html). If you are using [Nuxt.js](https://nuxtjs.org/), it is probably better to wait for Nuxt 3. + +### Expectations + +Please note that the migration build aims to cover only publicly documented Vue 2 APIs and behavior. If your application fails to run under the migration build due to reliance on undocumented behavior, it is unlikely that we'll tweak the migration build to cater to your specific case. Consider refactoring to remove reliance on the behavior in question instead. + +A word of caution: if your application is large and complex, migration will likely be a challenge even with the migration build. If your app is unfortunately not suitable for upgrade, do note that we are planning to backport Composition API and some other Vue 3 features to the 2.7 release (estimated late Q3 2021). + +If you do get your app running on the migration build, you **can** ship it to production before the migration is complete. Although there is a small performance/size overhead, it should not noticeably affect production UX. You may have to do so when you have dependencies that rely on Vue 2 behavior, and cannot be upgraded/replaced. + +The migration build will be provided starting with 3.1, and will continue to be published alongside the 3.2 release line. We do plan to eventually stop publishing the migration build in a future minor version (no earlier than EOY 2021), so you should still aim to switch to the standard build before then. + +## Upgrade Workflow + +The following workflow walks through the steps of migrating an actual Vue 2 app (Vue HackerNews 2.0) to Vue 3. The full commits can be found [here](https://github.com/vuejs/vue-hackernews-2.0/compare/migration). Note that the actual steps required for your project may vary, and these steps should be treated as general guidance rather than strict instructions. + +### Preparations + +- If you are still using the [deprecated named / scoped slot syntax](https://vuejs.org/v2/guide/components-slots.html#Deprecated-Syntax), update it to the latest syntax first (which is already supported in 2.6). + +### Installation + +1. Upgrade tooling if applicable. + + - If using custom webpack setup: Upgrade `vue-loader` to `^16.0.0`. + - If using `vue-cli`: upgrade to the latest `@vue/cli-service` with `vue upgrade` + - (Alternative) migrate to [Vite](https://vitejs.dev/) + [vite-plugin-vue2](https://github.com/underfin/vite-plugin-vue2). [[Example commit](https://github.com/vuejs/vue-hackernews-2.0/commit/565b948919eb58f22a32afca7e321b490cb3b074)] + +2. In `package.json`, update `vue` to 3.1, install `@vue/compat` of the same version, and replace `vue-template-compiler` (if present) with `@vue/compiler-sfc`: + + ```diff + "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" + } + ``` + + [Example commit](https://github.com/vuejs/vue-hackernews-2.0/commit/14f6f1879b43f8610add60342661bf915f5c4b20) + +3. In the build setup, alias `vue` to `@vue/compat` and enable compat mode via Vue compiler options. + + **Example Configs** + +
+ vue-cli + + ```js + // 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 + } + } + } + }) + } + ``` + +
+ +
+ Plain webpack + + ```js + // webpack.config.js + module.exports = { + resolve: { + alias: { + vue: '@vue/compat' + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + compilerOptions: { + compatConfig: { + MODE: 2 + } + } + } + } + ] + } + } + ``` + +
+ +
+ Vite + + ```js + // vite.config.js + export default { + resolve: { + alias: { + vue: '@vue/compat' + } + }, + plugins: [ + vue({ + template: { + compilerOptions: { + compatConfig: { + MODE: 2 + } + } + } + }) + ] + } + ``` + +
+ +4. At this point, your application may encounter some compile-time errors / warnings (e.g. use of filters). Fix them first. If all compiler warnings are gone, you can also set the compiler to Vue 3 mode. + + [Example commit](https://github.com/vuejs/vue-hackernews-2.0/commit/b05d9555f6e115dea7016d7e5a1a80e8f825be52) + +5. After fixing the errors, the app should be able to run if it is not subject to the [limitations](#known-limitations) mentioned above. + + You will likely see a LOT of warnings from both the command line and the browser console. Here are some general tips: + + - You can filter for specific warnings in the browser console. It's a good idea to use the filter and focus on fixing one item at a time. You can also use negated filters like `-GLOBAL_MOUNT`. + + - You can suppress specific deprecations via [compat configuration](#compat-configuration). + + - Some warnings may be caused by a dependency that you use (e.g. `vue-router`). You can check this from the warning's component trace or stack trace (expanded on click). Focus on fixing the warnings that originate from your own source code first. + + - If you are using `vue-router`, note `` and `` will not work with `` until you upgrade to `vue-router` v4. + +6. Update [`` class names](/guide/migration/transition.html). This is the only feature that does not have a runtime warning. You can do a project-wide search for `.*-enter` and `.*-leave` CSS class names. + + [Example commit](https://github.com/vuejs/vue-hackernews-2.0/commit/d300103ba622ae26ac26a82cd688e0f70b6c1d8f) + +7. Update app entry to use [new global mounting API](/guide/migration/global-api.html#a-new-global-api-createapp). + + [Example commit](https://github.com/vuejs/vue-hackernews-2.0/commit/a6e0c9ac7b1f4131908a4b1e43641f608593f714) + +8. [Upgrade `vuex` to v4](https://next.vuex.vuejs.org/guide/migrating-to-4-0-from-3-x.html). + + [Example commit](https://github.com/vuejs/vue-hackernews-2.0/commit/5bfd4c61ee50f358cd5daebaa584f2c3f91e0205) + +9. [Upgrade `vue-router` to v4](https://next.router.vuejs.org/guide/migration/index.html). If you also use `vuex-router-sync`, you can replace it with a store getter. + + After the upgrade, to use `` and `` with `` requires using the new [scoped-slot based syntax](https://next.router.vuejs.org/guide/migration/index.html#router-view-keep-alive-and-transition). + + [Example commit](https://github.com/vuejs/vue-hackernews-2.0/commit/758961e73ac4089890079d4ce14996741cf9344b) + +10. Pick off individual warnings. Note some features have conflicting behavior between Vue 2 and Vue 3 - for example, the render function API, or the functional component vs. async component change. To migrate to Vue 3 API without affecting the rest of the application, you can opt-in to Vue 3 behavior on a per-component basis with the [`compatConfig` option](#per-component-config). + + [Example commit](https://github.com/vuejs/vue-hackernews-2.0/commit/d0c7d3ae789be71b8fd56ce79cb4cb1f921f893b) + +11. When all warnings are fixed, you can remove the migration build and switch to Vue 3 proper. Note you may not be able to do so if you still have dependencies that rely on Vue 2 behavior. + + [Example commit](https://github.com/vuejs/vue-hackernews-2.0/commit/9beb45490bc5f938c9e87b4ac1357cfb799565bd) + +## Compat Configuration + +### Global Config + +Compat features can be disabled individually: + +```js +import { configureCompat } from 'vue' + +// disable compat for certain features +configureCompat({ + FEATURE_ID_A: false, + FEATURE_ID_B: false +}) +``` + +Alternatively, the entire application can default to Vue 3 behavior, with only certain compat features enabled: + +```js +import { configureCompat } from 'vue' + +// default everything to Vue 3 behavior, and only enable compat +// for certain features +configureCompat({ + MODE: 3, + FEATURE_ID_A: true, + FEATURE_ID_B: true +}) +``` + +### Per-Component Config + +A component can use the `compatConfig` option, which expects the same options as the global `configureCompat` method: + +```js +export default { + compatConfig: { + MODE: 3, // opt-in to Vue 3 behavior for this component only + FEATURE_ID_A: true // features can also be toggled at component level + } + // ... +} +``` + +### Compiler-specific Config + +Features that start with `COMPILER_` are compiler-specific: if you are using the full build (with in-browser compiler), they can be configured at runtime. However if using a build setup, they must be configured via the `compilerOptions` in the build config instead (see example configs above). + +## Feature Reference + +### Compatibility Types + +- ✔ Fully compatible +- ◐ Partially Compatible with caveats +- ⨂ Incompatible (warning only) +- ⭘ Compat only (no warning) + +### Incompatible + +> Should be fixed upfront or will likely lead to errors + +| ID | Type | Description | Docs | +| ------------------------------------- | ---- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| GLOBAL_MOUNT_CONTAINER | ⨂ | Mounted application does not replace the element it's mounted to | [link](/guide/migration/mount-changes.html) | +| CONFIG_DEVTOOLS | ⨂ | production devtools is now a build-time flag | [link](https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags) | +| COMPILER_V_IF_V_FOR_PRECEDENCE | ⨂ | `v-if` and `v-for` precedence when used on the same element has changed | [link](/guide/migration/v-if-v-for.html) | +| COMPILER_V_IF_SAME_KEY | ⨂ | `v-if` branches can no longer have the same key | [link](/guide/migration/key-attribute.html#on-conditional-branches) | +| COMPILER_V_FOR_TEMPLATE_KEY_PLACEMENT | ⨂ | `