写在前面话:
一、默认进行懒观察(lazy observation)
在 2.x 版本里,不管数据多大,都会在一开始就为其创建观察者。当数据很大时,这可能会在页面载入时造成明显的性能压力。3.x 版本,只会对「被用于渲染初始可见部分的数据」创建观察者,而且 3.x 的观察者更高效。
二、更精准的变更通知。
举例来说:2.x 版本中,使用 Vue.set 来给对象新增一个属性时,这个对象的所有 watcher 都会重新运行;3.x 版本中,只有依赖那个属性的 watcher 才会重新运行。
建立数据data
这里就是Vue2与Vue3最大的区别---Vue2使用 选项类型API对比Vue3合成型API
旧的选项型API在代码里分割了不同的属性,比如:data、computed、methods等等;新的合成型API能让我们用方法来分割,相比于旧的API使用属性来分担,这样代码会更加简便和整洁。
Vue2--这里把两个数据放在data属性中
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
}
}
Vue3--我们就需要使用一个新的setup()方法,此方法在组件初始化构造的时候触发
为了可以让开发者对反应型数据有更多的控制,我们可以直接使用到Vue3的反应API
使用一下三步来建立反应型数据;
1.从Vue引入reactive
2.使用reactive()方法来声明我们的数据为反应型数据
3.使用setup()方法来返回我们的反应型数据,从而我们的template可以获取这些反应型数据
看下代码,更容易理解
import { reactive } from 'vue'
export default {
props: {
title: String
},
setup () {
const state = reactive({
username: '',
password: ''
})
return { state }
}
}
Vue对比Vue3的metheds编写
Vue2的选项性API是把methods分割到独立的属性区域的,我们可以直接在这个属性里面添加方法来处理各种前端逻辑
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
},
methods: {
login () {
// 登陆方法
}
}
}
Vue3的合成型API里面的setup()方法也是可以用来操控methods的。创建声明方法其实和声明数据状态是一样的,我们需要先声明一个方法然后在setup()方法中返回(return),这样我们的组件内就可以调用这个方法了
export default {
props: {
title: String
},
setup () {
const state = reactive({
username: '',
password: ''
})
const login = () => {
// 登陆方法
}
return {
login,
state
}
}
}
声明周期钩子
在Vue2中,我们可以直接在组件属性中调用Vue的生命周期钩子,以下使用一个组件已挂载(mounted)生命周期触发钩子
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
},
mounted () {
console.log('组件已挂载')
},
methods: {
login () {
// login method
}
}
}
现在 Vue3 的合成型API里面的setup()
方法可以包含了基本所有东西。生命周期的钩子就是其中之一!
但是在 Vue3 生周期钩子不是全局可调用的了,需要另外从vue中引入。和刚刚引入reactive
一样,生命周期的挂载钩子叫onMounted
。
引入后我们就可以在setup()
方法里面使用onMounted
挂载的钩子了。
import { reactive, onMounted } from 'vue'
export default {
props: {
title: String
},
setup () {
// ..
onMounted(() => {
console.log('组件已挂载')
})
// ...
}
}
计算属性 - Computed Properties
我们一起试试添加一个计算属性来转换username
成小写字母。
在 Vue2 中实现,我们只需要在组件内的选项属性中添加即可
export default {
// ..
computed: {
lowerCaseUsername () {
return this.username.toLowerCase()
}
}
}
Vue3 的设计模式给予开发者们按需引入需要使用的依赖包。这样一来就不需要多余的引用导致性能或者打包后太大的问题。Vue2就是有这个一直存在的问题。
所以在 Vue3 使用计算属性,我们先需要在组件内引入computed
。
使用方式就和反应性数据(reactive data)
一样,在state
中加入一个计算属性:
import { reactive, onMounted, computed } from 'vue'
export default {
props: {
title: String
},
setup () {
const state = reactive({
username: '',
password: '',
lowerCaseUsername: computed(() => state.username.toLowerCase())
})
// ...
}
接收 Props
接收组件props
参数传递这一块为我们带来了Vue2和Vue3之间最大的区别。 this
在vue3中与vue2代表着完全不一样的东西。
在 Vue2,this
代表的是当前组件,不是某一个特定的属性。所以我们可以直接使用this
访问prop属性值。就比如下面的例子在挂载完成后打印处当前传入组件的参数title
。
mounted () {
console.log('title: ' + this.title)
}
但是在 Vue3 中,this
无法直接拿到props属性,emit events(触发事件)和组件内的其他属性。不过全新的setup()
方法可以接收两个参数:
props
- 不可变的组件参数context
- Vue3 暴露出来的属性(emit,slots,attrs)
所以在 Vue3 接收与使用props就会变成这样
setup (props) {
// ...
onMounted(() => {
console.log('title: ' + props.title)
})
// ...
}
事件 - Emitting Events
在 Vue2 中自定义事件是非常直接的,但是在 Vue3 的话,我们会有更多的控制的自由度。
举例,现在我们想在点击提交按钮时触发一个login
的事件。
在 Vue2 中我们会调用到this.$emit
然后传入事件名和参数对象。
login () {
this.$emit('login', {
username: this.username,
password: this.password
})
}
但是在 Vue3中,我们刚刚说过this
已经不是和vue2代表着这个组件了,所以我们需要不一样的自定义事件的方式。
那怎么办呀?! ლಠ益ಠ)ლ
不用慌,在setup()
中的第二个参数content
对象中就有emit
,这个是和this.$emit
是一样的。那么我们只要在setup()
接收第二个参数中使用分解对象法取出emit
就可以在setup方法中随意使用了。
然后我们在login
方法中编写登陆事件:
setup (props, { emit }) {
// ...
const login = () => {
emit('login', {
username: state.username,
password: state.password
})
}
// ...
}
真的是太棒了,能看到这里的童鞋们,你们现在基本都看到vue2与vue3其实概念与理念都是一样的。只是有一些属性获取方式和声名和定义方式稍微变了。一直在鬼哭狼嚎的小小前端开发猿人们,你们可以松一口气了吧。
总结一下,我觉得 Vue3 给我们前端开发者带来了全新的开发体验,更好的使用弹性,可控度也得到了大大的提升。如果你是一个学过或者接触过 React 然后现在想使用Vue的话,应该特别兴奋,因为很多使用方式都和React非常相近了 !
全新的合成式API(Composition API)
可以提升代码的解耦程度 —— 特别是大型的前端应用,效果会更加明显。还有就是按需引用的有了更细微的可控性,让项目的性能和打包大小有更好的控制。
最后我把完成的 Vue2 和 Vue3 的组件代码发出来给大家:
Vue2
<template>
<div class='form-element'>
<h2> {{ title }} </h2>
<input type='text' v-model='username' placeholder='Username' />
<input type='password' v-model='password' placeholder='Password' />
<button @click='login'>
Submit
</button>
<p>
Values: {{ username + ' ' + password }}
</p>
</div>
</template>
<script>
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
},
mounted () {
console.log('title: ' + this.title)
},
computed: {
lowerCaseUsername () {
return this.username.toLowerCase()
}
},
methods: {
login () {
this.$emit('login', {
username: this.username,
password: this.password
})
}
}
}
</script>
Vue3
<template>
<div class='form-element'>
<h2> {{ state.title }} </h2>
<input type='text' v-model='state.username' placeholder='Username' />
<input type='password' v-model='state.password' placeholder='Password' />
<button @click='login'>
Submit
</button>
<p>
Values: {{ state.username + ' ' + state.password }}
</p>
</div>
</template>
<script>
import { reactive, onMounted, computed } from 'vue'
export default {
props: {
title: String
},
setup (props, { emit }) {
const state = reactive({
username: '',
password: '',
lowerCaseUsername: computed(() => state.username.toLowerCase())
})
onMounted(() => {
console.log('title: ' + props.title)
})
const login = () => {
emit('login', {
username: state.username,
password: state.password
})
}
return {
login,
state
}
}
}
</script>