跳至内容

全局 API 应用实例
重大变更

Vue 2.x 有一系列全局 API 和配置,它们会全局地改变 Vue 的行为。例如,要注册一个全局组件,可以使用 Vue.component API,如下所示

js
Vue.component('button-counter', {
  data: () => ({
    count: 0
  }),
  template: '<button @click="count++">Clicked {{ count }} times.</button>'
})

类似地,这是声明全局指令的方式

js
Vue.directive('focus', {
  inserted: (el) => el.focus()
})

虽然这种方法很方便,但它会导致一些问题。从技术上讲,Vue 2 没有“应用”的概念。我们定义的应用仅仅是通过 new Vue() 创建的根 Vue 实例。从同一个 Vue 构造函数创建的每个根实例都 **共享相同的全局配置**。因此

  • 全局配置在测试期间很容易意外地污染其他测试用例。用户需要仔细存储原始全局配置并在每次测试后恢复它(例如,重置 Vue.config.errorHandler)。一些 API,如 Vue.useVue.mixin,甚至没有办法撤销它们的效果。这使得涉及插件的测试变得特别棘手。实际上,vue-test-utils 必须实现一个特殊的 API createLocalVue 来处理这个问题

    js
    import { createLocalVue, mount } from '@vue/test-utils'
    
    // create an extended `Vue` constructor
    const localVue = createLocalVue()
    
    // install a plugin “globally” on the “local” Vue constructor
    localVue.use(MyPlugin)
    
    // pass the `localVue` to the mount options
    mount(Component, { localVue })
  • 全局配置使得在同一页面上但具有不同全局配置的多个“应用”之间共享同一个 Vue 副本变得困难。

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

为了避免这些问题,在 Vue 3 中,我们引入了…

一个新的全局 API:createApp

调用 createApp 会返回一个应用实例,这是 Vue 3 中的一个新概念。

js
import { createApp } from 'vue'

const app = createApp({})

如果您使用的是 Vue 的 CDN 构建版本,那么 createApp 通过全局 Vue 对象公开。

js
const { createApp } = Vue

const app = createApp({})

应用实例公开了一部分 Vue 2 全局 API。经验法则是任何全局地改变 Vue 行为的 API 现在都已移至应用实例。以下是一个 Vue 2 全局 API 及其对应的实例 API 表格

2.x 全局 API3.x 实例 API (app)
Vue.configapp.config
Vue.config.productionTip已移除 (见下文)
Vue.config.ignoredElementsapp.config.compilerOptions.isCustomElement (见下文)
Vue.componentapp.component
Vue.directiveapp.directive
Vue.mixinapp.mixin
Vue.useapp.use (见下文)
Vue.prototypeapp.config.globalProperties (见下文)
Vue.extend已移除 (见下文)

所有其他不全局改变行为的全局 API 现在都是命名导出,如 全局 API 树摇 中所述。

config.productionTip 已移除

在 Vue 3.x 中,“使用生产构建”提示只会出现在使用“开发 + 全构建”(包含运行时编译器并带有警告的构建)时。

对于 ES 模块构建,由于它们与打包器一起使用,并且在大多数情况下,CLI 或样板会正确配置生产环境,因此此提示将不再显示。

迁移构建标志:CONFIG_PRODUCTION_TIP

config.ignoredElements 现在是 config.compilerOptions.isCustomElement

此配置选项是为了支持原生自定义元素而引入的,因此重命名更好地传达了它的作用。新选项还期望一个函数,它比旧的字符串/正则表达式方法提供了更大的灵活性

js
// before
Vue.config.ignoredElements = ['my-el', /^ion-/]

// after
const app = createApp({})
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ion-')

重要

在 Vue 3 中,检查元素是组件还是其他元素已移至模板编译阶段,因此此配置选项仅在使用运行时编译器时才生效。如果您使用的是运行时专用构建,则必须在构建设置中将 isCustomElement 传递给 @vue/compiler-dom - 例如,通过 vue-loader 中的 compilerOptions 选项

  • 如果在使用运行时专用构建时将 config.compilerOptions.isCustomElement 赋值,则会发出警告,指示用户在构建设置中传递此选项;
  • 这将成为 Vue CLI 配置中的一个新的顶级选项。

迁移构建标志:CONFIG_IGNORED_ELEMENTS

Vue.prototypeconfig.globalProperties 替换

在 Vue 2 中,Vue.prototype 通常用于添加在所有组件中都可以访问的属性。

Vue 3 中的等效项是 config.globalProperties。这些属性将在应用程序中实例化组件时被复制。

js
// before - Vue 2
Vue.prototype.$http = () => {}
js
// after - Vue 3
const app = createApp({})
app.config.globalProperties.$http = () => {}

使用 provide(在 下面 讨论)也应该被视为 globalProperties 的替代方案。

迁移构建标志:GLOBAL_PROTOTYPE

Vue.extend 已移除

在 Vue 2.x 中,Vue.extend 用于创建基本 Vue 构造函数的“子类”,其参数应该是包含组件选项的对象。在 Vue 3.x 中,我们不再有组件构造函数的概念。挂载组件应该始终使用 createApp 全局 API

js
// before - Vue 2

// create constructor
const Profile = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  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: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data() {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
}

Vue.createApp(Profile).mount('#mount-point')

类型推断

在 Vue 2 中,Vue.extend 也用于为组件选项提供 TypeScript 类型推断。在 Vue 3 中,defineComponent 全局 API 可以代替 Vue.extend 用于相同的目的。

请注意,虽然 defineComponent 的返回类型是一个类似构造函数的类型,但它仅用于 TSX 推断。在运行时,defineComponent 基本上是一个空操作,并将按原样返回选项对象。

组件继承

在 Vue 3 中,我们强烈建议优先考虑通过 组合 API 而不是继承和混入来进行组合。如果出于某种原因您仍然需要组件继承,可以使用 extends 选项 而不是 Vue.extend

迁移构建标志:GLOBAL_EXTEND

插件作者的说明

插件作者通常使用 Vue.use 在他们的 UMD 构建中自动安装插件。例如,这是官方 vue-router 插件在浏览器环境中安装自身的方式

js
var inBrowser = typeof window !== 'undefined'
/* … */
if (inBrowser && window.Vue) {
  window.Vue.use(VueRouter)
}

由于 use 全局 API 在 Vue 3 中不再可用,因此此方法将不再有效,调用 Vue.use() 现在将触发警告。相反,最终用户现在必须在应用实例上显式指定使用该插件

js
const app = createApp(MyApp)
app.use(VueRouter)

挂载应用实例

在使用 createApp(/* options */) 初始化后,可以使用应用实例 app 通过 app.mount(domTarget) 挂载根组件实例

js
import { createApp } from 'vue'
import MyApp from './MyApp.vue'

const app = createApp(MyApp)
app.mount('#app')

有了所有这些更改,我们在指南开头提到的组件和指令将被重写成类似于以下内容

js
const app = createApp(MyApp)

app.component('button-counter', {
  data: () => ({
    count: 0
  }),
  template: '<button @click="count++">Clicked {{ count }} times.</button>'
})

app.directive('focus', {
  mounted: (el) => el.focus()
})

// now every application instance mounted with app.mount(), along with its
// component tree, will have the same “button-counter” component
// and “focus” directive without polluting the global environment
app.mount('#app')

迁移构建标志:GLOBAL_MOUNT

提供/注入

与在 2.x 根实例中使用 provide 选项类似,Vue 3 应用实例也可以提供依赖项,这些依赖项可以被应用中任何组件注入

js
// in the entry
app.provide('guide', 'Vue 3 Guide')

// in a child component
export default {
  inject: {
    book: {
      from: 'guide'
    }
  },
  template: `<div>{{ book }}</div>`
}

使用 provide 在编写插件时特别有用,作为 globalProperties 的替代方案。

在应用程序之间共享配置

在应用程序之间共享配置(例如组件或指令)的一种方法是创建工厂函数,如下所示

js
import { createApp } from 'vue'
import Foo from './Foo.vue'
import Bar from './Bar.vue'

const createMyApp = (options) => {
  const app = createApp(options)
  app.directive('focus' /* ... */)

  return app
}

createMyApp(Foo).mount('#foo')
createMyApp(Bar).mount('#bar')

现在,focus 指令将在 FooBar 实例及其后代中可用。