从简单的列表页浅谈Vue组件

组件

什么是组件?

组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML 元素的形式,以 js 特性扩展。

例子

screen.png

举个小例子:豆瓣电影排行榜

此例子包含文章列表,分页,这里把列表和分页分别当作单独的组件,加上父页面组件一共是3个

父组件:app.vue

子组件:list.vue pagination.vue

接口用的是豆瓣的jsonp,https://api.douban.com/v2/movie/top250

首先在main.js引入父组件,引入less,引入VueResource,实例化vue

//main.js

import Vue from 'vue'
import app from './example1/app.vue'
import './example1/style.less'

import VueResource from 'vue-resource'
Vue.use(VueResource)

new Vue({
  render: h => h(app)
}).$mount('#app')

从分页开始,在src/example1/components文件夹下建立一个pagination.vue文件

<!--pagination.vue-->
<template>
  <div class="text-center">
    <ul class="pagination">
      <li></li>
    </ul>
  </div>

</template>

<script type="text/javascript">
export default {
  data () {
    return {
    }
  },
  props: {
  },
  methods: {
  },
  computed: {
  },
  components: {
  },
  mounted () {
  }
}
</script>

通过props从父组件接收数据。组件可以为props指定验证要求。如果未指定验证要求,Vue会发出警告,如果不需要验证,可以用数组形式

//pagination.vue

props: {
    //当前页码
    now: {
      type: Number,
      default: 1
    },
    //总页数
    all: {
      type: Number,
      default: 1,
      required: 1
    }
  }

父组件app.vue引入组件,并且把数据传给子组件

<!--app.vue-->

<!--代码片段-->
<pagination :now="page.now" :all="page.all"></pagination>
<!--代码片段-->

<script type="text/javascript">

import pagination from './components/pagination.vue'
export default {
  data () {
    return {
        page: {
            now: 1,
            all: 10
        }
           
    }
  },
  components: {
    pagination
  }
}
</script>

根据传入的数据计算分页数据

//pagination.vue

computed: {
    pages () {
      var pages = [];
      if(this.now > 1) pages.push({
        text: '上一页',
        num: this.now - 1
      });
      if(this.now - 1 > 2) pages.push({
        text: 1,
        num: 1
      });
      if(this.now - 1 > 3) pages.push({
        text: '...',
        num: 0
      });
      for (var i = 1; i <= this.all; i++) {
        if(this.now - i < 3 && this.now >= i || i - this.now < 3 && this.now <= i){
          pages.push({
            text: i,
            num: i
          });          
        }        
      }
      if(this.all - this.now > 3) pages.push({
        text: '...',
        num: 0
      });
      if(this.all - this.now > 2) pages.push({
        text: this.all,
        num: this.all
      });
      if(this.now < this.all) pages.push({
        text: '下一页',
        num: this.now + 1
      });
      
      return pages;
    }

  }

用v-for把分页渲染出来

<!--pagination.vue-->

<template>
  <ul class="pagination">
    <li v-for="page in pages" :class="{ 'active' : page.num == now }">
        <a v-if="page.num" @click="toPage(page.num)">{{ page.text }}</a>
        <span v-else>{{ page.text }}</span>
    </li>
  </ul>
</template>

子组件通过自定义事件向父组件传事件和数据

//pagination.vue

methods: {
    toPage (page) {
      this.$emit('toPage', page);

    }

  }

父组件监听这个事件,然后调用方法changePage

<!--app.vue-->

<pagination :now="page.now" :all="page.all" @toPage="changePage"></pagination>
    
<script>
//代码片段
methods: {
    changePage (page) {
      this.page.now = page
    } 

}
//代码片段
</script>

至此完成了父组件把数据丢给子组件处理,子组件把操作反馈给父组件这一过程

列表组件list.vue

<!--list.vue-->

<template>
  <div>
    <slot name="title"></slot>
    <div class="list clearfix row">
      <div class="col-xs-6" v-for="item in items">
        <div class="item">
          <div class="img">
            ![](item.images.small)</div>
          <slot name="year" :year="item.year">
            <div class="year">{{ item.year }}</div>
          </slot>
          <div class="title">{{ item.title }}</div>
          <div class="dis">{{ item.original_title }}</div>
        </div>
      </div>

    </div>
  </div>
</template>

<script type="text/javascript">
export default {
  data () {
    return {
      
    }
  },
  props: ['items']
}
</script>

来点动态的数据

<!--app.vue-->

<template>
  <div class="container">
    <div class="loading" v-show="loading"></div>

    <list :items="items">
      <h1 class="text-center" slot="title">豆瓣电影排行榜</h1>
      <template slot="year" scope="props">
        <div class="year red">{{ props.year }}</div>
      </template>
    </list>
    <pagination :now="page.now" :all="page.all" @toPage="changePage"></pagination>
  </div>
</template>

<script type="text/javascript">
import list from './components/list.vue'
import pagination from './components/pagination.vue'
export default {
  data () {
    return {
      loading: 0,
      params: {
        //每页显示条数
        count: 10,
        //从第几条开始显示,初始0
        start: 0,
        //总条数
        total: 250
      },      
      items: []      
    }
  },
  methods: {
    getItems () {
      this.loading = 1;
      this.$http.jsonp('https://api.douban.com/v2/movie/top250', {
        params: this.params
      }).then(response => {
        if(response.ok) {
          this.items = response.data.subjects;
          this.params = {
            count: response.data.count,
            start: response.data.start,
            total: response.data.total
          }
        }

      }, response => {
        console.log(response);

      }).finally(function(){
        this.loading = 0;
      })

    },
    changePage (page) {
      this.params.start = (page - 1) * this.params.count;
      this.getItems();
    } 

  },
  components: {
    list,
    pagination
  },
  computed: {
    page () {
      return {
        now: this.params.start / this.params.count + 1,
        all: Math.ceil(this.params.total / this.params.count)
      }      
    }

  },
  mounted () {
    this.getItems();
  }
}
</script>

几个点:

prop传递数据

组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。可以使用 props 把数据传给子组件。

类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件:

自定义事件

父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件

slot分发内容

组件不知道它的挂载点会有什么内容。挂载点的内容是由父组件决定的;组件很可能有它自己的模版

为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为 内容分发 (或 “transclusion” 如果你熟悉 Angular)。Vue.js 实现了一个内容分发 API ,参照了当前 Web 组件规范草案,使用特殊的 <slot> 元素作为原始内容的插槽。

通过slot插槽将标题插入到子组件

作用域插槽

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

作用域插槽是一种特殊类型的插槽,用作使用一个(能够传递数据到)可重用模板替换已渲染元素。
在子组件中,只需将数据传递到插槽,就像你将 prop 传递给组件一样

通过作用域插槽将子组件数据传给父组件,再作为自定义模板重新插入到子组件

计算

豆瓣接口接收参数和设计的不同,pages需要通过computed计算

分页的算法

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

推荐阅读更多精彩内容

  • 这篇笔记主要包含 Vue 2 不同于 Vue 1 或者特有的内容,还有我对于 Vue 1.0 印象不深的内容。关于...
    云之外阅读 5,043评论 0 29
  • 此文基于官方文档,里面部分例子有改动,加上了一些自己的理解 什么是组件? 组件(Component)是 Vue.j...
    陆志均阅读 3,798评论 5 14
  • 序 今年大前端的概念一而再再而三的被提及,那么大前端时代究竟是什么呢?大前端这个词最早是因为在阿里内部有很多前端开...
    一缕殇流化隐半边冰霜阅读 11,196评论 19 92
  • 9.1 什么是组件? 组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,...
    白水螺丝阅读 759评论 0 2
  • 最近看到简书上关于人际社交圈之类文章的评论区里出现不少这样提问,比如我不喜欢现在周围的这几个朋友,但是我又不敢把不...
    觉睡白了阅读 601评论 4 7