前言

vue 采用了 composition-api 之后,利用函数式特性,我们可以很简单地完成几个 hooks(就和 react hooks 相似),帮助我们实现以往方式难以完成的功能。

先展示几个源代码

监听屏幕大小 · useWindow

import { Ref, ref, onMounted, onUnmounted } from "@vue/composition-api";

export function useWindow(): {
	width: Ref<number>;
	height: Ref<number>;
} {
	const width = ref(0);
	const height = ref(0);
	const resizeAction = () => {
		return (() => {
			width.value = document.body.clientWidth;
			height.value = document.body.clientHeight;
		})();
	};

	onMounted(() => {
		width.value = document.body.clientWidth;
		height.value = document.body.clientHeight;
		window.addEventListener("resize", resizeAction);
	});

	onUnmounted(() => {
		window.removeEventListener("resize", resizeAction);
	});

	return { width, height };
}

监听 Style 变化 · useStyles

interface UseStyles {
	[key: string]: string;
}
function useStylesAsCreated(styles: string): Ref<UseStyles> {
	let stylesObject: UseStyles = {};

	const toHump = (str: string) =>
		str.replace(/-(\w)/g, (_, val) => val.toUpperCase());
	const stylesArr = styles
		.split(";")
		.map((v) => v.replace(/[\r\n]/g, "").replace(/\s+/g, ""))
		.filter((v) => v.length > 0)
		.map((v) => toHump(v));
	stylesArr.forEach((v) => {
		const styleFragment = v.split(":");
		const key = styleFragment[0];
		const val = styleFragment[1];
		stylesObject[key] = val;
	});
	return ref(stylesObject);
}

function useStylesAsChanged(styles: Ref<UseStyles>, newStyles: string): void {
	const newStylesArr = useStylesAsCreated(newStyles).value;
	styles.value = { ...styles.value, ...newStylesArr };
}

export function useStyles(styles: string): Ref<UseStyles>;
export function useStyles(styles: Ref<UseStyles>, newStyles: string): void;
export function useStyles(styles: string | Ref<UseStyles>, newStyles?: string) {
	if (arguments.length === 1) {
		return useStylesAsCreated(styles as string);
	} else {
		return useStylesAsChanged(styles as Ref<UseStyles>, newStyles!);
	}
}

监听键盘事件 · useKeyup

export function useKeyup(keyCode: number | string, callback?: any) {
	const keyupAction = (e: { keyCode: number; key: string }) => {
		if (e.keyCode === keyCode || e.key === keyCode) {
			if (callback) {
				callback();
			}
		}
	};
	onMounted(() => {
		window.addEventListener("keyup", keyupAction);
	});

	onUnmounted(() => {
		window.removeEventListener("keyup", keyupAction);
	});
}

加载远程 script · useScript

export function useScript(params: { src?: string; text?: string }): void {
	function loadScriptBySrc(link: string) {
		let script = document.createElement("script");
		script.type = "text/javascript";
		script.src = link;
		document.getElementsByTagName("head")[0].appendChild(script);
	}

	function loadScriptByText(text: string) {
		let script = document.createElement("script");
		script.type = "text/javascript";
		script.text = text;
		document.getElementsByTagName("head")[0].appendChild(script);
	}

	onMounted(() => {
		if (params.src) {
			loadScriptBySrc(params.src);
		}

		if (params.text) {
			loadScriptByText(params.text);
		}
	});
}

基本使用方法

  • 以 useStyles 为例
<template>
	<div :style="styles">
		<button @click="change">更改</button>
	</div>
</template>

<script lang="ts">
	import { useStyles } from "@/utils/useStyles";
	import { createComponent, ref } from "@vue/composition-api";

	export default createComponent({
		setup() {
			const styles = useStyles(`
    		background-color: red;
    		min-height: 100px;
    		width: 200px;
        `);
			const change = () => {
				useStyles(styles, "background-color: blue;");
			};
			return {
				styles,
				change,
			};
		},
	});
</script>