1. v-model实现自定义组件双向绑定

v-model其实是个语法糖,如果没按照相应的规范定义组件,直接写v-model是不会生效的。再说一遍,类似于v-on:click可以简写成@clickv-model是两个表达式合在一起的简写。记住这个,下面具体说明。

1.1 input双向绑定

子组件MyInput.vue

<template>
<div>输入
<input :value="value" @input="input"/>
</div>
</template> <script>
export default {
name: "MyInput",
props: {
value: {type: [String, Number]}
},
methods: {
input(e) {
this.$emit('input', e.target.value)
}
}
}
</script>

父组件App.vue中使用:

<template>
<div id="app">
<my-input v-model="haha" />
<div>{{haha}}</div>
</div>
</template> <script>
import MyInput from '@/components/MyInput'
export default {
name: 'App',
components: { MyInput },
data() {
return {
haha: '66666',
}
}
}
</script> <style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

<my-input v-model="haha" />是一个语法糖(简写),它相当于:

<my-input
:value="haha"
@input="onInput"
/> onInput(e) {
this.haha = e
}

两者效果一样,v-model减少了很多代码,不用你手动把传过来的值赋值给haha了,少定义了一个方法。作用还是挺明显的。

MyInput组件中的value不可以叫别的名字,$emit中抛出的方法也只能叫input ,否则都不可以用v-model语法糖,用了也没啥效果。

另外,获取 input 输入值的的方式是:$event.target.value,写成方法的话,$event实参省略了。

1.2. checkbox双向绑定

到了checkbox这,又有点不一样,首先,它绑定的值不叫value,触发事件也不叫input,具体看代码。

子组件MyCheckBox代码:

<template>
<div>
<input type="checkbox" :checked="checked" @change="change"/>
</div>
</template> <script>
export default {
name: "MyCheckBox",
model: {
prop: 'checked',
event: 'zhuangbi'
},
props: {
checked: Boolean
},
methods: {
change(e) {
this.$emit('zhuangbi', e.target.checked)
}
}
}
</script>

父组件App.vue中使用:

<template>
<div id="app">
<my-check-box v-model="changeV"></my-check-box>
<div>{{changeV}}</div>
</div>
</template> <script>
import MyCheckBox from '@/components/MyCheckBox'
export default {
name: 'App',
components: { MyCheckBox },
data() {
return {
changeV: true,
}
}
}
</script>

除了绑定的value变成了checked@input变成了@change,传到父组件的参数是e.target.checked

注意: 绝大多数例子中,$emit中抛出的事件都是change,我这里写的是zhuangbi,其实只要和model模块中event属性值一样即可

此外,外面的props也不可少。

2. .sync方式实现双向绑定

如果需要对一个prop进行双向绑定,可以使用.sync语法糖。举一个使用场景的例子:别人封装好的 CheckBox 组件,需要做一些样式修改或者功能组合再使用,这就需要对 v-model 的值再来一次双向绑定。拿上面的 MyCheckBox 来说,<my-check-box v-model="checked"/>,给这个checked传值可以用 props,但想把checked的值传给父组件并赋值给props的值,就有点麻烦,需要定义一个方法,使用$emit,父组件监听事件并作赋值操作。现在用.sync可以一句搞定。

子组件DiyCheckBox代码:

<template>
<div>
<my-check-box v-model="diyCheck" @change="dChange"/>
</div>
</template> <script>
import MyCheckBox from './MyCheckBox'
export default {
name: "DiyCheckBox",
components: {MyCheckBox},
props: {
diyCheck: Boolean,
test: String
},
methods: {
dChange(e) {
this.$emit('update:diyCheck', e)
}
}
}
</script>

父组件App.vue中使用:

<template>
<div id="app">
<diy-check-box :diyCheck.sync="dCheck" />
<div>{{dCheck}}</div>
</div>
</template> <script>
import DiyCheckBox from '@/components/DiyCheckBox' export default {
name: 'App',
components: { DiyCheckBox },
data() {
return {
dCheck: true,
}
}
}
</script>

:diyCheck.sync="dCheck"这句代码相当于:

:diyCheck="dCheck"
@update:diyCheck="dCheck = $event"

语法糖作用很明显,大大简化了代码。

上面代码可以实现想要的功能,只是控制台会有一个警告:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "diyCheck"

found in

---> <DiyCheckBox> at src/components/DiyCheckBox.vue
<App>
<Root>

避免直接改变props,使用data or computed代替。所以优化一下,换个写法:

<template>
<div>
<my-check-box v-model="comDiyCheck"/>
</div>
</template> <script>
import MyCheckBox from './MyCheckBox'
export default {
name: "DiyCheckBox",
components: {MyCheckBox},
props: {
diyCheck: Boolean,
test: String
},
computed: {
comDiyCheck: {
get() {
return this.diyCheck
},
set(e) {
this.$emit('update:diyCheck', e)
}
}
}
}
</script>

使用计算属性后,@change事件都可以不要了,get()就是获取props传值,set(e)MyCheckBoxv-model值改变了会触发的方法,这里面做法是直接把改变后的值通过$emit方式发出去。父组件中仍然通过.sync绑定,代码没有变化。

.sync可不光能用来做checkbox的双向绑定,涉及到props双向绑定的场景都可以用sync实现。

.sync传整个对象

如果有许多props属性需要做双向绑定操作,标签写起来就很长,像这样:

<coverage-charge
v-for="(item, index) in chargingPiles"
:key="index + 'index'"
:code.sync="item.code"
:address.sync="item.address"
:addressType.sync="item.addressType"
:kind.sync="item.kind"
:yearLimitType.sync="item.yearLimitType"
>
</coverage-charge>

官方文档说可以简写成这样:

<text-document v-bind.sync="doc"></text-document>
<!--对应我们的例子就是:-->
<coverage-charge
v-for="(item, index) in chargingPiles"
:key="index + 'index'"
v-bind.sync='item'
>
</coverage-charge>

官方还说:

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

按照这个说法,item 的 5 个属性,都会通过 prop 传到子组件,我再在子组件中添加不同的computed属性即可:

<script>
export default {
name: 'CoverageCharge',
props: {},
computed: {
code: {
get() {
return this.code
},
set(val) {
this.$emit('update:code', val)
}
},
address: {
get() {
return this.address
},
set(val) {
this.$emit('update:address', val)
}
}
... // 其他属性值
}
}
</script>

真这样写,会发现,this.codethis.address这些都是undefined,这里需要在子组件的props中再定义一遍属性名才行,可以省略各属性的typedefault值:

 props: [ 'code', 'address', 'addressType', 'kind', 'yearLimitType' ]

这样就可以了,.sync功能强大,用的地方也挺多的。

props里面东西太多的话,也可以统一定义成一个对象,然后父组件通过v-bind或者v-model传进来:

2.1.1 v-bind 方式

<!--子组件-->
props: {
zb: {
type: Object,
default: () => {}
}
},
computed: {
code: {
get() {
return this.zb.code
},
set(val) {
this.$emit('update:code', val)
}
},
address: {
get() {
return this.zb.address
},
set(val) {
this.$emit('update:address', val)
}
}
}
<!--父组件-->
<coverage-charge
v-for="(item, index) in chargingPiles"
:key="index + 'index'"
v-bind.sync='item'
:zb='item'
>
</coverage-charge>

2.2.2 v-model方式

<!--子组件-->
export default {
model: {
prop: 'zb',
event: 'update'
},
props: {
zb: {
type: Object,
default: () => {}
}
},
computed: {
code: {
get() {
return this.zb.code
},
set(val) {
this.$emit('update:code', val)
}
},
address: {
get() {
return this.zb.address
},
set(val) {
this.$emit('update:address', val)
}
}
}
}
<!--父组件-->
<coverage-charge
v-for="(item, index) in chargingPiles"
:key="index + 'index'"
v-bind.sync='item'
v-model='item'
>
</coverage-charge>

注意modelevent值是update

3. v-model.sync比较

上面第 2 节第一个例子目的就是把v-model再包一层,照着第 1 节,用v-model的方法也能做到。这里只写主要点,不贴源码了:

  • 定义model模块:model: {prop: 'checked', event: 'zhuangbi' }
  • props中定义checked属性
  • computed中定义MyChecked: { get(){ return this.checked},set(val) { this.$emit('zhuangbi', val) } }
  • DiyCheckBox组件中使用:<my-check-box v-model="MyChecked"/>
  • 父组件中使用:<diy-check-box v-model=suibian" />suibian是父组件data中定义的变量

可见,v-model适用于双向绑定单个props(一个标签中不能有多个v-model),.sync适用于双向绑定一到多个props(一个标签中允许使用多个:xxx.sync=yyy)。

源码

点击查看源码

vue 双向绑定(v-model 双向绑定、.sync 双向绑定、.sync 传对象)的更多相关文章

  1. vue.js初级入门之最基础的双向绑定操作

    首先在页面引入vue.js以及其他需要用到的或者可能要用到的插件(这里我多引用了bootstrap和jquery) 引用的时候需要注意文件的路径,准备工作这样基本就完成了,下面正式开始入门. vue. ...

  2. 【vue】跟着老马学习vue-数据双向绑定

    学习了node.js教程,只能说是有了一定的了解,之前也了解了webpack和es6的核心内容,也看过vue2.0的官网教程,并结合视频看过项目,但是理解和运用仍然存在很多问题,接下来的一段时间,跟着 ...

  3. Vue.js之Vue计算属性、侦听器、样式绑定

    前言 上一篇介绍了Vue的基本概念,这一篇介绍一下Vue的基本使用. 一.搭建一个Vue程序 1.1 搭建Vue环境 搭建Vue的开发环境总共有三种方法: 引入CDN <script src=& ...

  4. Vue.js的类Class 与属性 Style如何绑定

    Vue.js的类Class 与属性 Style如何绑定 一.总结 一句话总结:数据绑定一个常见需求是操作元素的 class 列表和它的内联样式.因为它们都是属性,我们可以用 v-bind 处理它们:我 ...

  5. 理解ASP.NET Core - 模型绑定&验证(Model Binding and Validation)

    注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或点击此处查看全文目录 模型绑定 什么是模型绑定?简单说就是将HTTP请求参数绑定到程序方法入参上,该变量可以是简单类 ...

  6. vue中如何在自定义组件上使用v-model和.sync

    自定义事件 tips 推荐始终使用 kebab-case 的事件名.(v-on会将事件名自动转换为小写,避免匹配不到) changeData × change-data √ 自定义组件的v-model ...

  7. v-bind绑定属性样式——class的三种绑定方式

    1.布尔值的绑定方式 <div id="demo"> <span v-bind:class="{‘class-a‘:isA ,‘class-b‘:isB ...

  8. input绑定datapicker控件后input再绑定blur或者mouseout等问题

    input绑定datapicker控件后input再绑定blur或者mouseout等问题 问题描述:今天在修改一个东西的时候需要给一个input输入域绑定blur事件,从而当它失去焦点后动态修改其中 ...

  9. python tips:类的绑定方法(bound)和非绑定方法(unbound)

    类属性只有类及其实例能够访问,可以理解为一个独立的命名空间. Python中类属性的引用方式有两种: 1. 通过类的实例进行属性引用,称为绑定方法(bound method),可以理解为方法与实例绑定 ...

随机推荐

  1. C++ 继承方式 //语法:class 子类 :继承方式 父类 //继承方式 三种: //1.公共继承 //2.保护继承 //3.私有继承

    1 //继承方式 2 //语法:class 子类 :继承方式 父类 3 //继承方式 三种: 4 //1.公共继承 5 //2.保护继承 6 //3.私有继承 7 8 #include <ios ...

  2. Windows协议 LDAP篇 - 域用户和计算机用户

    域用户 查询域用户 通过SAMR协议查询(net user就是通过这种协议查询的),samr也不是一种专门的协议,是一个RPC接口 在impacket里有个脚本samrdump.py,就是专门调用这个 ...

  3. Spark的两种核心Shuffle详解

    在 MapReduce 框架中, Shuffle 阶段是连接 Map 与 Reduce 之间的桥梁, Map 阶段通过 Shuffle 过程将数据输出到 Reduce 阶段中.由于 Shuffle 涉 ...

  4. JAVA集合体系之-开篇

    JAVA的集合体系是个庞大的知识体系,里面涵盖了,如数组结构,链表,红黑树,排序算法,线程安全等等知识点,接下来将会使用一系列的分享文章整理自己的学习心得,留的温故而知新.下图是整理出来的JAVA集合 ...

  5. Run Clojure Script with External Dependencies without leiningen

    The normal way of deploy clojure files is using leiningen. But if we have no leiningen, or the scrip ...

  6. python中两种拷贝目录方法的比较

    首先是用python自己的api: shutil.copytree('./build/tested/doc', './build/tested/build/doc') 优点是改变平台时不需要修改代码, ...

  7. MySQL-16-主从复制进阶

    延时从库 介绍 延时从库: 是我们人为配置的一种特殊从库,人为配置从库和主库延时N小时 为什么要有延时从库 数据库故障 物理损坏,普通的主从复制非常擅长解决物理损坏 逻辑损坏,普通主从复制没办法解决逻 ...

  8. 【XSS-labs】Level 11-15

    Level 11 和level 10 差不多的页面,传参后查看页面源代码:依旧是第3个可以正常传参. 尝试level 10 的payload 发现 " 被实体化 可以打开控制台将第三个inp ...

  9. Kerberos认证流程简述

    摸鱼了很长一段时间,被大佬按在地上摩擦,一时间精神恍惚想不起来写点啥,正好回来碰巧给别人讲kerberos协议认证流程,结果讲来讲去把自己讲晕了,就非常尴尬 于是有了这篇文章(友情提示:无事莫装X,装 ...

  10. noip38

    T1 有个朴素的暴力,枚举每一个子矩形,复杂度 \(O(n^{2}m^{2})\),观察数据范围,n很小,考虑枚举行,对于 \(m\) 用 \(two\;pointers\) 来维护. 先预处理出每一 ...