Vue学习笔记---初识组件

前言

Vue中,万物皆组件,学习组件乃是Vue的根基所在,假如一个页面相当于一张图,那么组件就是一个小小的拼图,通过组件可以拼出各式各样的图,这也是Vue的魅力所在。

正文

基本示例

Vue.component('button-counter',{
    data() {
      return {
        count: 0
      }
    },
    template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

上面我们创建了一个全局的名字为button-couter的组件,当我们要在根实例(app.vue)中使用它的时候,直接把这个组件作为自定义的标签来使用。

<div>
  <button-counter></button-counter>
</div>

局部注册

上面演示了如何全局注册一个组件,但是组件不能全部都全局注册,会造成代码的冗余,所以大部分的组件需要局部注册。

如何局部注册呢,首先把一个组件作为一个单个的文件放在components文件夹下,在文件中将组件export,然后在实例中import它就实现了组件的局部注册。

// 组件文件的基本格式
// components/buttonCounter.vue
<template>
  // 模板内容
</template>
<script>
  export default {
    name: '',  //这个name对外没有实际作用,但是可以让该组件递归调用自己
    data() {
      return {}
    }
  }
</script>
<style scoped>
  // scoped标明style里面样式仅适用于当前组件
</style>

下面我们在实例中调用它

import buttonCounter from './component/buttonCounter'
// 引入组件,组件的名字也可以随意定义(假如定一个abc),下面使用它的时候就用<abc></abc>
<buttonCounter></buttonCounter>

基础组件的自动全局化注册

有一些组件特别小,但是复用率特别高,必须输入框按钮之类的,这些组件就必须全局注册,但是组件又特别多,一个个注册显得代码特别臃肿,所以下面就用一段代码来实现自动注册。

假如我们把基础组件放在components文件夹下,组件的文件名都以Base开头,比如BaseInput,BaseButton。首先我们引入符合Base开头匹配规则的所有组件,这里要用到require.context方法,然后用文件的名字来注册全局组件。

import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'

const requireComponent = require.context(
    './components',  //根目录
    false,            // 是否查询子目录
    /Base[A-Z]\w+\.(vue|js)$/   //匹配规则
)
requireComponent.keys().forEach(fileName => {
    const componentConfig = requireComponent(fileName)

    const componentName = upperFirst(
        camelCase(
            fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
        )
    )
    Vue.component(
        componentName,
        componentConfig.default || componentConfig
        // 组件中 export default 和 export
    )
})

上面代码中upperFirstcamelCase都是lodash库里面的方法。

组件里面也是有computed,methods,watch生命周期钩子的,唯独没有el,这是根实例独有的

那么data为何是个函数而不是对象呢,这是因为组件是可以复用的,每次使用就创建一个新的实例,把data里面的属性作为函数的返回值的话,它就实现了每个实例的属性的私有化,跟闭包一样原理。

通过prop向组件传递数据

Vue.component('blog-post',{
    props: ['title','name'],
    template: '<h3>{{ name }}{{ title }}</h3>'
})

我们创建了一个blog-post组件,并用prop接收了2个参数titlename,下面在根实例中往组件中注入值

<blog-post title="myTitlt" name="myName"></blog-post>

上面是传了几个单个值,假如传的值比较多,或者传的值是个数组的时候就要换一个方式

Vue.component('blog-post',{
    props: ['post'],
    template: '<h3>{{ post.name }}{{ post.title }}</h3>'
})

根实例中传值

<blog-post v-for="post in posts" :post="post"></blog-post>

当父级传给子组件值时候,子组件改变这个值也会同时更改父级的值,这样就会发生混乱,所以当子组件有改变值的情况下,最好将父级传来的值赋给自己的属性

props: ['name'],
data() {
  return {
    myName: this.name
  }
}

组件向父级传值

这里要用到一个Vue的内置方法$emit,这个函数就相当于js中的trigger(),就是在组件中可以触发实例中绑定的事件,而且这个方法可以传递参数。

Vue.component('welcome-button', {
  template: `
    <button v-on:click="$emit('welcome',1)">
      Click me to be welcomed
    </button>
  `
})

组件中触发welcome事件并传了个参数1,那么在实例中绑定这个事件

<welcome-button @welcome="couter=$event"></welcome-button>
<welcome-button @welcome="sayHi"></welcome-button>

new Vue({
    data: {
      couter: 0
    },
    methods: {
      sayHi(argsCouter) {
        // ..
      }
    }
})

在实例中可以使用$event获取$emit传递的参数,如果$emit触发的事件引用的是另外一个事件,那么$emit的参数将传给它引用的事件(argsCouter即$emit传递的参数)

在组件中使用v-model

<input v-model="searchText">这是一个v-model的使用场景,那么它的本质其实是

<input :value="searchText" @input="searchText=$event.target.value">

那么在组件上使用v-model就相当于

<custom-input :value="searchText" @input="$event"></custom-input>
等同于
<custom-input v-model="searchText"></custom-input>

为了让它工作,在组件中首先得把value绑在prop上,然后它得触发input事件

Vue.component('custom-input',{
    prps: ['value'],
    template: `<input :value="value" @input="$emit('input',$event.target.value)"`
})

插槽

有时候我们在调用组件的时候,组件元素中间还有一些其他的东西,一般情况下,如果不处理这些东西会被舍弃的,但是如果加上<slot>元素,这些东西就会被渲染在组件之中

<custom-link>
这是一个链接
</custom-link>

那么在组件中,如果要使用这是一个链接,就需要加<slot>元素

<a href=''>
  <slot></slot>   //这是一个链接
</a>

组件元素的闭合标签中间内容就是插槽,在插槽可以加html模板,甚至可以加组件

具名插槽

上面的只是一个插槽,那它默认渲染的就是组件中的<slot>,但是当有多个插槽,并且插槽还有指定位置的时候,就必须要给<slot>加上name属性

<div class="container">
  <slot name="header"></slot>
  <slot></slot>
  <slot name="footer"></slot>
</div>

那么在父级提供插槽内容的时候,必须加一个<template>元素包裹

<base-layout>
  <template slot="header">
    // 这里的内容渲染header
  </template>
    // 这里的内容没有命名,所以渲染<slot></slot>
  <template slot="footer">
    // 这里的内容渲染footer
  </template>
</base-layout>

卡槽也可以有默认内容的,<slot>默认内容</slot>,当父级有传给子组件内容时,会覆盖默认内容

动态组件

前端开发中经常会出现选项卡之类的功能,实现它就需要不同的组件切来切去,这个功能可以通过Vue<component>元素加一个特殊属性is来实现

<component :is="currentTab"></component>

当点击选项卡的nav时,只需要把currentTab指向将要切换的组件名即可

但是这样实现有个问题,就是每次切换都会创建一个组件的实例,我们更多其实是希望它第一次创建以后缓存下来,那么就需要keep-alive元素

<keep-alive>
<component :is="currentTab"></component>
</keep-alive>

异步组件

有时候我们加载组件的时候并不希望它一开始就加载进来,而是调用它的时候再加载进来,例如在VueRouter里面,当我们跳转到路由后再去加载它对应的组件。

components: {
  'my-component': () => import('./my-component')
}
// 或者
component(resolve){
  require(['@/index.vue'],resolve)
}

使用异步组件的时候,返回的都是一个promise函数

结束语

组件相关的东西还有很多很多,比如官网的处理边界情况,只不过应用场景不是很多,所以暂时不深入研究。
就这样,收工。。。

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

推荐阅读更多精彩内容

  • 组件(Component)是Vue.js最核心的功能,也是整个架构设计最精彩的地方,当然也是最难掌握的。...
    六个周阅读 5,564评论 0 32
  • 什么是组件? 组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装...
    youins阅读 9,434评论 0 13
  • VUE介绍 Vue的特点构建用户界面,只关注View层简单易学,简洁、轻量、快速渐进式框架 框架VS库库,是一封装...
    多多酱_DuoDuo_阅读 2,677评论 1 17
  • 这篇笔记主要包含 Vue 2 不同于 Vue 1 或者特有的内容,还有我对于 Vue 1.0 印象不深的内容。关于...
    云之外阅读 5,040评论 0 29
  • 一粒高粱落进了玉米地。 小高粱刚出生的时候,它对这个世界充满了好奇。只是它母亲可能是早产的缘故,与周围玉米苗相比,...
    歌凝阅读 854评论 2 4