目录
2.8、Vue.prototype 改成 config.globalProperties
3.2 、$on,$off 和 $once 实例方法已被移除,应用实例不再实现事件触发接口。
四、vue3 + view-ui-plus + js 项目 升级
一、vue3值得注意的新特性
1.1、Teleport
Teleport 提供了一种方法,允许我们控制在 DOM 中哪个父节点下呈现 HTML,而不必求助于全局状态或将其拆分为两个组件。
比如 子组件中有个模态框,他默认的父集是他的父组件dom,但是我们需要的模态框是body,这是Teleport就是个子集组件定义他的父集dom
加入Teleport 后
app.component('modal-button', {
template: `
<button @click="modalOpen = true">
Open full screen modal! (With teleport!)
</button>
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
I'm a teleported modal!
(My parent is "body")
<button @click="modalOpen = false">
Close
</button>
</div>
</div>
</teleport>
`,
data() {
return {
modalOpen: false
}
}
})
1.2、Vue 3 现在正式支持了多根节点的组件
在 2.x 中,由于不支持多根节点组件,当开发者意外创建一个时会发出警告。为了修复这个问题,许多组件被包裹在一个 <div>
中。
<template>
<div>
<header>...</header>
<main>...</main>
<footer>...</footer>
</div>
</template>
在 3.x 中,组件可以包含多个根节点!但是,这要求开发者显式定义 attribute 应该分布在哪里。
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
1.3、自定义事件 emits
可以通过 emits
选项在组件上定义已发出的事件。
当在 emits
选项中定义了原生事件 (如 click
) 时,将使用组件中的事件替代原生事件侦听器。
app.component('custom-form', {
emits: {
// 没有验证
click: null,
// 验证submit 事件
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
},
methods: {
submitForm() {
this.$emit('submit', { email, password })
}
}
})
1.4、v-model
1.4.1、 v-model参数
就变化内容而言,此部分属于高阶内容:
- 非兼容:用于自定义组件时,
v-model
prop 和事件默认名称已更改:- prop:
value
->modelValue
; - event:
input
->update:modelValue
;
- prop:
- 非兼容:
v-bind
的.sync
修饰符和组件的model
选项已移除,可用v-model
作为代替; - 新增:现在可以在同一个组件上使用多个
v-model
进行双向绑定; - 新增:现在可以自定义
v-model
修饰符。
默认情况下,组件上的 v-model
使用 modelValue
作为 prop 和 update:modelValue
作为事件。我们可以通过向 v-model
传递参数来修改这些名称:
<my-component v-model:title="bookTitle"></my-component>
在本例中,子组件将需要一个 title
prop 并发出 update:title
要同步的事件:
app.component('my-component', {
props: {
foo: String
},
emits: ['update:title'],
template: `
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)">
`
})
1.4.2、多个 v-model
绑定
通过利用以特定 prop 和事件为目标的能力,我们现在可以在单个组件实例上创建多个 v-model 绑定。
每个 v-model 将同步到不同的 prop,而不需要在组件中添加额外的选项:
<user-name
v-model:first-name="firstName"
v-model:last-name="lastName"
></user-name>
app.component('user-name', {
props: {
firstName: String,
lastName: String
},
emits: ['update:firstName', 'update:lastName'],
template: `
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)">
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)">
`
})
1.4.3、处理 v-model
修饰符
在 2.x 中,我们对组件 v-model
上的 .trim
等修饰符提供了硬编码支持。但是,如果组件可以支持自定义修饰符,则会更有用。在 3.x 中,添加到组件 v-model
的修饰符将通过 modelModifiers
prop 提供给组件:
当我们学习表单输入绑定时,我们看到 v-model
有内置修饰符——.trim
、.number
和 .lazy
。但是,在某些情况下,你可能还需要添加自己的自定义修饰符。
例如 自定义修饰符 capitalize
,它将 v-model
绑定提供的字符串的第一个字母大写。
<my-component v-model.capitalize="myText"></my-component>
1.5 v-for 中的 Ref 数组
在 Vue 2 中,在 v-for
里使用的 ref
attribute 会用 ref 数组填充相应的 $refs
property。当存在嵌套的 v-for
时,这种行为会变得不明确且效率低下。
在 Vue 3 中,这样的用法将不再在 $ref
中自动创建数组。要从单个绑定获取多个 ref,请将 ref
绑定到一个更灵活的函数上 (这是一个新特性):
<div v-for="item in list" :ref="setItemRef"></div>
export default {
data() {
return {
itemRefs: []
}
},
methods: {
setItemRef(el) {
this.itemRefs.push(el)
}
},
beforeUpdate() {
this.itemRefs = []
},
updated() {
console.log(this.itemRefs)
}
}
1.6 异步组件
以前,异步组件是通过将组件定义为返回 Promise 的函数来创建的,例如:
const asyncPage = () => import('./NextPage.vue')
或者,对于带有选项的更高阶的组件语法:
const asyncPage = {
component: () => import('./NextPage.vue'),
delay: 200,
timeout: 3000,
error: ErrorComponent,
loading: LoadingComponent
}
vue3.语法:
现在,在 Vue 3 中,由于函数式组件被定义为纯函数,因此异步组件的定义需要通过将其包装在新的 defineAsyncComponent
助手方法中来显式地定义:
import { defineAsyncComponent } from 'vue'
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'
// 不带选项的异步组件
const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))
// 带选项的异步组件
const asyncPageWithOptions = defineAsyncComponent({
loader: () => import('./NextPage.vue'),
delay: 200,
timeout: 3000,
errorComponent: ErrorComponent,
loadingComponent: LoadingComponent
})
对 2.x 所做的另一个更改是,component
选项现在被重命名为 loader
,以便准确地传达不能直接提供组件定义的信息。
此外,与 2.x 不同,loader 函数不再接收 resolve
和 reject
参数,且必须始终返回 Promise
// 2.x 版本
const oldAsyncComponent = (resolve, reject) => {
/* ... */
}
// 3.x 版本
const asyncComponent = defineAsyncComponent(
() =>
new Promise((resolve, reject) => {
/* ... */
})
)
二、非兼容的变更
2.1、 Vue 2 没有“app”的概念,
我们定义的应用只是通过 new Vue()
创建的根 Vue 实例。从同一个 Vue 构造函数创建的每个根实例共享相同的全局配置
new Vue() 改成 createApp()
应用实例暴露当前全局 API 的子集,经验法则是,任何全局改变 Vue 行为的 API 现在都会移动到应用实例上,以下是当前全局 API 及其相应实例 API 的表:
2.x 全局 API | 3.x 实例 API (app ) |
---|---|
Vue.config | app.config |
Vue.config.productionTip | removed (见下方) |
Vue.config.ignoredElements | app.config.isCustomElement (见下方) |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use (见下方) |
Vue.prototype | app.config.globalProperties (见下方) |
所有其他不全局改变行为的全局 API 现在被命名为 exports
在 3.0 中,元素是否是组件的检查已转移到模板编译阶段,因此只有在使用运行时编译器时才考虑此配置选项。如果你使用的是 runtime-only 版本 isCustomElement
必须通过 @vue/compiler-dom
在构建步骤替换——比如,通过 compilerOptions option in vue-loader (opens new window)。
- 如果
config.isCustomElement
当使用仅运行时构建时时,将发出警告,指示用户在生成设置中传递该选项; - 这将是 Vue CLI 配置中新的顶层选项。
2.2、attribute 强制行为
这是一个低级的内部 API 更改,不会影响大多数开发人员。
- 删除枚举 attribute 的内部概念,并将这些 attribute 视为普通的非布尔 attribute
- 重大改变:如果值为布尔值,则不再删除 attribute
false
。相反,它被设置为 attr=“false”。移除 attribute,使用null
或者undefined
。
下表描述了 Vue2.0 如何使用普通非布尔 attribute 强制“枚举 attribute”:
绑定表达式 | foo 正常 |
draggable 枚举 |
---|---|---|
:attr="null" |
/ | draggable="false" |
:attr="undefined" |
/ | / |
:attr="true" |
foo="true" |
draggable="true" |
:attr="false" |
/ | draggable="false" |
:attr="0" |
foo="0" |
draggable="true" |
attr="" |
foo="" |
draggable="true" |
attr="foo" |
foo="foo" |
draggable="true" |
attr |
foo="" |
draggable="true" |
从上表可以看出,当前实现 true
强制为 'true'
但如果 attribute 为 false
,则移除该 attribute。这也导致了不一致性,并要求用户在非常常见的用例中手动强制布尔值为字符串,例如 aria-*
attribute 像 aria-selected
,aria-hidden
,等等。
下表描述了 Vue3.0 如何使用普通非布尔
绑定表达式 | foo 正常 |
draggable 枚举 |
---|---|---|
:attr="null" |
/ | / † |
:attr="undefined" |
/ | / |
:attr="true" |
foo="true" |
draggable="true" |
:attr="false" |
foo="false" † |
draggable="false" |
:attr="0" |
foo="0" |
draggable="0" † |
attr="" |
foo="" |
draggable="" † |
attr="foo" |
foo="foo" |
draggable="foo" † |
attr |
foo="" |
draggable="" † |
†: 变更
布尔 attributes 的强制保持不变。
2.x 和 3.x 行为的比较
Attributes | v-bind value 2.x |
v-bind value 3.x |
HTML 输出 |
---|---|---|---|
2.x “枚举attribute” i.e. contenteditable , draggable and spellcheck . |
undefined , false |
undefined , null |
removed |
true , 'true' , '' , 1 , 'foo' |
true , 'true' |
"true" |
|
null , 'false' |
false , 'false' |
"false" |
|
其他非布尔attribute eg. aria-checked , tabindex , alt , etc. |
undefined , null , false |
undefined , null |
removed |
'false' |
false , 'false' |
"false" |
2.3、属性,包含,class和style
vue2中
<my-component id="my-id" class="my-class"></my-component>
<label class="my-class">
<input type="text" id="my-id" />
</label>
vue3中
<label>
<input type="text" id="my-id" class="my-class" />
</label>
所以迁移有可能样式不对
2.4、自定义指令
在 Vue 2,自定义指令是通过使用下面列出的钩子来创建的,这些钩子都是可选的
- bind - 指令绑定到元素后发生。只发生一次。
- inserted - 元素插入父 DOM 后发生。
- update - 当元素更新,但子元素尚未更新时,将调用此钩子。
- componentUpdated - 一旦组件和子级被更新,就会调用这个钩子。
- unbind - 一旦指令被移除,就会调用这个钩子。也只调用一次。
下面是一个例子:
<p v-highlight="'yellow'">高亮显示此文本亮黄色</p>
Vue.directive('highlight', {
bind(el, binding, vnode) {
el.style.background = binding.value
}
})
在这里,在这个元素的初始设置中,指令通过传递一个值来绑定样式,该值可以通过应用程序更新为不同的值。
在 Vue 3 中,我们为自定义指令创建了一个更具凝聚力的 API。正如你所看到的,它们与我们的组件生命周期方法有很大的不同,即使我们正与类似的事件钩子,我们现在把它们统一起来了:
- bind → beforeMount
- inserted → mounted
- beforeUpdate:新的!这是在元素本身更新之前调用的,很像组件生命周期钩子。
- update → 移除!有太多的相似之处要更新,所以这是多余的,请改用
updated
。 - componentUpdated → updated
- beforeUnmount:新的!与组件生命周期钩子类似,它将在卸载元素之前调用。
- unbind -> unmounted
最终 API 如下:
const MyDirective = {
beforeMount(el, binding, vnode, prevVnode) {},
mounted() {},
beforeUpdate() {}, // 新
updated() {},
beforeUnmount() {}, // 新
unmounted() {}
}
生成的 API 可以这样使用,与前面的示例相同:
<p v-highlight="'yellow'">高亮显示此文本亮黄色</p>
const app = Vue.createApp({})
app.directive('highlight', {
beforeMount(el, binding, vnode) {
el.style.background = binding.value
}
})
2.5、自定义元素交互
- 非兼容:自定义元素白名单现在在模板编译期间执行,应该通过编译器选项而不是运行时配置来配置。
- 非兼容:特定
is
prop 用法仅限于保留的<component>
标记。 - 新增:有了新的
v-is
指令来支持 2.x 用例,其中在原生元素上使用了v-is
来处理原生 HTML 解析限制。
v-is
函数像一个动态的 2.x :is
绑定——因此,要按注册名称渲染组件,其值应为 JavaScript 字符串文本:
<!-- 不正确,不会渲染任何内容 -->
<tr v-is="blog-post-row"></tr>
<!-- 正确 -->
<tr v-is="'blog-post-row'"></tr>
2.6、Data 选项
- 非兼容:
data
组件选项声明不再接收纯 JavaScriptobject
,而需要function
声明。
当合并来自 mixin 或 extend 的多个 data
返回值时,现在是浅层次合并的而不是深层次合并的(只合并根级属性)
在 2.x 中,开发者可以定义 data
选项是 object
或者是 function
。
例如:
<!-- Object 声明 -->
<script>
const app = new Vue({
data: {
apiKey: 'a1b2c3'
}
})
</script>
<!-- Function 声明 -->
<script>
const app = new Vue({
data() {
return {
apiKey: 'a1b2c3'
}
}
})
</script>
虽然这对于具有共享状态的根实例提供了一些便利,但是由于只有在根实例上才有可能,这导致了混乱。
在 3.x,data
选项已标准化为只接受返回 object
的 function
。
使用上面的示例,代码只有一个可能的实现:
<script>
import { createApp } from 'vue'
createApp({
data() {
return {
apiKey: 'a1b2c3'
}
}
}).mount('#app')
</script>
2.7、Mixin 合并行为变更
此外,当来自组件的 data()
及其 mixin 或 extends 基类被合并时,现在将浅层次执行合并:
const Mixin = {
data() {
return {
user: {
name: 'Jack',
id: 1
}
}
}
}
const CompA = {
mixins: [Mixin],
data() {
return {
user: {
id: 2
}
}
}
}
在 Vue 2.x中,生成的 $data
是:
{
user: {
id: 2,
name: 'Jack'
}
}
在 3.0 中,其结果将会是:
{
user: {
id: 2
}
}
2.8、Vue.prototype
改成 config.globalProperties
在Vue 2中,Vue。prototype通常用于添加在所有组件中都可以访问的属性。
Vue 3中的等效项是config.globalproperty。这些属性将作为实例化应用程序内组件的一部分进行复制:
// before - Vue 2
Vue.prototype.$http = () => {}
// after - Vue 3
const app = Vue.createApp({})
app.config.globalProperties.$http = () => {}
2.9 、全局 API Treeshaking
如果你曾经在 Vue 中手动操作过 DOM,你可能会遇到以下模式:
import Vue from 'vue'
Vue.nextTick(() => {
// 一些和DOM有关的东西
})
在 Vue 3 中,全局和内部 API 都经过了重构,并考虑到了 tree-shaking 的支持。因此,全局 API 现在只能作为 ES 模块构建的命名导出进行访问。例如,我们之前的片段现在应该如下所示:
import { nextTick } from 'vue'
nextTick(() => {
// 一些和DOM有关的东西
})
直接调用 Vue.nextTick()
将导致臭名昭著的 undefined is not a function
错误。
通过这一更改,如果模块绑定器支持 tree-shaking,则 Vue 应用程序中未使用的全局 api 将从最终捆绑包中消除,从而获得最佳的文件大小
Vue 2.x 中的这些全局 API 受此更改的影响:
Vue.nextTick
Vue.observable
(用Vue.reactive
替换)Vue.version
Vue.compile
(仅全构建)Vue.set
(仅兼容构建)Vue.delete
(仅兼容构建)
插件中的用法
如果你的插件依赖受影响的 Vue 2.x 全局 API,例如:
const plugin = {
install: Vue => {
Vue.nextTick(() => {
// ...
})
}
}
在 Vue 3 中,必须显式导入:
import { nextTick } from 'vue'
const plugin = {
install: app => {
nextTick(() => {
// ...
})
}
}
如果使用 webpack 这样的模块捆绑包,这可能会导致 Vue 的源代码绑定到插件中,而且通常情况下,这并不是你所期望的。防止这种情况发生的一种常见做法是配置模块绑定器以将 Vue 从最终捆绑中排除。对于 webpack,你可以使用 externals (opens new window)配置选项:
// webpack.config.js
module.exports = {
/*...*/
externals: {
vue: 'Vue'
}
}
这将告诉 webpack 将 Vue 模块视为一个外部库,而不是捆绑它。
如果你选择的模块绑定器恰好是 Rollup (opens new window),你基本上可以免费获得相同的效果,因为默认情况下,Rollup 会将绝对模块 id (在我们的例子中为 'vue'
) 作为外部依赖项,而不会将它们包含在最终的 bundle 中。但是在绑定期间,它可能会发出一个“将 vue 作为外部依赖” (opens new window)警告,可使用 external
选项抑制该警告:
// rollup.config.js
export default {
/*...*/
external: ['vue']
}
2.10、内联模板 Attribute
在 2.x 中,Vue 为子组件提供了 inline-template
attribute,以便将其内部内容用作模板,而不是将其作为分发内容。 3.x将不再支持此功能。
<my-component inline-template>
<div>
<p>它们被编译为组件自己的模板</p>
<p>不是父级所包含的内容。</p>
</div>
</my-component>
2.11、key
attribute
- 新增:对于
v-if
/v-else
/v-else-if
的各分支项key
将不再是必须的,因为现在 Vue 会自动生成唯一的key
。- 非兼容:如果你手动提供
key
,那么每个分支必须使用唯一的key
。你不能通过故意使用相同的key
来强制重用分支。
- 非兼容:如果你手动提供
- 非兼容:
<template v-for>
的key
应该设置在<template>
标签上 (而不是设置在它的子节点上)。
Vue 2.x 建议在 v-if
/v-else
/v-else-if
的分支中使用 key
。
<!-- Vue 2.x -->
<div v-if="condition" key="yes">Yes</div>
<div v-else key="no">No</div>
这个示例在 Vue 3.x 中仍能正常工作。但是我们不再建议在 v-if
/v-else
/v-else-if
的分支中继续使用 key
attribute,因为没有为条件分支提供 key
时,也会自动生成唯一的 key
。
<!-- Vue 3.x -->
<div v-if="condition">Yes</div>
<div v-else>No</div>
2.12、按键修饰符
- 非兼容:不再支持使用数字 (即键码) 作为
v-on
修饰符 - 非兼容:不再支持
config.keyCodes
在 Vue 2 中,支持 keyCodes
作为修改 v-on
方法的方法。
<!-- 键码版本 -->
<input v-on:keyup.13="submit" />
<!-- 别名版本 -->
<input v-on:keyup.enter="submit" />
此外,你可以通过全局 config.keyCodes
选项。
Vue.config.keyCodes = {
f1: 112
}
<!-- 键码版本 -->
<input v-on:keyup.112="showHelpText" />
<!-- 自定别名版本 -->
<input v-on:keyup.f1="showHelpText" />
从KeyboardEvent.keyCode has been deprecated (opens new window)开始,Vue 3 继续支持这一点就不再有意义了。因此,现在建议对任何要用作修饰符的键使用 kebab-cased (短横线) 大小写名称。
<!-- Vue 3 在 v-on 上使用 按键修饰符 -->
<input v-on:keyup.delete="confirmDelete" />
因此,这意味着 config.keyCodes
现在也已弃用,不再受支持。
2.13、在 prop 的默认函数中访问this
替代方案:
-
把组件接收到的原始 prop 作为参数传递给默认函数;
-
注入 API 可以在默认函数中使用。
import { inject } from 'vue'
export default {
props: {
theme: {
default (props) {
// `props` 是传递给组件的原始值。
// 在任何类型/默认强制转换之前
// 也可以使用 `inject` 来访问注入的 property
return inject('theme', 'default-theme')
}
}
}
}
2.14、渲染函数 API
2.14.1 Render 函数参数
在 2.x 中,e render
函数将自动接收 h
函数 (它是 createElement
的常规别名) 作为参数:
// Vue 2 渲染函数示例
export default {
render(h) {
return h('div')
}
}
在 3.x 中,h
现在是全局导入的,而不是作为参数自动传递。
// Vue 3 渲染函数示例
import { h } from 'vue'
export default {
render() {
return h('div')
}
}
2.14.2、渲染函数签名更改
在 2.x 中,render
函数自动接收诸如 h
之类的参数。
// Vue 2 渲染函数示例
export default {
render(h) {
return h('div')
}
}
在 3.x 中,由于 render
函数不再接收任何参数,它将主要用于 setup()
函数内部。这还有一个好处:可以访问作用域中声明的响应式状态和函数,以及传递给 setup()
的参数。
import { h, reactive } from 'vue'
export default {
setup(props, { slots, attrs, emit }) {
const state = reactive({
count: 0
})
function increment() {
state.count++
}
// 返回render函数
return () =>
h(
'div',
{
onClick: increment
},
state.count
)
}
}
2.14.3 VNode Props 格式化
在 2.x 中,domProps
包含 VNode props 中的嵌套列表:
// 2.x
{
class: ['button', 'is-outlined'],
style: { color: '#34495E' },
attrs: { id: 'submit' },
domProps: { innerHTML: '' },
on: { click: submitForm },
key: 'submit-button'
}
在 3.x 中,整个 VNode props 结构是扁平的,使用上面的例子,下面是它现在的样子
// 3.x 语法
{
class: ['button', 'is-outlined'],
style: { color: '#34495E' },
id: 'submit',
innerHTML: '',
onClick: submitForm,
key: 'submit-button'
}
2.15、Slot 统一
this.$slots
现在将 slots 作为函数公开- 非兼容:移除
this.$scopedSlots
2.x 当使用渲染函数时,即 h
,2.x 用于在内容节点上定义 slot
data property。
// 2.x 语法
h(LayoutComponent, [
h('div', { slot: 'header' }, this.header),
h('div', { slot: 'content' }, this.content)
])
此外,在引用作用域 slot 时,可以使用以下方法引用它们:
// 2.x 语法
this.$scopedSlots.header
在 3.x 中,插槽被定义为当前节点的子对象:
// 3.x Syntax
h(LayoutComponent, {}, {
header: () => h('div', this.header),
content: () => h('div', this.content)
})
当你需要以编程方式引用作用域 slot 时,它们现在被统一到 $slots
选项中。
// 2.x 语法
this.$scopedSlots.header
// 3.x 语法
this.$slots.header
2.16、过渡的 class 名更改
过渡类名 v-enter
修改为 v-enter-from
、过渡类名 v-leave
修改为 v-leave-from
在v2.1.8版本之前, 为过渡指令提供了两个过渡类名对应初始和激活状态。
在 v2.1.8 版本中, 引入 v-enter-to
来定义 enter 或 leave 变换之间的过渡动画插帧, 为了向下兼容, 并没有变动 v-enter
类名:
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
这样做会带来很多困惑, 类似 enter 和 leave 含义过于宽泛并且没有遵循类名钩子的命名约定。
为了更加明确易读,我们现在将这些初始状态重命名为:
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
现在,这些状态之间的区别就清晰多了。
<transition>
组件相关属性名也发生了变化:
leave-class
已经被重命名为leave-from-class
(在渲染函数或 JSX 中可以写为:leaveFromClass
)enter-class
已经被重命名为enter-from-class
(在渲染函数或 JSX 中可以写为:enterFromClass
)
2.17、v-if 与 v-for 的优先级对比
- 非兼容:两者作用于同一个元素上时,
v-if
会拥有比v-for
更高的优先级。
2.x 版本中在一个元素上同时使用 v-if
和 v-for
时,v-for
会优先作用。
3.x 版本中 v-if
总是优先于 v-for
生效
2.18、v-bind 合并行为
- 不兼容:v-bind 的绑定顺序会影响渲染结果。
在 2.x,如果一个元素同时定义了 v-bind="object"
和一个相同的单独的 property,那么这个单独的 property 总是会覆盖 object
中的绑定。
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- result -->
<div id="red"></div>
在 3.x,如果一个元素同时定义了 v-bind="object"
和一个相同的单独的 property,那么声明绑定的顺序决定了它们如何合并。换句话说,相对于假设开发者总是希望单独的 property 覆盖 object
中定义的内容,现在开发者对自己所希望的合并行为有了更好的控制。
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- result -->
<div id="blue"></div>
<!-- template -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- result -->
<div id="red"></div>
三、移除的属性
3.1、$children
$children实例属性已从Vue 3.0中删除,不再受支持。
#2.x语法
在2.x中,开发人员可以使用此命令访问当前实例的直接子组件。$children:
在3。x、 $children属性已删除,不再受支持。相反,如果需要访问子组件实例,建议使用$refs。
<template>
<div>
<img alt="Vue logo" src="/vue3js/logo.png">
<my-button>Change logo</my-button>
</div>
</template>
<script>
import MyButton from './MyButton'
export default {
components: {
MyButton
},
mounted() {
console.log(this.$children) // [VueComponent]
}
}
</script>
3.2 、$on
,$off
和 $once
实例方法已被移除,应用实例不再实现事件触发接口。
3.3、filters 过滤器 删除
filters: {
currencyUSD(value) {
return '$' + value
}
}
在 3.x 中,过滤器已删除,不再支持。相反地,我们建议用方法调用或计算属性替换它们。
使用上面的例子,这里是一个如何实现它的例子。
<template>
<h1>Bank Account Balance</h1>
<p>{
{ accountInUSD }}</p>
</template>
<script>
export default {
props: {
accountBalance: {
type: Number,
required: true
}
},
computed: {
accountInUSD() {
return '$' + this.accountBalance
}
}
}
</script>
3.4、$listeners 删除
$listeners对象已在Vue 3中删除。事件侦听器现在是$attrs的一部分:
vue2.x
<template>
<label>
<input type="text" v-bind="$attrs" v-on="$listeners" />
</label>
</template>
<script>
export default {
inheritAttrs: false
}
</script>
vue.3.x
<template>
<label>
<input type="text" v-bind="$attrs" />
</label>
</template>
<script>
export default {
inheritAttrs: false
}
</script>
3.5、v-on的.native修饰符已删除。
默认情况下,传递给带有v-on的组件的事件侦听器仅通过发出带有此的事件来触发。$emit。要将本机DOM侦听器添加到子组件的根元素,可以使用.native修饰符:
<my-component
v-on:close="handleComponentEvent"
v-on:click.native="handleNativeClickEvent"
/>
v-on的.native修饰符已删除。同时,新的emits选项允许子级定义它确实发出的事件。
因此,Vue现在将所有未定义为子级中组件发射事件的事件侦听器作为本机事件侦听器添加到子级的根元素中(除非在子级的选项中设置了inheritAttrs:false)。
<my-component
v-on:close="handleComponentEvent"
v-on:click="handleNativeClickEvent"
/>
MyComponent.vue
<script>
export default {
emits: ['close']
}
</script>