前言
在我学习 vue3 之后,觉得相同功能的代码逻辑放在一起的方式确实比 vue2 那种变量按类型分开管理的好使;于是我尝试在 vue2 中找出类似 vue3 组合式函数那种的写法…
开始
我以两个小例子来说明
useMouse
官网例子的改写
这个是vue3的 组合式函数的例子
// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'
// 按照惯例,组合式函数名以“use”开头
export function useMouse() {
// 被组合式函数封装和管理的状态
const x = ref(0)
const y = ref(0)
// 组合式函数可以随时更改其状态。
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
// 一个组合式函数也可以挂靠在所属组件的生命周期上
// 来启动和卸载副作用
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
// 通过返回值暴露所管理的状态
return { x, y }
}
而在 vue2 中可以这么实现
原理
- 利用 Vue.observable 实现创建响应式数据
- 利用 mixins 实现生命周期的添加
具体实现
// hook/useMouse
import Vue from "vue";
export function useMouse() {
// 被组合式函数封装和管理的状态
const x = Vue.observable({ value: 0 });
const y = Vue.observable({ value: 0 });
// 组合式函数可以随时更改其状态。
function update(event) {
x.value = event.pageX;
y.value = event.pageY;
}
// 一个组合式函数也可以挂靠在所属组件的生命周期上
// 来启动和卸载副作用
const mixins = {
mounted() {
window.addEventListener("mousemove", update);
},
destroyed() {
window.removeEventListener("mousemove", update);
},
};
// 通过返回值暴露所管理的状态
return { x, y, mixins };
}
页面中使用
<template>
<div class="example">
<div>({{ mouseX }}, {{ mouseY }})</div>
</div>
</template>
<script>
import { useMouse } from "./hook/useMouse";
const { x, y, mixins: mouseMixins } = useMouse();
export default {
components: {},
props: {},
mixins: [mouseMixins],
data() {
return {
mouseX: x,
mouseY: y,
};
},
};
</script>
useScrollList
这个是把每次都要滚动加载的功能进行了一个简单抽离,以方便复用,去应付其他的滚动加载
直接上代码
import Vue from "vue";
function useScrollList(options = {}) {
const { requestApi, pageParams } = options;
const list = Vue.observable([]);
const pageData = Vue.observable({
page: 1,
pageSize: 15,
lastPage: 1,
});
if (pageParams) {
Object.assign(pageData, pageParams);
}
let loading = false;
async function getList(options) {
try {
if (pageData.page > pageData.lastPage || loading) return;
loading = true;
const res = await requestApi({
page: pageData.page,
pageSize: pageData.pageSize,
});
list.push(...res.data);
pageData.lastPage = res.last_page;
pageData.page += 1;
} catch (error) {
} finally {
loading = false;
}
}
return {
list,
getList,
};
}
export { useScrollList };
页面使用上
<template>
<div class="example" @scroll="handleScroll">
<ul>
<li v-for="game in gameList" :key="game.id">
<pre>
{{ game }}
</pre>
</li>
</ul>
</div>
</template>
<script>
import { getGameApi } from "./api/game";
import { useScrollList } from "./hook/useList";
import { useMouse } from "./hook/useMouse";
const { list: gameList, getList: getGameList } = useScrollList({
requestApi: getGameApi,
pageParams: {
pageSize: 12,
},
});
export default {
components: {},
props: {},
data() {
return {
gameList,
};
},
created() {
getGameList();
},
methods: {
handleScroll(event) {
const el = event.target;
const scrollTop = el.scrollTop;
const scrollHeight = el.scrollHeight;
const clientHeight = el.clientHeight;
if (Math.ceil(scrollTop + clientHeight) >= scrollHeight) {
getGameList();
}
},
},
};
</script>
<style>
.example {
height: 100vh;
overflow: hidden;
overflow-y: scroll;
}
</style>
最后再看一看
- Vue.observable 不能监听基础类型像 number;所以可以使用{value: 其他数据类型},就像vue3那样,但是在页面上是不会解构的,所以使用时别忘记加
.value
- Vue.observable 返回的变量具有响应式,所以它不应该被修改,比如想清空数组时候 直接
list = []
是不行的,可以用splice
或者其他方式,最重要的是不能直接修改原数组!
思考
- 这个其实像 vuex 状态管理,但是它不是全局的,是小范围的状态管理;因为我可能只在这个页面及其组件会用到某个数据;
- 减少了挺多父子组件之间的传参,比如有些人喜欢在父组件进行删除事件,有些人又在子组件进行删除事件,不统一的时候,会造成比较多的困惑;
源码
https://github.com/adcGG/article-project.git 里面的 vue-function 文件夹