vue-learning:28 - component - 组件事件的修饰符`.native / .sync`,以及组件属性`model`

组件事件的修饰符.native / .sync,以及组件属性model

.native 原生事件修饰符

  • 在一个组件中,如果我们为其绑定一个原生的点击事件@click,基本是无效的。
  • vue中对组件绑定原生事件需要带上原生事件修饰符.native
  • 在组件中同时存在原生事件和自定义事件,组件自定义事件先于原来事件执行
<div id="app">
        <p>this is event example for .native/@<p>
        <com-child @click="handleNativeClick">按钮@click</com-child>
        <com-child @click="handleNativeClick" @child-btn-click="handelChildBtnClick">按钮@click 和自定义事件@child-btn-click</com-child>
        <com-child @click.native="handleNativeClick">按钮@click.native</com-child>
        <com-child @click.native="handleNativeClick" @child-btn-click="handelChildBtnClick">按钮@click.native 和自定义事件@child-btn-click</com-child>
    </div>
const comChild = Vue.extend({
    template: `<div>
            <button @click="handleBtnClick" >
                <slot></slot>
            </button>
            </div>`,
    methods: {
        handleBtnClick() {
            this.$emit('child-btn-click')
        },

    },
})

const vm = new Vue({
    el: "#app",
    components: {
        comChild,
    },
    methods: {
        handelChildBtnClick() {
            alert("v-on绑定组件自定义事件")
        },
        handleNativeClick() {
            alert('native click')
        }
    }
})

.sync 双向数据绑定

在前面讲解porp时,明确了prop是数据单向下行的绑定,父组件通过porp向子组件传值。
v-on / $emit中,可以在子组件中触发父组件的事件监听器,以达到子组件向父母组件传值或通信的需要,可视为子组件向父组件的数据单向上行绑定。

所以常常将两者结合起来使用。那这里就会产生一个特殊的情况:父组件通过某个prop向子组件传值,而子组件通过v-on / $emit触发父组件监听器修改的是正是父组件中的这个porp值。

<div id="app">
    <p>this is event example for prop and $emit<p>
    <P>父组件中的num:{{ num }}</P>
    <button-counter :count='num' @child-btn-click="handelChildBtnClick"></button-counter>
</div>
const buttonCounter = Vue.extend({
    template: `<div>
            <button @click="handelIncrease" >子组件点击次数:{{ count }}</button>
            </div>`,
    props: {
        count: Number
    },
    methods: {
        handelIncrease() {
            this.$emit('child-btn-click', this.count+1)
        },
    },
})

const vm = new Vue({
    el: "#app",
    data: {
        num: 0,
    },
    components: {
        buttonCounter,
    },
    methods: {
        handelChildBtnClick(val) {
            this.num = val;
        },
    }
})

父子通信传递和修改的都是同一个prop时,Vue提供一个语法糖.sync,可以进行简写:

<div id="app">
    <p>this is event example for .sync<p>
    <P>父组件中的num:{{ num }}</P>
    <!-- <button-counter :count='num' @child-btn-click="handelChildBtnClick"></button-counter> -->
    <button-counter :count.sync='num'></button-counter>
</div>
const buttonCounter = Vue.extend({
    template: `<div>
            <button @click="handleIncrease">子组件点击次数:{{ count }}</button>
            </div>`,
    props: {
        count: Number
    },
    methods: {
        handleIncrease() {
            // this.$emit('child-btn-click', this.count+1)
            this.$emit('update:count', this.count+1)
        }
    }
})

const vm = new Vue({
    el: "#app",
    data: {
        num: 0,
    },
    components: {
        buttonCounter,
    },
    methods: {
        // handelChildBtnClick(val) {
        //     this.num = val;
        // },
    }
})

上面注释的内容就是省略的内容,可以看到.sync语法糖就是对父组件绑定自定义事件那部分代码进行了简略,并指定$emit触发的父组件监听事件名必须为update:count的写法。
实际上完全还原.sync的写法是:

<div id="app">
    <p>this is event example for .sync update:count<p>
    <P>父组件中的num:{{ num }}</P>
    <!-- <button-counter :count='num' @child-btn-click="handelChildBtnClick"></button-counter> -->
    <button-counter :count='num' @update:count="handelChildBtnClick"></button-counter>
</div>
const buttonCounter = Vue.extend({
    template: `<div>
            <button @click="handleIncrease">子组件点击次数:{{ count }}</button>
            </div>`,
    props: {
        count: Number
    },
    methods: {
        handleIncrease() {
            // this.$emit('child-btn-click', this.count+1)
            this.$emit('update:count', this.count+1)
        }
    }
})

const vm = new Vue({
    el: "#app",
    data: {
        num: 0,
    },
    components: {
        buttonCounter,
    },
    methods: {
        handelChildBtnClick(val) {
            this.num = val
        }
    }
})

对于v-bind=‘object形式传入一个对象时,实现对象某个属性值的双向绑定,可以简写改成这样:v-bind.sync=‘object‘

对prop各种类型传递写法还不清楚的可以点击查看vue-26-component-prop

  <div id="app">
    <p>this is event example for .sync object.lnag<p>
    <!-- <button-counter v-bind='frontend' @child-change-lang="handleChildChangeLang"></button-counter> -->
    <button-counter v-bind.sync='frontend'></button-counter>
</div>
const buttonCounter = Vue.extend({
    template: `<div>
            <p>前端语言:{{ lang }}</p>
            <p>前端框架:{{ framework }}</p>
            <p>前端编辑器:{{ editor }}</p>
            <button @click="handleChangeLang">子组件内点击改变前端语言</button>
            </div>`,
    props: {
        lang: String,
        framework: String,
        editor: String,
    },
    methods: {
        handleChangeLang() {
            this.$emit('update:lang', 'HTML')
        }
    }
})

const vm = new Vue({
    el: "#app",
    data: {
        num: 0,
        frontend: {
            lang: "javascript",
            framework: "vue",
            editor: "vscode",
        }
    },
    components: {
        buttonCounter,
    },
    methods: {
        // handleChildChangeLang(val) {
        //     console.log(val)
        //     this.frontend.lang = val
        // }
    }
})

v-model自定义组件的双向输入绑定

在上面.sync实现双向数据绑定时,概念上有一些熟悉,因为之前在基础指令学习时,有一个用于表单元素输入的双向数据绑定指令v-model,总结的时候,提到过这也是一个语法糖而已。

.sync实现时说过,$emit触发的事件名必须按要求写法update:prop的形式才行。同样的,v-model语法糖实现的前提也必须存在指定的值,默认是特性值是value,绑定的事件名是input
v-model在绑定原生的表单元素时,会根据表单元素的类型不同,选择对应的特性和事件来实现双向绑定。

  • text 和 textarea 元素使用 value 属性和 input 事件,值为字符串文本;
  • checkbox 和 radio 使用 checked 属性和 change 事件,checkbox为单个时绑定值为布尔值,多选为数组,radio绑定依value值类型;
  • select 字段将 value 作为 prop ,并将 change 作为事件。多选时为数组

那把v-model用在自定义的组件中时,v-model 默认会查找组件中名为 value 的 prop 和名为 input的事件。如果有一个不存在,则无法实现v-model双向绑定的效果。

<!-- 原生表单输入框元素直接用v-model -->
<div id="native-test">
    <p>data中iptValue的值是: {{ iptValue }}</p>
    <input type="text" v-model="iptValue">
</div>
const vm = new Vue({
    el: "#native-test",
    data: {
        iptValue: ''
    },
})

那如果现在有一个自定义的输入组件,根元素就是一个input元素,在组件在直接绑定v-model是否有效呢?

<div id="app">
    <p>显示iptValue的值:{{ iptValue }}</p>
    <!-- <input type="text" v-model="iptValue"> -->
    <com-input v-model="iptValue"></com-input>
</div>
const comInput = Vue.extend({
    template: `<input type="text"></div>`,
})

const vm = new Vue({
    el: "#app",
    data: {
        iptValue: '初始值'
    },
    components: {
        comInput,
    },
})

结果是无效,因为在模板编辑阶段子组件com-input上用v-model时没有在组件身上找到valueinput事件。也不会找到内部原生的input

自定义组件v-model指令

所以根据v-model绑定原生表单组件时的原理,手写一个组件双向输入绑定

 <div id="app">
    <p>显示iptValue的值:{{ iptValue }}</p>
    <!-- <input type="text" v-model="iptValue"> -->
    <com-input :value='iptValue' @input="handleComInput"></com-input>
</div>
const comInput = Vue.extend({
    template: `<input type="text" :value="value" @input="handleInnerIput"></div>`,
    props: ['value'],
    methods: {
        handleInnerIput(e) {
            this.$emit('input', e.target.value)
        }
    }
})

const vm = new Vue({
    el: "#app",
    data: {
        iptValue: '初始值'
    },
    components: {
        comInput,
    },
    methods: {
        handleComInput(val) {
            this.iptValue = val
        }
    }
})

这样我们就让组件com-input有了value属性和input事件,此时满足v-model有实现条件。我们就可以在组件上直接使用v-model绑定了

 <div id="app">
    <p>显示iptValue的值:{{ iptValue }}</p>
    <!-- <input type="text" v-model="iptValue"> -->
    <!-- <com-input :value='iptValue' @input="handleComInput"></com-input> -->
    <com-input v-model="iptValue"></com-input>
</div>

所以说,只要我们封装的组件能提供value属性和input事件,就可以对该组件使用v-model指令。而不需要组件内部一定是表单元素。

<div id="app">
    <p>显示iptValue的值:{{ iptValue }}</p>
    <!-- <input type="text" v-model="iptValue"> -->
    <!-- <com-input v-model="iptValue"></com-input> -->
    <com-div v-model='iptValue'></com-div>
</div>
const comDiv = Vue.extend({
    // template: `<input type="text" :value="value" @input="handleInnerIput"></div>`,
    template: `<div>
        <span @click="handleClick" style="border: 1px solid #000;">点击改变值的显示 {{ value }}</span>
    </div>`,
    props: ['value'],
    methods: {
        handleClick(e) {
            this.$emit('input', this.value+1)
        }
    }
})

const vm = new Vue({
    el: "#app",
    data: {
        iptValue: 0
    },
    components: {
        comDiv,
    },
})

组件的model属性

再进一步讲,v-model指令默认情况下是选择value属性和input事件,那是否像checkbox一样让v-model绑定其它属性和对应的事件呢。
vue 2.2.0以上版本为组件增加一个model对象属性,用来指定v-model绑定自定义的prop和event事件。

<div id="app">
    <p>显示iptValue的值:{{ iptValue }}</p>
    <!-- <input type="text" v-model="iptValue"> -->
    <com-input v-model="iptValue"></com-input>
    <com-div v-model='iptValue'></com-div>
</div>
const comDiv = Vue.extend({
    // template: `<input type="text" :value="value" @input="handleInnerIput"></div>`,
    template: `<div>
        <span @click="handleClick" style="border: 1px solid #000;">点击改变值的显示 {{ otherName }}</span>
    </div>`,
    model: {
        prop: 'otherName',
        event: 'some-event'
    },
    props: ['otherName'],
    methods: {
        handleClick(e) {
            this.$emit('some-event', this.otherName+1)
        }
    }
})

const vm = new Vue({
    el: "#app",
    data: {
        iptValue: 0
    },
    components: {
        comDiv,
    },
})

.sync 和 v-model 区别

从上面分析的组件.sync和v-model实现的原理上看,一个组件只能定义一个model属性用业指定prop和event。所以:

  • v-model在组件中只能实现一个prop的双向数据绑定,并且较适合用于绑定的prop类型为基本类型。
  • .sync可以在组件中指定多个prop的双向数据绑定,绑定的prop类型较宽,对象类型的绑定更方便。

比如当v-bind.sync=‘obj‘时,就会把obj对象中的每一个属性 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on 监听器,实现对象中每个属性的双向绑定。

原文地址:https://www.cnblogs.com/webxu20180730/p/11031252.html

时间: 2024-10-08 04:41:46

vue-learning:28 - component - 组件事件的修饰符`.native / .sync`,以及组件属性`model`的相关文章

Vue.js学习笔记(三) - 修饰符

本篇将简单介绍常用的修饰符. 在上一篇中,介绍了 v-model 和 v-on 简单用法.除了常规用法,这些指令也支持特殊方式绑定方法,以修饰符的方式实现.通常都是在指令后面用小数点“.”连接修饰符名称. 一.v-model的修饰符 v-model 是用于在表单表单元素上创建双向数据绑定的指令.在 <input> 和 <textarea> 上,默认通过监听元素的 input 事件来更新绑定的属性值. 为了能明显的看到绑定属性值的变化,需要在Chrome浏览器中安装Vue Devto

vue中常用的事件和修饰符简单总结

1:阻止冒泡事件 JS事件流其中一种是冒泡事件,当一个元素被触发一个事件时,该目标元素的事件会优先被执行,然后向外传播到每个祖先元素,恰如水里的一个泡泡似的,从产生就一直往上浮,到在水平面时,它才消失.在这个过程中,如果你只希望事件发生在目标元素,而不想它传播到祖先元素上去,那么你需要在"泡泡"离开对象之前刺破它. 在vue中怎么写? 2:阻止默认行为 在vue中怎么写? 3:键盘事件 获取键码?通过事件对象来获取 注意:keydown事件和keyup事件的区别? keydown是在键

事件扩展修饰符与位运算

今天查看别人重写的鼠标拖动事件的源码时,有一段代码很不解 1 public void mouseDragged(MouseEvent e) { 2 form.setCursor(mc); 3 if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0) 4 form.setLocation(e.getXOnScreen() - this.x, e.getYOnScreen() 5 - this.y); 6 } 这里(e.getModi

vue中的按键修饰符

在版本2中这样定义的 1. .enter  (回车键) 2. .tab 3..delete  (捕获”删除“和”退格“键) 4. .esc      (退出键) 5. .space  (空格键) 6.  . up 7.  .dowm 8.  .left 9.  .right  (右箭头) 可以通过 Vue.config.keyCodes.f2=113 自定义按键修饰符   f2 是自定义的名称   ,113是键盘对应的码值 原文地址:https://www.cnblogs.com/Progres

面向对象(一)封装 命名空间 访问修饰符等

一.封装 封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别:将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成"类",其中数据和函数都是类的成员. 封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部借口,以特定的访问权限来使用类的成员. public class 类名 { //成员变量 private 数据类型 _属性名; //属性 public 数据类型 属

Java中的访问修饰符详细解析

1.类的修饰符分为:可访问控制符和非访问控制符两种. 可访问控制符是:公共类修饰符 public 非访问控制符有:抽象类修饰符 abstract :最终类修饰符 final 1 )公共类修饰符 public : Java 语言中类的可访问控制符只有一个: public 即公共的.每个 Java 程序的主类都必须是 public 类作为公共工具供其它类和程序使用的应定义为 public 类. 2 )抽象类修饰符 abstract :凡是用 abstract 修饰符修饰的类,被称为抽象类.所谓抽象类

Java访问修饰符(访问控制符)

Java 通过修饰符来控制类.属性和方法的访问权限和其他功能,通常放在语句的最前端.例如: public class className { // body of class } private boolean myFlag; static final double weeks = 9.5; protected static final int BOXWIDTH = 42; public static void main(String[] arguments) { // body of meth

2.Java访问修饰符(访问控制符)

Java 通过修饰符来控制类.属性和方法的访问权限和其他功能,通常放在语句的最前端.例如: public class className { // body of class } private boolean myFlag; static final double weeks = 9.5; protected static final int BOXWIDTH = 42; public static void main(String[] arguments) { // body of meth

Kotlin基础-可见修饰符、嵌套类

/*2.7可见性修饰符 *设定类本身及其属性,方法,构造器 * 以及接口和对象的对外访问权限,即"可见性" *private 私有:仅当前类可见.最小的可见性 * protected 保护 :仅子类可见 * internal 内部:当前模块可见 * public 公开 :默认 对外完全可见 * * * 2.8嵌套类 * 类中可以嵌套其他类 * * * */ //新闻类 class News{ //默认地区 private var lang="cn" //新闻分类:嵌