一、认识VUE3
VUE2写法
<script>
export default{
data(){
return{
count:0
}
},
methods:{
addCount(){
this.count++
}
}
}
</script>
VUE3写法
<script setup>
import {ref} from 'vue'
const count = ref(0)
const addCount = ()=>count.value++
</script>
以上VUE2和VUE3的区别
- 代码量变少了
- 分散式维护转为集中式维护
VUE3更多的优势
- 更容易维护:
1.组合式API
2.更好的TypeScript支持 - 更快的速度:
1.重写diff算法
2.模板编译优化
3.更高效的组件初始化 - 更小的体积
1.良好的TreeShaking
2.按需引入 - 更优的数据响应式
1.Proxy
二、使用create-vue搭建Vue3项目
认识create-vue
create-vue是Vue官方新的脚手架工具,原先的Vue-CLI底层切换到了vite(下一代前端工具链),为开发提供极速响应。
使用create-vue创建项目
1.前提环境条件
已安装16.0node或更高的版本的Node.js
(node官网:https://nodejs.cn/download/,使用快捷键 win + R 打开运行 输入cmd打开终端。在终端里面输入node -v和npm -v查看是否都可用,如果显示版本号则证明安装成功。)
2.创建一个Vue应用
npm init vue@latest
这一指令将会安装并执行create-vue
三、熟悉项目目录和关键文件
项目目录
关键文件
1.vite.config.js - 项目的配置文件 基于vite的配置
2.package.json - 项目包文件 核心依赖项变成了vue3.X 和 vite
3.main.js - 入口文件 createApp函数创建应用实例
4.app.vue - 根组件 SFC单文件组件 script-template-style
变化一:脚本script和模板template顺序调整
变化二:模板template不再要求唯一根元素
变化三:脚本script添加setup标识支持组合式API
5.index.html - 单页入口 提供id为app的挂载点
四、组合式API - setup选项
setup
原始复杂写法
<script>
export default{
setup(){
// 数据
const message = 'this is message'
// 函数
const logMessage=()=>{
console.log(message);
}
return{
message,
logMessage
}
}
}
</script>
语法糖写法
<script setup>
// 数据
const message = 'this is message'
// 函数
const logMessage=()=>{
console.log(message);
}
</script>
总结
1.setup选项的执行实机是在beforeCreate钩子之前 自动执行
2.setup写代码的特点是 定义数据+函数,然后以对象方式return
3.(script setup)经过语法糖的封装更简单的使用组合式API
4.setup中的this指向undefined
五、组合式API - reactive和ref函数
reactive()
作用:接受对象类型数据的参数传入并返回一个响应式的对象
<script setup>
// 导入
import { reactive } from 'vue'
// 执行函数 传入参数 变量接收
const state = reactive(对象类型的数据)
</script>
例子
<script setup>
// 导入
import { reactive } from 'vue'
// 执行函数 传入参数 变量接收
const state = reactive({
count:0
})
const setCount=()=>{
state.count++
}
</script>
<template>
<div>
<button @click="setCount">{{state.count}}</button>
</div>
</template>
1.从vue包中导入reactive函数
2.在(script setup)中执行reactive函数并传入类型对象的初始值,并使用变量接收返回值
ref()
<script setup>
// 导入
import { ref } from 'vue'
// 执行函数 传入参数 变量接收
const state = ref(简单类型或者复杂类型数据)
</script>
例子
<script setup>
// 导入
import { ref } from 'vue'
// 执行函数 传入参数 变量接收
const count = ref(0)
const setCount=()=>{
// 脚本区域修改ref产生的响应式对象数据 必须通过.value属性
count.value++
}
</script>
<template>
<div>
<button @click="setCount">{{count}}</button>
</div>
</template>
1.从vue包中导入ref函数
2.在(script setup)中执行ref函数并传入初始值,并使用变量接收ref函数的返回值
总结
1.reactive和ref函数的共同作用是什么?
用函数调用的方式生成响应式数据
2.reactive vs ref 的区别?
(1)reactive不能处理简单类型的数据
(2) ref参数类型支持更好但是必须通过.value访问修改
(3) ref函数的内部实现依赖于reactive函数
3.在实际工作中推荐使用哪个
推荐使用ref函数,更加灵活。
六、组合式API - computed
computed
计算属性基本思想和Vue2完全一致,组合式API下的计算属性只是修改了写法。
<script setup>
// 导入
import {computed} from 'vue'
// 执行函数 变量接收 在回调函数中return计算值
const computedState=computed(()=>{
return 基于响应式数据做计算的值
})
</script>
1.导入computed函数
2.执行函数在回调参数中return基于响应式数据做计算的值,用变量接收
demo例子
<script setup>
// 导入
import {ref,computed} from 'vue'
const list = ref([1,2,3,4,5,6,7,8])
// 执行函数 变量接收 在回调函数中return计算值
const computedList=computed(()=>{
//返回大于2的元素的数组
return list.value.filter(item=>item>2)
})
</script>
<template>
<div>
原始响应式数组 - {{ list }}
</div>
<div>
计算属性数组 - {{ computedList }}
</div>
</template>
总结
1.计算属性中不应该有“副作用”
比如异步请求/修改dom(应该交给watch来做)
computed只能应该根据一个数据计算得到一个新数据
2.避免直接修改计算属性的值
计算属性应该是只读
七、组合式API - watch
作用:侦听一个或者多个数据的变化,数据变化时执行回调函数
两个额外参数:1.immediate(立即执行) 2.deep(深度侦听)
基础使用-侦听单个数据
<script setup>
// 导入watch
import {ref,watch} from 'vue'
const count = ref(0)
// 2.调用watch 侦听变化
watch(count,(newValue,oldValue)=>{
console.log(`count发生了变化,老值为${oldValue},新值为${newValue}`);
})
</script>
demo例子
<script setup>
// 导入watch
import {ref,watch} from 'vue'
const count = ref(0)
const setCount=()=>{
count.value++
}
// 2.调用watch 侦听变化
// TODO watch侦听单个数据源
// ref对象不需要加.value
watch(count,(oldValue,newValue)=>{
console.log(`count发生了变化,老值为${oldValue},新值为${newValue}`);
})
</script>
<template>
<div>
<button @click="setCount">+{{ count }}</button>
</div>
</template>
基础使用-侦听多个数据
说明:同时侦听多个响应式数据的变化,不管哪个数据变化都需要执行回调。
<script setup>
import {ref,watch} from 'vue'
const count = ref(0)
const name = ref('cp')
// 侦听多个数据源
watch(
[count,name],
([newCount,newName],[oldCount,oldName])=>{
console.log(`count或者name发生了变化,`[newCount,newName],[oldCount,oldName]);
}
)
</script>
例子demo
<script setup>
import {ref,watch} from 'vue'
const count = ref(0)
const changeCount = ()=>{
count.value++
}
const name = ref('cp')
const changeName = ()=>{
name.value='pc'
}
// 侦听多个数据源
watch(
[count,name],
([newCount,newName],[oldCount,oldName])=>{
console.log(`count或者name发生了变化,`,[newCount,newName],[oldCount,oldName]);
}
)
</script>
<template>
<div>
<button @click="changeCount">修改count--{{ count }}</button>
</div>
<div>
<button @click="changeName">修改name--{{ name }}</button>
</div>
</template>
immediate
说明:在侦听器创建时立即触发回调,响应式数据变化之后继续执行回调
<script setup>
import {ref,watch} from 'vue'
const count = ref(0)
watch(count,()=>{
console.log('立即监听count');
},{
immediate:true
})
</script>
<template>
<div>
<button>修改count--{{ count }}</button>
</div>
</template>
deep
默认机制:通过watch监听的ref对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep选项。
<script setup>
import {ref,watch} from 'vue'
const state = ref({count:0})
watch(state,()=>{
console.log('数据变化了');
},{
deep:true
})
const changeState=()=>{
// 直接修改属性 -> 不会触发回调
state.value.count++
}
</script>
<template>
<div>
<button @click="changeState">修改count--{{ state.count }}</button>
</div>
</template>
精确侦听对象的某个属性
需求:在不开启deep的前提下,侦听age的变化,只有age变化时才执行回调
<script setup>
import {ref,watch} from 'vue'
const state = ref({count1:0,age:18})
watch(
// 精确侦听某个具体属性
()=>state.value.count1,
()=>console.log('count1发生变化了')
)
const changeState=()=>{
state.value.count1++
}
</script>
<template>
<div>
<button @click="changeState">修改count--{{ state.count1 }}</button>
</div>
</template>
总结
1.作为watch函数的第一个函数,ref对象需要添加.value吗?
不需要,watch会自动读取
2.watch只能侦听单个数据吗?
不对,能监听单个或多个
3.不开启deep,直接修改嵌套属性能触发回调吗?
不能,默认是浅层侦听
4.不开启deep,想在某个层次比较深度属性变化时执行回调怎么做?
可以把第一个参数写成函数的写法,返回要监听的具体属性
八、组合式API - 生命周期函数
Vue3的生命周期API(选项式VS组合式)
生命周期函数的基本使用
1.导入生命周期函数
2.执行生命周期函数 传入回调
<script setup>
// 声明周期函数的使用
// 1.引入函数
import {onMounted} from 'vue'
// 2.执行函数 传入回调
onMounted(()=>{
console.log('组件挂载完毕mounted执行了');
})
</script>
<template>
<div>
</div>
</template>
执行多次
生命周期函数是可以执行多次的,多次执行时传入的回调会在时机成熟时依次执行
<script setup>
// 声明周期函数的使用
// 1.引入函数
import {onMounted} from 'vue'
// 2.执行函数 传入回调
onMounted(()=>{
console.log('组件挂载完毕mounted执行了');
console.log('组件挂载完毕mounted执行了2')
})
onMounted(()=>{
console.log('组件挂载完毕mounted执行了3');
})
</script>
总结
1.组合式API中生命周期函数的格式是什么?
on+生命周期名字
2.组合API中可以使用onCreated吗?
没有这个钩子函数,直接写到setUp中
3.组合式API中组件卸载完毕时执行哪个生命周期函数
onUnmounted
九、组合式API - 父子通信 - 父传子
基本思想
1.父组件中给子组件绑定属性
2.子组件内部通过props选项接收
父组件
<script setup>
// 引入子组件
import { ref } from 'vue';
import sonComVue from './son-com.vue'
const count = ref(100)
setTimeout(()=>{
count.value=200
},3000)
</script>
<template>
<!-- 绑定属性 -->
<h1>我是父组件</h1>
<!-- :动态绑定数据 可监听到数据变化 -->
<sonComVue :count="count" message="this is sonComVue"></sonComVue>
</template>
子组件
<script setup>
// 2.通过defineProps“编译器宏”接收子组件传递的数据
const props = defineProps({
message:String,
count:Number
})
</script>
<template>
<h2>我是子组件</h2>
父组件传入的数据----{{ message }} -- {{ count }}
</template>
总结
1.父传子的过程中通过什么方式接收props?
defineProps({属性名:类型})
2.setup语法糖中如何使用父组件传过来的数据?
const props = defineProps({属性名:类型})
十、组合式API - 父子通信 - 子传父
基本思想
1.父组件中给子组件标签通过@绑定事件
2.子组件内部通过$emit方法触发事件
父组件
<script setup>
// 引入子组件
import sonComVue from './son-com.vue'
const getMessage = (msg)=>{
console.log(msg);
}
</script>
<template>
<h1>我是父组件</h1>
<!-- 绑定自定义事件 -->
<sonComVue @get-message="getMessage"></sonComVue>
</template>
子组件
<script setup>
// 2.通过defineProps“编译器宏”接收子组件传递的数据
const emit = defineEmits(['get-message'])
const sendMSg = ()=>{
// 3.触发自定义事件 并传递参数
emit('get-message','this is son msg')
}
</script>
<template>
<h2>我是子组件</h2>
<button @click="sendMSg">子传父</button>
</template>
总结
1.子传父的过程中通过什么方式得到emit方法?
defineEmits([‘事件名称’])
十一、组合式API - 模板引用
概念
通过ref标识获取真实的dom对象或者组件实例对象
如何使用(以获取dom为例 组件同理)
<script setup>
import {ref} from 'vue'
// 1.调用ref函数得到ref对象
const h1Ref=ref(null)
// 组件挂载完毕之后才能获取
onMounted(()=>{
console.log(h1Ref.value);
})
</script>
<template>
<!-- 2.通过ref标识绑定ref对象 -->
<h1 ref="h1Ref">
我是dom标签h1
</h1>
</template>
1.通过ref函数生成一个ref对象
2.通过ref标识绑定ref对象到标签
defineExpose()
默认情况下在
<script setup>
// 引入子组件
import { onMounted, ref } from 'vue';
import testMessage from './testMessage.vue'
// 1.调用ref函数 -> ref对象
const comRef = ref(null)
onMounted(()=>{
comRef.value.setName()
console.log(comRef.value.name);
})
</script>
<template>
<!-- 2.通过ref标识绑定ref对象 -->
<h1>我是父组件</h1>
<testMessage ref="comRef"></testMessage>
</template>
<script setup>
import {ref} from 'vue'
const name = ref('test name')
const setName = ()=>{
name.value = 'test new name'
}
defineExpose({
name,
setName
})
</script>
<template>
<div>我是test组件</div>
</template>
总结
1.获取模板引用的时机是什么?
组件挂载完毕
2.defineExpose编译宏的作用是什么?
显示暴露组件内部的属性和方法
十二、组合式API - provide和inject
作用和场景
顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信
跨层传递普通数据
1.顶层组件通过provide函数提供数据
2.底层组件通过inject函数获取数据
顶层组件
provide('key',顶层组件中的数据)
底层组件
const message = inject('key')
例子
顶层组件RoomPage.vue
<script setup>
import RoomMsgItem from './RoomMsgItem.vue'
import {provide} from 'vue'
// 组件嵌套关系:
// RoomPage -> RoomMsgItem -> RoomMsgComment
// 1.顶层组件提供数据
provide('data-key','this is room data')
</script>
<template>
<div>
顶层组件
<RoomMsgItem />
</div>
</template>
中间组件RoomMsgItem,vue
<script setup>
import RoomMsgComment from './RoomMsgComment.vue'
</script>
<template>
<div>
中间组件
<RoomMsgComment />
</div>
</template>
底层组件RoomMsgComment.vue
<script setup>
import {inject} from 'vue'
// 2.接收数据
const roomData = inject('data-key')
</script>
<template>
<div>
底层组件
<div>
来自顶层组件中的数据为:{{ roomData }}
</div>
<div>
来自顶层组件的响应式数据:
</div>
</div>
</template>
跨层传递响应式数据
顶层组件
provide('app-key',ref对象)
底层组件
const message = inject('app-key')
例子
顶层组件RoomPage.vue
<script setup>
import RoomMsgItem from './RoomMsgItem.vue'
import {provide,ref} from 'vue'
// 组件嵌套关系:
// RoomPage -> RoomMsgItem -> RoomMsgComment
// 1.顶层组件提供数据(传递普通数据)
provide('data-key','this is room data')
// 传递响应式数据
const count = ref(0)
provide('count-key',count)
setTimeout(()=>{
count.value=100
},3000)
</script>
<template>
<div>
顶层组件
<RoomMsgItem />
</div>
</template>
中间组件RoomMsgItem,vue
<script setup>
import RoomMsgComment from './RoomMsgComment.vue'
</script>
<template>
<div>
中间组件
<RoomMsgComment />
</div>
</template>
底层组件RoomMsgComment.vue
<script setup>
import {inject} from 'vue'
// 2.接收数据(普通数据)
const roomData = inject('data-key')
// 接收响应式数据
const countData = inject('count-key')
</script>
<template>
<div>
底层组件
<div>
来自顶层组件中的数据为:{{ roomData }}
</div>
<div>
来自顶层组件的响应式数据:{{countData}}
</div>
</div>
</template>
跨层传递方法
顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据
顶层组件
const setCount = ()=>{
count.value++
}
provide('setCount-key',setCount)
底层组件
const setCount = inject('setCount-key')
例子`
顶层组件RoomPage.vue
<script setup>
import RoomMsgItem from './RoomMsgItem.vue'
import {provide,ref} from 'vue'
// 组件嵌套关系:
// RoomPage -> RoomMsgItem -> RoomMsgComment
// 1.顶层组件提供数据(传递普通数据)
provide('data-key','this is room data')
// 传递响应式数据
const count = ref(0)
provide('count-key',count)
setTimeout(()=>{
count.value=100
},3000)
// 传递方法(谁的数据谁负责修改)
const setCount = ()=>{
count.value++
}
provide('setCount-key',setCount)
</script>
<template>
<div>
顶层组件
<RoomMsgItem />
</div>
</template>
中间组件RoomMsgItem,vue
<script setup>
import RoomMsgComment from './RoomMsgComment.vue'
</script>
<template>
<div>
中间组件
<RoomMsgComment />
</div>
</template>
底层组件RoomMsgComment.vue
<script setup>
import {inject} from 'vue'
// 2.接收数据(普通数据)
const roomData = inject('data-key')
// 接收响应式数据
const countData = inject('count-key')
// 接收方法
const setCount = inject('setCount-key')
</script>
<template>
<div>
底层组件
<div>
来自顶层组件中的数据为:{{ roomData }}
</div>
<div>
来自顶层组件的响应式数据:{{countData}}
</div>
<div>
<button @click="setCount">修改顶层组件的数据count</button>
</div>
</div>
</template>
总结
1.provide和inject的作用是什么?
跨层组件通信
2.如何在传递的过程中保持数据响应式?
第二个参数传递ref对象
3.底层组件想要通知顶层组件做修改,如何做?
传递方法,底层组件调用方法
4.一颗组件树中只有一个顶层或底层组件吗?
相对概念,存在多个顶层和底层的关系
十三、Pinia
什么是Pinia
Pina是Vue的专属的最新状态管理库,是Vuex状态管理工具的替代品。
1.提供更加简单的API(去掉了mutation)
2.提供符合组合式风格的API(和Vue3新语法统一)
3.去掉了modules的概念,每一个store都是一个独立的模块
4.搭配TypeScript一起使用提供可靠的类型推断
添加Pinia到Vue项目
1.使用create-vue创建空的新项目
npm init vue@latest
2.按照官方文档安装pinia到项目
npm install pinia
3.在main.js文件配置pinia
//1.导入createPinia
import { createPinia } from 'pinia'
//2.执行方法得到实例
const pinia = createPinia()
//3.把pinia实例加入到app应用中
createApp(App).use(pinia).mount('#app')
实现Pinia实现计数器案例(Pinia-counter的基础使用)
1.在stores/counter定义Store(state+action 同步+异步)
2.在vue组件使用Store
3.结果
Pinia-getters和异步action
getters的实现:Pinia中的getters直接使用computed函数进行模拟
action如何实现异步:action中实现异步和组件中定义数据和方法的风格完全一致
![在这里插入图片描述](https://img-blog.csdnimg.cn/9ee8aef5517f49fe8aa360205399f2f6.png
Pinia-storeToRefs和调试
storeToRefs:使用storeToRefs函数可以辅助保持数据(state+getter)的响应式解构
用与不用storeToRefs的区别:
注意方法直接从原来的counterStore中解构赋值
总结
1.Pinia是用来做什么的?
集中状态管理工具,新一代的vuex
2.Pinia中还需要mutation吗?
不需要,action既支持同步也支持异步
3.Pinia如何实现getter?
computed计算属性函数
4.Pinia产生的Store如何解构赋值数据保持响应式?
storeToRefs