Global API

    Similarly, this is how a global directive is declared:

    1. Vue.directive('focus', {
    2. inserted: el => el.focus()
    3. })

    While this approach is convenient, it leads to a couple of problems. Technically, Vue 2 doesn’t have a 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. As a result:

    • 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 like Vue.use and Vue.mixin don’t even have a way to revert their effects. This makes tests involving plugins particularly tricky. In fact, vue-test-utils has to implement a special API createLocalVue to deal with this:

      1. import { createLocalVue, mount } from '@vue/test-utils'
      2. // create an extended `Vue` constructor
      3. const localVue = createLocalVue()
      4. // install a plugin “globally” on the “local” Vue constructor
      5. localVue.use(MyPlugin)
      6. // pass the `localVue` to the mount options
      7. mount(Component, { localVue })
    • Global configuration makes it difficult to share the same copy of Vue between multiple “apps” on the same page, but with different global configurations.

      1. // this affects both root instances
      2. Vue.mixin({
      3. /* ... */
      4. })
      5. const app1 = new Vue({ el: '#app-1' })

    To avoid these problems, in Vue 3 we introduce…

    Calling createApp returns an app instance, a new concept in Vue 3.

    All other global APIs that do not globally mutate behavior are now named exports, as documented in Global API Treeshaking.

    In Vue 3.x, 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 approach:

    1. // before
    2. Vue.config.ignoredElements = ['my-el', /^ion-/]
    3. // after
    4. 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 .

    • 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.
    1. var inBrowser = typeof window !== 'undefined'
    2. /* … */
    3. if (inBrowser && window.Vue) {
    4. window.Vue.use(VueRouter)
    5. }

    As the use global API is no longer available in Vue 3, this method will cease to work and calling Vue.use() will now trigger a warning. Instead, the end-user will now have to explicitly specify using the plugin on the app instance:

    1. const app = createApp(MyApp)
    2. app.use(VueRouter)

    After being initialized with createApp(VueInstance), the app instance app can be used to mount a Vue root instance with app.mount(domTarget):

    With all these changes, the component and directive we have at the beginning of the guide will be rewritten into something like this:

    1. const app = createApp(MyApp)
    2. app.component('button-counter', {
    3. data: () => ({
    4. count: 0
    5. }),
    6. template: '<button @click="count++">Clicked {{ count }} times.</button>'
    7. })
    8. app.directive('focus', {
    9. inserted: el => el.focus()
    10. })
    11. // component tree, will have the same “button-counter” component
    12. // and “focus” directive without polluting the global environment
    13. app.mount('#app')

    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:

    1. // in the entry
    2. app.provide({
    3. [ThemeSymbol]: theme
    4. })
    5. // in a child component
    6. export default {
    7. inject: {
    8. theme: {
    9. from: ThemeSymbol
    10. }
    11. },
    12. template: `<div :style="{ color: theme.textColor }" />`
    13. }

    One way to share configurations e.g. components or directives among apps is to create a factory function, like this:

    1. import { createApp } from 'vue'
    2. import Foo from './Foo.vue'
    3. import Bar from './Bar.vue'
    4. const createMyApp = (VueInstance) => {
    5. const app = createApp(VueInstance)
    6. app.directive('focus' /* ... */)
    7. return app
    8. }

    Now the focus directive will be available in both Foo and Bar instances and their descendants.