Vue.js自定义指令实现全选与反选功能详解

作者:云间搭桥时间:2026-10-30 13:39:19浏览:219

本文介绍如何利用Vue.js自定义指令重构传统的全选逻辑。通过封装v-check-all指令,实现全选状态与列表数据的自动同步,解决原生computed方案复用性差的问题。文中详细解析了指令的bind与update钩子用法,以及$watch监听数组变化的实现细节,并提供完整代码示例。

Vue.js自定义指令实现列表全选功能教程

由于初期对Vue.js掌握尚浅,全选功能的实现思路主要借鉴了社区中的常见做法。

第一步,从服务器获取数据后,为列表中的每个项目添加checked属性。

第二步,计算已选中项目的数量selectCount。当该数量与列表总项目数selectItems相等时,触发全选状态selectAll为选中。

第三步,点击全选框时,将所有项目的checked属性设为true;取消全选时,则设为false。

第四步,每当selectItems中的属性发生变化,系统会将所有checked为true的项目收集到数组checkedGroups中。

以下是基于上述逻辑的实现代码:


 //全选
 data: function() {
  return {
   selectItems: [], // 从服务器拿到的数据
  }
 },
 computed: {
  // 全选checkbox绑定的model
  selectAll: {
   get: function() {
    return this.selectCount == this.selectItems.length;
   },
   set: function(value) {
    this.selectItems.forEach(function(item) {
     item.checked = value;
    });
    return value;
   }
  },
  //选中的数量
  selectCount: {
   get: function() {
    var i = 0;
    this.selectItems.forEach(function(item) {
     if (item.checked) {
      i++;
     }
    });
    return i;
   }
  },
  //选中的数组
  checkedGroups: {
   get: function() {
    var checkedGroups = [];
    this.selectItems.forEach(function(item) {
     if (item.checked) {
      checkedGroups.push(item);
     }
    });
    return checkedGroups;
   }
  }
 }

这种传统方式存在明显缺陷:复用性极低,每次使用都需重新编写computed逻辑;此外,selectAll、checkedGroups和selectItems等变量名固定,缺乏灵活性。

因此,在本项目中,我尝试通过Vue.js的自定义指令重新实现全选功能。其核心思路与computed类似,具体代码见下:


export default {
 'check-all': {
  twoWay: true,
  params: ['checkData'],
  bind() {
   /**
    - 如果所有的列表的checked属性都为true,则选中全选框,否则不选中全选框
    */
   this.vm.$watch(this.params.checkData, (checkData) => {
    if (checkData.every((item) => item.checked)) {
     this.set(true);
    } else {
     this.set(false);
    }
   }, { deep: true });
  },
  // checkAll发生更改时
  update(checkAll) {
   /**
    - 如果全选框被选中,则将列表的所有checked属性转为true,否则转为false
    */
   if (checkAll) {
    this.vm[this.params.checkData].forEach((item) => {
     item.checked = true;
    });
   } else {
    this.vm[this.params.checkData].forEach((item) => {
     item.checked = false;
    });
   }
  },
 },
};

调用方式如下:


<input type="checkbox" v-model="checkAll" v-check-all="checkAll" check-data="checkData">
  <ul>
   <li v-for="item in checkData">
    <input type="checkbox" v-model="item.checked">
    {{item.text}}
   </li>
  </ul>

这种新用法的主要优势在于:

首先,使用便捷,只需在目标元素上添加v-check-all指令并绑定check-data即可。

其次,全选的model名称和数组名均可自定义。例如,全选model可不叫checkAll而叫checkAllData,列表数组也可不叫checkData而叫dataFromServer。

在指令定义中,设置twoWay为true,即可通过this.set(value)来更新checkAll的值。同时,使用params接收绑定在指令元素上的属性值checkData,即需要操作的目标数组。

利用this.vm获取当前指令的上下文,并调用该上下文的$watch方法来监听checkData的变化。若checkData中所有项目均被选中,则将checkAll设为true;否则设为false。

当指令绑定的值(即checkAll)发生变化时,若为true,则将checkData中所有项目的checked属性设为true;若为false,则设为false。至此,一个完整的全选指令逻辑即告完成。

在开发此指令时,曾尝试使用paramWatchers来监听checkData的变化,但发现checkData变动时并未触发paramWatchers的回调。查阅源码后得知,paramWatchers底层同样调用$watch,但不支持深度检测。


Directive.prototype._setupParamWatcher = function (key, expression) {
 var self = this;
 var called = false;
 var unwatch = (this._scope || this.vm).$watch(expression, function (val, oldVal) {
 self.params[key] = val;
 // since we are in immediate mode,
 // only call the param change callbacks if this is not the first update.
 if (called) {
  var cb = self.paramWatchers && self.paramWatchers[key];
  if (cb) {
  cb.call(self, val, oldVal);
  }
 } else {
  called = true;
 }
 }, {
 immediate: true,
 user: false
 });(this._paramUnwatchFns || (this._paramUnwatchFns = [])).push(unwatch);
};

总结

以上即为本文的全部内容。如有不足之处,恳请指正。希望本文内容能对大家有所帮助。

作者最新文章