- Start Date: 2019-04-08
- Target Major Version: 3.x
- Reference Issues: N/A
- Implementation PR: N/A
Re-design app bootstrapping and global API.
-
Global APIs that globally mutate Vue's behavior are now moved to app instances created the new
createApp
method, and their effects are now scoped to that app instance only. -
Global APIs that are do not mutate Vue's behavior (e.g.
nextTick
and the APIs proposed in Advanced Reactivity API) are now named exports as specified in the Global API Treeshaking RFC.
import Vue from 'vue'
import App from './App.vue'
Vue.config.ignoredElements = [/^app-/]
Vue.use(/* ... */)
Vue.mixin(/* ... */)
Vue.component(/* ... */)
Vue.directive(/* ... */)
new Vue({
render: h => h(App)
}).$mount('#app')
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp()
app.config.ignoredElements = [/^app-/]
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)
app.mount(App, '#app')
Some of Vue's current global API and configurations permanently mutate global state. This leads to a few problems:
-
Global configuration makes it easy to accidentally pollute other test cases during testing. Users need to carefully store original global configuration and restore it after each test (e.g. resetting
Vue.config.errorHandler
). Some APIs (e.g.Vue.use
,Vue.mixin
) don't even have a way to revert their effects. This makes tests involving plugins particularly tricky.vue-test-utils
has to implement a special APIcreateLocalVue
to deal with this
-
This also makes it difficult to share the same copy of
Vue
between multiple "apps" on the same page, but with different global configurations:// this affects both root instances Vue.mixin({ /* ... */ }) const app1 = new Vue({ el: '#app-1' }) const app2 = new Vue({ el: '#app-2' })
Technically, Vue 2 doesn't have the concept of an "app". What we define as an app is simply a root Vue instance created via new Vue()
. Every root instance created from the same Vue
constructor shares the same global configuration.
In this proposal we introduce a new global API, createApp
:
import { createApp } from 'vue'
const app = createApp()
Calling createApp
returns an app instance. An app instance provides an app context. The entire component tree mounted by the app instance share the same app context, which provides the configurations that were previously "global" in Vue 2.x.
An app instance exposes a subset of the current global APIs. The rule of thumb is any APIs that globally mutate Vue's behavior are now moved to the app instance. These include:
- Global configuration
- Asset registration APIs
Vue.component
->app.component
Vue.directive
->app.directive
- Behavior Extension APIs
Vue.mixin
->app.mixin
Vue.use
->app.use
All other global APIs that do not globally mutate behavior are now named exports as proposed in Global API Treeshaking.
The app instance can mount a root component with the mount
method. It works similarly to the 2.x vm.$mount()
method and returns the mounted root component instance:
const rootInstance = app.mount(App, '#app')
The mount
method can also accept props to be passed to the root component via the third argument:
app.mount(App, '#app', {
// props to be passed to root component
})
An app instance can also provide dependencies that can be injected by any component inside the app:
// in the entry
app.provide({
[ThemeSymbol]: theme
})
// in a child component
export default {
inject: {
theme: {
from: ThemeSymbol
}
},
template: `<div :style="{ color: theme.textColor }" />`
}
This is similar to using the provide
option in a 2.x root instance.
In 3.0, the "use production build" tip will only show up when using the "dev + full build" (the build that includes the runtime compiler and has warnings).
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.
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 version:
// before
Vue.config.ignoredElements = ['my-el', /^ion-/]
// after
const app = Vue.createApp()
app.config.isCustomElement = tag => tag.startsWith('ion-')
Important: in 3.0, the check of whether an element is a component or not has been moved to the template compilation phase, therefore this config option is only respected when using the runtime compiler. If you are using the runtime-only build, isCustomElement
must be passed to @vue/compiler-dom
in the build setup instead - for example, via the compilerOptions
option in vue-loader
.
-
If
config.isCustomElement
is assigned to when using a runtime-only build, a warning will be emitted instructing the user to pass the option in the build setup instead; -
This will be a new top-level option in the Vue CLI config.
Many Vue 2.x libraries and plugins offer auto installation in their UMD builds, for example vue-router
:
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vue-router"></script>
Auto installation relies on calling Vue.use
which is no longer available. This should be a relatively easy migration, and we can expose a stub for Vue.use
that emits a warning instead.
N/A
- The transformation is straightforward (as seen in the basic example).
- Moved methods can be replaced with stubs that emit warnings to guide migration.
- A codemod can also be provided.
- For
config.ingoredElements
, a compat shim can be easily provided.