# vue-demos **Repository Path**: steveouyang/vue-demos ## Basic Information - **Project Name**: vue-demos - **Description**: Vue的一些进阶案例: 自定义指令 自定义hook 线上错误日志 权限控制 等等 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-07-20 - **Last Updated**: 2023-09-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 什么是自定义 hook - Vue3 强大的跨组件逻辑复用方式; - 通过将【响应式数据创建逻辑 + 生命周期回调逻辑 + 数据侦听逻辑】集成为一个函数,最终对外返回一组响应式数据; - 通过这种方式,轻松在组件间共享业务逻辑,且没有任何额外副作用; - 典型手写案例:useMouse, useCountDown, useAxios, useScroll... ### 什么是自定义指令 - Vue 在元素上逻辑复用的主要方式; - Vue 指令格式:v-指令名:指令参数.指令修饰符="指令值"; - 指令定义方式: ```js app.directive(name,{ onMounted(el,binding){...}, onUpdated(el,binding){...}, }) app.directive(name,(el,binding)=>{ // 在元素的宿主组件onMounted和onUpdated时做什么 }) ``` - 典型手写案例:v-pin, v-change... #### 案例:点击在组件外部 当使用 Vue 3 重构自定义指令时,需要按照新的 Vue 3 指令语法和 API 进行修改。下面是将上述`click-outside`指令重构为 Vue 3 风格的示例: ```js // 创建一个自定义指令对象 export default const clickOutsideDirective = { // 使用 `mounted` 钩子来替代 Vue 2 中的 `bind` 钩子 mounted(el, binding) { const handleClickOutside = event => { if (!el.contains(event.target)) { binding.value(); } }; document.addEventListener('click', handleClickOutside); // 在元素销毁前移除事件监听器 el._ClickOutsideEvent = handleClickOutside; }, // 使用 `unmounted` 钩子来替代 Vue 2 中的 `unbind` 钩子 unmounted(el) { document.removeEventListener('click', el._ClickOutsideEvent); delete el._ClickOutsideEvent; } }; ``` **全局注册指令** ```js import COD from "@directives/ClickOutsideDirective"; app.directive("click-outside", COD); ``` 在上面的示例中,我们使用`Directive`类从自定义指令对象`clickOutsideDirective`中创建了一个指令实例,并导出该指令实例。在实例中,我们使用了新的钩子函数`mounted`和`unmounted`来替代 Vue 2 中的`bind`和`unbind`钩子。 使用指令实例时,可以在模板中使用`v-click-outside`指令,并将要执行的方法作为指令的值传递给它: ``` ``` 需要注意的是,在 Vue 3 中,指令不再支持修饰符,但可以使用参数来扩展指令的功能。 #### 案例:触底加载下一页 先复习几个与滚动有关的概念 ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a15ac33310704d65bdf2fa76a0d707b3~tplv-k3u1fbpfcp-watermark.image?) 此处一个典型的自定义指令案例是实现无限滚动加载的指令。这个指令可以用于当滚动到页面底部时,自动加载更多数据。下面是一个基于 Vue 3 的示例: ```js // 在 Vue 3 中,需要单独导入 Directive 类 import { Directive } from "vue"; // 创建一个自定义指令对象 const infiniteScrollDirective = { // 使用 `mounted` 钩子来替代 Vue 2 中的 `bind` 钩子 mounted(el, binding) { const handleScroll = () => { const { scrollTop, scrollHeight, clientHeight } = document.documentElement; // 滚动到页面底部时,触发绑定的处理函数 if (scrollTop + clientHeight >= scrollHeight) { binding.value(); } }; window.addEventListener("scroll", handleScroll); // 在元素销毁前移除事件监听器 el._InfiniteScrollEvent = handleScroll; }, // 使用 `unmounted` 钩子来替代 Vue 2 中的 `unbind` 钩子 unmounted(el) { window.removeEventListener("scroll", el._InfiniteScrollEvent); delete el._InfiniteScrollEvent; }, }; // 创建一个指令实例,并导出 export const InfiniteScrollDirective = Directive.from(infiniteScrollDirective); ``` 在上面的示例中,我们创建了一个名为`infiniteScrollDirective`的自定义指令对象。在`mounted`钩子中,我们使用`addEventListener`监听`scroll`事件,即滚动事件。每当页面滚动时,都会执行`handleScroll`函数。 在`handleScroll`函数中,我们通过获取`document.documentElement`的`scrollTop`、`scrollHeight`和`clientHeight`属性来判断是否滚动到页面底部。当滚动到底部时,我们触发自定义指令绑定的处理函数,即`binding.value()`,来加载更多数据。 使用这个自定义指令时,可以在需要实现无限滚动加载的元素上使用`v-infinite-scroll`指令,并将要执行的加载数据的方法作为指令的值传递给它: ```html ``` 通过这个自定义指令,我们可以轻松地实现无限滚动加载数据的功能,提升用户体验和页面的交互性。用户只需滚动到页面底部,就可以自动加载更多数据,无需点击或其他操作。这种指令在处理大量数据时非常实用。 #### 案例:鼠标覆盖改变样式 ```js const hoverHandler = { // v-hover="{ backgroundColor: 'green', color: 'white' }" mounted(el, binding, vnode) { // 存储原来的样式 const originalStyle = window.getComputedStyle(el); const obj = {}; for (const key in binding.value) { obj[key] = originalStyle[key]; } // 鼠标覆盖时 使用binding定义的样式 el._mouseover = (e) => { for (const key in binding.value) { el.style[key] = binding.value[key]; } }; // 鼠标移出时 恢复原来的样式 el._mouseout = (e) => { for (const key in obj) { el.style[key] = obj[key]; } }; el.addEventListener("mouseover", el._mouseover); el.addEventListener("mouseout", el._mouseout); }, unmounted(el) { el.removeEventListener("mouseover", el._mouseover); el.removeEventListener("mouseout", el._mouseout); delete el._mouseover; delete el._mouseout; }, }; export default hoverHandler; ``` ```html
失败乃你之母
``` ```js import hoverHandler from "./directives/hover"; app.directive("hover", hoverHandler); ``` **执行效果** ![Video_2023-07-20_153626 00_00_00-00_00_30.gif](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3f6b285e5e4145f28e88892d4a9f15a1~tplv-k3u1fbpfcp-watermark.image?)