從簡單的列表頁淺談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從父組件接收數(shù)據(jù)。組件可以為props指定驗證要求。如果未指定驗證要求,Vue會發(fā)出警告,如果不需要驗證,可以用數(shù)組形式

//pagination.vue

props: {
    //當前頁碼
    now: {
      type: Number,
      default: 1
    },
    //總頁數(shù)
    all: {
      type: Number,
      default: 1,
      required: 1
    }
  }

父組件app.vue引入組件,并且把數(shù)據(jù)傳給子組件

<!--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>

根據(jù)傳入的數(shù)據(jù)計算分頁數(shù)據(jù)

//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>

子組件通過自定義事件向父組件傳事件和數(shù)據(jù)

//pagination.vue

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

    }

  }

父組件監(jiān)聽這個事件,然后調(diào)用方法changePage

<!--app.vue-->

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

}
//代碼片段
</script>

至此完成了父組件把數(shù)據(jù)丟給子組件處理,子組件把操作反饋給父組件這一過程

列表組件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>

來點動態(tài)的數(shù)據(jù)

<!--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: {
        //每頁顯示條數(shù)
        count: 10,
        //從第幾條開始顯示,初始0
        start: 0,
        //總條數(shù)
        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傳遞數(shù)據(jù)

組件實例的作用域是孤立的。這意味著不能并且不應(yīng)該在子組件的模板內(nèi)直接引用父組件的數(shù)據(jù)。可以使用 props 把數(shù)據(jù)傳給子組件。

類似于用 v-bind 綁定 HTML 特性到一個表達式,也可以用 v-bind 動態(tài)綁定 props 的值到父組件的數(shù)據(jù)中。每當父組件的數(shù)據(jù)變化時,該變化也會傳導給子組件:

自定義事件

父組件可以在使用子組件的地方直接用 v-on 來監(jiān)聽子組件觸發(fā)的事件

slot分發(fā)內(nèi)容

組件不知道它的掛載點會有什么內(nèi)容。掛載點的內(nèi)容是由父組件決定的;組件很可能有它自己的模版

為了讓組件可以組合,我們需要一種方式來混合父組件的內(nèi)容與子組件自己的模板。這個過程被稱為 內(nèi)容分發(fā) (或 “transclusion” 如果你熟悉 Angular)。Vue.js 實現(xiàn)了一個內(nèi)容分發(fā) API ,參照了當前 Web 組件規(guī)范草案,使用特殊的 <slot> 元素作為原始內(nèi)容的插槽。

通過slot插槽將標題插入到子組件

作用域插槽

父組件模板的內(nèi)容在父組件作用域內(nèi)編譯;子組件模板的內(nèi)容在子組件作用域內(nèi)編譯

作用域插槽是一種特殊類型的插槽,用作使用一個(能夠傳遞數(shù)據(jù)到)可重用模板替換已渲染元素。
在子組件中,只需將數(shù)據(jù)傳遞到插槽,就像你將 prop 傳遞給組件一樣

通過作用域插槽將子組件數(shù)據(jù)傳給父組件,再作為自定義模板重新插入到子組件

計算

豆瓣接口接收參數(shù)和設(shè)計的不同,pages需要通過computed計算

分頁的算法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容,還有我對于 Vue 1.0 印象不深的內(nèi)容。關(guān)于...
    云之外閱讀 5,180評論 0 29
  • 此文基于官方文檔,里面部分例子有改動,加上了一些自己的理解 什么是組件? 組件(Component)是 Vue.j...
    陸志均閱讀 3,949評論 5 14
  • 序 今年大前端的概念一而再再而三的被提及,那么大前端時代究竟是什么呢?大前端這個詞最早是因為在阿里內(nèi)部有很多前端開...
    一縷殤流化隱半邊冰霜閱讀 11,374評論 19 92
  • 9.1 什么是組件? 組件(Component)是 Vue.js 最強大的功能之一。組件可以擴展 HTML 元素,...
    白水螺絲閱讀 889評論 0 2
  • 最近看到簡書上關(guān)于人際社交圈之類文章的評論區(qū)里出現(xiàn)不少這樣提問,比如我不喜歡現(xiàn)在周圍的這幾個朋友,但是我又不敢把不...
    覺睡白了閱讀 670評論 4 7

友情鏈接更多精彩內(nèi)容