官方文档:
官方也说明了,v-model只不过是一个语法糖而已,真正的实现靠的还是
1. v-bind : 绑定响应式数据
2. 触发 input 事件 并传递数据 (核心和重点)
大体就是:
监听原生组件的事件, 当获取到原生组件的值后把值通过调用 $emit('input' ,data) 方法去触发 input事件
demo:
父组件代码:
<template> <div class="hello"> <button @click="ifShow=!ifShow">点击显示</button> <show-alert v-model="ifShow"></show-alert> </div> </template> <script> import showAlert from './showAlert.vue' export default { name: 'HelloWorld', components:{ showAlert, }, data () { return { ifShow:false, } } } </script>
子组件代码:
<template> <div id="showAlert" :value="value" v-if="ifValue"> <div>showAlert 内容</div> <button class="close" @click="ifValue=false">关闭</button> </div> </template> <script> export default{ props:{ value:{ type:Boolean, default:false, } }, data:function(){ return{ ifValue:false, } }, watch:{ value(bool){ this.ifValue=bool; console.log('bool='+ bool); }, ifValue(val){ /*使用了v-model的组件会自动监听 input 事件, * 并把这个input事件所携带的值 传递给v-model所绑定的属性, * 这样组件内部的值就给到了父组件了 */ this.$emit('input',val);//传值给父组件, 让父组件监听到这个变化 } }, } </script> <style scoped> .close{ background:red; color:white; } </style>
实现效果:
点击显示按钮以后:
前提: this.$emit('input',data);
点击子组件关闭按钮后:
如果未加this.$emit('input',data);
点击子组件关闭按钮后:
如果未通过$emit把值传到父组件, 则父组件监听不到子组件的变化.
====================================================================
封装一个可复用的弹窗组件
<!-- 弹出窗口组件, 调用方法: 父组件import 当前vue文件, 在对应的components中注册即可使用, 注意: 需要在父组件给一个boolean的属性 通过v-model的形式关联即可 其他属性看下方注释, params(父组件调用 标签属性): title-info 弹出框显示文字 left-button 左边按钮文字 right-button 右边按钮文字 left-click 左边按钮点击回调函数 right-click 右边按钮点击回调函数 中间部分可以从父元素自定义设置通过slot放入 例子: <show-model> <p slot="reference">111</p> </show-model> 例子: <show-model v-model="flag" title-info="优惠码输入错误,请重试" left-button="取消" right-button="确定" @left-click="left_button" @right-click="right_button"></show-model> **** watch中增加. 当弹出窗弹出时,阻止后面页面可以滚动的问题 --> <template> <div class="model-wrapper" :value="value" v-if="ifValue"> <div class="mask" @click="close_click"></div> <div class="model-content"> <p v-if="titleInfo">{{titleInfo}}</p> <div class="button-group" v-if="leftButton || rightButton"> <div v-if="leftButton" @click="left_click">{{leftButton}}</div> <div v-if="rightButton" @click="right_click">{{rightButton}}</div> </div> </div> </div> </template> <script> export default{ props:{ value:{ type:Boolean, default:false }, titleInfo:{ type:String, default:'' }, leftButton:{ type:String }, rightButton:{ type:String } }, data:function(){ return { ifValue:false, } }, methods:{ close_click:function(){ this.$emit('close-click'); }, left_click:function(){ this.$emit('left-click'); }, right_click:function(){ this.$emit('right-click'); } }, watch:{ value(bool){ this.ifValue=bool; }, ifValue(val){ this.$emit('input',val); } } } </script> <style scoped> .model-wrapper{ width:100%; height:100%; position:fixed; top:0; left:0; z-index:999; } .mask{ width:100%; height:100%; position:absolute; left:0; top:0; z-index:1; background-color:rgba(0,0,0,0.5); } .model-content{ width:80%; background:white; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); z-index:10; } .button-group{ margin-top:40px; display:flex; padding:0 10px 10px; } .button-group div{ flex:1; height:40px; line-height:40px; box-sizing:border-box; cursor:pointer; } .button-group div:first-child{ background:#000; color:#fff; } .button-group div:last-child{ border:1px solid gray; } </style>
在首页调用
<button @click="showModel">点击显示弹窗</button>
<show-model v-model="ifShowModel" titleInfo="嘿嘿嘿" leftButton="确定" rightButton="取消" @close-click="close_click" @left-click="left_click" @right-click="right_click" ></show-model>
showModel:function(){ this.ifShowModel=true; }, close_click:function(){ this.ifShowModel=false; }, left_click:function(){ this.ifShowModel=false; }, right_click:function(){ this.ifShowModel=false; }