Vue3基础
选项式 API
组件的初始化还是和Vue2那样
<template>
<!-- 可以有多个根元素 -->
<div>当前鼠标位置</div>
<div>x: {{ mouse.x }}</div>
<div>y: {{ mouse.y }}</div>
<div>当前点击次数:{{ count }}</div>
<button @click="add">点击</button>
</template>
<script>
export default {
// vue2 中采用的是 options API
// 常见的配置项: data created methods watch computed components
data() {
return {
mouse: {
x: 100,
y: 100,
},
count: 0
}
},
mounted() {
document.addEventListener("mousemove", this.mouseMove)
},
// vue2 叫 destroyed
destroyed() {
document.removeEventListener("mousemove", this.mouseMove)
},
methods: {
add() {
this.count++
},
mouseMove(e) {
this.mouse.x = e.clientX
this.mouse.y = e.clientY
}
}
}
</script>
组合式API
eg:
<template>
<!-- 可以有多个根元素 -->
<div>当前鼠标位置</div>
<div>x: {{ mouse.x }}</div>
<div>y: {{ mouse.y }}</div>
<div>当前点击次数:{{ count }}</div>
<button @click="add">点击</button>
</template>
<script>
import { onMounted, onUnmounted, reactive, ref } from 'vue'
export default {
setup() {
const count = ref(0)
const add = () => {
count.value++
}
const mouse = reactive({
x: 0,
y: 0,
})
const move = (e) => {
mouse.x = e.pageX
mouse.y = e.pageY
}
onMounted(() => {
document.addEventListener('mousemove', move)
})
onUnmounted(() => {
document.removeEventListener('mousemove', move)
})
return {
count,
add,
mouse,
}
},
}
</script>
代码抽离—hooks 钩子函数
import { ref, reactive, onMounted, onUnmounted } from 'vue'
// 抽离---hooks 钩子函数
// 业务 A
const useCount = () => {
const count = ref(0)
const add = () => {
count.value++
}
onMounted(() => {
// ❗✅多个生命周期,不会被合并
console.log('业务 A 生命周期')
})
return { count, add }
}
// 业务 B
const useMouseMove = () => {
const mouse = reactive({ x: 100, y: 100 })
const mouseMove = e => {
mouse.x = e.clientX
mouse.y = e.clientY
}
onMounted(() => {
document.addEventListener('mousemove', mouseMove)
})
onUnmounted(() => {
document.removeEventListener('mousemove', mouseMove)
})
return { mouse }
}
export default {
setup() {
const { count, add } = useCount()
const { mouse } = useMouseMove()
return { count, add, mouse }
}
}
Vue3生命周期图示
❗✅多个生命周期,不会被合并
setup 函数
❗setup
函数是 Vue3
特有的选项,作为组合式API的起点,注意以下三点:
- 从组件生命周期看,它在
beforeCreate
之前执行 - 函数中
this
不是组件实例,是undefined
- ✨如果数据或者函数在模板中使用,需要在
setup
返回
经验:
今后在vue3的项目中几乎用不到 this
reactive 函数
只能用于复杂类型数据,数组类型也不建议用
1. 导入 reactive 函数
import { reactive } from 'vue'
2. 通过 reactive 函数包装成响应式对象
const obj = reactive({
name:'zs',
age:18
})
const btn = () => {
obj.age++
}
3. 返回给模板使用
return { obj,btn }
组合式API-ref函数
ref 函数,可以把任意类型数据转换成响应式。
- 从
vue
中导入ref
函数 - 在
setup
函数中,使用ref
函数包装成响应式数据(不分类型) - 最后
setup
函数返回一个对象,包含该响应式数据即可
❗使用 ref
的两个注意点:
-
<script>
中需要.value
-
<template>
会自动解套,不需要.value
script setup 语法糖✨
不需要在setup函数里面写代码了,不需要 return,vue3 自动返回,直接变成:
<script setup>
import { onMounted, onUnmounted, reactive, ref } from 'vue'
let mouse = reactive({ x: 100, y: 100 })
function mouseMove(e) {
mouse.x = e.clientX
mouse.y = e.clientY
}
let count = ref(0)
const add = () => {
count.value++
}
onMounted(() => {
document.addEventListener('mousemove', mouseMove)
})
onUnmounted(() => {
document.removeEventListener('mousemove', mouseMove)
})
</script>
computed 函数
-
导入 computed 函数
import {computed} from 'vue'
-
调用 computed 函数并传入一个回调函数
const computedRes = computed(() => { return xx})
-
函数内部记得 return 返回结果
const nextAge = computed(() => {
return age.value + 1
})
computed 函数的另一个写法
const nextAge2 = computed({
get() {
return age.value + 2
},
set(value) {
age.value = value - 2
}
})
watch 函数
watch 参数
-
被监听的数据
-
回调函数
-
配置项(可选)
基本写法
基础类型
import { ref,watch } from 'vue'
const money = ref(1400)
watch(money,() => {
console.log('钱变化了', money.value)
})
复杂类型
const arr = JSON.parse(localStorage.getItem('todoVue3')) || [
{ id: 1, content: 'eat', done: true },
{ id: 2, content: 'play', done: false },
]
const list = ref(arr)
watch(list,() => {
localStorage.setItem('todoVue3', JSON.stringify(list.value))
},{
deep:true,
immediate:true
}
)
进阶写法
基础类型
import { ref,watch } from 'vue'
const money = ref(1400)
watch(money,(newVal,oldVal) => {
console.log('钱变化了',newVal,oldVal)
})
复杂类型
不常用,✨工作经验:一般是一个watch 监听一个值,监听对象属性的话建议监听整个属性(但是监听整个对象
newValue
此处和oldValue
是相等的,因为它们是同一个对象)
通过 getters 函数写法,可以侦听对象的某个属性
通过数组,可同时侦听多个数据
watch(
[()=> obj.value.age,() => obj.value.cp.age],
(newVal,oldVal){
console.log('obj变化了',newVal,oldVal)
},
{
deep:true
}
)
模板ref
获取DOM元素
-
创建 ref
const hRef = ref(null)
-
模板 ref 建立关联
<h1 ref="hRef"></h1>
-
组件挂载完毕后可以访问 DOM节点
onmounted { hRfe.value }
应用场景
//需求:获取输入框焦点
const iptRef = ref(null)
<input ref="inputRef" type="text" />
const focusClick = () =>{
iptRef.value.focus()
}
// 需求:获取输入框焦点
<audio ref="audioRef" controls>
audioRef.value.play()
❗获取组件的 DOM 元素的注意点
- 父组件想使用子组件的方法需要子组件用
defineExpose
主动暴露方法
代码实现
子
const sayHi = () => {
console.log('sayHi~')
}
// 暴露 方法
defineExpose({ sayHi })
父
// script
const footerRef = ref(null)
const clickBtn = () => {
sonRef.value.sayHi()
}
// template
<Son ref="sonRef"/>
父传子
基本结构
在 script 中用defineProps
获取 props
// father:
const money = ref(100)
<Children :money="money">
// son:
<script setup>
// 在 script 中获取 props
const props = defineProps({
money:Number,
car:{
type:String,
default:'宝马'
}
})
console.log('在script中获取props', props.money)
</script>
子传父 defineEmits
父
<ChildCom @change-money="changeMoneyFn" :money="money"></ChildCom>
子
定义 emit ,显式声明事件名称
const emits = defaultEmits(['changeMoney'])
const btn = () => {
emit('changeMoney',100) // ❗注意没有 this
}
// @click="emits('changeMoney')"
跨组件通讯
provide 提供数据
每个 provide 提供一个数据,可重复使用
provide('count',count)
inject 获取数据
每个 inject 接收一个数据,可重复使用
const count = inject('count')
辅助函数toRefs
const user = reactive({ name:'zg',age:80 })
const res = toRfes(user)
把 obj 的每一个属性都变成独立的 ref 对象,所以解构出来的每个属性都是响应式的 ref
const { name,age } = toRefs(user)
核心库
vue-router
基础使用
安装 npm i vue-router@4 --save
导入 创建路由实例的函数 和 创建哈希模式(createWebHashHistory )历史模式(createWebHistory) 的函数
import { createRouter,createWebHashHistory } form 'vue-router'
// 创建路由实例
const router = createRouter({
// vue2 mode:'hash'
history:createWebHashHistory,
routes:[]
})
export default router
挂载路由:
main.js
import router from './router'
// 以插件形式使用 router
const app = createApp(App)
app.use(router)
app.mount('#app')
路由配置和路由组件
在路由实例的 路由表
routes: [
{ path: '/home', component: () => import('../views/HomePage.vue') },
{ path: '/login', component: () => import('../views/LoginPage.vue') },
],
路由链接与路由出口
<router-link>
<router-view>
vue-router-组合式API
因为我们在 setup 里面没有访问 this,所以我们不能再直接访问 this.$router
或 this.$route
使用 useRouter
和 useRoute
函数:
import { useRouter,useRoute } from 'vue-router'
获取全局路由实例
const router = useRouter()
获取当前路由信息
const route = useRoute()
使用:
router.push('/home')
route.query/route.path/route.fullPath/route.params
✨非组件跳转页面方法:
❌useRouter 使用有限制,只能使用于 setup 组合式 API 内
// 非组件中,可通过导入路由实例,用于实现页面跳转
import router from '@/router';
// const router = useRouter();
// console.log(22, router); // undefined
// router.push('/');
// ✅非组件直接导入路由实例 import router from '@/router';
router.push('/');
pinia
Pinia 是 Vue.js 的轻量级状态管理库
为什么学习pinia?
- pinia和vuex4一样,也是vue官方的状态管理工具(作者是 Vue 核心团队成员)
- pinia相比vuex4,对于vue3的兼容性更好
- pinia相比vuex4,具备完善的类型推荐
- pinia同样支持vue开发者工具,最新的开发者工具对vuex4支持不好
- Pinia 的 API 设计非常接近
Vuex 5
的提案。
基础使用
安装 npm i pinia@2 --save
在 main.js
中挂载
import { createPinia } from 'pinia'
app.use(createPinia())
pinia 的 store
三个概念,state
、getters
和 actions
,可以假设这些概念相当于组件中的 data、 computed 和 methods。
✨pinia没有mutation,用起来更加方便
pinia 支持两种 API 风格:
defineStore('唯一标识',选项式API配置)
defineStore('唯一标识', 组合式API函数)
返回值是一个用于获取 store 的函数
建议命名规范:useXxxStore
export const useCounterStore = defineStore('counter',{
state:() => { return { count:0 }}
})
使用
import { useCounterStore } from './store/counter'
// 调用获取 store 的函数
const counterStore = useCounterStore()
{{ counterStore.count }}
选项式API写法
state:()=>{
// 建议 vue2、vue3 都在state return 一个对象
return { count:100 }
}
// getters 看做 computed
getters: {
doubleCount() {
// ❗❗不用再写state,通过 this 使用即可
return this.count * 2
},
},
// 使用 {{ counterStore.doubleCount }}
// actions 看做 methods,支持同步和异步,传参也很方便
actions: {
// 支持同步
addCount(val) {
this.count += val
},
// 支持异步
addCountASync() {
setTimeout(() => {
this.count++
}, 1000)
},
},
// 使用 @click="couterStore.addCount(10)"
❗state 与 getters 同名,以 getters 的为准
组合式API写法
import { defineStore } from 'pinia'
import { computed,ref } from 'vue'
export const useMoneyStore = defineStore('money',()=>{
// state
const salary = ref(1400)
// getters
const doubleSalary = computed(()=> saraly.value*2)
// actions
const addSalaryAsync = () =>{
setTomeout(()=>{
salary.value++
},1000)
}
// actions
const addSalary = (val)=>{
salary.value += val
}
// 🚨 注意:要使用的数据或方法,记得通过 return 返回
return {salary,doubleSalary,addSalaryAsync}
})
辅助函数storeToRefs
简化使用 store
❌解构出的方法用不了
✅✨工作经验:不使用 storeToRefs,直接使用 toRefs 就行,只不过官网上有,介绍一下而已
import { storeToRefs } from 'pinia'
import { useMoneyStore } from './store/money'
const moneyStore = useMoneyStore()
❌直接解构数据会失去响应式
// const { salary,doubleSalary } = moneyStore
✅storeToRefs 解构后还支持响应式
const { salary,doubleSalary } = storeToRefs(moneyStore)
使用:{{ salary }}
{{ doubleSalary }}
合并模块管理
从合并管理的模块中导入所需的 store
src/store/index.js
// 导出模块合集
export * from './modules/counter'
export * from './modules/money'
App.vue
import { useCounterStore,useMoneyStore } from './store'
不用分别从 ./store/counter
、 ./store/money
中导入
TodoList案例
store return 回去时记得+{ }
…未完待续