# Vue 编写一个长按指令插件

# 1. 如何编写 Vue 插件

在以往的 Vue 项目开发过程中,我们使用插件的方法是Vue.use(plugin)。如:

import filters from "./filter/filters";
Vue.use(filters);
1
2

plugin 为 Object 对象,需内置一个install()方法方可使用。该方法第一个参数为Vue对象,其余参数由使用者传入决定。

plugin.install = function(Vue, options) {}
1

# 2. 编写 Vue 长按指令

  1. 根据官方文档:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化
  bind: function (el, binding, vnode, oldVnode) {
  },
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  },
  // 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
  update: function (el, binding, vnode, oldVnode) {
  },
  // 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  componentUpdated: function (el, binding, vnode, oldVnode) {
  },
  // 只调用一次,指令与元素解绑时调用。
  unbind: function (el, binding, vnode, oldVnode) {
  },
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

此次长按指令我们只用到了bind()函数

  1. 分析长按功能实现

监听 mousedown 和 mouseout (移动端为 touchstart 和 touchend),间隔时间大于某个时间则视为长按事件触发。

因此需设置一个变量存放定时器let pressTimer = null;

一个开始和取消定时器方法——

// 创建计时器( 1秒后执行函数 )
let start = (e) => {
  if (e.type === 'click' && e.button !== 0) {
    return;
  }
  if (pressTimer === null) {
    pressTimer = setTimeout(() => {
      // 执行函数
      handler();
    }, 1000)
  }
}
// 取消计时器
let cancel = (e) => {
  // 检查计时器是否有值
  if ( pressTimer !== null ) {
    clearTimeout(pressTimer);
    pressTimer = null;
  }
}         
// 运行函数
const handler = (e) => {
  // 执行传递给指令的方法
  binding.value(e)
};  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

给各种事件设置监听——

// 添加事件监听器
el.addEventListener("mousedown", start);
el.addEventListener("touchstart", start);
// 取消计时器
el.addEventListener("click", cancel);
el.addEventListener("mouseout", cancel);
el.addEventListener("touchend", cancel);
el.addEventListener("touchcancel", cancel);
1
2
3
4
5
6
7
8

功能优化,当指令传入的值不为函数时提醒用户——

// 确保提供的表达式是函数        
if (typeof binding.value !== 'function') {            
  // 获取组件名称            
  const compName = vNode.context.name;            
  // 将警告传递给控制台            
  let warn = `[longpress:] provided expression '${binding.expression}' is not afunction, but has to be `;
  if (compName) { warn += `Found in component '${compName}' `}
  console.warn(warn);
}  
1
2
3
4
5
6
7
8
9

# 3. 使长按时间可定制化

// longpress.js
export default {
  install(Vue, options = {
    time: 2000
  }) {
    // ...
  }
}
1
2
3
4
5
6
7
8

且定时器中的时间改为options.time,然后Vue.use(plugin, {time: 5000})即可。

# 4. 完整代码

// longpress.js
export default {
  install(Vue, options = {
    time: 2000
  }) {
    Vue.directive('longpress', {    
      bind: function(el, binding, vNode) {         
        // 确保提供的表达式是函数        
        if (typeof binding.value !== 'function') {            
          // 获取组件名称            
          const compName = vNode.context.name;            
          // 将警告传递给控制台            
          let warn = `[longpress:] provided expression '${binding.expression}' is not afunction, but has to be `;
          if (compName) { warn += `Found in component '${compName}' `}
          console.warn(warn);
        }      
        // 定义变量
        let pressTimer = null;
        // 定义函数处理程序
        // 创建计时器( 1秒后执行函数 )
        let start = (e) => {
          if (e.type === 'click' && e.button !== 0) {
            return;
          }
          if (pressTimer === null) {
            pressTimer = setTimeout(() => {
              // 执行函数
              handler();
            }, options.time)
          }
        }
        // 取消计时器
        let cancel = (e) => {
          // 检查计时器是否有值
          if ( pressTimer !== null ) {
            clearTimeout(pressTimer);
            pressTimer = null;
          }
        }         
        // 运行函数
        const handler = (e) => {
          // 执行传递给指令的方法
          binding.value(e)
        };  
        // 添加事件监听器
        el.addEventListener("mousedown", start);
        el.addEventListener("touchstart", start);
        // 取消计时器
        el.addEventListener("click", cancel);
        el.addEventListener("mouseout", cancel);
        el.addEventListener("touchend", cancel);
        el.addEventListener("touchcancel", cancel);
      }
    })
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

main.js

// main.js
import LongPress from 'longpress.js'
Vue.use(LongPress, {time: 2500})
1
2
3

component.vue

// template
<div v-longpress="test"></div>
// methods
methods: {
  test(event) {
    console.log(111)
  }
}
1
2
3
4
5
6
7
8

希望本文对你有帮助!!