开篇
Vue3与Vue2对比语法是最大的区别就是新增的组合式 API,使用组合式API可以大大的提高代码的可读性、可维护性。
回顾一下我们使用Vue2语法去修改一个功能时候,首先通过路由定位到对应的视图(.vue文件),再通过模板(templete)定位到相应模板元素,再去methods中找到元素上的绑定方法,方法中又涉及到data()中的数据,data()中的数据可能来源自props也可能来自生命周期函数中的API调用,若是一个纯展示的模块,可能还涉及到computed(计算属性)。若这个组件中涉及多个业务逻辑,那么整个文件中的代码是相当冗长的,也就造成了官方提到的“关注点分离”问题,而使用组合式API就是为了解决这个痛点。(个人理解、轻喷)
Vue3新特性
1.Composition API
组合式API中涉及的知识点较多,接下来我们一一展示,首先用Vue-CLI创建一个vue3项目(不是本文重点,自行查阅官方文档)。
1.1 setup()、ref
<template>
<div>
<ul>
<li v-for="(item,index) in menu" :key="item+index" @click="choose(index)">
{{`${index}---${item}`}}
</li>
</ul>
<h2>{{chooseFood}}</h2>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name:'test',
setup() {
const menu = ref(['红烧茄子','油焖茄子','干瘪茄子'])
console.log('menu----',menu);
console.log('created');
const chooseFood = ref('')
const choose = index =>{
chooseFood.value = menu.value[index]
console.log(menu.value[index]);
}
return{
menu,
chooseFood,
choose
}
}
}
</script>
//setup()函数,组合式API存放的位置,效果上相当于beforecreate+created,也就是说我们可以在这里动态的获取数据,变相的取代了data、methods但需要注意声明顺序;
//使用ref函数声明响应式数据,需要在视图中使用的数据、方法通过return返回;
1.2 reactive
<template>
<div>
<ul>
<li v-for="(item,index) in data.menu" :key="item+index" @click="data.choose(index)">
{{`${index}---${item}`}}
</li>
</ul>
<h2>{{data.chooseFood}}</h2>
</div>
</template>
<script>
import { ref,reactive } from 'vue'
export default {
name:'test',
setup() {
const data = reactive({
menu : ['红烧茄子','油焖茄子','干瘪茄子'],
chooseFood:'',
choose : index =>{
data.chooseFood = data.menu[index]
console.log(data.menu[index]);
}
})
console.log('menu----',data.menu);
console.log('created');
return{
data
}
}
}
</script>
//通过reactive将数据、方法代理在data对象中,达到的效果跟1.1一致。
1.3 toRefs
<template>
<div>
<ul>
<li v-for="(item,index) in menu" :key="item+index" @click="choose(index)">
{{`${index}---${item}`}}
</li>
</ul>
<h2>{{chooseFood}}</h2>
</div>
</template>
<script>
import { ref,reactive,toRefs } from 'vue'
export default {
name:'test',
setup() {
const data = reactive({
menu : ['红烧茄子','油焖茄子','干瘪茄子'],
chooseFood:'',
choose : index =>{
data.chooseFood = data.menu[index]
console.log(data.menu[index]);
}
})
const refData = toRefs(data)
console.log('refData---',refData);
return{
...refData
}
}
}
</script>
//若直接return {...data}则数据不是响应式;
//通过toRefs包装后,return{...refData}等价于return{menu: Ref<Object>,chooseFood:Ref<String>,choose: Ref<function>
}
toRefs
:将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的ref
。
1.4 生命周期函数
Vue2--------------vue3 Vue3新增:
beforeCreate -> setup() onRenderTracked(跟踪每一个值)
created -> setup() onRenderTriggered(跟踪变化值)
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured
首先是新增的setup函数,然后是名称上的变化,Vue2中的生命周期函数还是可以照常使用的(但建议还是使用新的hooks),然后是新增的两个用于跟踪数据状态,简化调试的钩子(onRenderTracked、onRenderTriggered),setup中使用其他生命周期函数需要先引入;
<template>
<div>
<ul>
<li v-for="(item,index) in menu" :key="item+index" @click="choose(index)">{{`${index}---${item}`}}</li>
</ul>
<h2>{{chooseFood}}</h2>
</div>
</template>
<script>
import { ref,reactive,toRefs,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onRenderTracked,onRenderTriggered } from 'vue'
export default {
name:'test',
setup() {
const data = reactive({
menu : ['红烧茄子','油焖茄子','干瘪茄子'],
chooseFood:'',
choose : index =>{
data.chooseFood = data.menu[index]
console.log(data.menu[index]);
}
})
const refData = toRefs(data)
console.log('1-开始创建组件-----setup()',);
onBeforeMount(() => {
console.log("2-组件挂载到页面之前执行-----onBeforeMount()");
})
onMounted(() => {
console.log("3-组件挂载到页面之后执行-----onMounted()");
})
onBeforeUpdate(() => {
console.log("4-组件更新之前-----onBeforeUpdate()");
})
onUpdated(() => {
console.log("5-组件更新之后-----onUpdated()");
})
// onRenderTracked((event)=>{
// console.log("onRenderTracked--------------->");
// console.log(event);
// }),
onRenderTriggered((event)=>{
console.log("onRenderTriggered--------------->");
console.log(event);
})
return{
...refData
}
}
}
</script>
1.5 watch
它接受 3 个参数:
- 一个想要侦听的响应式引用或 getter 函数
- 一个回调
- 可选的配置选项
与Vue2中watch对比,首先针对多个数据监听时,用数组的形式而不是写多个watch;然后监听使用reactive函数声明的变量时,使用getter函数的形式,个人理解就是相当于Vue中监听复杂类型时,开启deep:true深度监听一个性质。
<template>
<div>
<ul>
<li v-for="(item,index) in menu" :key="item+index" @click="choose(index)">
{{`${index}---${item}`}}
</li>
</ul>
<h2>{{chooseFood}}</h2>
<div>
<h3>{{name}}</h3>
<button @click="rename">rename</button>
</div>
</div>
</template>
<script>
import { ref,reactive,toRefs, watch } from 'vue'
export default {
name:'test',
setup() {
const name = ref('luo')
const rename = ()=>{
name.value = 'joker'
}
const data = reactive({
menu : ['红烧茄子','油焖茄子','干瘪茄子'],
chooseFood:'',
choose : index =>{
data.chooseFood = data.menu[index]
console.log(data.menu[index]);
}
})
console.log(name);
console.log(data);
watch([()=>data.chooseFood,name],(newValue,oldValue)=>{
console.log(`new--->${newValue}`);
console.log(`old--->${oldValue}`);
})
const refData = toRefs(data)
return{
...refData,
name,
rename
}
}
}
</script>
1.6 computed
<template>
<div>
<ul>
<li v-for="(item,index) in menu" :key="item+index" @click="choose(index)">
{{`${index}---${item}`}}
</li>
</ul>
<h2>{{chooseFood}}</h2>
<div>
<h3>{{name}}</h3>
<h3>{{`doubleName:${doubleName}`}}</h3>
<button @click="rename">rename</button>
</div>
</div>
</template>
<script>
import { ref,reactive,toRefs,computed,watch } from 'vue'
export default {
name:'test',
setup() {
const name = ref('luo')
const rename = ()=>{
name.value = 'joker'
}
const data = reactive({
menu : ['红烧茄子','油焖茄子','干瘪茄子'],
chooseFood:'',
choose : index =>{
data.chooseFood = data.menu[index]
console.log(data.menu[index]);
}
})
watch([()=>data.chooseFood,name],(newValue,oldValue)=>{
console.log(`new--->${newValue}`);
console.log(`old--->${oldValue}`);
})
const doubleName = computed(()=>{return name.value+name.value})
const refData = toRefs(data)
return{
...refData,
name,
rename,
doubleName
}
}
}
</script>
1.7 Teleport组件
在Vue2中所有的组件都是挂载在#app节点下的,为避免一些样式侵入需要做一些复杂的嵌套,而Vue3的Teleport组件可以让我们将组件挂载到任意节点中去。
//Modal.vue
<template>
<div id="center">
<h2>test demo</h2>
</div>
</template>
<script>
export default {}
</script>
<style scoped>
#center {
width: 200px;
height: 200px;
border: 2px solid black;
background: white;
display: flex;
justify-content: center;
align-items: center;
}
</style>
//App.vue
<template>
<Modal />
</template>
<script lang="ts">
import Modal from '../src/components/Modal.vue'
const app = {
name: "App",
components: {
Modal
}
};
export default app;
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
使用teleport组件
//Modal.vue
<template>
<teleport to='#modal'>
<div id="center">
<h2>test demo</h2>
</div>
</teleport>
</template>
1.8 Suspense组件
//AsyncShow.vue
<template>
<div>
<h1>{{ result }}</h1>
</div>
</template>
<script>
export default ({
setup() {
return new Promise((resolve,reject)=>{
setTimeout(() => {
return resolve({result: 'check suspense'})
}, 3000);
})
}
})
</script>
//App.vue
<template>
<Suspense>
<template #default>
<AsyncShow />
</template>
<template #fallback>
<div>
Loading....
</div>
</template>
</Suspense>
</template>
用setup()做mixin的工作
在Vue2中我们使用mixin复用一些通用逻辑代码块,像Pagination分页等;确实提高了开发效率,但是在具体的业务页,很难分清属性是来自哪里;
//timeHooks.js
import {ref} from "vue"
const nowtime = ref("00:00:00");
const getNowTime = () => {
const now = new Date();
const hour = now.getHours() < 10? "0" + now.getHours() : now.getHours();
const minutes = now.getMinutes() < 10? "0" + now.getMinutes() : now.getMinutes();
const seconds = now.getSeconds() < 10? "0" + now.getSeconds() : now.getSeconds();
nowtime.value = hour + ":" + minutes + ":" +seconds
setTimeout(getNowTime,1000);
};
export {nowtime , getNowTime}
//App.vue
</template>
<div>
{{nowtime}}
</div>
</template>
<script>
import {getNowTime,nowtime} from '../src/hooks/timeHooks.js'
const app = {
name: "App",
setup() {
getNowTime()
return {
nowtime
};
},
};
export default app;
</script>
更加的直观。
以上内容为自身结合vue3官方文档与胖老师的教程的学习笔记,欢迎指正。