随着2023年末vue官方宣布vue2日后不在更新,vue3将成为日后开发框架的新首选项。特此记录下vue2与vue3中的语法,常用插件等使用差异
一,api及语法
1,双向绑定原理
Vue2使用Object.defineProperty 为每个属性创建getter和setter,通过getter和setter来捕获操作以实现响应式更新; 很多情况下,属性的新增和删除拦截不到(比如数组的长度变化)
Vue3 通过Proxy实现,proxy是ES6新特性,提供了更多的拦截操作;能监听到属性的删除和新增
v2 | 使用object.defineProperty来劫持数据的setter和getter方法,对象改变需要借助api去深度监听 |
v3 | Proxy来实现数据劫持,删除了一些api($on,$once,$off) fiter等,优化了Block tree,solt,diff 算法等 |
Vue2在data中定义的数据就会自动遍历绑定Object.defineProperty以实现响应式;Vue3中要用ref包装,通过返回值的 .value
属性获取响应式的值 ,修改也需要对 .value进行修改
(注意在js中要.value,在模板html中解析时会自动添加.value,不需要添加)
<template> <div> <!-- 不需要.value --> {{ testOne }} {{ testTwo.directionD}} </div> </template> <script setup> const testOne= ref(0) const testTwo= ref({ directionD: '', directionA: '', arr: [] }) const dataThree= ref({}) // 使用需要.value console.log(testOne.value); console.log(dataThree.value); console.log(testTwo.value.directionD); console.log(testTwo.value.directionD); </script>
复制
除了ref()之外,还有reative也能实现响应式(reative不加.value)
二者用法差异不在此赘述,可见大佬文章:http://t.csdnimg.cn/wpu6r
2,vue3支持碎片化
v2中只能存在一个根节点,v3中可以保持如下多个根节点,一定程度上减少了标签的层级
注意:下列情况若子组件中存在多个根节点, 则使用组件时不能使用 v-show(无法对子组件根节点添加display)
解决: 1, 可用v-if代替(可能存在首次渲染失效) 2,只保留一个根节点
<template> <div class="container"> <!-- 此时v-show无效 --> <son v-show="false"></son> </div> </template> -----son 组件 <template> <div class="son_container"> 1 </div> <div class="son_container2"> 2 </div> <div class="son_container3"> 3 </div> </template>
复制
3,生命周期
Vue2 | Vue3 | |
beforeCreate() | setup() | 组件开始创建数据实例之前 |
created() | setup() | 组件实例完成 |
beforeMount() | onBeforeAMount() | DOM挂载之前 |
mounted() | onMounted() | DOM挂载完成 |
beforeUpdate() | onBeforeUpdate() | 组件更新之前 |
undated() | onUpdated() | 组件更新之后 |
beforeDestroy() | onBeforeunmount() | 组件销毁之前 |
destroyed() | onUnmounted() | 组件销毁之后 |
大部分生命周期在vue2的周期前加 on 即可;vue3没有beforeCreate 和 created两个周期,那想在这两个周期中进行逻辑处理怎么办呢? setup中写好了调用即可👇
4, 去除this
Vue3中没有this, 使用this报错 需要组件内的某个方法直接使用即可(注意使用的数据必须在调用前定义)
5,组件传值props和emit
Vue2中是 props和 this.$emit Vue3中则是[defineEmits defineProps] props emit;
需要注意的是 Vue2中传值给子组件可以的属性可以直接使用,Vue3中子组件接收的值在props对象中,所以在ts/js中使用需要 props.name
emit触发的事件,需要defineProps声明接收数据,defineEmits 声明以明确事件定义
// 父组件中使用子组件 <son ref="bottomContract" @transferData="transferData" @reloadEcharts="reloadEcharts" :dataOne="tableTime" :dataTwo="echartsColumnData" :dataThree="dataThree" /> // 子组件 <script lang="ts" setup> //子组件中接收 const props = defineProps({ dataOne: { default: () => 0 }, dataThree: { type: Number, default: () => 0 }, dataTwo: { default: () => [] } }) // 也可以数组形式 // const emit = defineEmits(["dataFour", "dataFive", "dataSix"]) // 子组件中使用 const test = () => { console.log(props.dataOne) } // defineEmits明确事件定义 const emit = defineEmits(["transferData", "reloadEcharts"]) const testTwo = ()=> { emit("transferData", 'value') emit("transferData", 'value') } </script>
复制
6,watch和computed
watch
watch,computed与vue2用法差别不大,写法改变了; vue2是将要监听的值放在watch:{ }中

Vue3,监听watch第一个参数是直接传入要监听的对象 ;深度监听复杂对象 {deep: true}
const demo = reactive({ name: '前端', nickName: '1', Yiqian: { name: '', nickName: '' } }) // 深度监听复杂对象 {deep: true} watch(demo, (newValue, oldValue) => { console.log('watch 已触发', newValue) }, {deep: true}) // 也可以只监听其中的某个属性 watch(() => ({ ...demo }), (newValue, oldValue) => { console.log('watch 已触发', newValue) })
复制
监听一个属性就要用一个watch,是不是不太妙? 那当然也可以组合到一起,此时的第一个参数是一个数组,第二参数箭头函数的参数也是数组的形式,按照数组下标对应监听值
watch(() => [demo.name, demo.nickname], (newValue, oldValue) => { console.log(newValue); // 此时newValue是数组,按照数据下标获取对应监听属性值 console.log(newValue[0]) console.log(newValue[1]) })
复制
computed
与vue2的computed配置写法基本一致
const user = ref({ testOne: 'A', testTwo: 'B' }); // 只有getter的计算属性 const fullName1 = computed(() => { return user.value.testOne+ '-' + user.value.testTwo; }) // 有getter与setter的计算属性 const fullName2 = computed({ get () { return user.value.testOne+ '-' + user.value.testTwo; }, set (value: string) { const names ='111'; user.value.testOne= names; user.value.testTwo= names; } });
复制
7,子组件实例,调用组件方法
Vue2中 子组件定义ref="name"后使用this.$refs.name 就能拿到组件name的实例;同时可以this.$refs.name.test() 的方式直接调用子组件的test()方法
Vue3中,子组件定义ref="name",需要用ref()来定义引用,将其绑定到对应子组件上;若想直接调用子组件的方法,需要在子组件中defineExpose
显示暴露出对应的方法(组件封装性),若不暴露出来则子组件实例上不会存在此方法
// -----父组件------ <template> <ChildComponent ref="timingEchartsModule" /> </template> <script setup> import { ref } from 'vue'; import ChildComponent from './ChildComponent.vue'; // 定义一个 ref 来引用子组件实例 const timingEchartsModule = ref(); // 在某个生命周期钩子中访问子组件实例 onMounted(() => { console.log(timingEchartsModule.value); timingEchartsModule.value.test() //调用子组件方法 }); </script> // ----子组件---- <script setup> const test= () => { console.log('someMethod called'); }; // defineExpose暴露方法 若不暴露,此方法不会存在组件实例上 defineExpose({ test }) </script>
复制
8,选项式api和组合式api
Vue2中 选项式的api,创建组件时需要使用各种选项 data methods watch等

Vue3组合式允许将相关的代码逻辑放在一起处理,让代码更易于理解和维护
const message = ref('Hello Vue!'); const greet = () => { alert(message.value); }; const test = computed(() => { return meaages }) watch(() => message, (newValue: any, oldValue: any) => { })
复制
9,mixins和hooks
Vue 2 中,Mixins 是一种全局特性,可以在多个组件之间共享代码。你可以创建一个 Mixin 对象,然后在组件中通过 Mixins 选项引入这个对象,从而将 Mixin 中的属性和方法合并到组件中;如果多个 Mixins 中有相同的属性或方法,可能会导致命名冲突。另外,由于 Mixins 是全局的,它们会增加组件的耦合度,使得代码难以维护
Vue3的Hooks允许你将相关的逻辑组合到一起,形成一个逻辑单元,组件内部使用的,而不是全局的,这减少了命名冲突和耦合度。
import { ref } from 'vue' export default function() { const count = ref(0); const add = () => { count.value++; } const decrement = () => { count.value--; } // 把方法和数据返回出去 return { count, add, decrement } } // 在用到的文件中引入此hook.js 文件 <script setup> //引入hooks文件 import useCount from "../hooks/useCount" // 导入 const { count, add, decrement } = useCount() </script>
复制
二,插件
Vue3中,部分相关插件的用法也存在差异
1,vue-router
基本类似,在使用时需要引入; route和router, router获取路由器实例 ; route
对象包含了当前路由的各种信息
const router = useRouter() // 此为引入router router.push({path:'name'}) router.back(); const route = useRoute() // 此为引入route console.log(route.params) console.log(route.query)
复制
2,状态管理Vuex 和 Pinia
Vuex使用 store
、state
、mutations
、actions
和 getters
的概念,结构化更严格
Pinia 更简洁和模块化,使用 defineStore
函数创建状态,避免了冗长的代码结构
详细使用步骤见大佬文章:http://t.csdnimg.cn/mgdAX
import { defineStore } from 'pinia' const useTimingInfoStore = defineStore('timingApproval', { state: () => ({ testOne: 1, testTwo: 2, }), actions: { addSchemeData(data){ this.testOne= data }, addTimingData(data){ this.testTwo= data } }, getters: { doubleCount: (state) => state.testOne* 2 } }) export default useTimingInfoStore // 组件中使用 import { useTimingInfoStore} from '@/stores/timingApproval'; const timingInfoStore = useTimingInfoStore() console.log(timingInfoStore.testOne) timingInfoStore.addSchemeData(2)
复制
码字不易,望各位未来大牛点赞支持一波~