不知不觉已经11月份了,时间过得真快,在新的一年希望能够了解一下Vue3的开发思想。(Vue2都还没学透呢 /(ㄒoㄒ)/~~)
如何看待Vue3?
虽然说vue2.x是一个比较稳定的版本,也是很长一段时间我们在使用的版本,社区生态已经十分完善。但是呢?前端的技术迭代更新就是这么快,作为前端领域必不可少的一门技能,能够提前去接触到也是提升自我技术的必经之路。
什么是vite?
刚遇到这个名词一脸懵逼,以为是类似于Vue3这种框架,其实是前端开发与构建工具,也就是我们一直用的webpack,不得不提webpack的水很深,这时又来了一个vite,实在是前端永无止境。那么vite的优势是什么呢?
vite优势
一个字,快!
webpack在我们本地开发的过程中,每一次做细微的修改,都会造成很长时间的重新打包,对于很多大型项目,这样消耗的时间实在太久了,这也是长久以来的一个痛点,于是,vue3携带vite出世了,vite依然是基于node来工作的,原理是利用浏览器现在已经支持es6的import了,遇到import会发送一个http请求去加载文件,vite拦截这些请求,做一些预编译,省去了webpack冗长打包的时间,提升开发体验,让本地开发更为高效。
简单的点名系统
首先写一个简单的Demo,感受vue2和vue3的区别。同时学习setup() 和 ref()。
<template>
<div>
<p>点名系统Vue3小测试</p>
<div v-for="(item, i ) in studentArr">
<h1 @click="selectStuFn(i)">{{item}}</h1>
</div>
<h1>请{{selecPeople}}回答问题。</h1>
</div>
</template>
<script lang="ts">
import { defineComponent, ref} from 'vue'
export default defineComponent({
name: 'App',
setup() {
const studentArr = ref(['张三', '李四', '老王'])
const selecPeople = ref("")
const selectStuFn = (index: number) => {
selecPeople.value = studentArr.value[index]
}
return {studentArr, selecPeople, selectStuFn}
}
})
</script>
vue2的思路
在vue2中,我们开发上面的点名系统,需要在data中初始化studentArr 以及 selecPeople ,然后在methods 中编写selectStuFn 函数。
vue3的思路
在vue3中,直接全部写在setup函数中就行,定义的两个属性和方法都定义到了setup其中,最后需要return回去。
区别
- 在vue2中定义在data里面的值就会自动变成响应式,不需要我们做什么操作,就可以在template里面直接使用。
- 在vue3中,我们需要通过ref关键字进行包括,这样的操作才能将它定义为一个响应式的数据。
- 在vue3中,我们对其赋值或者取值的时候必须是通过**.value 写法去拿或者取。
深入setup函数和ref函数
- setup函数其实是一个生命周期钩子,它对应的其实就是Vue2中的beforeCreate和create,并且他是vue3的composition API的入口函数。
- 在vue3中我们通过这个函数来定义vue2中的data,methods,watch,computed属性
- setup函数必须有返回值,必须返回一个对象,对象里包含所有在template模板中需要使用到的属性(包含data,methods等)
- 在setup里面通过ref生命的响应式数据,去取值或者赋值的时候必须通过**.value的方法去拿,但是template却不需要使用.value**
- ref也可以调用原生dom
- setup接收第一个参数是props,用于接收props,也就是定义在组件上的属性(同vue2),但是接收的props必须先在props属性中定义,否则是不会被接收到的
- setup接收的第二个参数是context,在js里面这个形参代表了上下文,它暴露组件的 property,他就是一个普通的javaScript对象,在这里面我们可以取到attrs, slots,emit等属性。
为什么要这么搞?
此处引用vue3文档的一段话,在我开发WebMaker深有感悟。确实有些组件已经是不能再细分了,写着写着就变成了超大组件,Vue3这种开发方式也很好的多人合作,特别是有了TS的加层,可以面向接口编程了。
使用 (data、computed、methods、watch) 组件选项来组织逻辑通常都很有效。然而,当我们的组件开始变得更大时,逻辑关注点的列表也会增长。尤其对于那些一开始没有编写这些组件的人来说,这会导致组件难以阅读和理解。
如果能够将同一个逻辑关注点相关代码收集在一起会更好。而这正是组合式 API 使我们能够做到的。
使用reactive函数优化程序
<template>
<div>
<p>点名系统Vue3小测试</p>
<div v-for="(item, i ) in data.studentArr">
<h1 @click="data.selectStuFn(i)">{{item}}</h1>
</div>
<h1>请{{data.selecPeople}}回答问题。</h1>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive} from 'vue'
export default defineComponent({
name: 'App',
setup() {
const data = reactive({
studentArr : ['张三', '李四', '老王'],
selecPeople : "",
selectStuFn : (index: number) => {
data.selecPeople = data.studentArr[index]
}
})
return {data}
}
})
</script>
- 不再使用ref对属性进行申明响应式,而是和类似vue2的写法,我只需要定义一个data对象即可,想要的属性都放在data,最后统一返回data即可。
- 不再使用**.value的方式而改用data.**这样的方式,更方便自己理解,当然同理,template也需要加上
- 不需要再return一堆东西了,只需要return一个对象就可以了,写法更加简洁。
toRefs()函数对reavtive()函数解构返回
这时或许我们有疑惑,这还是得在template加上data才能访问啊,此时肯定有聪明哥说解构赋值rerurn {...data}
返回所有属性就好啦。但是这样呢会破坏响应式特性。那怎么办?
使用官方提供的 toRefs() 函数即可完成。
<template>
<div>
<p>点名系统Vue3小测试</p>
<div v-for="(item, i ) in studentArr">
<h1 @click="selectStuFn(i)">{{item}}</h1>
</div>
<h1>请{{selecPeople}}回答问题。</h1>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs} from 'vue'
export default defineComponent({
name: 'App',
setup() {
const data = reactive({
studentArr : ['张三', '李四', '老王'],
selecPeople : "",
selectStuFn : (index: number) => {
data.selecPeople = data.studentArr[index]
}
})
let newData = toRefs(data)
return {...newData}
}
})
</script>
这个方法就是为了通过其方法解构后不去破坏他的响应式特性,具体原理还未深入了解,本文只是知道大概使用方法和功能。
vue3的生命周期
vue2 | vue3 | 差异 |
---|---|---|
beforeCreate | setup | setup() :开始创建组件之前,在beforeCreate和created之前执行。创建的是data和method |
created | setup | |
beforeMount | onBeforeMount | |
mounted | onMounted | |
beforeUpdate | onBeforeUpdate | |
updated | onUpdated | |
beforeDestroy | onBeforeUnmount | |
destroyed | onUnmounted | |
activated | onActivated | |
deactivated | onDeactivated |
- vue2的beforeCreate和create变成了setup
- vue2的destroyed和beforDestroy变成了onBeforeUnmount,onUnmounted
watch的变化和watchEffect
watch就是监听一个变量的改变,在vue2中采用对象的方式传递监听函数,那么在vue3是怎么样的呢?
let value = ref('')
watch(value, (newVal, oldVal)=>{
console.log('值被改变了。')
})
value.value = '443'
这里的wacth函数接收两个参数,第一个参数是我们要检测的值,第二个是回调函数,和以前一样拥有新旧两个值。
vue3的watch支持同时监听多个值,以数组的形式,如下:
let value = ref('')
let value2 = ref('')
watch([value, value2], ([newVal, newVal2], [oldVal, oldVal2])=>{
console.log('值被改变了。')
})
value.value = '443'
value2.value = '443'
特别注意:watch只能监听通过 ref() 方法定义的响应式数据,通过reactive()函数定义的变量将会报错。那怎么办呢?
当你去监听一个对象或者数组的时候,前后对象就是循环引用,会发现newVal和oldVal的值始终一样,所以,才会出现上面这种情况,所以,要想解决上面的问题也很简单,可以通过一个函数直接return回来一个新值,这样就可以解决引用问题。
<template>
<div>
<p>点名系统Vue3小测试</p>
<div v-for="(item, i ) in studentArr">
<h1 @click="selectStuFn(i)">{{item}}</h1>
</div>
<h1>请{{selecPeople}}回答问题。</h1>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRefs, watch} from 'vue'
export default defineComponent({
name: 'App',
setup() {
const data = reactive({
studentArr : ['张三', '李四', '老王'],
selecPeople : "",
selectStuFn : (index: number) => {
data.selecPeople = data.studentArr[index]
}
})
watch(()=> data.selecPeople, (newVal,oldVal)=>{
console.log('值被改变了。')
})
let newData = toRefs(data)
return {...newData}
}
})
</script>
了解完了,那么watchEffect是啥?
let name = ref('34234')
watchEffect(() => console.log(name.value))
name.value = '你好'
他是一个方法,接收一个回调函数,他不需要去指定监听谁,它会去自动收集依赖,只要在回调函数中使用了的属性,在发生变化的时候,都会去触发这个回调函数,只能说太强了。
Vue3模块化
在使用vue2项目中,如果复用某些功能,我们会采用mixin的方式进行混入,而在vue3中,新的模块儿化的重用机制将会更加便捷。
test.ts
import { defineComponent, reactive, ref, toRefs, watch, watchEffect} from 'vue'
const data = reactive({
studentArr : ['张三', '李四', '老王'],
selecPeople : "",
selectStuFn : (index: number) => {
data.selecPeople = data.studentArr[index]
}
})
watch(()=> data.selecPeople, (newVal,oldVal)=>{
console.log('值被改变了。')
})
let name = ref('34234')
watchEffect(() => console.log(name.value))
name.value = '你好'
let newData = toRefs(data)
export default {...newData, name}
App.vue
<template>
<div>
<p>点名系统Vue3小测试</p>
<div v-for="(item, i ) in studentArr">
<h1 @click="selectStuFn(i)">{{item}}</h1>
</div>
<h1>请{{selecPeople}}回答问题。</h1>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRefs, watch, watchEffect} from 'vue'
import test from './test'
export default defineComponent({
name: 'App',
setup() {
return {...test}
}
})
</script>
vue3的瞬间移动组件Teleport
Teleport翻译过来就是瞬移,vue的入口文件的html只有一个容器就是
,我们称之为入口,同时页面上的所有组件实际都是挂载在这个节点的,打开开发者工具可以看到最外层只有一有个节点就是这个app。瞬间移动组件适合撰写弹窗组件或者消息提示组件,像这种全局只有一个状态的组件,我们不希望他混入我们的组件中,我们希望他独立于app组件,所以,在大多数优秀的ui框架中,这一类的高级组件,一般会去采用新创建一个节点的方式,抽离出app节点,这样更为容易控制,并且在使用完毕后即时的删除。那么Teleoirt组件实际上就是这样的一个东西,可以独立于app之外的新的节点。
这样的组件在对于一些状态单一的组件创建中,变得更加丝滑了,不会对使用的组件内部造成任何破坏,也不会出现样式污染等各类问题,非常的实用。
Vue3的异步请求组件Suspense
这个异步组件(Suspense)呢是vue3新加的一种组件,再日常的开发中,异步组件是不可缺少的一部分,例如接口请求,图片请求,各种异步操作所导致的请求都属于异步组件,在vue2中这些状态的判断都需要我们自己手动去判断,但是vue3提供这一非常贴心的组件,这个中文翻译过来就是悬念的意思,他提供了两个template 也就是两个插槽,一个是没请求回来的时候显示什么,一个是请求成功显示什么。
使用非常简单,通过Suspense标签包裹的组件就是异步组件,在其中,提供了两个插槽,分别是成功和失败的插槽,会对应显示其中的内容。
其他关于Vue3的知识点
1. 什么是Composition API?
Compositon API不是一个api,而是很多个API组合的一套API,我们统称为这名字,vue2中,我们会在methods,computed,watch,data中等等定义属性和方法,共同处理页面逻辑,我们称这种方式为Options API。这样的方式在项目过大的时候我们发现,一个methods定义数十个方法的时候,还得准备知道每个方法的this,作用,干嘛的就会变得非常麻烦。
2. Vue2和Vue3的生命周期可以混用么?
可以,vue3的优先级更高。
3. Vue3中可以使用vue2的写法么?
可以
前端路漫长,大家互相学习,以下是vue3的思维导图
本文参考于 九儿的小书屋 的 万字长文带你全面掌握Vue3