对比
入口使用方式
首先我们先打开main.js文件,会发现它与过去的版本发生了一些变化:
//vue3.0
import { createApp } from 'vue';
import App from './App.vue'
createApp(App).mount('#app')
//vue2.0
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
常用方法
接下来我会从以下几个属性及常用的方法,将2.0和3.0进行一些对比:
- Data
- Method
- LifeCycle
- Computed
- Components
- Props,Emit
1.Data使用变化
// vue2.0 Data返回的对象
export default {
data(){
return{
}
}
},
// 取而代之是使用以下的方式去初始化数据:
<template>
<div class="hello">
123
</div>
<div>{{name.name}}</div>
</template>
import { reactive } from 'vue'
export default {
setup(){
const name = reactive({
name:'hello 番茄'
})
return {name}
}
}
tip:在新版当中setup等效于之前2.0版本当中得到beforeCreate,和created,它是在组件初始化的时候执行,甚至是比created更早执行。值得注意的是,在3.0当中如果你要想使用setup里的数据,你需要将用到值return出来,返回出来的值在模板当中都是可以使用的。
假设如果你不return出来,而直接去使用的话浏览器是会提醒你:
runtime-core.esm-bundler.js?5c40:37 [Vue warn]: Property "name" was accessed during render but is not defined on instance.
at <Anonymous>
at <Anonymous> (Root)
这个也是3.0当中需要注意的地方。细心的朋友应该已经发现,我在模板里放入2个子节点,其实这个在2.0里是不被允许的,这也是3.0的一项小的改变 reactive是3.0提供的一个数据响应的方式,它主要是对对象进行数据响应,接下来会介绍另一种数据响应的方式ref。
2.Method使用变化
<template>
<div class="hello">
<div>{{name.name}}</div>
<div>{{count}}</div>
<button @click="increamt">button</button>
</div>
</template>
<script>
import {reactive,ref} from 'vue'
export default {
setup(){
const name = reactive({
name:'王'
})
const count=ref(0)
const increamt=()=>{
count.value++
}
return {name, count, increamt}
}
}
在介绍Method的代码中,我引用了vue提供的ref新函数,它的作用是用来创建一个引用值,它主要是对String,Number,Boolean的数据响应作引用。也许有人会问,为什么不直接给count赋值,而是采用ref(0)这样的方式来创建呢,按我的理解就是,如果直接给count赋值就是等于把这个值直接抛出去了,就很难在找到它,而采用ref这种方法等于你在向外抛出去值的是同时,你还在它身上牵了一根绳子,方便去追踪它。
需要注意的时,在ref的函数中,如何你要去改变或者去引用它的值,ref的这个方法提供了一个value的返回值,对值进行操作。
3.LifeCycle(Hooks)
3.0当中的生命周期与2.0的生命周期出现了很大的不同:
- beforeCreate -> 请使用 setup()
- created -> 请使用 setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
- errorCaptured -> onErrorCaptured
import {reactive, ref, onMounted} from 'vue'
export default {
setup(){
const name = reactive({
name:'王'
})
const count=ref(0)
const increamt=()=>{
count.value++
}
onMounted(()=>{
console.log('123')
})
return {name,count,increamt}
}
4.computed
<template>
<div class="hello">
<div>{{name.name}}</div>
<div>{{count}}</div>
<div>计算属性{{computeCount}}</div>
<button @click="increamt">button</button>
</div>
</template>
<script>
import {reactive, ref, onMounted,computed} from 'vue'
export default {
setup(){
const name = reactive({
name:'王'
})
const count = ref(0)
const increamt = ()=>{
count.value++
}
const computeCount = computed(()=>count.value*10)
//使用computed记得需要引入,这也是刚接触3.0容易忘记的事情
onMounted(()=>{
console.log('123')
})
return {name, count, increamt, computeCount}
}
}
</script>
5.Props,Emit
Vue2.0 样例
- 父组件
<template>
<div id="app">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
- 子组件
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data(){
return{
}
},
method:{
handleClick(){
this.$emit('childclick','123')
}
}
}
</script>
以上是最常见的父子组件之间的调用,但是在vue3.0中就存在差异。
vue3
- 父组件
<template>
<div class="hello">
<div>123</div>
<NewComp :name="name" @childClick="parentClick"/>
</div>
</template>
<script>
import {reactive} from 'vue'
import NewComp from './newComp.vue'
export default {
components:{
NewComp
},
setup(){
const name=reactive({
name:'hello 番茄'
})
const parentClick=(e)=>{
console.log(e)
console.log('123')
}
return {name,parentClick}
}
}
</script>
- vue3 子组件
<template>
<div>
<button @click="handleClick">组件</button>
</div>
</template>
<script>
export default {
setup(props,{emit} ){
const handleClick=()=>{
emit('childClick','hello')
}
return {
props,
handleClick
}
}
}
</script>
通过上面的vue3.0父子组件之间的调用,我们不难发现:
- 父组件当中在调用子组件时,基本与2.0相同,
- 在子组件当中,要想获取到父组件传递过来的参数,我们是直接在setup()中直接获取到props值和emit事件。
这是因为setup为我们提供了props以及context这两个属性,而在context中又包含了emit等事件。
变化
vue3的变化可以总结为以下几点:
- 更小
- 更快
- 加强typescript支持
- Api一致性
- 提高可维护能力
- 开放更多底层功能
其中前三点是最主要的变化。
更小:
vue2采用面向对象编程的思想,vue3则采用函数式编程的思想。
/// vue2源码中代码是这样组织的:
function vue(){...}
vue.prototype.init = ...
/// vue3源码中是这样组织的:
//监听方法:
function watch(){...}
//渲染方法:
function render(){...}
原因:充分利用函数式编程组合大于继承的优势,采用函数式编程更利于逻辑功能的复用,webpack打包时更有利于tree-shaking,更利于代码的压缩,更利于返回值类型校验,压缩后的文件体积更小。
更快:
vue3修改了虚拟dom的算法
vue2需要diff所有的虚拟dom节点,而vue3参考了SVELTE框架的思想,先分层次-然后找不变化的层-针对变化的层进行diff,更新速度不会再受template大小的影响,而是仅由可变的内容决定。经过尤雨溪自己的测试,大概有6倍的速度提升。
加强typescript支持:
vue3的源码开始采用了ts进行编写,给开发者也提供了支持ts的开发模式。
Api一致性
vue3最开始的版本可以完美兼容vue2的api。
提高可维护能力
从源码的层面上提供了更多的可维护能力。
开放更多底层功能
把更多的底层功能开放出来,比如render、依赖收集功能,我们可以更好的进行自定义化开发,可以写更多的高阶组件。
最后我们再谈谈两者在数据双向绑定方面的区别。
数据双向绑定:
关于数据双向绑定的实现,vue2 采用了defineProperty,而vue3则采用了proxy。
优点:
使用proxy不污染源对象,会返回一个新对象,defineProperty是注入型的,会破坏源对象
使用proxy只需要监听整个源对象的属性,不需要循环使用Object.defineProperty监听对象的属性
使用proxy可以获取到对象属性的更多参数,使用defineProperty只能获取到监听属性的新值newvalue
/* vue2.0*/
var a = { b:123, c:444};
Object.defineProperty(a, 'b', {
set: function(newvalue){
console.log('i am be set')
}
})
//只能获取到newvalue这个参数
/* vue3.0 */
var a={ b:123, c:444};
var newa = new Proxy(a,{
set:function(target,key,newvalue){
console.log(target,key,newvalue)
}
})
//可以获取到target,key,newvalue三个参数
参考链接:
- https://www.jianshu.com/p/b1fb5d549fcd
- https://blog.csdn.net/weixin_39938331/article/details/111134611