Vue3对比Vue2的一些差异及特性
1. Vue3和Vue2的风格对比
Vue2一般称之为选项式(Options)API
Vue3一般称之为组合式(Composition)API
如图所示 :


2. Vue2选项式和Vue3组合式API的关系
组合式API的目的是增强,不是取代选项式API,vue3对两种API都支持
简单的场景使用选项式API更加简单方便
需要强烈支持TS的项目首选组合式API
需要大量逻辑复用的场景首选组合式API
3. Vue3对比Vue2的一些注意事项
Vue 2 中的 template 标签仅允许包含一个根元素,在对 template 进行操作时,请确保将其中的 root 元素直接地移动至 #app 位置
Vue3中没有该约束允许放置任意多个根元素。将template中的所有根元素作为appendChild的方式添加到#app中而不是将其替换为其他内容。
vue2 在 methods 中访问 data 的数据都要加 this, 在 template 中可以不用
vue3 使用 ref 声明的数据, 访问时都需要加 .value, 在 template 中可以不用
vue3中compositionAPI 的数据不在 data 中声明,而是在新钩子函数setup()中声明
在 setup 函数中要返回一个对象, 对象中的成员就可以直接在 template 中使用
4. 在 setup 函数中使用生命周期钩子函数
使用步骤
先从vue中导入以on打头的生命周期钩子函数
在setup函数中调用生命周期函数并传入回调函数
生命周期钩子函数可以调用多次
示例:
<template>
<div id="dd"></div>
</template>
<script>
// 1. 导入 onMounted 函数
// onCreated 没有
import { onMounted, onBeforeMount } from "vue";
export default {
// Vue 2 生命周期回顾:
// 初始化阶段: beforeCreate created 只执行一次
// 挂载阶段: beforeMount mounted 只执行一次
// 更新阶段: beforeUpdate updated 零次或 N 次
// 销毁阶段: beforeDestroy destroyed 只执行一次
setup() {
// let dd = document.querySelector('#dd')
// console.log(dd)
// 如何在 setup 中使用生命周期的钩子函数?
// 2. 在 setup 中调用 onMounted
// 参数: 回调函数, 在 mounted 生命周期时执行
onMounted(() => {
let dd = document.querySelector("#dd");
console.log(dd);
console.log("onMounted 了");
});
onMounted(() => {
console.log("on 又 Mounted 了");
});
onBeforeMount(() => {
console.log('onBeforeMount 了')
})
// 注意啦: vue 中没有提供初始化阶段的两个钩子函数: onBeforeCreate onCreated
// 因为 Vue3 认为这两个阶段要做的事情, 都可以在 setup 里面直接做
// console.log(onCreated)
// onCreated(() => {
// console.log('onCreated 了')
// })
},
mounted() {
console.log("mounted 生命周期钩子函数");
}
};
</script>
5. Vue2和Vue3的生命周期对比
| 选项式API下的生命周期函数使用 | 组合式API下的生命周期函数使用 |
|---|---|
beforeCreate |
不需要(直接写到setup函数中) |
created |
不需要(直接写到setup函数中) |
beforeMount |
onBeforeMount |
mounted |
onMounted |
beforeUpdate |
onBeforeUpdate |
updated |
onUpdated |
beforeDestroy Vue 3: beforeUnmount |
onBeforeUnmount |
destroyed Vue 3: unmounted |
onUnmounted |
6. 父子通信
在vue3的组合式API框架内,默认的父子组件连接方式一致。其核心理念仍然是:父子组件之间的数据传递主要依赖prop属性实现,默认情况下无状态事件参与。
实现步骤
setup函数接受两个关键输入变量:props和context。其中,props用于表示状态信息,而context则包含环境配置。
props被视为一个对象,在其内部整合了来自父组件的所有prop数据。context对象则包括了attrs、slots以及emit属性。值得注意的是,在context中定义的emit特性能够引发生态事件并实现子组件到父组件的数据交互。
示例:
Father.vue
<template>
<Son
msg="这是爸爸给你的消息"
:title="title"
:list="list"
@getValue="getValueHandler"
></Son>
</template>
<script>
import { ref } from "vue";
// 父子通信: 父传子
// 1. 父组件中给子组件传递数据, 属性绑定
// 2. 在子组件中定义 props 接收并使用
// props 特点: 单向数据流, 单向下行绑定(父亲变了儿子会同步, 但儿子改数据 老子不认)
// 如果传入的是引用数据类型, 修改对象的属性是可行的, 但是 ESLint 默认会检查并报错, 所以需要单独配置或关闭 ESLint
// 父子通信: 子传父
// 1. 父组件中给子组件自定义一个事件
// 2. 子组件在合适的时机触发事件并传递数据
import Son from "./components/Son.vue";
export default {
components: {
Son,
},
setup() {
const title = ref("这是爸爸的title");
function getValueHandler(value) {
console.log('收到儿子的数据:', value)
}
const list = ref([1, 2, 3]);
return { title, list, getValueHandler };
},
};
</script>
Son.vue
<template>
<div>
<p>这里是子组件</p>
<p>收到父组件的msg: {{ msg }}</p>
<p>收到父组件的title: {{ title }}</p>
<button @click="title = '改成儿子的title'">点我修改爸爸的title</button>
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
<button @click="list.push(4)">点我往list中加数据</button>
<hr>
<button @click="sendValue">点我向爸爸发射消息</button>
</div>
</template>
<script>
export default {
props: {
// 这里定义的是父组件要传递的数据, 必须在这里定义
msg: String,
title: String,
list: Array
},
// setup 钩子函数中有两个参数:
// 参数1: props 父组件传过来的数据都会挂载到这个对象上
// 参数2: context 上下文对象, 有一些全局的实例方法, 例如 emit slots attrs
// setup(props, context) {
setup(props, { emit }) {
// 以前使用 props 数据: this.msg
// 现在没有 this 了, 如果要在 setup 中使用 props 的数据, 则需要接受第一个参数: props
// console.log(props)
// console.log(context)
// this // 没有 this
function sendValue() {
// 在此处触发自定义事件, 携带数据给父组件
// 这里的 this 是指 setup 返回的对象, 所以没有办法调用 $emit 函数
// 故而无法在此处使用 this.$emit() 触发事件, 怎么办呢?
// console.log(this.$emit)
// 参数1: 事件名
// 参数2~n: 要传递的参数
emit('getValue', '这是儿子孝敬您的数据')
// 为什么 Vue2 的实例方法都是 $ 开头?
// 因为 Vue2 设计时所有的方法都放到原型上
// data 和 methods 的成员也会被挂载到对象本身 this.msg
// 如果原型上的成员不用 $ 开头, 很容易命名冲突, 命名冲突之后的结果
// this.$set this.$nextTick() this.$emit() this.$mount()
// Vue3 就没有这样设计了, 以上下文对象的形式暴露给用户使用, compositionAPI设计的出现, 让我们更少的依赖 data 和 methods 了
}
return {sendValue}
}
};
</script>
7. provide 和 inject
通常通过props实现组件之间数据的传递。然而,在嵌入层次较多的情况下,默认逐层向下传播会变得相对复杂。借助提供(provide)与注入(inject)功能的协同工作特性,则能够便捷地实现从顶层组件向任意层级下的组件高效传输数据。
实现步骤
顶层组件在setup方法中使用provide函数提供数据
provide('key',数据)
任何底层组件在setup方法中使用inject函数获取数据
const data = inject('key')
示例:
App.vue
<template>
<Father></Father>
<button @click="msg = '在 App 中修改的新数据'">在 App 中修改数据</button>
</template>
<script>
// 目标: 从 App 中将数据传递给 Son
// 使用 provide 和 inject 实现
// 1. 在 App 中导入 provide 函数
// 2. 在 setup 中调用 provide 函数提供数据
// 3. 在 Son 中导入 inject 函数
// 4. 在 setup 中调用 inject 函数获取数据
import Father from './components/Father.vue'
import { provide, ref } from 'vue'
export default {
components: {
Father
},
setup() {
// 参数1: 字符串, key, 提供的数据标识
// 参数2: 数据
// let msg = '这是 App 的数据' // 静态数据
const msg = ref('这是 App 的数据') // 响应式的数据
// provide('msg', msg.value) // .value 表示将数据取出来后传递给子组件, 丧失了响应式的特性, 如果希望数据也是响应式的传递, 则不能加 .value
// 如果不加 .value 表示传递对象, 加了就是获取静态数据传递过去
provide('msg', msg)
return {
msg
}
}
};
</script>
Father.vue
<template>
这是 Father 组件
<Son></Son>
</template>
<script>
import Son from './Son.vue'
export default {
components: {
Son
}
}
</script>
Son.vue
<template>
<div>
这是 Son 组件
<p>这是收到的数据: {{ msg }}</p>
</div>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
// 参数: 要获取的数据标识 key
// 返回值: 数据
let msg = inject('msg')
return {
msg
}
}
};
</script>
8. TemplateRef
在模板中使用ref,我们都很清楚,它一般有三种使用场景
ref + 普通dom标签 获取真实dom对象 this.$refs.box
ref + 组件标签 获取组件实例对象 this.$refs.form.validate()
ref + v-for 获取由dom对象(实例对象)组成的数组 (不经常使用)
实现步骤
使用ref函数传入null创建 ref对象 = > const hRef = ref(null)
在模板中,通过将ref属性设置为1生成的ref对象名称实现了与
元素之间的关联。把hRefreturn出去
使用 = > hRef.value
示例:
<template>
<!-- Vue2 的做法 -->
<!-- <p ref="cuteP">这是一个可爱的 p</p>
<Father ref="dad"></Father> -->
<!-- 3. 在需要获取对象的标签上使用 ref 绑定返回的数据 -->
<p ref="cp">这是一个可爱的 p</p>
<Father ref="father"></Father>
</template>
<script>
import { onMounted, ref } from 'vue';
// 目标: 在 setup 函数中使用 ref 获取原生 DOM 对象
import Father from './components/Father.vue'
export default {
components: {
Father
},
setup() {
// 1. 使用 ref 创建一个空的数据 用于后面产生关联
const cp = ref(null)
const father = ref(null)
// 此时无法获取, 都是 null, 因为 setup 在 beforeCreate 之前执行
console.log(cp.value)
console.log(father.value)
onMounted(() => {
// 4. 在合适的时机使用 数据.value 即可访问原生 DOM
// 在原生 DOM 挂载完毕后执行
console.log(cp.value)
console.log(father.value)
})
// 2. 将数据返回出去供 template 使用
return {
cp,
father
}
}
// mounted() {
// // Vue2 的做法: 必须在 mounted 中获取
// console.log(this.$refs.cuteP)
// console.log(this.$refs.dad)
// }
};
</script>
9. Vue3的非兼容语法
Vue3遵循Vue2的大部分语法支持其功能实现,并且也引入了一些破坏性更新需要特别需要注意
具体实现方式删除(现有实现模式不再适用,可以选择调用第三方插件来完成功能)
Event Bus
1. A Vue instance of eventBus is created via `Vue.prototype.eventBus = new Vue()
. 2\. Within a component receiving data, callthis.eventBus.on('get-msg', (msg) => {})`. 3\. Within a component sending data, invoke `this.eventBus.emit('get-msg', '传递的数据')`.
4. By default, event bus functionality is unsupported in Vue 3; third-party plugins are required for emulation.
2. 移除过滤器filter (在插值表达式中无法继续使用过滤器可用methods来代替)
filter过滤器
例如:字符串的格式化 方法 接收原字符串 返回格式化之后的字符串{{ msg | formatMsg }}
vue3直接移除了该语法 可以直接使用methods替代
{{ formatMsg('this is msg') }} // 渲染的结果是函数return值
methods:{
formatMsg(msg){
return msg + 'zs'
}
}
3. sync语法移除 (和v-model语法合并)
同步机制
配置项设置为elementUI指向Dialog,并启用visible属性以显示showFlag状态
其主要作用包括简化父级与子级之间的通信流程,并支持自定义事件的触发
同步机制与v-model实现绑定的核心
