美文网首页
Vue学习笔记---初识组件

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

作者: 邓生的邓生 | 来源:发表于2018-12-22 09:28 被阅读0次

前言

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函数

结束语

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

相关文章

网友评论

      本文标题:Vue学习笔记---初识组件

      本文链接:https://www.haomeiwen.com/subject/qbtikqtx.html