Vue3 学习笔记(四)组合式函数、自定义指令、插件

组合式函数

利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数

鼠标跟踪器示例(可以借鉴下思路)

// event.js
import { onMounted, onUnmounted } from 'vue'
 
export function useEventListener(target, event, callback) {
  // 如果你想的话,
  // 也可以用字符串形式的 CSS 选择器来寻找目标 DOM 元素
  onMounted(() => target.addEventListener(event, callback))
  onUnmounted(() => target.removeEventListener(event, callback))
}
 
// mouse.js
import { ref } from 'vue'
import { useEventListener } from './event'
 
export function useMouse() {
  const x = ref(0)
  const y = ref(0)
 
  useEventListener(window, 'mousemove', (event) => {
    x.value = event.pageX
    y.value = event.pageY
  })
 
  return { x, y }
}
<script setup>
import { useMouse } from './mouse.js'

const { x, y } = useMouse()
</script>

<template>Mouse position is at: {{ x }}, {{ y }}</template>

最佳实践举例
命名:组合式函数约定用驼峰命名法命名,并以use作为开头。
输入参数:使用unref解包可能是ref的值
返回值:返回一个包含多个 ref 的普通的非响应式对象,这样该对象在组件中被解构为 ref 之后仍可以保持响应性。

// fetch.js
import { ref, isRef, unref, watchEffect } from 'vue'

export function useFetch(url) {
 const data = ref(null)
 const error = ref(null)

 function doFetch() {
   // 在请求之前重设状态...
   data.value = null
   error.value = null
   // unref() 解包可能为 ref 的值
   fetch(unref(url))
     .then((res) => res.json())
     .then((json) => (data.value = json))
     .catch((err) => (error.value = err))
 }

 if (isRef(url)) {
   // 若输入的 URL 是一个 ref,那么启动一个响应式的请求
   watchEffect(doFetch)
 } else {
   // 否则只请求一次
   // 避免监听器的额外开销
   doFetch()
 }

 return { data, error }
}

<script setup>
import { useFetch } from './fetch.js'

const { data, error } = useFetch('...')
</script>

在选项式 API 中使用组合式函数

import { useMouse } from './mouse.js'
import { useFetch } from './fetch.js'

export default {
  setup() {
    const { x, y } = useMouse()
    const { data, error } = useFetch('...')
    return { x, y, data, error }
  },
  mounted() {
    // setup() 暴露的属性可以在通过 `this` 访问到
    console.log(this.x)
  }
  // ...其他选项
}

自定义指令

一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会接收到指令所绑定元素作为其参数。

组件注册

export default {
  setup() {
    /*...*/
  },
  directives: {
    // 在模板中启用 v-focus
    focus: {
      /* ... */
    }
  }
}

全局注册

const app = createApp({})

// 使 v-focus 在所有组件中都可用
app.directive('focus', {
  /* ... */
})

指令钩子函数

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
}

注意:除了 el 外,其他参数都是只读的,不要更改它们。

指令的钩子会传递以下几种参数:

el:指令绑定到的元素。这可以用于直接操作 DOM。
binding:一个对象,包含以下属性。

  • value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2。
  • oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
  • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"。
  • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。
  • instance:使用该指令的组件实例。
  • dir:指令的定义对象。
    vnode:代表绑定元素的底层 VNode。
    prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。

举例:

<div v-demo="{ color: 'white', text: 'hello!' }"></div>  

app.directive('demo', (el, binding) => {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text) // => "hello!"
})

动态指令参数

<div id="dynamicexample">
  <h2>Scroll down the page</h2>
  <input type="range" min="0" max="500" v-model="pinPadding">
  <p v-pin:[direction]="pinPadding">Stick me {{ pinPadding + 'px' }} from the {{ direction || 'top' }} of the page</p>
</div>

const app = Vue.createApp({
  data() {
    return {
      direction: 'right',
      pinPadding: 200
    }
  }
})

app.directive('pin', {
  mounted(el, binding) {
    el.style.position = 'fixed'
    //拿到direction 默认为right
    const s = binding.arg || 'top'
    el.style[s] = binding.value + 'px'
  },
  updated(el, binding) {
    const s = binding.arg || 'top'
    el.style[s] = binding.value + 'px'
  }
})

//简写(在 mounted 和 updated 时触发相同行为,而不关心其他的钩子函数)
app.directive('pin', (el, binding) => {
  el.style.position = 'fixed'
  const s = binding.arg || 'top'
  el.style[s] = binding.value + 'px'
})

插件

插件 (Plugins) 是一种能为 Vue 添加全局功能的工具代码。

import { createApp } from 'vue'
const app = createApp({})
app.use(myPlugin, {
  /* 可选的选项 */
})

一个插件可以是一个拥有 install() 方法的对象,也可以直接是一个安装函数本身。
安装函数会接收到安装它的应用实例和传递给 app.use() 的额外选项作为参数。

const myPlugin = {
  install(app, options) {
    // 配置此应用 options 额外参数
  }
}

插件没有严格定义的使用范围,但是插件发挥作用的常见场景主要包括以下几种:
- 通过 app.component()app.directive() 注册一到多个全局组件或自定义指令。
- 通过 app.provide() 使一个资源可被注入进整个应用。
- 向 app.config.globalProperties 中添加一些全局实例属性或方法

比如:

// plugins/i18n.js
export default {
  install: (app, options) => {
    // 注入一个全局可用的 $translate() 方法
    app.config.globalProperties.$translate = (key) => {
      // 获取 `options` 对象的深层属性
      // 使用 `key` 作为索引
      return key.split('.').reduce((o, i) => {
        if (o) return o[i]
      }, options)
    }
     app.provide('i18n', options) 
  }
}
//引入插件
import i18nPlugin from './plugins/i18n'

app.use(i18nPlugin, {
  greetings: {
    hello: 'Bonjour!'
  }
})
<!--模板中使用-->
<h1>{{ $translate('greetings.hello') }}</h1>

<script setup>
import { inject } from 'vue'

const i18n = inject('i18n') //获取provide中的字典数据

console.log(i18n.greetings.hello)
</script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容