Vue.js 组件

组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。
所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。

使用组件的原因

提高代码的复用性

组件的使用方法

  1. 全局注册
Vue.component('my-component', {
  template: '<div>我是组件的内容</div>'
})

优点:所有的 Vue 实例都可以用。
缺点:权限太大,容错率降低。

  1. 局部注册
var app = new Vue({
  el: '#app',
  components: {
    'my-component': {
      template: '<div>我是组件的内容</div>'
    }
  }
})
  1. Vue 组件的模板在某些情况下会受到 html 标签的限制,比如 <table> 中只能含有 <tr><td><tbody> 这些元素,所以直接在 table 中使用组件是无效的,此时可以使用 is 属性来挂载组件。
<table>
  <tbody is="my-component"></tbody>
</table>

组件使用的技巧

  1. 必须使用小写字母,短横分隔命名( kebab-case )。
  2. template 中的元素必须被一个 DOM 元素包裹。
  3. 在组件的的定义中,除了 template 之外还可以有 data 、computed 、methods 等。
  4. 组件的 data 必须是一个方法。

使用 props 传递数据

  1. 在组件中使用 props 来从父组件接收参数。
  2. props 是来自父级的数据,而组件中 data return 的数据是组件自己的数据,它们的作用域都是组件本身,可以在 template ,computed ,methods 中直接使用。
  3. props 的值有两种,一种是字符串数组,一种是对象。
  4. 可以使用 v-bind 动态绑定父组件来的内容,不使用 v-bind 传递的是字符串,使用 v-bind 会按照 JS 语法来解析传递的内容。

单向数据流

通过 props 传递数据是单向的,也就是父组件数据变化时会传递给子组件,但是反过来不行。
这样可以避免子组件无意中修改了父组件的状态。
业务中经常会遇到两种需要改变 props 的情况:
一种是父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域下可以随意使用和修改。这种情况可以在组件 data 内再声明一个数据,引用父组件传来的 prop 。
另一种是 prop 作为需要被转变的原始值传入,这种情况用计算属性就可以了。

prop 数据验证

驼峰式命名(camelCase)与短横线命名(kebab-case)

  • 在 html 中,不区分大小写,因此组件在 html 中使用必须使用短横线命名方式,不允许使用驼峰式!!!!!!
  • 在组件中,父组件给子组件传递数据必须用短横线式命名。
  • 在组件的 template 中,必须使用驼峰式命名方式。
  • 在组件的 data 中,用 this.xxx 引用时,只能是驼峰命名方式。

验证的 type 类型可以是

  • Number
  • String
  • Boolean
  • Object
  • Array
  • Function
  • Date
    为了定制 prop 的验证方式,你可以为 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
      }
    }
  }
})

注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的属性 (如 data、computed 等) 在 default 或 validator 函数中是不可用的。

组件通讯

自定义事件---子组件给父组件传递数据

使用 v-on 除了监听 DOM 事件外,还可以用于组件之间的自定义事件。
JavaScript 的设计模式——观察者模式,dispatchEventaddEventListener 这两个方法。Vue 组件也有与之类似的一套模式,子组件用 $emit() 来触发事件,父组件用 v-on 来监听子组件的事件。

<div id="app">
  {{number}}
  <son-component @update="updateNumber"></son-component>
</div>
<script>
  Vue.component('son-component', {
    template: '<button @click="add1">+1</button>',
    data: function(){
      return {count: 0}
    },
    methods: {
      add1(){
        this.count += 1
        this.$emit('update', this.count)
      }
    }
  })
  var app = new Vue({
    el: '#app',
    data: {
      number: 0
    },
    methods: {
      updateNumber(count){
        this.number = count
      }
    }
  })
</script>

在组件中使用 v-model

v-model 其实是一个语法糖,这背后做了两个操作:

  1. v-bind 绑定一个数据。
  2. v-on 指令给当前元素监听一个 input 事件,并将 input 事件传递过来的值赋给绑定的数据。
<div id="app">
  {{number}}
  <son-component v-model="number"></son-component>
</div>
<script>
  Vue.component('son-component', {
    template: '<button @click="add1">+1</button>',
    data: function(){
      return {count: 0}
    },
    methods: {
      add1(){
        this.count += 1
        this.$emit('input', this.count)
      }
    }
  })
  var app = new Vue({
    el: '#app',
    data: {
      number: 0
    }
  })
</script>

非父子组件之间的通信

有时候两个组件也需要通信(非父子关系)。在简单场景下,可以使用一个空的 Vue 实例作为中央时间总线。

var bus = new Vue()

bus.$emit() 触发事件,用 bus.$on() 监听事件。

<div id="app">
  <a-component></a-component>
  <b-component></b-component>
</div>
<script>
  Vue.component('a-component', {
    template: `
      <div>
        {{msg}}
        <button @click="handle">点我传递数据到b组件</button>
      </div>
    `,
    data: function(){
      return { msg: '我是a中的数据' }
    },
    methods: {
      handle(){
        this.$root.bus.$emit('xxx', this.msg)
      }
    }
  })
  Vue.component('b-component', {
    template: '<div>{{msg}}</div>',
    data: function(){
      return { msg: '我是b中的数据'}
    },
    created(){
      this.$root.bus.$on('xxx', function(msg){
        alert(msg)
      })
    }
  })
  var app = new Vue({
    el: '#app',
    data: {
      bus: new Vue()
    }
  })
</script>

父链

this.$parent

Vue.component('child-component', {
  template: '<button @click="setParentData">点我修改父亲的数据</button>',
  methods: {
    setParentData(){
      console.log(this.msg);
      this.$parent.msg = '数据已经修改了'
    }
  }
})

子链

this.$children

var app = new Vue({
  el: '#app',
  data: {
    msg: '我是你爸爸'
  },
  methods: {
    getChildData(){
      console.log(this.$children);
      this.msg = this.$children[0].msg
    }
  }
})

Vue 提供了为子组件提供索引的方法,用特殊属性 ref 为其增加一个索引。

<div id="app">
  {{msg}}
  <button @click="getChildData">点我拿到儿子的数据</button>
  <a-component ref="a"></a-component>
  <b-component ref="b"></b-component>
</div>
<script>
  Vue.component('a-component', {
    template: '<div>{{msg}}</div>',
    data(){
      return {
        msg: '我是a'
      }
    }
  })
  Vue.component('b-component', {
    template: '<div>{{msg}}</div>',
    data() {
      return {
        msg: '我是b'
      }
    }
  })
  var app = new Vue({
    el: '#app',
    data: {
      msg: '我是你爸爸'
    },
    methods: {
      getChildData(){
        this.msg = this.$refs.a.msg
      }
    }
  })
</script>

使用 slot 分发内容

什么是 slot

为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为内容分发。Vue.js 实现了一个内容分发 API ,使用特殊的 slot 元素作为原始内容的插槽。

编译的作用域

父组件模板的内容在父组件作用域内编译,子组件模板的内容在子组件作用域内编译。

单个插槽

template: `
  <div>
    <slot></slot>
  </div>
`

具名插槽

<div id="app">
  <a-component>
    <h3 slot="header">我是标题</h3>
    <p>我是正文内容</p>
    <p>我是第二段正文内容</p>
    <p slot="footer">我是底部内容</p>
  </a-component>
</div>
<script>
  Vue.component('a-component', {
    template: `
      <div>
        <div class="header">
          <slot name="header"></slot>
        </div>
        <div class="container">
          <slot></slot>
        </div>
        <div class="footer">
          <slot name="footer"></slot>
        </div>
      </div>
    `
  })
var app = new Vue({
  el: '#app'
})
</script>

作用域插槽

作用域插槽是一种特殊的 slot ,可以从子组件获取数据。
slot 的 name 属性无法获取。
template 模板标签不会被渲染。
在 Vue.js 2.5.0 之后的版本不再需要 template 模板,可以直接写在任意标签里。

<div id="app">
  <a-component>
    <template slot="xxx" slot-scope="prop">
      {{prop.text}}
      {{prop.abc}}
    </template>
  </a-component>
</div>
<script>
  Vue.component('a-component', {
    template: `
      <div>
        <slot name="xxx" text="子组件中的数据" abc="123"></slot>
      </div>
    `
  })
var app = new Vue({
  el: '#app'
})
</script>

访问 slot

通过 this.$slots.name访问具名插槽 、this.$slots.default 访问默认插槽。得到的是内容为 vnode 的数组。

mounted(){
  console.log(this.$slots.header[0].elm.innerText)
  console.log(this.$slots.header[0].elm.innerHTML)
}

动态组件

VUE 给我们提供了一个元素叫 component ,作用是用来挂载动态组件。

<div id="app">
  <component :is="view"></component>
  <button @click="handleView('a')">第一句</button>
  <button @click="handleView('b')">第二句</button>
  <button @click="handleView('c')">第三句</button>
  <button @click="handleView('d')">第四句</button>
</div>
<script>
  Vue.component('a-component', {
    template: '<div>锄禾日当午</div>'
  })
  Vue.component('b-component', {
    template: '<div>汗滴禾下土</div>'
  })
  Vue.component('c-component', {
    template: '<div>谁知盘中餐</div>'
  })
  Vue.component('d-component', {
    template: '<div>粒粒皆辛苦</div>'
  })
  var app = new Vue({
    el: '#app',
    data: {
      view: 'a-component'
    },
    methods: {
      handleView(tag){
        this.view = tag + '-component'
  
      }
    }
  })
</script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 组件(Component)是Vue.js最核心的功能,也是整个架构设计最精彩的地方,当然也是最难掌握的。...
    六个周阅读 5,571评论 0 32
  • 什么是组件 组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用...
    angelwgh阅读 774评论 0 0
  • 本文章是我最近在公司的一场内部分享的内容。我有个习惯就是每次分享都会先将要分享的内容写成文章。所以这个文集也是用来...
    Awey阅读 9,417评论 4 67
  • 组件的作用 作用: 提高代码的可复用性。 组件的使用方法 全局注册: 形式 代码示例: 优点:所有的Vue示例都可...
    学的会的前端阅读 183评论 0 0
  • 有一篇非常棒的关于vue.js的组件的文章,写的特别好,特别清楚,容易理解。链接:上篇:http://www.cn...
    恰皮阅读 1,763评论 0 8