Vue 2.0 发布于 8年前的 2016 年,这是 Vue 成为主流框架过程中的一个重要里程碑。Vue 团队于 2020 年 9 月 18日发布了 Vue 3.0版本,2023年12月31日宣布Vue2将停止维护,如今Vue3生态系统正在蓬勃发展,本篇文章将全面介绍Vue2和Vue3到底有什么区别,以及我们如何更高效的使用Vue3来进行项目开发。
一、Vue3的介绍
「Vue 新版本的理念成型于 2018 年末,当时 Vue 2的代码库已经有两岁半了。比起通用软件的生命周期来这好像也没那么久,但在这段时期,前端世界已经今昔非比了 在我们更新(和重写)Vue的主要版本时,主要考虑两点因素:首先是新的 JavaScript语言特性在主流浏览器中的受支持水平;其次是当前代码库中随时间推移而逐渐暴露出来的一些设计和架构问题」
Vue3的新变化
- 速度更快
- 体积减少
- 更易维护
- 更接近原生
- 更易使用
1.速度更快
Vue3相比Vue2
- 重写了虚拟Dom实现
- 编译模板的优化
- 更高效的组件初始化
- undate性能提高1.3~2倍
- SSR速度提高了2~3倍
2.体积更小
通过webpack
的tree-shaking
功能,可以将无用模块“剪辑”,仅打包需要的
能够tree-shaking,有两大好处:
1.对开发人员,能够对vue实现更多其他的功能,而不必担忧整体体积过大
2.对使用者,打包出来的包体积变小了
vue可以开发出更多其他的功能,而不必担忧vue打包出来的整体体积过多
3.更易维护
compositon Api
可与现有的Options API
一起使用
灵活的逻辑组合与复用: Vue3模块可以和其他框架搭配使用
更好的Typescript支持: Vue3是基于typescipt
编写的,可以享受到自动的类型定义提示
4.更接近原生
可以自定义渲染 API
5.更易使用
响应式 API 暴露出来
二 、Vue2和Vue3的差异
###1.双向绑定原理
Vue2使用Object.defineProperty
为每个属性创建getter
和setter
,通过getter和setter来捕获操作以实现响应式更新; 很多情况下,属性的新增和删除拦截不到(比如数组的长度变化)
Vue3 通过Proxy
实现,proxy是ES6新特性,提供了更多的拦截操作;能监听到属性的删除和新增
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({})// 使用需要.valueconsole.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中我们是通过new Vue(),
在执行beforeCreate
与created
接着问你有没有vm.$mount(el)
vue3中是先准备好所有后再执行
区别:beforeCreate与created并没有组合式API中,setup
就相当于这两个生命周期函数
setup中
beforeCreate===>Not needed*
created=======>Not needed*
beforeMount =>onBeforeMount
mounted=====>onMounted
beforeUpdate===>onBeforeUpdate
updated =======>onUpdatedupdated
beforeUnmount ==>onBeforeUnmount
unmounted =====>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()
的方式直接调用子组件的this.$refs.name.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>
10.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)
11.状态管理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)
11.webpack和vite
① vue2使用的是webpack
形式去构建项目
webpack是一开始是入口文件,然后分析路由,然后模块,最后进行打包,然后告诉你,服务器准备好了可以开始干了
②vue3使用vite
构建项目
先告诉你服务器准备完成,然后等你发送HTTP请求,然后是入口文件,Dynamic import(动态导入)code split point(代码分割)
最大的好处和区别就是为了让项目中一些代码文件多了以后去保存更新数据时更快能够看到实际效果,也就是所谓的(热更新)
12.main.js文件
vue2中我们可以使用pototype(原型)的形式去进行操作,引入的是构造函数
vue3中需要使用结构的形式进行操作,引入的是工厂函数
vue3中app组件中可以没有根标签