1.$emit 的使用
在组件中注册自定义事件
$emit(事件名, 参数) //该参数会当作第一个参数传入绑定的函数中
下面用一个菜单栏例子来说明,如下图所示
组件
Vue.component('menu-cmp', { //注册prop props: { menu: Object, isActive: Boolean, menuIndex: Number }, template: ` <div> <div class="first-menu" //注册自定义事件,事件名为handle-second-menu,第二个参数则作为被绑定函数的第一个参数 @click="$emit('handle-second-menu', {isActiev:menu.isActive,id:menuIndex})"> {{menu.firstMenu}} </div> <div v-show="menu.isActive" class="second-menu" v-for="secondMune in menu.secondMenuList"> {{ secondMune }} </div> </div> ` })
组件引用
<div id="app"> <menu-cmp class="menu-list" v-for="(menu,menuIndex) in menuList" :menu='menu' :menu-index='menuIndex' @handle-second-menu="handleSecondMenu"> //给自定义事件绑定函数 //注:这里的事件处理为函数handleSecondMenu,如果逻辑量少,无需函数处理,那么$emit的第二个参数可以通过$event访问 </menu-cmp> </div>
Vue实例
const vm = new Vue({ el: '#app', data: { menuList: [ { firstMenu: '一级菜单1', secondMenuList: ['二级菜单1', '二级菜单2', '二级菜单3'], isActive: true, }, { firstMenu: '一级菜单2', secondMenuList: ['二级菜单1', '二级菜单2', '二级菜单3'], isActive: true, }, { firstMenu: '一级菜单3', secondMenuList: ['二级菜单1', '二级菜单2', '二级菜单3'], isActive: true, }, ] }, methods: { handleSecondMenu(res) { //res是$emit()的第二个参数 this.menuList[res.id].isActive = !this.menuList[res.id].isActive } } })
css
.menu-list { width: 200px; height: 50px; text-align: center; display: inline-block; vertical-align: top; background-color: brown; box-sizing: border-box; } .menu-list:not(:last-of-type) { border-right: 1px solid #eee; } .menu-list .first-menu { width: 100%; height: 50px; line-height: 50px; box-sizing: border-box; border-bottom: 1px solid #eee; } .menu-list .second-menu { width: 100%; height: 50px; line-height: 50px; } .menu-list .second-menu.active { display: block; }
2.事件名
不同于组件和prop:
- 事件名不存在任何自动化的大小写转换,而是触发的事件名需要完全匹配监听这个事件所有的名称,即如果触发一个handleMenu 事件,那么监听 handle-menu 是没有效果的;
- 事件名不会被当作一个JS变量名或者属性名,所以就没有理由使用大驼峰或者小驼峰式命名
另外,v-on事件监听器在DOM模板中会被自动转换为全小写,所以@handleMenu 将会变为@handlemenu ,导致事件监听失败,因此,应当使用短横线命名法。
3.修饰符.native _ 将原生事件绑定到组件
在组件上监听事件时,监听的是组件自动触发的自定义事件,但是在一些情况下可能想要在一个组件的根元素上直接监听一个原生事件。这时,可以用 v-on 指令的 .native 修饰符,如:
组件
Vue.component('my-input', { template:` <input type="text"> ` })
组件引用
<div id="app"> <my-input @focus.native="onFocus"></my-input> </div>
Vue实例
const vm = new Vue({ el:"#app", methods:{ onFocus(){ console.log('focus'); } } })
【结果】
4.$listeners
当<input>标签不是组件的根元素,即包裹在其他标签中时,父级的.native 监听器将静默失败。虽然不会产生报错,但是 onFocus 处理函数不会被调用。为了解决这个问题,Vue提供了一个 $listeners 属性,它是一个对象,里面包含了作用在这个组件上所有的监听器,有了这个 $linteners 属性,我们可以配合 v-on="$linteners" 将所有的事件监听器指向这个组件的某个特性的子元素,如:
组件
Vue.component('my-input', { template: ` <label> 姓名 <input type="text" v-on="$listeners"> </label> ` })
组件引用
<my-input @focus="onFocus"></my-input>
5.在组件上使用v-model
由于自定义事件的出现,在组件上也可以使用 v-model 指令。
在 input 元素上使用 v-model 指令时,相当于绑定了 value 特性以及监听了 input 事件
<input v-model="searchText" />
等价于
<input :value="searchText" @input="searchText = $event.target.value" >
当把 v-model 指令引用在组件上时
<base-input v-model="searchText" />
等价于
<base-input :value="searchText" @input="searchText = $event" />
同 input 元素一样,在组件上使用 v-model 指令,也是绑定了 value 特性,监听了 input 事件。所以 为了让 v-model 指令正常工作,这个组件内的<input>元素必须满足
- 将其 value 特性绑定到一个叫 value 的 prop 上
- 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出
组件
Vue.component('my-input',{ props:['value'], template:` <input :value="value" @input="$emit('input',$event.target.value)"> ` })
组件引用
<my-input v-model="searchText"></my-input>
Vue实例
const vm = new Vue({ el:"#app", data:{ searchText:'' } })
【结果】
综上所述,一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value 特性用于不用的目的。遇到这种情况,我们可以利用 model 选项来避免冲突:
组件
Vue.component('my-checkbox', { //model选项 model: { prop: 'checked', event: 'change' }, props: { checked: Boolean }, template: ` <input type="checkbox" :checked="checked" @change="$emit('change',$event.target.checked)"> ` })
组件引用
<my-checkbox v-model="isChecked"></my-checkbox>
Vue实例
const vm = new Vue({ el: "#app", data: { isChecked: '' } })
【结果】
6. .sync修饰符(语法糖)
除了使用 v-model 指令实现组件与外部数据的双向绑定外,还可以用 v-bind 指令的修饰符. sync 来实现
组件
Vue.component('my-input1',{ props:['value'], template:` <input :value="value" @input="$emit('update:value',$event.target.value)"> ` })
组件引用
<my-input1 :value.sync="searchText"></my-input1>
Vue实例
const vm = new Vue({ el:"#app", data:{ searchText:'' } })
【注】当用一个对象同时设置多个 prop 时,也可以将 .sync 修饰符和 v-bind 配合使用
<base-input v-bind.sync="obj"></base-input>
注意:
- 带有 .sync 修饰符的 v-bind 指令,只能提供想要绑定的属性名,不能和表达式一起使用,如::title.sync="1+1",这样操作是无效的
- 将 v-bind.sync 用在一个字面量对象上,如 v-bind.sync="{titel: 'haha'}",是无法工作的,因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑。
7.v-model VS .sync
- 两者都是用于实现双向数据传递的,实现方式都是语法糖,最终通过 prop + 事件 来达成目的
- vue 1.x 的.sync 和 v-model 是完全两个东西,vue 2,3 之后可以理解为一类特性,使用场景略有区别
- 当一个组件对外只暴露一个受控的状态,且都符合统一的标准时,使用 v-model 来处理; .sync 则更为灵活,凡是需要双向数据传递的,都可以使用