通过案例,彻底理解 Vue 中的 sync 修饰符

sync 修饰符是一个非常重要的知识点,将页面拆分成一个个组件的时候就经常用到 sync 修饰符。比如你做过 vue+element-ui 的管理系统,要对分页组件拆分成一个独立的子组件这个时候一定会使用到 sync 修饰符

为了深入理解,我们需要两个组件 myDialog.vue(对话框组件) 与 test.vue。其中 myDialog.vue 为子组件, test.vue为父组件。需要完成的功能:当我们点击父组件的显示按钮会弹出对话框组件。并且当我们点对话框的“确定”或“取消”按钮也要将子组件隐藏。实际效果就是模仿 element-ui 制作一个自己的 dialog 组件。

  • myDialog.vue 子组件
<template>
  <div>
    <div>
      <h1>Hello</h1>
      <div>
        <button>取消</button>
        <button>确定</button>
      </div>
    </div>
  </div>
</template><script>
export default {
  name: 'myDialog',
}
</script>
  • test.vue 父组件
<template>
  <div>
    <button>点击显示对话框</button>
    <my-dialog />
  </div>
</template><script>
import myDialog from './myDialog'
export default {
  name: 'test',
  components: {
    myDialog,
  },
}
</script>

解决思路:

  • 我们要在父组件中定义一个 isShow 变量默认值为false,并将 isShow 传给对话框组件,整个对话框的显示和隐藏都是由父组件的 isShow 来决定
  • 点击父组件的显示按钮将 isShow 进行取反。
  • 那么点击对话框的“确定”或者“显示”的时候我们就需要改变父组件的 isShow。(给对话框的按钮绑定点击事件,点击利用 发出一个 update 事件

补充:你可能会想在子组件中直接修改,父组件传过来的 isShow 岂不是更加简单。但是父组件传给子组件的 prop 是单向数据流,数据是只读的,不允许修改。强行修改不仅没有效果并且控制台会报错

所以代码可以修改如下:

<!-- 父组件 -->
<template>
  <div>
    <button id="btn" @click="isShow = !isShow">点击显示对话框</button>
    <my-dialog :isShow="isShow" @update="isShow = $event" />
  </div>
</template><script>
import myDialog from './myDialog'
export default {
  name: 'test',
  data() {
    return {
      isShow: false,
    }
  },
  components: {
    myDialog,
  },
}
</script><!-- 对话框组件 -->
<template>
  <div id="test" v-show="isShow">
    <div class="box">
      <h1>Hello</h1>
      <div>
        <button @click="updateShow">取消</button>
        <button @click="updateShow">确定</button>
      </div>
    </div>
  </div>
</template><script>
export default {
  name: 'myDialog',
  props: {
    isShow: {
      type: Boolean,
      default() {
        return false
      },
    },
  },
  methods: {
    
  },
}
</script>

关于事件中的 $event

  • 如果是js原生事件,那么 $event 为”事件对象“。
  • 如果是子组件利用 $emit() 发出的自定事件,那么 $event 表示自定义事件传过来的参数
  • 所以如上代码可以直接在组件标签上进行赋值:@update="isShow = $event"

为了更加能够语义化,更加清楚的表示需要修改的数据,可将部分代码修改如下

  • 子组件
    • updateShow() {
      //你可以将添加的 :isShow 理解为标识
      //这也是在 sync 做铺垫,要使用 sync 子组件比如遵从如下写法(‘update:xx’)
      this.$emit(‘update:isShow’, !this.isShow)
      },
  • 父组件
    • <!– 监听子组件发出的事件也要携带 :isShow 标识 –>
      <my-dialog :isShow=”isShow” @update:isShow=”isShow = $event” />
  • 补充:这里的 update:xx 其中 update 其实不是固定写法,你也可以换成其他字符,比如 u:xx,父子组件都要同时改为 u 。但是!!当你使用 sync 修饰符的时候 update: 就是一个固定的写法,为了方便 update: 我们一般都认为这是固定的写法,不管有没有使用 sync 修饰符

sync 修饰符:

sync 修饰符就是用于简化父组件 @update:isShow="isShow = $event" 这一行代码而诞生的。

使用:

<my-dialog :isShow.sync="isShow" />

使用了 sync 修饰符,vue会自动帮你编译出 @update:isShow="isShow = $event" 这一行代码。

注意!!子组件中的写法还是一样需要携带标识:this.$emit('update:isShow', !this.isShow)

通过如上的分析你现在一定能理解 element-ui 中 dialog 组件为什么会使用 :visible.sync=""。你也能写出与 element-ui 类似的组件

<el-dialog
  :visible.sync="dialogVisible"
</el-dialog>

最终完整代码

<!-- 父组件 -->
<template>
  <div>
    <button id="btn" @click="isShow = !isShow">点击显示对话框</button>
    <my-dialog :isShow.sync="isShow" />
  </div>
</template>
<script>
import myDialog from './myDialog'
export default {
  name: 'test',
  data() {
    return {
      isShow: false,
    }
  },
  components: {
    myDialog,
  },
}
</script><style>
#btn {
  background-color: sandybrown;
  color: #fff;
  border: none;
  margin: 50px;
  height: 35px;
  padding: 0 20px;
}
</style>
  
<!-- 子组件 -->
<template>
  <div id="test" v-show="isShow">
    <div class="box">
      <h1>Hello</h1>
      <div>
        <button @click="updateShow">取消</button>
        <button @click="updateShow">确定</button>
      </div>
    </div>
  </div>
</template><script>
export default {
  name: 'myDialog',
  props: {
    isShow: {
      type: Boolean,
      default() {
        return false
      },
    },
  },
  methods: {
    updateShow() {
      this.$emit('update:isShow', !this.isShow)
    },
  },
}
</script><style>
#test {
  position: absolute;
  z-index: 10px;
  background-color: rgba(0, 0, 0, 0.3);
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
}
#test .box {
  width: 400px;
  height: 300px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 1px 1px 10px #999;
  background-color: #fff;
}
#test .box div {
  position: absolute;
  bottom: 0;
  right: 0;
}
#test .box div button {
  width: 80px;
  height: 35px;
  margin-right: 10px;
  border: none;
}
</style>
赞 (2) 打赏