生命周期钩子
setup 可以用来替代 data 、 methods 、 computed 、watch 等等这些选项,也可以替代 生命周 期钩子
可以使用直接导入的 onX 函数注册生命周期钩子
可以看到的是 在composition api中,是没有created和beforeCreate这两个钩子函数的
因为setup函数会在组件被创建完成的时候就被调用
所以原本在created和beforeCreate中需要执行的逻辑,直接写在setup函数内部即可
<template>
<div>
<button @click="changeCount">{{ count }}</button>
</div>
</template>
<script>
import {
ref,
onMounted,
onUpdated,
onBeforeUnmount
} from 'vue'
export default {
name: 'Home',
setup() {
let count = ref(0)
console.log('setup 函数会最先被调用')
// 在Vue3中生命周期函数可以被多次调用
onMounted(() => console.log('onMounted1'))
onMounted(() => console.log('onMounted2'))
onUpdated(() => console.log('onUpdated'))
onBeforeUnmount(() => console.log('onBeforeUnmount'))
const changeCount = () => count.value++
return {
count,
changeCount
}
}
}
</script>
provide和inject
事实上我们之前还使用过Provide和Inject,Composition API也可以替代之前的 Provide 和 Inject 的选项
我们可以通过 provide 方法来定义每个 Property(提供数据),
在 后代组件 中可以通过 inject 来注入需要的属性和对应的值(注入需要的属性和对应的值)
provide可以传入两个参数:
- name:提供的属性名称
- value:提供的属性值
inject可以传入两个参数:
- 要 inject 的 property 的 name
- 默认值
父组件 --- 数据提供者
<template>
<div>
<h2>Home组件中的name {{ name }}</h2>
<h2>Home组件中的age {{ age }}</h2>
<button @click="changeAgeInHome">修改Home组件中的age</button>
<Home />
</div>
</template>
<script>
import { ref, readonly, provide } from 'vue'
import Home from './components/Home.vue'
export default {
name: 'App',
components: {
Home
},
setup() {
const name = ref('Klaus')
const age = ref(23)
// 提供数据
// 参数1 --- key
// 参数2 --- value
// provide给后代组件的数据最好修改为readonly
// 这样后代组件就无法修改父组件provide过来的数据
// 如果要修改, 后代组件只能emit一个事件,让父组件(数据提供者)这里修改
// 这么做的目的是为了确保单向数据流,即数据都是从父组件传递给子组件的
// 这样后期调试的时候,数据来源会变得统一,便于调试和维护
provide('name', readonly(name))
provide('age', readonly(age))
const changeAgeInHome = () => age.value++
return {
name,
age,
changeAgeInHome
}
}
}
</script>
子组件 --- 数据使用者
<template>
<div>
<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
</div>
</template>
<script>
import { readonly, ref, inject } from 'vue'
export default {
name: 'Home',
setup() {
// inject用于接收provide提供的值 --- 函数的返回值就是我们所需要使用的那个值
// inject函数有两个参数
// 参数1: 接收的数据名称
// 参数2: 默认值 --- 可选参数
const name = inject('name')
// 默认值设置为readonly也是为了避免子组件随意修改父组件传入的数据
const age = inject('age', readonly(ref(18))
return {
name,
age
}
}
}
</script>
阶段练习
计数器
使用
<template>
<div>
<p>count: {{ count }}</p>
<p>doubleCount: {{ doubleCount }}</p>
<button @click="increment">increment</button>
<button @click="decrement">decrement</button>
</div>
</template>
<script>
// 传入的hook函数的名称可以任意取,但是推荐参考react中的hook函数,取名的时候使用use开头的小驼峰
import useCount from './hooks/useCount'
export default {
name: 'Home',
setup() {
const {
count,
doubleCount,
increment,
decrement
} = useCount() // 引入的hook是函数,所以要先调用
return {
count,
doubleCount,
increment,
decrement
}
}
}
</script>
hook -- /src/hooks/useCount.js
import { ref, computed } from 'vue'
export default function() {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const increment = () => count.value++
const decrement = () => count.value--
return {
count,
doubleCount,
increment,
decrement
}
}
另一种使用方式
<template>
<div>
<p>count: {{ count }}</p>
<p>doubleCount: {{ doubleCount }}</p>
<button @click="increment">increment</button>
<button @click="decrement">decrement</button>
</div>
</template>
<script>
// 可以直接对hook函数的返回值直接解构挂载到模板中进行使用
import useCount from './hooks/useCount'
export default {
name: 'Home',
setup() {
return {
// 不推荐 --- 解构出的数据需要使用在模板上
// 如果引入的hook函数过多,那么就无法方便的获知,
// 那个数据来源于哪一个hook函数,不利于调试
...useCount()
}
}
}
</script>
监听滚动位置
使用
<template>
<div class="screen">
<div class="hint">
<p>scrollX: {{ scrollX }}</p>
<p>scrollY: {{ scrollY }}</p>
</div>
</div>
</template>
<script>
import useScrollPostion from './hooks/useScrollPostion'
export default {
name: 'App',
setup() {
const { scrollX, scrollY } = useScrollPostion()
return {
scrollX,
scrollY
}
}
}
</script>
<style scoped>
.screen {
width: 300vw;
height: 300vh;
}
.hint {
position: fixed;
right: 30px;
bottom: 30px;
}
</style>
hook
import { ref } from 'vue'
export default function() {
const scrollX = ref(0)
const scrollY = ref(0)
window.addEventListener('scroll', () => {
scrollX.value = window.scrollX
scrollY.value = window.scrollY
})
return {
scrollX,
scrollY
}
}
监听鼠标滚动
import { ref } from 'vue'
export default function() {
const mouseX = ref(0)
const mouseY = ref(0)
window.addEventListener('mousemove', e => {
// 可以使用event中的pageX和pageY来获取到鼠标移动的位置
mouseX.value = e.pageX
mouseY.value = e.pageY
})
return {
mouseX,
mouseY
}
}
localStorage
hook
import { ref, watch } from 'vue'
export default function(key, value) {
const data = ref(value)
if (value) {
localStorage.setItem(key, JSON.stringify(value))
} else {
data.value = JSON.parse(localStorage.getItem(key))
}
// data是一个ref对象,所以监听的时候,要监听data.value
// v 是 newValue的值,也就是data的value属性值
// tips: 如果ref的值是一个对象,那么ref对象的value值所对应的那个对象默认是一个reactive对象
watch(data.value, v => localStorage.setItem(key, JSON.stringify(v)))
return data
}
使用
<template>
<div>
<h2>{{ storage.name }}</h2>
<button @click="changeStorage">change</button>
</div>
</template>
<script>
import useLocalStorage from './hooks/useLocalStorage'
export default {
name: 'App',
setup() {
const storage = useLocalStorage('info', { name: 'Klaus' })
console.log(useLocalStorage('info').value?.name)
const changeStorage = () => {
storage.value.name = 'Alex'
}
return {
storage,
changeStorage
}
}
}
</script>
setup顶层编写
<script setup>
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖最后还是会被编译为使用
setup
函数的形式编写的vue组件
父组件
<template>
<div>
<Cpn :message="msg" @changeMsg="changeMsg" />
</div>
</template>
<script setup>
import { ref } from 'vue'
// script setup中无须定义组件的name属性,其值会被命名为SFC的文件名称
// 在这里 组件引入以后直接使用即可,不需要在进行任何的注册操作
import Cpn from './components/Cpn.vue'
const msg = ref('Hello World')
const changeMsg = v => msg.value = v
// 所有需要在模板中使用的方法和数据不需要在return出来
</script>
子组件
<template>
<div>
<h2>{{ message }}</h2>
<button @click="change">change</button>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
// 通过defineProps来接收父组件传入的props
defineProps({
message: {
type: String,
required: true
}
})
// 通过defineEmits方法来定义需要触发的事件
// 该方法会返回一个函数, 所有在注册过的方法都可以使用这个方法触发一些事件
const emit = defineEmits(['changeMsg'])
const change = function() {
emit('changeMsg', 'Hello Vue')
}
</script>