一、生命周期钩子
我们前面说过 setup 可以用来替代 data 、 methods 、 computed 、watch 等等这些选项,也可以替代 生命周期钩子。
那么setup中如何使用生命周期函数呢?
可以使用直接导入的 onX 函数注册生命周期钩子;
<template>
<div>
<button @click="increment">{{counter}}</button>
</div>
</template>
<script>
import { onMounted, onUpdated, onUnmounted, ref } from 'vue';
export default {
setup() {
const counter = ref(0);
const increment = () => counter.value++
onMounted(() => {
console.log("App Mounted1");
})
onMounted(() => {
console.log("App Mounted2");
})
onUpdated(() => {
console.log("App onUpdated");
})
onUnmounted(() => {
console.log("App onUnmounted");
})
return {
counter,
increment
}
}
}
</script>
二、Provide函数、Inject函数
事实上我们之前还学习过Provide和Inject,Composition API也可以替代之前的 Provide 和 Inject 的选项。
provide
我们可以通过 provide来提供数据:
可以通过 provide 方法来定义每个 Property;
provide可以传入两个参数:
name:提供的属性名称;
value:提供的属性值;
inject
在 后代组件 中可以通过 inject 来注入需要的属性和对应的值:
可以通过 inject 来注入需要的内容;
inject可以传入两个参数:
要 inject 的 property 的 name;
默认值;
数据响应式
为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 和 reactive。
修改响应式Property
provide将数据传递给子孙组件,inject子孙组件接收数据,数据流最好是单向的,如顶层组件有一个counter改变,数据流传递到子孙组件counter改变,但是不能子孙组件的counter自己改变,影响顶层组件改变。如果想实现,则可以在子孙组件添加一个事件,改变counter,在顶层组件添加监听事件,监听到要改变,则在顶层组件进行counter的修改。所以顶层组件传递给子孙组件值时,用readonly属性。
如果我们需要修改可响应的数据,那么最好是在数据提供的位置来修改:
我们可以将修改方法进行共享,在后代组件中进行调用;
//顶层组件
<template>
<div>
<home/>
<h2>App Counter: {{counter}}</h2>
<button @click="increment">App中的+1</button>
</div>
</template>
<script>
import { provide, ref, readonly } from 'vue';
import Home from './Home.vue';
export default {
components: {
Home
},
setup() {
const name = ref("coderwhy");
let counter = ref(100);
provide("name", readonly(name));
provide("counter", readonly(counter));
const increment = () => counter.value++;
return {
increment,
counter
}
}
}
</script>
//子孙组件:home.vue
<template>
<div>
<h2>{{name}}</h2>
<h2>{{counter}}</h2>
<button @click="homeIncrement">home+1</button>
</div>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const name = inject("name");
const counter = inject("counter");
// 此处事件counter并不会+1,因为顶层组件传过来的是只读
const homeIncrement = () => counter.value++
return {
name,
counter,
homeIncrement
}
}
}
</script>
三、useCounter
我们先来对之前的counter逻辑进行抽取:
//在hooks中抽取出到useCounter.js
import {ref, computed} from 'vue';
export default function() {
const counter = ref(0);
const doubleCounter = computed(() => counter.value * 2);
const increment = () => counter.value++;
const decreament = () => counter.value--;
return {
counter,
doubleCounter,
increment,
decreament
}
}
//在app.vue中调用
<template>
<div>
<h2>当前计数:{{counter}}</h2>
<h2>当前计数*2:{{doubleCounter}}</h2>
<button @click="increment"> +1 </button>
<button @click="decreament"> -1 </button>
</div>
</template>
<script>
import {ref, computed} from 'vue';
import {useCounter} from './hooks/useCounter';//引入
export default{
setup() {
// 调用获取数据
const {counter,doubleCounter,increment,decreament} = useCounter();
}
}
</script>
四、我们编写一个修改title的Hook:
//useTitle.js
import { ref, watch } from 'vue';
export default function(title = "默认的title") {
const titleRef = ref(title);
watch(titleRef, (newValue) => {
document.title = newValue
}, {
immediate: true
})
return titleRef
}
import { ref, computed } from 'vue';
import {
useCounter,
useTitle
} from './hooks';
export default {
setup() {
// title
const titleRef = useTitle("coderwhy");
setTimeout(() => {
titleRef.value = "kobe"
}, 3000);
return {
}
}
}
</script>
五、useScrollPosition
我们来完成一个监听界面滚动位置的Hook:
//useScrollPosition.js
import { ref } from 'vue';
export default function() {
const scrollX = ref(0);
const scrollY = ref(0);
document.addEventListener("scroll", () => {
scrollX.value = window.scrollX;
scrollY.value = window.scrollY;
});
return {
scrollX,
scrollY
}
}
<template>
<div class="scroll">
<div class="scroll-x">scrollX: {{scrollX}}</div>
<div class="scroll-y">scrollY: {{scrollY}}</div>
</div>
</template>
<script>
import { ref, computed } from 'vue';
import {
useScrollPosition,
} from './hooks';
export default {
setup() {
// 滚动位置
const { scrollX, scrollY } = useScrollPosition();
return {
scrollX,
scrollY,
}
}
}
</script>
<style scoped>
.content {
width: 3000px;
height: 5000px;
}
.scroll {
position: fixed;
right: 30px;
bottom: 30px;
}
</style>
六、useMousePosition
//useMousePosition
import { ref } from 'vue';
export default function() {
const mouseX = ref(0);
const mouseY = ref(0);
window.addEventListener("mousemove", (event) => {
mouseX.value = event.pageX;
mouseY.value = event.pageY;
});
return {
mouseX,
mouseY
}
}
七、useLocalStorage
//useLocalStorage.js
import { ref, watch } from 'vue';
export default function(key, value) {
const data = ref(value);
if (value) {
window.localStorage.setItem(key, JSON.stringify(value));
} else {
data.value = JSON.parse(window.localStorage.getItem(key));
}
watch(data, (newValue) => {
window.localStorage.setItem(key, JSON.stringify(newValue));
})
return data;
}
// 一个参数: 取值
// const data = useLocalStorage("name");
// // 二个参数: 保存值
// const data = useLocalStorage("name", "coderwhy");
// data.value = "kobe";
index.js:所有hooks的出口
import useCounter from './useCounter';
import useTitle from './useTitle';
import useScrollPosition from './useScrollPosition';
import useMousePosition from './useMousePosition';
import useLocalStorage from './useLocalStorage';
export {
useCounter,
useTitle,
useScrollPosition,
useMousePosition,
useLocalStorage
}
<template>
<div>
<h2>当前计数: {{counter}}</h2>
<h2>计数*2: {{doubleCounter}}</h2>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<h2>{{data}}</h2>
<button @click="changeData">修改data</button>
<p class="content"></p>
<div class="scroll">
<div class="scroll-x">scrollX: {{scrollX}}</div>
<div class="scroll-y">scrollY: {{scrollY}}</div>
</div>
<div class="mouse">
<div class="mouse-x">mouseX: {{mouseX}}</div>
<div class="mouse-y">mouseY: {{mouseY}}</div>
</div>
</div>
</template>
<script>
import { ref, computed } from 'vue';
import {
useCounter,
useLocalStorage,
useMousePosition,
useScrollPosition,
useTitle
} from './hooks';
export default {
setup() {
// counter
const { counter, doubleCounter, increment, decrement } = useCounter();
// title
const titleRef = useTitle("coderwhy");
setTimeout(() => {
titleRef.value = "kobe"
}, 3000);
// 滚动位置
const { scrollX, scrollY } = useScrollPosition();
// 鼠标位置
const { mouseX, mouseY } = useMousePosition();
// localStorage
const data = useLocalStorage("info");
const changeData = () => data.value = "哈哈哈哈"
return {
counter,
doubleCounter,
increment,
decrement,
scrollX,
scrollY,
mouseX,
mouseY,
data,
changeData
}
}
}
</script>
<style scoped>
.content {
width: 3000px;
height: 5000px;
}
.scroll {
position: fixed;
right: 30px;
bottom: 30px;
}
.mouse {
position: fixed;
right: 30px;
bottom: 80px;
}
</style>