vue组件(24)组件的emits和emit

vue组件(24)组件的emits和emit,第1张

这个是定义组件的自定义事件的,如果组件都是用原生事件,那么可以不用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获取自组件方法和属性注意问题等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9815482.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-02
下一篇2023-05-02

发表评论

登录后才能评论

评论列表(0条)

    保存