Vue.js破冰系列-6组件基础(二)

1单文件组件

前面在vue.js破冰系列2中提到HTML模板有4中方式定义,分别是:在挂载点内定义模板、在template选项中定义模板、以x-template方式类型模板,以及使用单文件组件(single-file components)的方式。这里我们在讲解单文件组件。

单文件组件顾名思义,一个文件就是一个组件,文件的扩展名为.vue。它的好处有很多,比如支持语法高亮,css样式等。最重要的是,有效的支持前端工程化以及关注点分离。前端工程化一般使用webpack等构建工具,当我们项目越来越大时,使用单文件组件方便代码的组织和维护(关注点分离)。

一个单文件组件.vue包含3个顶级语言块<template><scirpt><style>,vue通过外部loader将这3个语言块解析为一个组件选项对象。

一个单文件中只能包含一个template和一个script语言块,但是可以包含多个style语言块,在template标签中只能存在一个根节点。格式如下:

//xxx.vue
<template></template>
<scirpt></scirpt>
<style></style>

2Props数据传递

2.1 props的定义

在注册组件时,使用props选项定义组件的特征,父组件通过这些特征向子组件传递数据,props可以接收数组和对象作为参数,数组中包含string类型的值,如果要对属性做更多的配置,则需要使用object类型。

<template>
    <div>
    <!--props的引用-->
    <h4>{{message}}</h4>
  </div>
</template>
<script>
    export default{
    //数组的方式定义props
    //props:["message"],
    //对象的方式定义props
    props:{
      message:String,
    }
  }
</script>

props的数组写法相对简单,只需要定义属性名即可。如果要对属性做一些约定则需要用到对象写法,对象的键为属性名,值可以是类型或一个属性参数对象。当属性值为类型时,表示对该属性做类型约束,如果是属性参数对象,则可以做更高级的约束:

<script>
    export default{
    props:{
      属性名:{
        type:'属性类型,String|Number|Boolean|Array|Object|Data|Function|Symbol|[可能的类型集合]',
        default:'默认值,Object和Array类型的prop需要使用函数的方式(工厂函数)返回,原理同组件中的data使用函数返回一样',
        required:'是否为必填项,Boolean',
        validator:'验证函数,返回Boolean值,如果返回false,在控制台会发出警告'
      }
    }
  }
</script>

可以看到props使用对象语法时,可以定义属性的类型,默认值,是否为必须已经验证信息等。

Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

2.2 props的使用

html标签是不区分大小写的,如果porps使用Pascal命名,Vue会将其转换为kebab-case形式(我们在上一章提到过Pascal和kebab)。

//子组件welcome.vue
<tempalte>
    <div>
    <span>你好 {{name}},上次登录时间:{{lastLoginTime}}</span>
  </div>
</tempalte>
<script>
    export default{
    props:['name','lastLoginTime']
  }
</script>

父组件调用:

<tempalte>
    <div>
    <!-- 使用kebab形式调用子组件的属性 -->
    <welcome :name="name" :last-login-time="time" />
  </div>
</tempalte>
<script>
  //导入
    import Welcome from 'welcome.vue'
  export default{
    //局部注册
    components:{
      Welcome,
    },
    data(){
      return {
        name:'张三',
        time:'2019-09-17',
      }
    }
  }
</script>

2.3 单向数据流

子组件定义props,父组件向子组件传递数据,当父组件中的数据改变后,子组件会重新渲染,数据从父向子流动叫做数据的单向流动。Vue不允许在子组件中改变props的值,如果强行修改,Vue会在控制台中发出警告信息。

//子组件 child-component.vue
<template>
    <div>
    <h4>子组件中的title:{{title}}</h4>
    <button @click="changePropsHandle">修改Props</button>
  </div>
</template>
<script>
    export default{
    props:{
      title:String,
    },
    methods:{
      changePropsHandle(){
        this.title="子组件修改的title"
      }
    }
  }
</script>
<template>
    <div>
    <h4>父组件中的title:{{title}}</h4>
    <child :title="title" />
  </div>
</template>
<script>
  import Child from 'child-component.vue'
    export default{
    components:{
      Child
    },
    data(){
      return {
        title:"我是父组件中的title"
      }
    }
  }
</script>

这个例子在点击子组件中的按钮是会在控制台中出现警告信息,原因是Vue中不允许直接修改props中的数据。如果要修改props,可以用data和computed替待。

//子组件 child-component.vue
<script>
    export default{
    props:{
      title:String,
    },
    //title作为初始值保存在localTilte中
    data(){
      return{
        loaclTitle:this.title,
      }
    },
    computed:{
      //将title转换为另一个字符串,
      computeTilte:function(){
        return "Hello"+this.title;
      }
    },
  }
</script>

注意:当props的类型为Object或Array时,在子组件中修改props的项时,会影响到父组件,原因可以参考其他语言的引用传递和值传递。

3组件间通信

组件之间的关系可以简单的分为父子关系和非父子关系。父子关系的组件,父组件直接调用子组件,比如使用子组件的props传值给组件,或者使用子组件的slot插槽将内容分发到子组件。

3.1自定义事件

上面说了,子组件中不允许修改父组件传递的数据。有些情况下,子组件需要修改父组件传递来的值,这时,可以使用自定义事件通知父组件,由父组件修改其数据,间接的的达到子组件修改props。

我们在vue.js破冰系列2中提到v-on事件监听的内部指令,过程大致是这样的,父组件在子组件的标签中使用v-on监听事件,子组件中使用实例的内部方法$emit触发事件,$emit方法的定义如下:

vm.$emit('evnetName',[...args])

vm表示的是实例,在具体的组件中,可以使用this指代。$emit的第1个参数为事件名称,他是一个字符串,vue不能自动转换其大小写,然而,使用v-on监听事件是在DOM标签中,我们知道DOM标签是不区分大小写的,DOM会将全部标签转换为小写,更进一步,要事件的触发,$emit中事件名必须完全匹配v-on指令中的参数(事件名),所以vue建议在$emitv-on定义的事件名使用kebab样式,或者也可以使用全小写的方式。

<div id="app">
  <!--DOM 会将MyEvent转换为小写myevnet -->
  <child @MyEvent="myEventHandler"></child>
  <!-- 推荐使用kebab形式的事件名 -->
  <child @my-event="myEventHandler" />
</div>

<!--子组件-->
<script>
export default{
  methods:{
    btnClicked(){
      //该emit无效,因为v-on指令监听的事件名为myevent,而这里触发的是MyEvent事件
      this.$emit('MyEvent');
      //该emit有效
      this.$emit('my-event');
    }
  }
}
</script>

$emit的第2个参数为不定参数,表示的是该事件对应处理函数的实参。关于自定义事件可以参考vue.js破冰系列2中的v-onv-model章节。

3.2中央事件总线

上面我们讲了父子组件之间的通信,他们是逐级传递的,也就是说父组件要和孙子组件通信,需要经过子组件,其他的还有兄弟组件之间的通信需要通过父组件。这种非父子组件之间的通信Vue推荐使用中央事件总线(bus),原理是这样的,创建一个vue的空实例,使用vue实例方法$emit来触发事件,使用vue实例方法$on来监听事件。

//创建一个vue的空实例,注意不能使用一个Object实例,因为我们要用到Vue实例的$emit和$on方法
var bus = new Vue();


//组件A注册监听bus上事件
export ComA={
  created(){
    //注册监听bus上的事件
    bus.$on('my-evnet',myEvnetHandler)
  },
    methods:{
    myEvnetHandler(arg1){
      //事件处理逻辑
    },
  }
}

//组件B
export ComB = {
  methods:{
    clicked(){
      //触发bus上的my-event事件
      bus.$emit('my-event','Hello World!')
    }
  }
}

上面的例子演示了组件A和组件B之间的通信。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 一轮冷月清, 百般相思情。 年花上市时, 更待归家急。 2014年1月22日于广州天河
    桓舟子阅读 147评论 0 1
  • 大家好,欢迎大家来到美糖直播间,今天讲解一些清洁产品的分类:皂基和非皂基洗面奶 皂这个东西一直被很多关注洁面的...
    美糖化妆品分析0阅读 6,395评论 0 1
  • 自由,简单的两个字,单看我们如何定义。 提到自由,我脑子里不禁想起两部电影的镜头,《勇敢的心》中主角的那一声响彻云...
    金桔金阅读 169评论 0 0