全局 API 应用实例重大变更
Vue 2.x 有一系列全局 API 和配置,它们会全局地改变 Vue 的行为。例如,要注册一个全局组件,可以使用 Vue.component
API,如下所示
Vue.component('button-counter', {
data: () => ({
count: 0
}),
template: '<button @click="count++">Clicked {{ count }} times.</button>'
})
类似地,这是声明全局指令的方式
Vue.directive('focus', {
inserted: (el) => el.focus()
})
虽然这种方法很方便,但它会导致一些问题。从技术上讲,Vue 2 没有“应用”的概念。我们定义的应用仅仅是通过 new Vue()
创建的根 Vue 实例。从同一个 Vue 构造函数创建的每个根实例都 **共享相同的全局配置**。因此
全局配置在测试期间很容易意外地污染其他测试用例。用户需要仔细存储原始全局配置并在每次测试后恢复它(例如,重置
Vue.config.errorHandler
)。一些 API,如Vue.use
和Vue.mixin
,甚至没有办法撤销它们的效果。这使得涉及插件的测试变得特别棘手。实际上,vue-test-utils 必须实现一个特殊的 APIcreateLocalVue
来处理这个问题jsimport { 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 中的一个新概念。
import { createApp } from 'vue'
const app = createApp({})
如果您使用的是 Vue 的 CDN 构建版本,那么 createApp
通过全局 Vue
对象公开。
const { createApp } = Vue
const app = createApp({})
应用实例公开了一部分 Vue 2 全局 API。经验法则是任何全局地改变 Vue 行为的 API 现在都已移至应用实例。以下是一个 Vue 2 全局 API 及其对应的实例 API 表格
2.x 全局 API | 3.x 实例 API (app ) |
---|---|
Vue.config | app.config |
Vue.config.productionTip | 已移除 (见下文) |
Vue.config.ignoredElements | app.config.compilerOptions.isCustomElement (见下文) |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use (见下文) |
Vue.prototype | app.config.globalProperties (见下文) |
Vue.extend | 已移除 (见下文) |
所有其他不全局改变行为的全局 API 现在都是命名导出,如 全局 API 树摇 中所述。
config.productionTip
已移除
在 Vue 3.x 中,“使用生产构建”提示只会出现在使用“开发 + 全构建”(包含运行时编译器并带有警告的构建)时。
对于 ES 模块构建,由于它们与打包器一起使用,并且在大多数情况下,CLI 或样板会正确配置生产环境,因此此提示将不再显示。
config.ignoredElements
现在是 config.compilerOptions.isCustomElement
此配置选项是为了支持原生自定义元素而引入的,因此重命名更好地传达了它的作用。新选项还期望一个函数,它比旧的字符串/正则表达式方法提供了更大的灵活性
// 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.prototype
被 config.globalProperties
替换
在 Vue 2 中,Vue.prototype
通常用于添加在所有组件中都可以访问的属性。
Vue 3 中的等效项是 config.globalProperties
。这些属性将在应用程序中实例化组件时被复制。
// before - Vue 2
Vue.prototype.$http = () => {}
// after - Vue 3
const app = createApp({})
app.config.globalProperties.$http = () => {}
使用 provide
(在 下面 讨论)也应该被视为 globalProperties
的替代方案。
Vue.extend
已移除
在 Vue 2.x 中,Vue.extend
用于创建基本 Vue 构造函数的“子类”,其参数应该是包含组件选项的对象。在 Vue 3.x 中,我们不再有组件构造函数的概念。挂载组件应该始终使用 createApp
全局 API
// 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')
// 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
。
插件作者的说明
插件作者通常使用 Vue.use
在他们的 UMD 构建中自动安装插件。例如,这是官方 vue-router
插件在浏览器环境中安装自身的方式
var inBrowser = typeof window !== 'undefined'
/* … */
if (inBrowser && window.Vue) {
window.Vue.use(VueRouter)
}
由于 use
全局 API 在 Vue 3 中不再可用,因此此方法将不再有效,调用 Vue.use()
现在将触发警告。相反,最终用户现在必须在应用实例上显式指定使用该插件
const app = createApp(MyApp)
app.use(VueRouter)
挂载应用实例
在使用 createApp(/* options */)
初始化后,可以使用应用实例 app
通过 app.mount(domTarget)
挂载根组件实例
import { createApp } from 'vue'
import MyApp from './MyApp.vue'
const app = createApp(MyApp)
app.mount('#app')
有了所有这些更改,我们在指南开头提到的组件和指令将被重写成类似于以下内容
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')
提供/注入
与在 2.x 根实例中使用 provide
选项类似,Vue 3 应用实例也可以提供依赖项,这些依赖项可以被应用中任何组件注入
// 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
的替代方案。
在应用程序之间共享配置
在应用程序之间共享配置(例如组件或指令)的一种方法是创建工厂函数,如下所示
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
指令将在 Foo
和 Bar
实例及其后代中可用。