
这个是定义组件的自定义事件的,如果组件都是用原生事件,那么可以不用emits。
其实就算有自己定义的事件也可以不写emits,程序也可以正常运行。
但是如果写了emits的话,那么就要按照要求写,否则会出现各种warn的警告,虽然也不影响运行,但是这明细是一个隐患,另外报一大堆warn也是很烦人的。
事件命名最好都是小写,因为不区分大小写,有时候会造成错误,可以用连字符来区分多个单词。
另外还可以像props 那样做一个验证
主要是验证参数是否符合要求的,然后用return true/false 表示。
这个大家都熟悉,是组件内部向父组件提交事件的,也可以用来修改props属性值。
看了一下官网,emit和v-model是一起介绍的,但是却没提emits的事。
如果这时候我们写了emits,就需要加上 emits: ['update:modelValue'] ,否则就会出现警告。
emit还可以提交其他的各种事件,比如一个select,本来是没有input事件的,但是我们可以用emit提交,然后在emits里面定义一下就可以。
封装表单控件,每个子控件都要挨个确认属性和事件,还是非常头疼的事情。不过嘛,反要一口一口的吃,早晚能够啃完。
另外一种写法
说明 selectType 为父调用组件绑定数据时,绑定属性的名称
注意: 参数名称必须为$event
说明:子向父组件传值本质上为子调用父组件的函数,函数中获取子组件传入的值
传值根据值的类型分为传值(非对象类型)和传引用(对象),传引用时,传的值在任意位置修改时,所有和当前对象绑定的内容均会发生变化。
ref可以绑定dom节点或字组件,用于获取子组件的方法和属性。但是只有组件 完成渲染 时,才可以获取得到,且$refs也不是响应式的!
响应式处理可以包括以下几种方法:也就是说,在子组件完成渲染以后,动态修改的data或method,在父组件都可以实时获取。
(1)使用nexttick();子组件 同步 更改数据可反映到父组件上,nexttick属于微任务,也就是说,在本轮事件循环完成之前,可以执行异步 *** 作,从而保证实时性。
(2)如果子组件数据更新是异步,比如说从接口请求回来的这种,使用nexttick,甚至settimeout(()=>{},0) 都无法读取到已经修改的data,因为在消息队列里面,异步任务作为宏任务始终排在队尾。常发生的情况是,接口请求的数据还没到,父组件中已经读取了refs[componets]xxx的数据,这会导致这个数据打印出来是undefined。
解决办法是:
>>>settimeout(100ms)的等待,这种方法虽然可以解决,但是非常不好,原因是你无法控制接口要多少秒才能到达响应结果
打印结果:
由此可见 settimeout 0 会在接口响应前执行。
>>> 使用回调的方式。接口响应完,在then里,通过$emit触发父组件获取refs的方法。这种回调必然可以保证数据已经set到,再次手动获取,即可得到响应值。
(3)以上分别用于解决子组件同步和异步的问题,但如果子组件使用v-if(资料发现v-for同样会有这种问题),子组件未被渲染,同样会出现$refs无法读取的问题。暴力解决法是,v-if换成v-show。区别在于:v-show不会发生重拍,只是display:none。这种方式虽然可以保证 $refs获取到子组件的数据,但在某些特定的业务场景下(比如子组件请求必须在父组件完成某 *** 作的时候进行),会导致一些逻辑错误。因此合理的解决办法是,用(2)中回调的方法。
其他解决办法待更新
模块文件VUE
<template>
<div v-if="showDialog" class="dialog" @touchmoveprevent>
<div v-if="showDialog" class="back-drop"></div>
<div class="alert" :class="{'alert-active':showDialogActive,'alert-big':big,'alert-middle':middle}" :style="'width:'+width+'px'">
<div class="title">
{{title}}
<div class="iconfont close" @click="close"></div>
</div>
<div class="dialog-content">
<slot></slot>
</div>
</div>
</div>
</template>
<script>
import { setTimeout } from "timers";
export default {
name: "v-templateCreat",
props: {
title: {
type: String,
default: "温馨提示"
},
cancelText: {
type: String,
default: "取消"
},
doneText: {
type: String,
default: "确定"
},
middle:{
type:Boolean,
default:false
},
big:{
type:Boolean,
default:false
},
show: {
type: Boolean,
default: false
},
dismiss: {
type: Boolean,
default: false
},
width: {
default: 498
},
open:Boolean
},
data() {
return {
showDialog: false,
message: "",
cancel: false,
value: "",
templateCreat: false,
showDialogActive: false
};
},
methods: {
close() {
thisshowDialogActive = false;
this$emit('update:open', false);
setTimeout(() => (thisshowDialog = false), 320);
},
openFn(){
thisshowDialog = true;
setTimeout(() => (thisshowDialogActive = true));
}
},
watch: {
open:function(data){
if(data) thisopenFn();
if(!data) thisclose();
},
show: function(data) {
thisopenFn();
},
dismiss: function() {
thisclose();
},
},
mounted(){
}
};
</script>
<style lang="scss">
@import "/filters/css/allcss";
dialog {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0);
z-index: 106;
iconfont {
float: right;
cursor: pointer;
color: #9b9b9b;
}
back-drop {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 04);
z-index: 106;
}
alert {
width: 498px;
min-height: 260px;
// overflow-y: scroll;
background: #fff;
left: calc(50% - 249px);
top: calc(50% - 280px);
position: fixed;
z-index: -1;
transform: scale(123);
opacity: 0;
transition: all 032s;
position: relative;
title {
height: 44px;
width: 100%;
padding: 0 20px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
line-height: 44px;
background: #f2f2f2;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
font-size: 16px;
}
message {
padding: 18px;
min-height: 100px;
overflow: auto;
}
}
alert-middle{
width:614px !important;
left:calc(50% - 307px);
}
alert-big{
width:748px !important;
left:calc(50% - 374px);
}
alert-active {
z-index: 9999;
transform: scale(1);
opacity: 1;
}
}
</style>
////////////////////////////////
使用
import templateCreat from "@/components/templateCreat";
components: {
templateCreat
},
<!-- 店铺地址 -->
<templateCreat
:opensync="creatShowTemplate"
title="店铺地址"
:width="450"
>
<div class="exampleCenten">
<div class="flex">
<div class="leftTxt">所在地区</div>
<div class="rightViewImg">
<el-cascader
style="width:310px;height:40px;"
size="large"
:options="regionOptions"
v-model="formselectedOptions"
@change="addressChange"
></el-cascader>
</div>
</div>
<div class="flex">
<div class="leftTxt"></div>
<div class="rightViewImg">
<div class="amapsView">
<div style="padding:0 0 10px;">确认坐标,方便到店消费</div>
<div class="amaps" :style="events'width:300px;height:240px;':''">
<el-amap
ref="map"
vid="amapDemo"
:amap-manager="amapManager"
:center="center"
:zoom="zoom"
:plugin="plugin"
:events="events"
class="amap-demo"
></el-amap>
<div class="amapsSon"></div>
</div>
<div style="padding:10px 0;width:300px;">{{formaddress}}</div>
</div>
</div>
</div>
</div>
<div class="flex msgboxBtns">
<el-button type="primary" style="width:100px;border-radius:0;" @click="shopSiteClick">保存</el-button>
</div>
</templateCreat>
import VueAMap from "vue-amap";
VueAMapinitAMapApiLoader({
key: "e1dedc6bdd765d46693986ff7ff969f4",
plugin: [
"AMapAutocomplete", //输入提示插件
"AMapPlaceSearch", //POI搜索插件
"AMapScale", //右下角缩略图插件 比例尺
"AMapOverView", //地图鹰眼插件
"AMapToolBar", //地图工具条
"AMapMapType", //类别切换控件,实现默认图层与卫星图、实施交通图层之间切换的控制
"AMapPolyEditor", //编辑 折线多,边形
"AMapCircleEditor", //圆形编辑器插件
"AMapGeolocation" //定位控件,用来获取和展示用户主机所在的经纬度位置
],
uiVersion: "10"
});
let amapManager = new VueAMapAMapManager();
data() {
let self = this;
return {
amapManager, //地图
regionOptions: regionData,
zoom: 12,
center: [],
getCertificationData: "",
uploadActions: "",
uploadType: "",
initShow: false,
form:{
address:‘’} //地址
plugin: [
{
pName: "Geolocation",
events: {
init(o) {
if (!selfinitShow) {
selfinitShow = true;
// o 是高德地图定位插件实例
ogetCurrentPosition((status, result) => {
if (result && resultposition) {
consolelog(result);
selfformlongitude = resultpositionlng;
selfformlatitude = resultpositionlat;
selfcenter = [selfformlongitude, selfformlatitude];
selfformaddress = resultformattedAddress;
}
});
}
}
}
}
],
events: {
init: o => {
ogetCity(result => {});
},
moveend: () => {},
zoomchange: () => {},
dragend: e => {
var lgt = this$refsmap$$getCenter();
selfformlatitude = lgt[1];
selfformlongitude = lgt[0];
selfasdasdashowswxcasd = false;
selfcenter = [selfformlongitude, selfformlatitude];
// 这里通过高德 SDK 完成。
var geocoder = new AMapGeocoder({
radius: 1000,
extensions: "all"
});
geocodergetAddress(this$refsmap$$getCenter(), function(
status,
result
) {
if (status === "complete" && resultinfo === "OK") {
if (result && resultregeocode) {
selfcenter = [selfformlongitude, selfformlatitude];
selfformaddress = resultregeocodeformattedAddress;
}
}
});
}
},
}
“阿弥陀佛,善哉善哉。”
“魔镜啊!魔镜,谁是这世界上最美丽的女人?”
“愿上帝保佑你,阿门!”
等等,这些语句是不是特别经典?特别有画面感?眼前立马浮现出家人、后母和牧师?
通过言语,可以判断一个人的职业、身份、地位。
通过特殊语句可以与神明通话,可以和精灵共舞。
(1)props / $emit 适用 父子组件通信
父组件注入,子组件接收。
这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。
注意:props是单向数据流,既只能从父级传到子级,如果想要达到双向,子级能够修改父级,则需要给props加sync修饰符。(文章后部分有详细介绍)
(2)ref 与 $parent / $children 适用 父子组件通信
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
$parent / $children:访问父 / 子实例
(3)$attrs / $listeners 适用于 隔代组件通信
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。
当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。
$listeners:包含了父作用域中的 (不含 native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
(4)provide / inject 适用于 隔代组件通信
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
(5)EventBus ($emit / $on) 适用于 父子、隔代、兄弟组件通信
这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。
(6)Vuex 适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vuejs 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
vuex的详细使用方法: vuex管理状态仓库使用详解
Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你知道的回答出越多方法越加分,表明你对 Vue 掌握的越熟练。
举例props使用
avue 引用了一个detail组件
详解eventBus通信方法
第一步:首先需要创建事件总线并将其导出,以便其它模块可以使用或者监听它。
两个初始化事件中心的方法:
第二步:创建了 EventBus ,接下来你需要做到的就是在你的组件中加载它,并且调用同一个方法,就如你在父子组件中互相传递消息一样。
假设你有两个Vue页面需要通信: A 和 B ,A页面 在按钮上面绑定了点击事件,发送一则消息,想通知 B页面。
接下来,我们需要在 B页面 中接收这则消息。
同理我们也可以在 B页面 向 A页面 发送消息。这里主要用到的两个方法:
如果使用不善,EventBus会是一种灾难,到底是什么样的“灾难”了?大家都知道vue是单页应用,如果你在某一个页面刷新了之后,与之相关的EventBus会被移除,这样就导致业务走不下去。还要就是如果业务有反复 *** 作的页面,EventBus在监听的时候就会触发很多次,也是一个非常大的隐患。这时候我们就需要好好处理EventBus在项目中的关系。通常会用到,在vue页面销毁时,同时移除EventBus事件监听。
如果想移除事件的监听,可以像下面这样 *** 作:
$on事件是不会自动清楚销毁的,需要我们手动来销毁,否则在b组件每次加载一次就会创建一个监听,会重复监听到数据。
可以使用EventBus$off('aMsg')来移除应用内所有对此某个事件的监听。
或者直接调用EventBus$off()来移除所有事件频道,不需要添加任何参数 。
总结: 所以,如果想要用bus 来进行页面组件之间的数据传递,需要注意两点,组件A$emit事件应在beforeDestory生命周期内。其次,组件B内的$on记得要销毁。
以上就是关于vue组件(24)组件的emits和emit全部的内容,包括:vue组件(24)组件的emits和emit、vue 组件传值 props $emit $event、vue获取使用$refs获取自组件方法和属性注意问题等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)