Vue3 优势
- 更容易维护
- 组合式API
- 更好的TypeScript支持
- 更快的速度
- 重写diff算法
- 模拟编辑优化
- 更高效的组件初始化
- 更优的数据响应
- Proxy
4.更小的体积
- 良好的TreeShaking
- 按需引入
Vue2 选项式 VS Vue3组合式API
需求:点击按钮,数字+1
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>
代码量变少了
分散式维护转为集中式维护,更易封装复用
create-vue搭建Vue3项目
认识create-vue
create-vue是Vue官方新的脚手架工具,底层切换到了 vite (下一代前端工具链),为开发提供极速响应
使用create-vue创建项目
1. 前提环境条件
已安装16.0或更高版本的Node.js
node -v
- 创建一个Vue应用
npm init vue@latest
这一指令将会安装并执行 create-vue
项目目录和关键文件
-
new Vue ()创建一个应用实例=>createeApp()
-
createRouter() createStore()将创建实例进行封装,保证每个实例的独立封闭性
-
不再要求唯一根元素
组合式API - setup选项
setup选项的写法和执行时机
写法
<script>
export default {
setup(){
},
beforeCreate(){
}
}
</script>
执行时机
<script>
export default{
setup (){
console.log('setup函数')
},
beforeCreate(){
console.log('beforeCreate函数')
}
}
</script>
setup执行时机在beforeCreate钩子之前
setup函数中获取不到this(this是undefined)
setup中写代码的特点
在setup函数中写的数据和方法需要在末尾以对象的方式return,才能给模版使用
<script>
export default {
setup(){
const message = 'this is message'
const logMessage = ()=>{
console.log(message)
}
// 必须return才可以
return {
message,
logMessage
}
}
}
</script>
语法糖写法
<script setup>
//数据
const message = 'this is message'
//函数
const logMessage = ()= > {
console.log(message)
}
</script>
语法糖原理
总结
reactive 和ref函数
reactive
接受对象类型数据的参数传入并返回一个响应式的对象
<script setup>
// 导入
import { reactive } from 'vue'
// 执行函数 传入参数 变量接收
const state = reactive({
msg:'this is msg'
})
const setSate = ()=>{
// 修改数据更新视图
state.msg = 'this is new msg'
}
</script>
<template>
{{ state.msg }}
<button @click="setState">change msg</button>
</template>
ref
接收简单类型或者对象类型的数据传入并返回一个响应式的对象
本质:是在原有传入数据的基础上,外层包了一层对象,包成了复杂类型
底层包成复杂类型之后,再借助reactive实现的响应式
注意点:
- 脚本访问数据,需要通过.value
- 在template中,.value不需要(帮我们扒了一层)
<script setup>
// 导入
import { ref } from 'vue'
// 执行函数 传入参数 变量接收
const count = ref(0)
const setCount = ()=>{
// 修改数据更新视图必须加上.value
count.value++
}
</script>
<template>
<button @click="setCount">{{count}}</button>
</template>
reactive VS ref
-
共同点:都是用来生成响应式数据
-
不同点
(1) reactive不能处理简单类型的数据
(2) ref参数类型支持更好,但是必须通过.value做访问修改
(3) ref函数内部的实现依赖于reactive函数
-
在实际工作中的推荐
推荐使用ref函数,更加灵活
组合式API - computed
计算属性基本思想和Vue2保持一致,组合式API下的计算属性只是 修改了API写法
核心步骤
- 导入conputed函数
- 执行函数 在回调函数中return基于响应式数据做计算的值,用变量接收
<script setup>
// 导入
import {ref, computed } from 'vue'
// 原始数据
const count = ref(0)
// 计算属性
const doubleCount = computed(()=>count.value * 2)
// 原始数据
const list = ref([1,2,3,4,5,6,7,8])
// 计算属性list
const filterList = computed(item=>item > 2)
</script>
计算属性小案例
<script setup>
import {ref, computed } from 'vue'
const list = ref([1,2,3,4,5,6,7,8])
// 基于list诞生一个计算属性,从list中过滤出>2
const computedList = computed(() =>{
return list.value.filter(item =>item>2)
})
</script>
<template>
<div>
<div>原始数据:{{list}}</div>
<div>计算后的数据:{{computedList}}</div>
</div>
</template>
效果如下:
总结
- 计算属性中不应该有“副作用”
- 如异步请求/修改dom
2.避免直接修改计算属性的值
- 计算属性应该是只读的,特殊情况可以配置 get set
watch 函数
作用:侦听一个或多个数据变化,数据变化时执行回调函数
两个额外参数:immediate(立即执行), deep(深度侦听)
侦听单个数据
<script setup>
// 1. 导入watch
import { ref, watch } from 'vue'
const count = ref(0)
// 2. 调用watch 侦听变化
watch(count, (newValue, oldValue)=>{
console.log(`count发生了变化,老值为${oldValue},新值为${newValue}`)
})
</script>
watch(ref对象,(newValue,oldValue) =>{...})
watch(count,(newValue,oldValue) =>{
console.log(newValue,oldValue)
})
侦听多个数据
侦听多个数据,第一个参数可以改写成数组的写法
<script setup>
// 1. 导入watch
import { ref, watch } from 'vue'
const count = ref(0)
const name = ref('cp')
// 2. 调用watch 侦听变化
watch([count, name], ([newCount, newName],[oldCount,oldName])=>{
console.log(`count或者name变化了,[newCount, newName],[oldCount,oldName])
})
</script>
watch{[ref对象1,ref对象2],(newARR,oldArr)=>{...}}
实例:
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const nickname = ref('李明德')
const changeCount = () =>{
count.value++
}
const changeNickname = () =>{
nickname.value = 'iu'
}
</script>
<template>
<div>{{ count }}</div>
<button @click="changeCount">改数字</button>
<div>{{ nickname }}</div>
<button @click="changeNickname">改昵称</button>
</template>
效果如下:
immediate
在侦听器创建时立即触发回调,响应式数据变化之后继续执行回调
<script setup>
// 1. 导入watch
import { ref, watch } from 'vue'
const count = ref(0)
// 2. 调用watch 侦听变化
watch(count, (newValue, oldValue)=>{
console.log(`count发生了变化,老值为${oldValue},新值为${newValue}`)
},{
immediate: true
})
</script>
deep
通过watch监听的ref对象默认是 浅层侦听 的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep
<script setup>
// 1. 导入watch
import { ref, watch } from 'vue'
const state = ref({ count: 0 })
// 2. 监听对象state
watch(state, ()=>{
console.log('数据变化了')
})
const changeStateByCount = ()=>{
// 直接修改不会引发回调执行
state.value.count++
}
</script>
<script setup>
// 1. 导入watch
import { ref, watch } from 'vue'
const state = ref({ count: 0 })
// 2. 监听对象state 并开启deep
watch(state, ()=>{
console.log('数据变化了')
},{deep:true})
const changeStateByCount = ()=>{
// 此时修改可以触发回调
state.value.count++
}
</script>
deep 深度监视,默认watch进行浅层监视
const ref1 = ref(简单类型) 可以直接监视
const ref2 = ref(复杂类型) 监视不到复杂类型内部数据的变化
对于对象中的属性进行监视
需求:在不开启deep的前提下,侦听age变化,只有age变化时才执行回调
const info = ref({
name:'cp',
age:18
})
watch(
() => info.value.age,
() => console.log('age发生变化了')
)
实例:
watch(() => userInfo.value.age,(newValue,oldValue) => {
console.log(newValue,oldValue)
})
总结:
组合式API
生命周期函数
选项式 VS 组合式
beforeCreate 和created 的相关代码一律放在setup中执行
生命周期函数基本使用
- 导入生命周期函数
- 执行生命周期函数,传入回调
<scirpt setup>
import { onMounted } from 'vue'
onMounted(()=>{
// 自定义逻辑
})
</script>
执行多次
生命周期函数执行多次的时候,会按照顺序依次执行
<script setup>
const getList =() =>{
}
//一进入页面的请求
getList()
//如果有写代码需要在mounted中执行
onMounted (() =>{
console.log('mounted生命周期函数')
})
//写成函数的调用方式可以调用多次,并不会冲突,而是按照顺序依次执行
</script>
<template>
</template>
父子通信
父传子
基本思路
- 父组件中给子组件绑定属性
- 子组件内部通过props选项接收数据
实例:
App.vue
<script setup>
//局部组件导入进来就能用
import {ref} from'vue'
import SonCom from'@/components/son-com.vue'
const money = ref(100)
const getMoney = () =>{
money.value +=10
}
</script>
<template>
<div>
<h3>
我是父组件-{{ money }}
<button @click="getMoney">挣钱</button>
</h3>
<!--给子组件,添加属性的方式传值 -->
<SonCom car="宝马车" :money="money"></SonCom>
</div>
</template>
son-com.vue
<script setup>
//由于写了setup所以无法直接配置props选项
//所以此处借助编译器宏函数接收子组件传递的数据
//子组件
const props = defineProps({
cart:String,
money:Number
})
console.log(props.car)
console.log(props.money)
</script>
<template>
<!-- 对于子组件传递过来的数据,模板中可以直接使用 -->
<div class="son">我是子组件 - {{ car }} - {{ money }}</div>
</template>
<style>
.son{
border:1px solid #000;
padding: 30px;
}
</style>
效果如下:
defineProps原理:
就是编译阶段一个标识,实际编译器解析式、时,遇到后会进行编译转换
子传父
基本思路
-
子组件内部通过 emit 方法触发事件
-
父组件中给子组件标签通过@绑定事件
实例:
App.vue
<script setup>
//局部组件导入进来就能用
import {ref} from'vue'
import SonCom from'@/components/son-com.vue'
const money = ref(100)
const getMoney = () =>{
money.value +=10
}
const changFn =(newMoney) =>{
money.value = newMoney
}
</script>
<template>
<div>
<h3>
我是父组件-{{ money }}
<button @click="getMoney">挣钱</button>
</h3>
<!--给子组件,添加属性的方式传值 -->
<SonCom
@change-money="changFn"
car="宝马车"
:money="money"></SonCom>
</div>
</template>
son-com.vue
<script setup>
//由于写了setup所以无法直接配置props选项
//所以此处借助编译器宏函数接收子组件传递的数据
//子组件
const props = defineProps({
cart:String,
money:Number
})
const emit = defineEmits(['changeMoney'])
console.log(props.car)
console.log(props.money)
const buy = () =>{
//需要emit触发事件
emit('changeMoney',5)
}
</script>
<template>
<!-- 对于子组件传递过来的数据,模板中可以直接使用 -->
<div class="son">我是子组件 - {{ car }} - {{ money }}</div>
<button>花钱</button>
</template>
<style>
.son{
border:1px solid #000;
padding: 30px;
}
</style>
效果如下:
总结:
模板引用
概念:
通过 ref标识 获取真实的 dom对象或者组件实例对象
实现步骤:
- 调用ref函数生成一个ref对象
- 通过ref标识绑定ref对象到标签
<script setup>
import { ref } from 'vue'
//1. 调用ref函数生成一个ref对象
const h1Ref = ref(null)
</script>
<template>
//2. 通过ref标识绑定ref对象到标签
<h1 ref = "h1Ref">我是dom标签h1</h1>
</template>
defineExpose()
默认情况下在<script setup>语法糖下 组件内部的属性和方法是不开放 给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法容许访问
说明:指定testMessage属性可以被访问到
总结
provide 和inject
作用和场景
顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信
跨层传递数据
实现步骤
-
顶层组件通过
provide函数提供数据 -
底层组件通过
inject函数提供数据
顶层组件:
provide('key',顶层组件中的数据)
底层组件:
const message = inject('key')
实例
App.vue
<script setup>
import { provide } from 'vue';
import centerCom from './components/center-com.vue';
//1.跨层传递普通数据
provide('theme-color','pink')
//2.跨层传递响应式数据
const count =ref(100)
provide('count',count)
setTimeout(() => {
count.value = 500
},2000)
//3.跨层级传递函数=>给孙后代传递可以改数据的方法
provide('changeCount',(newCount) =>{
count.value = newCount
})
</script>
<template>
<div>
<h1>我是顶层组件</h1>
<centerCom></centerCom>
</div>
</template>
center-com.vue
<script setup>
import BottomCom from './bottom-com.vue'
</script>
<template>
<div>
<h2>我是中间组件</h2>
<BottomCom></BottomCom>
</div>
</template>
bottom-com.vue
<script setup>
import { inject } from 'vue'
const themeColor = inject('theme-color')
const count = inject('count')
const changeCount = inject('changeCount')
const clickFn = () => {
changeCount(1000)
}
</script>
<template>
<div>
<h3>我是底层组件-{{ themeColor }} - {{ count }}</h3>
<button @click="clickFn">更新count</button>
</div>
</template>