前言

组件 (Component) 是 Vue.js 最强大的功能之一,使用组件化方式开发,可以封装可重用的代码,减少重复劳动。接下来,我们来注册一个组件:

语法:Vue.component(tagName, options)
// 注册组件
Vue.component('component-a', {
   template: '<div>自定义组件</div>'
})

component-a是注册的组件标签,下面就可以使用这个组件了

<div id="app">
   <component-a></component-a>
</div>
// 创建根实例
new Vue({
  el: '#app'
})

最后,渲染为:

<div id="app">
    <div>自定义组件</div>
</div>

全局组件

通过Vue.component方式注册的组件是一个全局组件,我们还可以创建局部组件,通过某个 Vue 实例/组件的实例选项 components 注册仅在其作用域中可用的组件。

var Child = {
  template: '<div>A custom component!</div>'
}
var vm = new Vue({
	el: '#app',
	components:{
		'component-a':Child
	}
})

注意:如果使用Vue.Component 定义全局组件的时候,组件的名称使用了驼峰命名,则在引用组件的时候需要把大写的驼峰该为小写的字母,同时,两个单词之前,使用 – 链接。如果不使用则直接拿名称来使用即可。

<div id="app">
	<!--组件不使用驼峰命名-->
	<mycomponent></mycomponent>
	<!--组件使用驼峰命名-->
	<my-component></my-component>
</div>
<script>
	Vue.component('mycomponent', {
		template: '<div>不使用驼峰命名,则直接拿名称来使用即可</div>'
	})
	Vue.component('myComponent', {
		template: '<div>使用驼峰命名,需要把 大写的驼峰改为小写的字母,同时,两个单词之前,使用 - 链接</div>'
	})
	// 创建 Vue 实例,得到 ViewModel
	var vm = new Vue({
		el: '#app',
		data: {},
		methods: {}
	});
</script>

局部组件

我们也可以在实例选项中注册局部组件,这样组件只能在这个实例中使用。局部组件的创建和全局组件的创建方法一样。唯一区别的是,局部组件是在vue实例中定义的。如:

<div id="app">
    <my-component></my-component>
</div>
 
<script>
var Child = {
  template: '<h1>自定义组件!</h1>'
}
// 创建根实例
new Vue({
  el: '#app',
  components: {
    // <my-component> 将只在父模板可用
    'myComponent': Child
  }
})
</script>

博客实例如下:

//定义子组件
<template>
  <div class="sideBar">
   <div class="sidebar_categories">
        <div class="sidebar_postcategory">
          <h3 class="catListTitle">文章分类</h3>
          <ul>
            <li v-for="item in categorylist">
              <a :href="'/list/'+item._id">{{item.categoryname}}</a>
            </li>
          </ul>
        </div>
      </div>
  </div>
</template>
<script>
  export  default {
    name:'sliderBar',
    props:['hotarticles','categorylist'],
    data(){
      return{
        searchKey:''
      }
    },
    methods:{
      
    }
  }
</script>

在给子组件定义属性的时候,通常第一个属性就是 name。定义 name 属性一般有三个方面的作用:

1、为了方便在组件自身调用自身出现递归的时候便于调用。

2、单独取消某个页面的 keepalive 缓存时更好的调用。

3、使用 vue 工具进行查看网页结构的时候更好的查看网页结构。

子组件在父组件中的使用:

<template>
  <div class="index">
    <slider-bar :hotarticles="hotarticles" :categorylist="categorylist"></slider-bar>
  </div>
</template>
<script>
  import {mapState,mapActions} from 'vuex'
  import sliderBar from '../../components/sliderBar'
  export default {
    data(){
      return{
        pageName:''
      }
    },
    computed:{
      ...mapState(['categorylist'])
    },
    mounted(){
      this.getCategoryList();
    },
    methods:{
      ...mapActions(['getCategoryList'])
    },
    components:{
      sliderBar
    }
  }
</script>

组件通信

组件 A 在它的模板中使用了组件 B,它们之间必然需要相互通信,父组件可能要给子组件下发数据,子组件则可能要将它内部发生的事情告知父组件。

父子组件通信主要是使用props和自定义事件,父组件通过 props 给子组件下发数据,子组件通过事件给父组件发送消息。

父子组件的关系可以总结为 props 向下传递,事件向上传递。

<div id="app">
<div>
  <input v-model="parentMsg">
  <br>
  <child v-bind:message="parentMsg"></child>
</div>
</div>
<script>
// 注册
Vue.component('child', {
  // 声明 props
  props: ['message'],
  // 同样也可以在 vm 实例中像 “this.message” 这样使用
  template: '<span>{{ message }}</span>'
})
// 创建根实例
new Vue({
  el: '#app',
  data: {
parentMsg: '父组件内容'
  }
})
</script>

代码示例来源于菜鸟教程,运行效果如下:

GIF.gif

父组件向子组件传值

1、 父组件通过 属性绑定(v-bind:) 的形式, 把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用。

2、 子组件需要在内部定义props 属性。例如 props : ['parentmsg'] 把父组件传递过来的 parentmsg 属性,先在 props 数组中,定义一下,这样,才能使用这个数据。

3、 所有props 中的数据都是通过 父组件传递过来的。

子组件向父组件传值

子组件向父组件传值是通过方法的方式,子组件通过事件给父组件发送消息,子组件通过$emit触发事件。

this.$emit('reply','子组件消息')

在组件标签中通过v-on进行监听

<component-a v-bind:msg="msg" @reply="replay"></component-a>

监听到事件后触发reply函数,接收到子组件发送的消息

replay:function(msg){
  this.replyMsg = msg
}

总结

父子组件的数据传递是单向数据流,即子组件只能使用父组件传递过来的数据,不能修改这些数据。因为这些数据很可能在其他地方被其他组件进行使用。

所以当子组件在收到父组件传递过来的数据,并在后续可能要对这些数据进行修改时。可以先将 props 里面获取到的数据,在子组件自己的 data 的 return 中使用一个 number 进行复制,并在后续修改这个 number 即可。

分页组件

下面记录一段博客中自定义的分页组件,代码如下:

<template>
  <div class="page-bar">
    <ul class="pagination">
      <li :class="{'disabled': current == 1}"><a href="javascript:;" @click="setCurrent(current - 1)"> « </a></li>
      <li v-for="p in grouplist" :class="{'active': current == p.val}" @click="setCurrent(p.val)">
        <a href="javascript:;"> {{ p.text }} </a>
      </li>
      <li :class="{'disabled': current == page}"><a href="javascript:;" @click="setCurrent(current + 1)"> »</a></li>
    </ul>
  </div>
</template>
<script>
  export default {
    name:'pagination',
    data(){
      return{
        current:this.pageIndex
      }
    },
    props:{
      pageIndex:{
        type:Number,
        default:1
      },
      pageSize:{
        type:Number,
        default:6
      },
      total:{
        type:Number,
        default:0
      },
      pageGroup:{ //分页条数
        type:Number,
        default:5
      }
    },
    computed:{
      page:function () { //总页数
        return Math.ceil(this.total / this.pageSize);
      },
      grouplist: function () { // 获取分页页码
        var len = this.page,
            temp = [],
            list = [],
            count = Math.floor(this.pageGroup / 2),
            center = this.current;
        if (len <= this.pageGroup) {
          while (len--) {
            temp.push({text: this.page - len, val: this.page - len});
          };
          return temp;
        }
        while (len--) {
          temp.push(this.page - len);
        };
        var idx = temp.indexOf(center);
        (idx < count) && ( center = center + count - idx);
        (this.current > this.page - count) && ( center = this.page - count);
        temp = temp.splice(center - count - 1, this.pageGroup);
        do {
          var t = temp.shift();
          list.push({
            text: t,
            val: t
          });
        } while (temp.length);
        if (this.page > this.pageGroup) {
          (this.current > count + 1) && list.unshift({text: '...', val: list[0].val - 1});
          (this.current < this.page - count) && list.push({text: '...', val: list[list.length - 1].val + 1});
        }
        return list;
      }
    },
    methods:{
      setCurrent:function (index) {
        if(this.current !== index && index > 0 && index < this.page + 1){
          this.current = index;
          this.$emit('pageChange',this.current)
        }
      }
    }
  }
</script>

父组件中的使用:

<template>
  <div class="manContent">
    <div class="flow">
      <v-pagination :total="total" :pageSize="pageSize" :pageIndex="pageIndex" @pageChange="pageChange"></v-pagination>
    </div>
  </div>
</template>
<script type="text/javascript">
  import VPagination from "./Pagination/pagination";
  export default{
    name:'conMain',
    data(){
      return{
        pageIndex:1,
        pageSize:10,
        total:0
      }
    },
    methods:{
      pageChange:function(index){
        if(index === this.pageIndex) return;
        this.pageIndex = index;
        this.fetchData()
      }
    },
    components:{
      VPagination
    }
  }
</script>

分页效果见本博客主页右下角的分页组件。

作者: 一蓑烟雨

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类: Vue.js
posted 阅读(9 ) 评论(0 )

评论内容: