sync修饰符做双向绑定

9149次阅读 636人点赞 作者: WuBin 发布时间: 2021-04-22 11:04:04
扫码到手机查看

推荐使用update:myPropName模式取代双向绑定

在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。所以在封装组件的时候,就需要使用watch或者v-model去实现组件间的数据传输。具体请参考我的其他两篇文章:《使用watch实现组件props双向绑定》和 《v-model实现双向绑定自定义组件》

这也是为什么官方推荐以update:myPropName的模式触发事件取而代之。

举个例子,在一个包含titleprop 的假设的组件中,我们可以用以下方法表达对其赋新值的意图:

this.$emit('update:title', newTitle)

然后父组件可以监听那个事件并根据需要更新一个本地的数据 property。例如:

<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

为了方便起见,我们为这种模式提供一个缩写,即.sync修饰符:

<text-document v-bind:title.sync="doc.title"></text-document>
注意带有.sync修饰符的v-bind不能和表达式一起使用 (例如v-bind:title.sync=”doc.title + ‘!’”是无效的)。取而代之的是,你只能提供你想要绑定的 property 名,类似v-model

使用sync同步修改单一属性的例子

示例是说明问题最好的手段。我们来看这样一个例子:父子组件都可以通过点击修改父组件的状态,而父组件状态修改是自动发生的。我们不使用传统的watch方式去实现。

<style type="text/css">
  #app{
    width: 600px;margin: 20px auto;
  }
  .switchbtn{
    width: 100%;height: 200px;color: #fff;text-align: center;line-height: 200px;font-size: 5em;
    margin-bottom: 20px;cursor: pointer;user-select: none;
  }
  .switchbtn.cls-open{
    background-color: green;
  }
  .switchbtn.cls-close{
    background-color: red;
  }
  button{
    display: block;
    width: 100%;
    height: 50px;
  }
</style>
<div id="app">
    <!-- 监听@update:open事件 触发父组件的toggle函数 改变父组件中值的状态 -->
    <switchbtn :open="open" @update:open="toggle"></switchbtn>
    
    <!-- 以下3种操作效果等价 -->
    <switchbtn v-bind:open="open" v-on:update:open="open = $event"></switchbtn>
    <switchbtn :open="open" @update:open="open = $event"></switchbtn>
    <switchbtn :open.sync="open"></switchbtn>
    <button type="button" @click="toggle">{{open ? '开' : '关'}}</button>
</div>
Vue.component("switchbtn", {
    template:`
        <div @click="toggleOpen"
            :class="cls"
        >
            {{myOpen ? '开' : '关'}}
        </div>
    `,
    props:{
        open: {
            type: Boolean,
            default() {
                return false;
            }
        }
    },
    computed: {
        myOpen: {
          get() {
            return this.open;
          },
          set(val) {
            // update:open update:后面必须跟着props中传递值的名称!不可以随便起
            // 计算属性值改变的时候 向外部发送事件
            this.$emit('update:open', val);
          }
        },
        cls() {
          let cls = 'cls-open';
          if (!this.myOpen) {
            cls = 'cls-close';
          }
          return `switchbtn ${cls}`;
        }
    },
    methods:{
        // 子组件中修改使用计算属性接收的值 从而触发set
        toggleOpen(){
          this.myOpen = !this.myOpen;
        }
    }
});

var vm = new Vue({
  el: '#app',
  data () {
    return {
      open: true
    }
  },
  methods: {
    toggle() {
      // 只有@update:open="toggle"触发的时候才会log
      console.log('@update:open="toggle"');
      this.open = !this.open;
    }
  }
});

这里需要注意的是:update:传递到子组件属性名。名字不可以随便起。否则:myPropName.sync会找不到子组件向父组件发送的事件!

<switchbtn :open="open" @update:open="open = $event"></switchbtn>
<switchbtn :open.sync="open"></switchbtn>
// :open.sync="open" 实际上执行的就是如下两步的操作!
// :open="open" @update:open="open = $event"

将一整个对象作为props

当用一个对象同时设置多个 prop 的时候,也可以将这个.sync修饰符和v-bind配合使用:

<text-document v-bind.sync="doc"></text-document>

这样会把doc对象中的每一个 property (如title) 都作为一个独立的 prop 传进去,然后各自添加用于更新的v-on监听器。

v-bind.sync用在一个字面量的对象上,例如v-bind.sync=”{ title: doc.title }”,是无法正常工作的,因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑。

还是将上面的例子进行修改。

<div id="app">
    <!-- 监听@update:open事件 触发父组件的toggle函数 改变父组件中值的状态 -->
    <switchbtn :status="status" @update:status="toggle"></switchbtn>
    
    <!-- 以下2种操作等价 -->
    <switchbtn :status="status" @update:status="status = $event"></switchbtn>
    <switchbtn :status.sync="status"></switchbtn>
    <button type="button" @click="toggle">{{status.title}}</button>
</div>
let msg_old ='wubin.work';
let msg_new = 'is a website';

Vue.component("switchbtn", {
    template:`
        <div @click="toggleOpen"
            :class="cls"
        >
            {{myStatus.title}}
        </div>
    `,
    props:{
        status: {
            type: Object,
            default() {
                return {};
            }
        }
    },
    computed: {
        myStatus: {
          get() {
            return this.status;
          },
          set(val) {
            // update:status update:后面必须跟着props中传递值的名称!不可以随便起
            // 计算属性值改变的时候 向外部发送事件
            this.$emit('update:status', val);
          }
        },
        cls() {
          let cls = 'cls-open';
          if (!this.myStatus.open) {
            cls = 'cls-close';
          }
          return `switchbtn ${cls}`;
        }
    },
    methods:{
        // 子组件中修改使用计算属性接收的值 从而触发set
        toggleOpen(){
          if (this.myStatus.open) {
            this.myStatus.title = msg_new;
            this.myStatus.open = false;
          } else {
            this.myStatus.title = msg_old;
            this.myStatus.open = true;
          }
        }
    }
});

var vm = new Vue({
  el: '#app',
  data () {
    return {
      status: {
        title: 'wubin.work',
        open: true
      }
    }
  },
  methods: {
    toggle() {
      if (this.open) {
        this.open = false;
        this.status.title = msg_new;
      } else {
        this.open = true;
        this.status.title = msg_old;
      }
    }
  }
});

附件下载

相关资料

点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:vuesync双向绑定
推荐阅读
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    9094次阅读 588人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • Vue

    盘点Vue2和Vue3的10种组件通信方式

    Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异;本文将通过选项式API组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式。

    3845次阅读 287人点赞 发布时间: 2022-08-19 09:40:16 立即查看
  • JS

    几个高级前端常用的API

    推荐4个前端开发中常用的高端API,分别是MutationObserver、IntersectionObserver、getComputedstyle、getBoundingClientRect、requ...

    14071次阅读 914人点赞 发布时间: 2021-11-11 09:39:54 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    12908次阅读 442人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    11469次阅读 920人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    15588次阅读 1244人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    14797次阅读 1205人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    28950次阅读 2273人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    24982次阅读 1925人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录