1、Vue3 新特性
1-1 组合式API setup
1-2 Fragment翻译为:“碎片”
Teleport 提供了一种干净的方法,允许我们控制在 DOM 中哪个父节点下渲染了 HTML,而不必求助于全局状态或将其拆分为两个组件。
让我们修改 modal-button 以使用
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}}})
2、非兼容的变更
2-1 全局API
调用 createApp 返回一个应用实例,一个 Vue 3 中的新概念。
import { createApp } from 'vue'const app = createApp({})
Vue.prototype 替换为 config.globalProperties
在 Vue 2 中, Vue.prototype 通常用于添加所有组件都能访问的 property。
在 Vue 3 中与之对应的是 config.globalProperties。这些 property 将被复制到应用中,作为实例化组件的一部分。
// 之前 - Vue 2Vue.prototype.$http = () => {}
// 之后 - Vue 3const app = createApp({})app.config.globalProperties.$http = () => {}
Vue.extend 移除
在 Vue 2.x 中,Vue.extend 曾经被用于创建一个基于 Vue 构造函数的“子类”,其参数应为一个包含组件选项的对象。在 Vue 3.x 中,我们已经没有组件构造器的概念了。应该始终使用 createApp 这个全局 API 来挂载组件:
// 之前 - Vue 2// 创建构造器const Profile = Vue.extend({template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',data() {return {firstName: 'Walter',lastName: 'White',alias: 'Heisenberg'}}})// 创建一个 Profile 的实例,并将它挂载到一个元素上new Profile().$mount('#mount-point')
// 之后 - Vue 3const Profile = {template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',data() {return {firstName: 'Walter',lastName: 'White',alias: 'Heisenberg'}}}Vue.createApp(Profile).mount('#mount-point')
组件集成
在 Vue 3 中,我们强烈建议使用 组合式 API 来替代继承与 mixin。如果因为某种原因仍然需要使用组件继承,你可以使用 extends选项 来代替 Vue.extend。
插件开发者须知
在 UMD 构建中,插件开发者使用 Vue.use 来自动安装插件是一个通用的做法。例如,官方的 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)
2-2 模板指令
组件上v-model用法已更改,以替换v-bind.sync
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" /><!-- 是以下的简写: --><ChildComponent:title="pageTitle"@update:title="pageTitle = $event":content="pageContent"@update:content="pageContent = $event"/>
和非 v-for 节点上的 key 用法已更改//当使用 <template v-for> 时如果存在使用 v-if 的子节点,则 key 应改为设置在 <template> 标签上。
<!-- Vue 2.x -->
<template v-for="item in list">
<div v-if="item.isVisible" :key="item.id">...</div>
<span v-else :key="item.id">...</span>
</template>
<!-- Vue 3.x -->
<template v-for="item in list" :key="item.id">
<div v-if="item.isVisible">...</div>
<span v-else>...</span>
</template>
两者作用于同一个元素上时,v-if 会拥有比 v-for 更高的优先级。
v-bind=”object” 现在排序敏感
<!-- 模板 -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- 结果 -->
<div id="blue"></div>
<!-- 模板 -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- 结果 -->
<div id="red"></div>
移除v-on.native修饰符
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>
2-3 组件
组件事件现在需要在 emits 选项中声明
在 Vue 2 中,你可以定义一个组件可接收的 prop,但是你无法声明它可以触发哪些事件:
现在可以通过 emits 选项来定义组件可触发的事件:
迁移策略
强烈建议使用 emits 记录每个组件所触发的所有事件。
这尤为重要,因为我们移除了.native修饰符。任何未在 emits 中声明的事件监听器都会被算入组件的 $attrs,并将默认绑定到组件的根节点上。
eg:
//对于向其父组件透传原生事件的组件来说,这会导致有两个事件被触发:
<template>
<button v-on:click="$emit('click', $event)">OK</button>
</template>
<script>
export default {
emits: [] // 不声明事件
}
</script>
//当一个父级组件拥有 click 事件的监听器时:
<my-button v-on:click="handleClick"></my-button>
该事件现在会被触发两次:
- 一次来自 $emit()。
- 另一次来自应用在根元素上的原生事件监听器。
//当使用 <template v-for> 时如果存在使用 v-if 的子节点,则 key 应改为设置在 <template> 标签上。
<!-- Vue 2.x -->
<template v-for="item in list">
<div v-if="item.isVisible" :key="item.id">...</div>
<span v-else :key="item.id">...</span>
</template>
<!-- Vue 3.x -->
<template v-for="item in list" :key="item.id">
<div v-if="item.isVisible">...</div>
<span v-else>...</span>
</template>
<!-- 模板 -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- 结果 -->
<div id="blue"></div>
<!-- 模板 -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- 结果 -->
<div id="red"></div>
因此,对于子组件中未被定义为组件触发的所有事件监听器,Vue 现在将把它们作为原生事件监听器添加到子组件的根元素中 (除非在子组件的选项中设置了 inheritAttrs: false)。
<my-component
v-on:close="handleComponentEvent"
v-on:click="handleNativeClickEvent"
/>
// MyComponent.vue
<script>
export default {
emits: ['close']
}
</script>
现在可以通过 emits 选项来定义组件可触发的事件:
强烈建议使用 emits 记录每个组件所触发的所有事件。
这尤为重要,因为我们移除了.native修饰符。任何未在 emits 中声明的事件监听器都会被算入组件的 $attrs,并将默认绑定到组件的根节点上。
eg:
//对于向其父组件透传原生事件的组件来说,这会导致有两个事件被触发:
<template>
<button v-on:click="$emit('click', $event)">OK</button>
</template>
<script>
export default {
emits: [] // 不声明事件
}
</script>
//当一个父级组件拥有 click 事件的监听器时:
<my-button v-on:click="handleClick"></my-button>
