vue watch 为什么触发了两次呢?

我只往sieteItems 里push 了一次。为啥 watch 执行了两次呢?

怎么可以知道是哪儿导致触发的?

组件 A里的

clipboard.png

clipboard.png

clipboard.png

下面是组件B里的

clipboard.png

clipboard.png

<template>
  <div class="grid resizable" ref="draggable" @contextmenu.prevent.stop v-bind:style="{ left: grid.position.x + 'px', top: grid.position.y + 'px',width:grid.position.w+'px',height:grid.position.h+'px'}">
    <h4 v-if="!grid.isRoot" :class="handle">{{grid.gridName}}</h4>
    <div :class="{'contentBox':true, 'isRoot':grid.isRoot}" @contextmenu.prevent.stop="show">
      <siteItem class="siteItemWrap"  v-for="item in grid.siteItems" :item.sync="item" :key="item.siteId" @deleteItem="deleteItem" @moveItem="moveItem"></siteItem>
      <gridItem v-for="grid in grid.gridItems" :grid.sync="grid" :key="grid.gridId" @resizGrid="resizGrid" @moveGrid="moveGrid"></gridItem>
      <div v-show="contextmenu.isShowContextmenu" v-bind:style="{ left: contextmenu.x + 'px', top: contextmenu.y + 'px'}" class="contextmenu">
        <el-button @click="addItem" :plain="true" type="info" icon="plus">新增网址</el-button>
        <br>
        <el-button @click="addGrid" :plain="true" type="info" icon="plus">新建格子</el-button>
        </li>
      </div>
    </div>
  </div>
</template>
<script type="text/javascript">
import siteItem from './siteItem';

export default {
  props: {
    grid: {
      type: Object,
      default: {
        gridId: '',
        gridName: '格子一',
        position: { x: '0', y: '0', w: '100', h: '100' },
        siteItems: [{
          siteId: '5',
          site: 'http://www.zhihu.com',
          name: '知乎',
          position: { x: '10', y: '20' }
        }],
        gridItems: [{
            gridId: '',
            gridName: '格子一',
            position: { x: '0', y: '0' },
            siteItems: [],
            gridItems: []
          },
          {
            gridId: '',
            gridName: '格子一',
            position: { x: '0', y: '0' },
            siteItems: [],
            gridItems: []
          }

        ]
      }
    }
  },
  data() {
    return {
      contextmenu: {
        isShowContextmenu: false,
        x: 0,
        y: 0
      },
      draggable: null

    }
  },
  computed: {
    handle: function() {
      return 'handle' + this.grid.gridId;
    }
  },
  watch: {
    'grid.siteItems' () {
      var self = this;
      console.log(this)
      //self.minPos();

    }
  },
  methods: {
    minPos() {
      var self = this;

      function setPos() {
        var x, y;
        $(self.draggable).children('.contentBox').children('.siteItemWrap,.grid').each(function(index, ele) {
          ele = $(ele);
          var disX = ele.position().left + ele.width();
          var disY = ele.position().top + ele.height();
          if (x == undefined) {
            x = disX;
            y = disY;
          } else {
            if (disX > x) {
              x = disX;
            }
            if (disY > y) {
              y = disY;
            }
          }
        })
        return { x, y: y + 30 };
      }
      if (!self.grid.isRoot) {
        self.$nextTick(function() {
          var minPos = setPos();
          $(self.draggable).resizable("option", "minHeight", minPos.y);
          $(self.draggable).resizable("option", "minWidth", minPos.x);
        })
      }
    },

    show(ev) {
      var rect = ev.target.getBoundingClientRect();
      this.contextmenu.x = ev.clientX - rect.left;
      this.contextmenu.y = ev.clientY - rect.top;
      this.$bus.$emit('isShowContextmenu', this.contextmenu);
    },

    addGrid() {
      var self = this;

      self.$bus.$emit('addGrid', self.grid, self.contextmenu);

    },
    moveGrid(grid) {
      var self = this;
      console.log(789)
      self.minPos();
      self.$bus.$emit('moveGrid', self.grid);

    },
    editGrid(grid) {
      var self = this;

      self.$bus.$emit('editGrid', self.grid);

    },
    deleteGrid() {
      var self = this;

      self.$bus.$emit('deleteGrid', self.grid);

    },
    addItem() {
      var self = this;
      self.$bus.$emit('addItem', self.grid, self.contextmenu);

    },
    deleteItem(item) {
      var self = this;
      self.$bus.$emit('deleteItem', self.grid, item);

    },
    moveItem(item) {
      var self = this;

      self.minPos();
      self.$bus.$emit('moveItem', item)


    },
    resizGrid() {
      var self = this;
      self.minPos();
    }


  },

  updated() {},
  mounted() {
    var self = this;
    self.draggable = self.$refs.draggable;

    self.$nextTick(function() {
      if (!self.grid.isRoot) {


        $(self.draggable).draggable({
          handle: '.' + self.handle,
          containment: "parent",
          stop: function(event, ui) {
            $(self.draggable).css('zIndex', self.$bus.zIndex++);

            self.grid.position.x = ui.position.left;
            self.grid.position.y = ui.position.top;
            self.$emit('moveGrid', self.grid);
            event.stopPropagation()
          }
        })
        $(self.draggable).resizable({
          stop: function(event, ui) {
            self.grid.position.w = ui.size.width;
            self.grid.position.h = ui.size.height;
            self.$emit('resizGrid');
            console.log('grid')
          },
          autoHide: true,
          containment: "parent"
          /*,
          helper: "ui-resizable-helper"*/
        })
        self.minPos();
      }
      $(self.draggable).children('.contentBox').droppable({
        greedy: true,
        accept: '.siteItemWrap',
        drop: function(event, ui) {

          console.log(ui)

          //ui.position.left < 0 || ui.position.top < 0||ui.position.left> ui.draggable.parent().width() ||ui.position.top> ui.draggable.parent().height()
          if (ui.draggable.parent().get(0)!== this) {

            ui.draggable.get(0).out = true;
            var left = ui.offset.left - $(this).offset().left;
            var top = ui.offset.top - $(this).offset().top;
            var oVue = ui.draggable.get(0).oVue;
            var grid = oVue.grid;
            var item = oVue.item;

            oVue.deleteItem();
            item.position.x = left;
            item.position.y = top;
            self.$bus.$emit('addItemFromOther', self.grid, item);
          }else{
            console.log(1)
          }
        }
      });

    })


  },
  created() {
    //alert('created grid')
  },
  name: 'gridItem',
  components: { siteItem }
}

</script>
<template>
  <div class="stage">
    <gridItem class="rootGrid" :grid.sync="rootGrid"  @click.native="close"></gridItem>
    <el-dialog @contextmenu.native.prevent.stop :title="siteOption=='add'?'新增书签':'编辑书签'" :visible.sync="dialogFormVisible" size="tiny" :modal="false">
      <el-form :label-position="'right'" label-width="80px" :model="form">
        <el-form-item label="网址">
          <el-input v-model="form.site" auto-complete="off" placeholder="请输入网址"></el-input>
        </el-form-item>
        <el-form-item label="名称">
          <el-input v-model="form.name" auto-complete="off" placeholder="请输入名称"></el-input>
        </el-form-item>
        <el-form-item label="格子">
          <el-select v-model="form.grid" placeholder="请选择格子">
            <el-option label="区域一" value="shanghai"></el-option>
            <el-option label="区域二" value="beijing"></el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button v-if="siteOption=='add'" type="primary" @click="submitAddForm">确 定</el-button>
        <el-button v-if="siteOption=='edit'" type="primary" @click="submitEditForm(form)">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import qs from 'qs';
import gridItem from './gridItem';
export default {
  data() {
    return {
      rootGrid: {
        gridId: '1',
        gridName: '格子1',
        position: { x: '0', y: '0' ,w:'500',h:'500'},
        isRoot:true,
        siteItems: [{
            siteId: '5',
            site: 'http://www.zhihu.com',
            name: '知乎',
            position: { x: '10', y: '20' },
            isShowEdit:false
          }

        ],
        gridItems: [{
            gridId: '1-1',
            gridName: '格子1-1',
            position: { x: '10', y: '10',w:'200',h:'100' },
            siteItems: [],
            gridItems: []
          },
          {
            gridId: '1-2',
            gridName: '格子1-2',
            position: { x: '20', y: '20',w:'500',h:'500' },
            siteItems: [{
              siteId: '51',
              site: 'http://www.zhihu.com',
              name: '知乎1',
              position: { x: '10', y: '20' },
              isShowEdit:false
            }],
            gridItems: [
              {
                gridId: '1-2-1',
                gridName: '格子1-2-1',
                position: { x: '50', y: '50' ,w:'300',h:'100'},
                siteItems: [{
                  siteId: '523',
                  site: 'http://www.zhihu.com',
                  name: '知乎1',
                  position: { x: '10', y: '20' },
                  isShowEdit:false
                }],
                gridItems: []
              }


            ]
          },
          {
            gridId: '1-3',
            gridName: '格子1-3',
            position: { x: '50', y: '50' },
            siteItems: [{
              siteId: '52',
              site: 'http://www.zhihu.com',
              name: '知乎1',
              position: { x: '10', y: '20',w:'300',h:'100' },
              isShowEdit:false
            }],
            gridItems: []
          }

        ]
      },

      siteOption: "add",
      currentGrid:{},
      currentContextmenu:{},
      currentItem:{},
      dialogFormVisible: false,
      form: {
        siteId: '5',
        site: '',
        name: '',
        grid: '',
        position: { x: '10', y: '20' }
      }
    }
  },
  beforeUpdate() {

    //alert('beforeUpdated hello')
  },
  updated() {

    //alert('updated hello111')
  },
  methods: {
    moveItem(item) {
      var self = this;
      self.currentItem = item;

    },

    deleteItem(grid, item) {
      var self = this;
      self.$axios.post('/mock/items/submitDelete', qs.stringify(item))
        .then(function(response) {
          self.$message({
            type: 'success',
            message: '删除成功'
          });
          var index = grid.siteItems.indexOf(item);
          grid.siteItems.splice(index, 1);
        })
        .catch(function(error) {
          console.log(error);
        });
    },
    close(ev) {
      this.currentItem.isShowEdit = false;
      this.currentContextmenu.isShowContextmenu = false;
    },
    addGrid() {
      this.gridItems.push({
        gridId: '',
        gridName: '格子一',
        gridSites: []
      })
    },
    editGrid() {

    },
    deleteGrid() {

    },
    submitAddForm() {
      var self = this;
      console.log(self.form,self.currentContextmenu,Object.assign({}, self.form),11)

      self.$axios.post('/mock/items/submitAdd', qs.stringify(Object.assign({}, self.form)))
        .then(function(response) {
          self.$message({
            type: 'success',
            message: '新增成功'
          });
          debugger;
          self.currentGrid.siteItems.push(response.data);
          self.dialogFormVisible = false;
        })
        .catch(function(error) {
          console.log(error);
        });
    },
    submitEditForm(data, isPos) {
      var self = this;
      self.$axios.post('/mock/items/submitEdit', qs.stringify(data))
        .then(function(response) {
          if (!isPos) {
            self.$message({
              type: 'success',
              message: '修改成功'
            });
          }
          Object.assign(self.currentItem, JSON.parse(JSON.stringify(data)));
          self.dialogFormVisible = false;
        })
        .catch(function(error) {
          console.log(error);
        });
    },
    init() {
      var self = this;
      self.$axios.get('/mock/items/all')
        .then(function(response) {
          console.log(response);


          //response.data;
        })
        .catch(function(error) {
          console.log(error);
        });
    }
  },
  created() {
    //alert('created hello')
    var self = this;    
    self.init();


    self.$bus.$on('isShowEdit', function (item) {
        self.currentItem.isShowEdit = false;
        self.currentContextmenu.isShowContextmenu = false;
        self.currentItem =  item; 
        self.currentItem.isShowEdit = true;

    })

    self.$bus.$on('isShowContextmenu', function (contextmenu) {
        self.currentItem.isShowEdit = false;
        self.currentContextmenu.isShowContextmenu = false;
        self.currentContextmenu = contextmenu;
        self.currentContextmenu.isShowContextmenu = true;

    })

    self.$bus.$on('addItem', function (grid,contextmenu) {
      self.currentGrid = grid;
      self.currentContextmenu = contextmenu;
      self.siteOption = "add";
      self.form.siteId = '',
      self.form.site = '',
      self.form.name = '',
      self.form.grid = '',
      self.form.position = contextmenu;
      self.dialogFormVisible = true;
    })

    self.$bus.$on('addItemFromOther', function (grid,item) { //新增从其他格子里移动进来的

      console.log(222)
      self.currentGrid = grid;
      self.$axios.post('/mock/items/submitAdd', qs.stringify(Object.assign({},item)))
        .then(function(response) {
          self.$message({
            type: 'success',
            message: '移动成功'
          });

          self.currentGrid.siteItems.push(response.data);

        })
        .catch(function(error) {
          console.log(error);
        });
    })

    self.$bus.$on('editItem', function (item) {
      self.currentItem = item;
      self.siteOption = "edit";
      Object.assign(self.form, JSON.parse(JSON.stringify(item)));

      self.dialogFormVisible = true;

    })
    
    self.$bus.$on('deleteItem', function (grid, item) {
      console.log(33)
      self.deleteItem(grid, item);
    })


  },
  mounted() {
    //alert('mounted hello')
  },
  components: { gridItem }
}

</script>

大体效果如下

clipboard.png

阅读 20.6k
2 个回答

等会= =、
既然你讲currentGrid和grid是同一个东西,那么如果值发生变化

  1. 事件回调里的self.currentGrid = grid会触发。
  2. 后面的push也会触发。

你可以先注释掉一个看看。


你的siteItem.vue组件中

<el-popover popper-class="edit" ref="popover{{siteId}}" 
placement="right" v-model="item.isShowEdit" trigger="manual">
  <ul class="showEdit">
    <li @click="edit">编辑</li>
    <li @click="open">打开</li>
    <li @click="deleteItem">删除</li>
  </ul>
</el-popover>
<el-button class="siteItem " type="text" size="large" v-popover:popover{{siteId}}>
  {{name}}
</el-button>

初步判定是v-model="item.isShowEdit"导致的。
查调用栈,第二次触发的时候有一次isShowEdit的修改,至于这个值得变化是怎么引起的(可能跟element-ui的popover组件有关),你再自己看看。
把上方的代码块注了就没事了,解决办法你再研究研究。

watch 如果你没指定是old 和new 哪个变化发生改变而去触发事件的话 会触发俩次

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题