一.为什么学vue3(vue3的优点)?
- 性能更好(运行速度更快)
- 体积更小(按需编译)
- 类型推断(支持TS)
- 高级给予(暴露更底层的API和提供更丰富的组件)
- 组合性API(能够更好的组织逻辑、封装逻辑、复用逻辑)
二.选项API(vue2)和组合API(vue3)对比
1.选项API写法
- 代码风格:data选项写数据,methods选项写函数…,一个功能逻辑的代码分散。
- 优点:易于学习和使用,写代码的位置已经约定好
- 缺点:代码组织性差,相似的逻辑代码不便于复用,逻辑复杂代码多了不好阅读。
2.组合API写法
- 代码风格:一个功能逻辑的代码组织在一起(包含数据,函数…)
- 优点:功能逻辑复杂繁多情况下,各个功能逻辑代码组织再一起,便于阅读和维护
- 缺点:需要有良好的代码组织能力和拆分逻辑能力
注意:vue3同样支持选项API写法
三.组合API
1.setup函数
- setup函数作为组合api的起点
- 在组件实例创建之前执行(相当于vue2中beforeCreate)
- 此时this获取不到组件实例(this为undefind)
- 在模板中使用的数据和函数,需要在setup中返回
2.生命周期
钩子函数 | 触发时机 |
---|---|
setup | 创建实例前 |
onBeforeMount | 挂载DOM前 |
onMounted | 挂载DOM后 |
onBeforeUpdate | 更新组件前 |
onUpdated | 更新组件后 |
onBeforeUnmount | 卸载销毁前 |
onUnmounted | 卸载销毁后 |
3.reactive函数
作用:reactive是一个函数,用来定义一个复杂数据类型,成为响应式数据,通常是用来定义响应式对象数据。
<template>
<div class="container">
<div>{{obj.name}}</div>
<div>{{obj.age}}</div>
<button @click="updateName">修改数据</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup () {
// 普通数据
// const obj = {
// name: 'ls',
// age: 18
// }
const obj = reactive({
name: 'ls',
age: 18
})
// 修改名字
const updateName = () => {
console.log('updateName')
obj.name = 'zs'
}
return { obj ,updateName}
}
}
</script>
4.toRef函数
toRef函数是用来转化响应式对象中的某个属性为单独响应式数据
代码示例:
<template>
<div class="container">
{{name}} <button @click="updateName">修改数据</button>
</div>
</template>
<script>
import { reactive, toRef } from 'vue'
export default {
name: 'App',
setup () {
// 1. 响应式数据对象
const obj = reactive({
name: 'ls',
age: 10
})
console.log(obj)
// 2. 模板中只需要使用name数据
// 注意:从响应式数据对象中解构出的属性数据,不再是响应式数据
// let { name } = obj 不能直接解构,出来的是一个普通数据
const name = toRef(obj, 'name')
// console.log(name)
const updateName = () => {
console.log('updateName')
// toRef转换响应式数据包装成对象,value存放值的位置
name.value = 'zs'
}
return {name, updateName}
}
}
</script>
<style scoped lang="less"></style>
使用场景:有一个响应式对象数据,但是模版中只需要使用其中一项数据。
5.toRefs函数
toRefs是转化响应式对象中所有属性为单独响应式数据,对象为普通对象
代码示例:
<template>
<div class="container">
<div>{{name}}</div>
<div>{{age}}</div>
<button @click="updateName">修改数据</button>
</div>
</template>
<script>
import { reactive, toRef, toRefs } from 'vue'
export default {
name: 'App',
setup () {
// 1. 响应式数据对象
const obj = reactive({
name: 'ls',
age: 10
})
console.log(obj)
// 2. 解构或者展开响应式数据对象
// const {name,age} = obj
// console.log(name,age)
// const obj2 = {...obj}
// console.log(obj2)
// 以上方式导致数据就不是响应式数据了
const obj3 = toRefs(obj)
console.log(obj3)
const updateName = () => {
// obj3.name.value = 'zs'
obj.name = 'zs'
}
return {...obj3, updateName}
}
}
</script>
<style scoped lang="less"></style>
使用场景:剥离响应式对象(解构|展开),想使用响应式对象中的多个或者所有属性作为响应式数据
6.ref函数
ref函数常用于定义简单数据类型为响应式数据
修改值或获取值时需要用.value
代码演示:
<template>
<div class="container">
<div>{{name}}</div>
<div>{{age}}</div>
<button @click="updateName">修改数据</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App',
setup () {
// 1. name数据
const name = ref('ls')
console.log(name)
const updateName = () => {
name.value = 'zs'
}
// 2. age数据
const age = ref(10)
// ref常用定义简单数据类型的响应式数据
// 其实也可以定义复杂数据类型的响应式数据
// 对于数据未之的情况下 ref 是最适用的
// const data = ref(null)
// setTimeout(()=>{
// data.value = res.data
// },1000)
return {name, age, updateName}
}
}
</script>
使用场景:当明确需要使用的是一个响应式数据对象就使用reactive,其他情况使用ref
7.computed函数
computed函数用来定义计算属性
代码演示:(让计算属性双向绑定)
<template>
<div class="container">
<div>今年:{{age}}岁</div>
<div>后年:{{newAge}}岁</div>
<!-- 使用v-model绑定计算属性 -->
<input type="text" v-model="newAge">
</div>
</template>
<script>
import { computed, ref } from 'vue'
export default {
name: 'App',
setup () {
// 1. 计算属性:当你需要依赖现有的响应式数据,根据一定逻辑得到一个新的数据。
const age = ref(16)
// 得到后年的年龄
// const newAge = computed(()=>{
// // 该函数的返回值就是计算属性的值
// return age.value + 2
// })
// 计算属性高级用法,传人对象
const newAge = computed({
// get函数,获取计算属性的值
get(){
return age.value + 2
},
// set函数,当你给计算属性设置值的时候触发
set (value) {
age.value = value - 2
}
})
return {age, newAge}
}
}
</script>
总结:计算属性两种用法
1.给computed传入函数,返回值就是计算属性的值
2.给computed传入对象,get()方法获取计算属性的值,set()方法监听计算属性改变,计算属性改变时触发
8.watch函数侦听器
可以侦听ref和reactive定义的响应式数据,还可以监听多个响应式数据
代码示例:
// 监听多个数据的变化
watch([count, obj], ()=>{
console.log('监听多个数据改变了')
},{
deep:true, // 是否深度监听
immediate:true // 是否默认触发
})
9.ref属性
ref属性可以获取DOM元素或组件实例
1.vue2写法
(1)获取单个元素
- 通过ref属性绑定该元素(ref=“box”)
- 通过this.$refs.box获取元素
(2)获取v-for遍历的多个元素
- 通过ref属性绑定被遍历元素
- 通过this.$refs.li 获取所有遍历元素
2.vue3写法
(1)获取单个元素
先申明ref响应式数据,返回给模版使用,通过ref绑定数据
(2)获取v-for遍历的多个元素
- 定义一个空数组,接收所有的LI
- 定义一个函数,往空数组push DOM
注意:组件更新时会重复设置dom元素给数组,所以需要在组件更新的时候重置接受dom元素的数组,如下:
onBeforeUpdate(()=>{
list = []
})
代码示例:
<template>
<div class="container">
<div ref="dom">我是box</div>
<ul>
<li v-for="i in 4" :key="i" :ref="setDom">第{{i}}LI</li>
</ul>
</div>
</template>
<script>
import { onMounted, ref } from 'vue'
export default {
name: 'App',
setup () {
// 1. 获取单个元素
// 1.1 先定义一个空的响应式数据ref定义的
// 1.2 setup中返回该数据,你想获取那个dom元素,在该元素上使用ref属性绑定该数据即可。
const dom = ref(null)
onMounted(()=>{
console.log(dom.value)
})
// 2. 获取v-for遍历的元素
// 2.1 定义一个空数组,接收所有的LI
// 2.2 定义一个函数,往空数组push DOM
const domList = []
const setDom = (el) => {
domList.push(el)
}
onMounted(()=>{
console.log(domList)
})
return {dom, setDom}
}
}
</script>
<style scoped lang="less"></style>
10.父子通讯
1.父传子
首先在父组件中通过自定义属性将数据传给子组件
<div class="container">
<h1>父组件</h1>
<p>{{money}}</p>
<hr>
<Son :money="money" />
</div>
然后在子组件中通过props接收父组件传过来的值
export default {
name: 'Son',
// 子组件接收父组件数据使用props即可
props: {
money: {
type: Number,
default: 0
}
},
2.子传父
首先在子组件setup函数中通过emit属性将数据传给父组件
+ setup (props, {emit}) {
// 获取父组件数据money
console.log(props.money)
// 向父组件传值
+ const changeMoney = () => {
// 消费50元
// 通知父组件,money需要变成50
+ emit('change-money', 50)
+ }
+ return {changeMoney}
然后在父组件中通过自定义事件接收子组件传递过来的值
<template>
<div class="container">
<h1>父组件</h1>
<Son :money="money" @change-money="updateMoney" />
</div>
</template>