vue3中的其他变化
其他小改变
destroyed
生命周期选项被重命名为unmounted
beforeDestroy
生命周期选项被重命名为beforeUnmount
default
prop 工厂函数不再可以访问this
上下文- 自定义指令的 API 已更改为与组件生命周期一致,且
binding.expression
已移除 data
选项应始终被声明为一个函数- 来自 mixin 的
data
选项现在为浅合并 - Attribute 强制策略已更改
- 一些过渡的 class 被重命名
<TransitionGroup>
不再默认渲染包裹元素- 当侦听一个数组时,只有当数组被替换时,回调才会触发,如果需要在变更时触发,则必须指定
deep
选项 - 没有特殊指令的标记 (
v-if/else-if/else
、v-for
或v-slot
) 的<template>
现在被视为普通元素,并将渲染为原生的<template>
元素,而不是渲染其内部内容。 - 已挂载的应用不会取代它所挂载的元素
- 生命周期的
hook:
事件前缀改为vnode-
被移除的 API
keyCode
作为v-on
修饰符的支持- $on、$off 和 $once 实例方法
- 过滤器 (filter)
- 内联模板 attribute
$children
实例 propertypropsData
选项$destroy
实例方法。用户不应再手动管理单个 Vue 组件的生命周期。- 全局函数
set
和delete
以及实例方法$set
和$delete
。基于代理的变化检测已经不再需要它们了。
在prop的默认函数中访问this
vue3中,生成prop默认值的工厂函数不再能访问this
替代方案:
- 把组件接收到的原始 prop 作为参数传递给默认函数;
- inject API 可以在默认函数中使用
案例:
app.vue
<template> <div> <defaults :parentMsg="parentMsg"></defaults> </div> </template> <script> import defaults from "./components/default.vue" export default { components:{ defaults }, data(){ return { parentMsg:'父组件数据' } } } </script>
default.vue
<template> <div> props默认值的工厂函数 <br/> {{parentMsg}} </div> </template> <script> export default { props:{ parentMsg:{ default:'默认值' } } } </script>
此时正常加载数据
此时我们不接受父组件传来的数据时,默认显示的是默认值
<template> <div> <defaults ></defaults> </div> </template>
可以使用工厂函数返回一个值
<template> <div> props默认值的工厂函数 <br/> {{parentMsg}} </div> </template> <script> export default { props:{ parentMsg:{ default(){ return "工厂函数的值" } } } } </script>
prop默认值的工厂函数不再能访问this
我们可以看一下输出的this是什么
props:{ parentMsg:{ default(){ console.log(this,'this'); return "工厂函数的值" } } }
把组件接收到的原始 prop 作为参数传递给默认函数,这个prop是传递给组件的原始值
我们输出prop看一下
console.log(prop,'prop');
inject API 可以在默认函数中使用
在任何类型/默认强制转换之前,也可以使用inject来访问注入的property
app.vue
<template>
<div>
<defaults ></defaults>
</div>
</template>
<script>
import defaults from "./components/default.vue"
import {provide} from "vue"
export default {
components:{
defaults
},
data(){
return {
parentMsg:'父组件数据'
}
},
setup(){
// 第一个注入属性的名字,第二个是对应的值
provide('theme',"provideTheme")
}
}
</script>
default.vue
<template>
<div>
props默认值的工厂函数
<br/>
{{parentMsg}}
</div>
</template>
<script>
import {inject} from 'vue'
export default {
props:{
parentMsg:{
default(prop){
console.log(prop,'prop');
return inject('theme','defaultTheme')
}
}
}
}
</script>
自定义指令 API 已更改为与组件生命周期一致
vue3中指令api和组件的api保持一致,具体的表现有:
- created - 新增!在元素的 attribute 或事件监听器被应用之前调用。
- bind → beforeMount
- inserted → mounted
- beforeUpdate:新增!在元素本身被更新之前调用,与组件的生命周期钩子十分相似。
- update → 移除!该钩子与
updated
有太多相似之处,因此它是多余的。请改用updated
。 - componentUpdated → updated
- beforeUnmount:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用。
- unbind -> unmounted
案例:
main.js
我们新建一个自定义指令
import { createApp,createRenderer} from "vue"; import App from "./App.vue"; createApp(App) .directive('light',{ beforeMount(el,binding,vnode){ el.style.color=binding.value } }) .mount("#app");
app.vue
<h1 v-light="'red'">自定义指令内容变色</h1>
Data选项始终被声明为一个函数
- 非兼容:组件选项
data
的声明不再接收纯 JavaScriptobject
,而是接收一个function
。 - 非兼容:当合并来自 mixin 或 extend 的多个
data
返回值时,合并操作现在是浅层次的而非深层次的 (只合并根级属性)
vue3中data选项统一为函数形式,返回响应式数据
在 2.x 中,可以通过 object
或者是 function
定义 data
选项。
<!-- 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>
Mixin 合并行为变更
将共享数据提取到外部对象并将其用作 data 中的 property
重写对共享数据的引用以指向新的共享对象
当来自组件的 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 } }
Transition类名变更
vue2中
vue3中,过渡类名 v-enter 修改为 v-enter-from、过渡类名 v-leave 修改为 v-leave-from
transition.vue
<template> <div id="demo"> <button @click="show=!show">change</button> <Transition name="fade"> <div class="box" v-if="show"></div> </Transition> </div> </template> <script> export default { data(){ return { show:true } } } </script> <style> .box{ width: 200px; height: 200px; background-color: red; } .fade-enter-active, .fade-leave-active{ transition:all 1s ; } .fade-enter-from, .fade-leave-to{ opacity: 0; } </style>
app.vue
<template> <div> <transitions></transitions> </div> </template> <script> import transition from "./components/transition.vue" import {provide} from "vue" export default { components:{ transitions } </script>
watch变更
vue3中,当侦听一个数组时,只有当数组被替换时才会触发回调。如果你需要在数组改变时触发回调,必须指定 deep 选项
watch: { bookList: { handler(val, oldVal) { console.log('book list changed') }, deep: true }, }
watch.vue
<template> <div> <button @click="change">替换</button> <ul> <li v-for="(item,index) in list" :key="item.id"> {{item.name}} <button @click="del(index)"> 删除</button> </li> </ul> </div> </template> <script> export default { data(){ return { list:[ {id:1,name:"js"}, {id:2,name:"html"}, {id:3,name:"vue"}, {id:4,name:"css"} ] } }, watch:{ booklist:{ handler(val,oldval){ console.log("booklist changed") }, // 数组改变时触发回调,必须指定deep选项 deep:true } }, methods:{ del(index){ this.list.splice(index,1) }, change(){ //数组替换 this.list=[ {id:1,name:"语文"}, {id:2,name:"数学"}, {id:3,name:"英语"}, {id:4,name:"体育"} ] } } } </script>
没有特殊指令的标记 (v-if/else-if/else
、v-for
或 v-slot
) 的 <template>
现在被视为普通元素,并将渲染为原生的 <template>
元素,而不是渲染其内部内容。
被挂载的应用不会替换元素
template
的应用时,被渲染的内容会替换我们要挂载的目标元素。在 Vue 3.x 中,被渲染的应用会作为子元素插入,从而替换目标元素的 innerHTML
。 在 Vue 2.x 中,我们为 new Vue()
或 $mount
传入一个 HTML 元素选择器
<body>
<div id="app">
</div>
<script>
new Vue({
el: '#app',
data() {
return {
message: 'Hello Vue!'
}
},
template: `<div id="rendered">{{ message }}</div>`
})
</script>
</body>
在渲染结果中,上面提及的 div
将会被应用所渲染的内容替换:
在 Vue 3.x 中,当我们挂载一个应用时,其渲染内容会替换我们传递给 mount
的元素的 innerHTML
:
<body> <div id="app"> </div> <script> const app = Vue.createApp({ data() { return { message: 'Hello Vue!' } }, template: ` <div id="rendered">{{ message }}</div> ` }) app.mount('#app') </script> </body>
当这个应用挂载到拥有匹配 id="app"
的 div
的页面时,结果会是:
生命周期的 hook:
事件前缀改为 vnode-
在 Vue 2 中,我们可以通过事件来监听组件生命周期中的关键阶段。这些事件名都是以 hook:
前缀开头,并跟随相应的生命周期钩子的名字。
这些事件名和相应的生命周期钩子一致,并带有 hook:
前缀:
<template> <child-component @hook:updated="onUpdated"> </template>
在 Vue 3 中,这个前缀已被更改为 vnode-
。额外地,这些事件现在也可用于 HTML 元素,和在组件上的用法一样。
事件名附带的是 vnode-
前缀:
<template> <child-component @vnode-updated="onUpdated"> </template>
或者在驼峰命名法的情况下附带前缀 vnode
:
<template> <child-component @vnodeUpdated="onUpdated"> </template>