认识Vue3
目录
1. Vue3的优势
2. Vue2 选项式 API vs Vue3 组合式API
使用create-vue搭建Vue3项目
1. 认识create-vue
2. 使用create-vue创建Vue3项目
熟悉Vue3项目目录和关键文件
组合式API - setup选项
1. setup选项的写法和执行时机
2. setup中写代码的特点
组合式API - reactive函数和ref函数
1. reactive
2. ref
3. reactive 对比 ref
组合式API - computed
组合式API - watch
1. 侦听单个数据
2. 侦听多个数据
3. immediate
4. deep
5. 精确侦听对象的某个属性
组合式API中的生命周期函数写法
1. 选项式对比组合式
2. 生命周期函数基本使用
组合式API - 父子通信
1. 父传子
2. 子传父
组合式API - 模版引用
1. 基本使用
2. defineExpose
组合式API - provide和inject
1. 作用和场景
2. 跨层传递普通数据
3. 跨层传递响应式数据
4. 跨层传递方法
Vue3.3 新特性-defineOptions
Vue3.3新特性-defineModel
1. Vue3的优势
2. Vue2 选项式 API vs Vue3 组合式API
选项式API就是在整个配置项中有着一个一个的选项,如下图:data是一个选项、methods是一个选项、computed是一个项目、watch是一个选项。
同功能的数据声明、方法、计算属性都是散落在各个位置的。
在VUE3 组合式API中,提供数据不用在data配置项中提供,而是可以调用方法的时候再提供,它将同功能的数据声明、方法的提供、计算属性全部放到了一起:
而且,因为所有的功能都集中管理,所以到另一个页面中,如果要复用的话,可以把同功能的一整块代码封装成一个函数,在函数中声明数据,在函数中声明方法、在函数中声明计算属性等等。
将来哪个页面要用,在页面中一调函数就可以将这段内容生成
使用create-vue搭建Vue3项目
1. 认识create-vue
create-vue是Vue官方新的脚手架工具,底层切换到了 vite (下一代前端工具链),为开发提供极速响应;vue-cli是创建Vue2项目的脚手架工具。
2. 使用create-vue创建Vue3项目
前置条件 - 已安装16.0或更高版本的Node.js
执行如下命令,这一指令将会安装并执行 create-vue
npm init vue@latest
熟悉Vue3项目目录和关键文件
vite.config.js
可以放一些和vite相关的配置内容
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue({
script: {
defineModel: true
}
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
vue3的main.js
//Vue3的main.js中,通过createApp函数去创建vue实例
//包括创建路由实例、仓库实例,都是用createRouter() createStore()函数
// 将创建实例进行了封装,保证每个实例的独立封闭性
import { createApp } from 'vue'
import App from './App.vue'
// mount 设置挂载点 #app (id为app的盒子)(往id为app的盒子上去挂载)
createApp(App).mount('#app')
组合式API - setup选项
1. setup选项的写法和执行时机
写法
<script>
export default {
//setup写成一个函数,将来里面就可以编写一些组合式API,在里面调各种函数
setup(){
},
beforeCreate(){
}
}
</script>
执行时机
在beforeCreate生命周期钩子之前执行
2. setup中写代码的特点
在setup函数中写的数据和方法需要在末尾以对象的方式return,就可以在模版中使用
1. setup函数的执行时机,比beforeCreate生命周期函数还要早
2. 在setup函数中,获取不到this (this是undefined)
3. 在setup函数中提供的数据 和 函数,需要在 setup函数 最后 return,才能模板<template></template>中应用!!!!!!
<script>
export default {
setup () {
// 数据
const message = 'hello Vue3'
// 函数
const logMessage = () => {
console.log(message)
}
return {
message,
logMessage
}
},
beforeCreate () {
console.log('beforeCreate函数')
}
}
</script>
<template>
<div>{{ message }}</div>
<button @click="logMessage">按钮</button>
</template>
问题:每次都要return,好麻烦?
可以通过 setup 语法糖简化代码
在script标签上添加 setup标记后,setup函数不用写了,而且也不需要再写导出语句,默认会添加导出语句
<script setup>
const message = 'this is a message'
const logMessage = () => {
console.log(message)
}
</script>
<template>
<div>{{ message }}</div>
<button @click="logMessage">按钮</button>
</template>
组合式API - reactive函数和ref函数
1. reactive
作用:接受 一个对象类型的数据,并返回一个响应式的对象
核心步骤:
1.从vue包中导入reactive函数
2.在<script setup>中调用reactive函数,并传入对象类型的初始值,并使用变量来接收返回值
<script setup>
import { reactive } from 'vue'
const state = reactive({
count: 100
})
const setCount = () => {
state.count++
}
</script>
<template>
<button @click="setCount">{{state.count}}</button>
</template>
2. ref
接收简单类型或者对象类型的数据,并返回一个响应式的对象
本质:是在原有传入数据的基础上,外层包了一层对象,包成了复杂类型,底层包成复杂类型之后,再借助 reactive 实现的响应式
注意点:
1. 因为在原有数据的基础上包了一层对象,所以脚本中访问数据,需要通过 .value访问
2. 在template中,.value不需要加 (帮我们扒了一层)
3.推荐:以后声明数据,统一用 ref => 统一了编码规范
核心步骤:
1.从vue包中导入reactive函数
2.在<script setup>中调用ref函数,并传入初始值,并使用变量来接收ref函数返回的响应式的对象
<script setup>
import { ref } from 'vue'
const count = ref(0)
const setCount = () => {
count.value++
}
</script>
<template>
<div>
<div>{{ count }}</div>
<button @click="setCount">+1</button>
</div>
</template>
3. reactive 对比 ref
-
都是用来生成响应式数据
-
不同点
-
reactive不能处理简单类型的数据
-
ref参数类型支持更好,但是必须通过.value访问
-
ref函数内部的实现依赖于reactive函数
-
-
在实际工作中的推荐
-
推荐使用ref函数,减少记忆负担,小兔鲜项目都使用ref
-
组合式API - computed
计算属性基本思想和Vue2保持一致,组合式API下的计算属性只是修改了写法
核心步骤:
1.从vue包中,导入computed函数
2.调用computed函数,里面传入一个箭头函数,在箭头函数中 返回基于响应式数据 计算出来的新值
-
const 计算属性 = computed(() => { return 计算返回的结果 })
案例:
<script setup>
// const 计算属性 = computed(() => {
// return 计算返回的结果
// })
import { computed, ref } 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)
})
// 定义一个修改数组的方法
const addFn = () => {
list.value.push(666)
}
</script>
<template>
<div>
<div>原始数据: {{ list }}</div>
<div>计算后的数据: {{ computedList }}</div>
<button @click="addFn" type="button">修改</button>
</div>
</template>
组合式API - watch
侦听一个或者多个数据的变化,当数据变化时执行回调函数。
俩个额外参数 immediate控制立刻执行,deep开启深度侦听。
1. 侦听单个数据
步骤:
1.从vue包中导入watch函数
2.调用watch函数,传入要侦听的响应式数据和回调函数
-
watch(ref对象, (newValue, oldValue) => { ... })
newValue是变化后的响应式数据
-
oldValue是变化前的响应式数据
案例:
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const nickname = ref('张三')
const changeCount = () => {
count.value++
}
const changeNickname = () => {
nickname.value = '李四'
}
// 1. 监视单个数据的变化
// watch(ref对象, (newValue, oldValue) => { ... })
watch(count, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
</script>
<template>
<div>{{ count }}</div>
<button @click="changeCount">改数字</button>
<div>{{ nickname }}</div>
<button @click="changeNickname">改昵称</button>
</template>
2. 侦听多个数据
同时侦听多个响应式数据的变化,就是第一个参数改写成数组的写法,无论哪个数据变了,都会触发回调函数
语法:
watch([ref对象1, ref对象2], (newArr, oldArr) => { ... })
- newArr是变化后的多个响应式数据组成的数组
- oldArr是变化前的多个响应式数据组成的数组
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const nickname = ref('张三')
const changeCount = () => {
count.value++
}
const changeNickname = () => {
nickname.value = '李四'
}
// 2. 监视多个数据的变化
// watch([ref对象1, ref对象2], (newArr, oldArr) => { ... })
watch([count, nickname], (newArr, oldArr) => {
console.log(newArr, oldArr)
})
</script>
<template>
<div>{{ count }}</div>
<button @click="changeCount">改数字</button>
<div>{{ nickname }}</div>
<button @click="changeNickname">改昵称</button>
</template>
3. immediate
一进页面,在侦听器创建时立即触发回调函数,响应式数据变化之后继续执行回调
语法:在watch函数的第三个形参位置传一个对象进去,然后对象中添加immediate属性。
// 3. immediate 立刻执行
watch(count, (newValue, oldValue) => {
console.log(newValue, oldValue)
}, {
immediate: true
})
4. deep
deep进行深度监视, 默认 watch 进行的是 浅层监视
const ref1 = ref(简单类型) 可以直接监视
const ref2 = ref(复杂类型) 监视不到复杂类型内部数据的变化,监视复杂类型时,监视的是其地址值
<script setup>
import { ref, watch } from 'vue'
const userInfo = ref({
name: 'zs',
age: 18
})
const setUserInfo = () => {
// 修改了 userInfo.value 修改了对象的地址,才能监视到
// userInfo.value = { name: 'ls', age: 50 }
userInfo.value.age++
}
// deep 深度监视
watch(userInfo, (newValue) => {
console.log(newValue)
}, {
deep: true
})
</script>
<template>
<div>{{ userInfo }}</div>
<button @click="setUserInfo">修改userInfo</button>
</template>
5. 精确侦听对象的某个属性
在不开启deep的前提下,侦听age的变化,只有age变化时才会执行回调函数
<script setup>
import { ref, watch } from 'vue'
const userInfo = ref({
name: 'zs',
age: 18
})
const setUserInfo = () => {
userInfo.value.age++
}
// 5. 对于对象中的单个属性,进行监视
watch(() => userInfo.value.age, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
</script>
<template>
<div>{{ userInfo }}</div>
<button @click="setUserInfo">修改userInfo</button>
</template>
组合式API中的生命周期函数写法
1. 选项式对比组合式
2. 生命周期函数基本使用
从vue包中,导入生命周期函数
调用生命周期函数,传入回调函数
注意:生命周期函数可以调用多次,其会按照顺序依次执行
<script setup>
import { onMounted } from 'vue';
// beforeCreate 和 created 函数中的相关代码一律放在 setup 中执行
const getList = () => {
setTimeout(() => {
console.log('发送请求,获取数据')
}, 2000)
}
// 一进入页面,就直接调用getList函数发送请求
getList()
// 如果有些代码需要在mounted生命周期函数中执行
onMounted(() => {
console.log('mounted生命周期函数 - 逻辑1')
})
// 写成函数的调用方式,可以调用多次,并不会冲突,而是按照顺序依次执行
onMounted(() => {
console.log('mounted生命周期函数 - 逻辑2')
})
</script>
<template>
<div></div>
</template>
组合式API - 父子通信
1. 父传子
基本思想
父组件中给子组件标签添加属性
子组件内部通过props选项接收数据
2. 子传父
基本思想
父组件中给子组件标签通过@绑定一个自定义事件
子组件内部通过 emit 方法触发自定义事件,传递数据
App.vue
<script setup>
//在vue3中,组件的局部注册,只要导入进来就可以使用
import SonCom from '@/components/son-com.vue';
import { ref } from 'vue'
const money =ref(100)
const getMoney = () => {
money.value += 10;
}
const changeFn = (newMoney) => {
money.value = newMoney;
}
</script>
<template>
<div>
<h3>父组件 - {{ money }}
<button @click="getMoney">挣钱</button>
</h3>
<SonCom car="宝马车"
:money="money"
@changeMoney="changeFn"
>
</SonCom>
</div>
</template>
son.com.vue
<script setup>
// 注意:由于写了 setup,所以无法直接配置 props 选项
// 所以:此处需要借助于 “编译器宏” 函数提供props选项接收父组件传递的数据
const props = defineProps({
car: String,
money: Number
})
const emit = defineEmits(['changeMoney'])
console.log(props.car)
console.log(props.money)
const buy = () => {
// 需要 emit函数 触发事件
emit('changeMoney', 5)
}
</script>
<template>
<!-- 对于props传递过来的数据,模板中可以直接使用 -->
<div class="son">
我是子组件 - {{ car }} - {{ money }}
<button @click="buy">花钱</button>
</div>
</template>
<style scoped>
.son {
border: 1px solid #000;
padding: 30px;
}
</style>
组合式API - 模版引用
概念:通过 ref标识 获取真实的 dom对象或者组件实例对象
1. 基本使用
实现步骤:
调用ref函数创建一个值为null的ref对象
然后在标签上添加ref标识,绑定创建的ref对象到标签上
最后使用ref对象.value即可访问到绑定的元素(必须模板渲染完成后,才能拿到绑定的元素)
2. defineExpose
默认情况下在 <script setup>语法糖下组件内部的属性和方法是不开放给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法容许访问。比如:指定testMessage属性可以被访问到
vue.app
<script setup>
//在vue3中,组件的局部注册,只要导入进来就可以使用
import TestCom from '@/components/test-com.vue'
import { onMounted, ref } from 'vue'
//---------------获取dom元素--------------
const inp = ref(null)
onMounted(() => {
console.log(inp.value)
inp.value.focus()
})
const clickFn = () => {
inp.value.focus()
}
//----------------获取组件实例----------------------
const testRef = ref(null)
const getCom = () => {
console.log(testRef.value.count)
testRef.value.sayHi()
}
</script>
<template>
<div>
<input ref="inp" type="text">
<button @click="clickFn">点击让输入框聚焦</button>
</div>
<TestCom ref="testRef"></TestCom>
<button @click="getCom">获取组件</button>
</template>
test-com.vue
<script setup>
const count = 999
const sayHi = () => {
console.log('打招呼')
}
defineExpose({
count,
sayHi
})
</script>
<template>
<div>
我是用于测试的组件 - {{ count }}
</div>
</template>
组合式API - provide和inject
1. 作用和场景
顶层组件向任意底层组件传递数据和方法,实现跨层级组件通信
2. 跨层传递普通数据
实现步骤
顶层组件通过
provide
函数提供数据
第一个形参是数据的key
第二个形参是真正要传递的数据
底层组件通过
inject
函数接收数据
通过数据的key来接收
3. 跨层传递响应式数据
在调用provide函数时,第二个参数设置为ref对象
4. 跨层传递方法
顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件的数据
案例:
App.vue(顶层组件)
<script setup>
import CenterCom from '@/components/center-com.vue';
import { provide,ref } from '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>
Vue3.3 新特性-defineOptions
背景说明:
有 <script setup> 之前,如果要定义 props, emits 可以轻而易举地添加一个与 setup 平级的属性。
但是用了 <script setup> 后,就没法这么干了 setup 属性已经没有了,自然无法添加与其平级的属性。
为了解决这一问题,引入了 defineProps 与 defineEmits 这两个宏。但这只解决了 props 与 emits 这两个属性。
如果我们要定义组件的 name 或其他自定义的属性,还是得回到最原始的用法——再添加一个普通的 <script> 标签。
这样就会存在两个 <script> 标签。让人无法接受。
所以在 Vue 3.3 中新引入了 defineOptions 宏。顾名思义,主要是用来让用户可以定义 选项式API中 的选项。可以用 defineOptions 定义任意的选项, props, emits, expose, slots 除外(因为这些可以使用 defineXXX 来做到)
Vue3.3新特性-defineModel
在Vue3中,自定义组件上使用v-model, 相当于传递一个modelValue属性,同时触发 update:modelValue 事件
我们需要先定义 props,再定义 emits 。其中有许多重复的代码。如果需要修改此值,还需要手动调用 emit 函数。
于是乎 defineModel 诞生了。
生效需要配置 vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue({
script: {
defineModel: true
}
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})