歡迎你們前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~javascript
本文由 蔡述雄發表於 雲+社區專欄
組件庫是作UI和前端平常需求中常常用到的,把一個按鈕,導航,列表之類的元素封裝起來,方便平常使用,調用方法只需直接寫上<qui-button></qui-button>或者<qui-nav></qui-nav>這樣的代碼就能夠,是否是很方便呢,接下來咱們將要完成如下頁面:css
這是咱們組件庫的首頁,包含三個子頁面,按鈕頁面、列表頁面、導航頁面;點擊進去子頁面會由路由來配置。先看咱們的目錄結構:html
pages目錄存放咱們的頁面,包括首頁和三個子頁面;components目錄存放咱們的具體組件,包括按鈕組件,箭頭組件,列表組件和導航組件(組件和頁面實際上是同樣的文件類型,只是因爲功能不同,咱們就叫不一樣的稱呼)前端
先看路由配置的代碼吧!vue
import Vue from 'vue' import Router from 'vue-router' // 引用頁面模板->供路由使用 import index from '../pages/index.vue' import pageQuiButton from '../pages/pageQuiButton.vue' import pageQuiList from '../pages/pageQuiList.vue' import pageQuiNav from '../pages/pageQuiNav.vue' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'index', component: index }, { path: '/btn', name: 'btn', component: pageQuiButton }, { path: '/list', name: 'list', component: pageQuiList }, { path: '/nav', name: 'nav', component: pageQuiNav } ] })
有了上一篇的分析以後,這裏應該很容易看出來幾個路由地址java
首頁:http://localhost:8080/#/
vue-router
按鈕子頁:http://localhost:8080/#/btn
數組
列表子頁:http://localhost:8080/#/list
app
導航子頁:http://localhost:8080/#/nav
框架
具體每一頁的內容分別對應每一頁的.vue文件,不知你們是否還記得入口頁App.vue
,這個文件承載着一些公用的元素,還有就是一個路由容器,咱們的首頁index.vue
到時候也是掛載在路由容器中的,看看App.vue
的代碼
<template> <div id="app"> <h1 class="page-title"><a href="#/">開發組件庫</a></h1> <router-view></router-view> </div> </template> <script> export default { name: 'app' } </script> <style scoped> @import './assets/css/App.css'; </style>
簡單分析一下入口頁的代碼,h1標籤是一個公用元素,也就是說到時候每一個子頁面都會帶着這個h1,他的做用就是方便咱們快速回到首頁,子頁面的內容會注入到router-view
中。這裏值得關注的地方是style標籤,咱們能夠在style標籤裏面直接寫樣式,也能夠直接引入一個樣式文件,scoped關鍵字表示這個樣式是私有的,也就是說,即便兩個組件寫着同樣的#app{}
樣式也不會衝突,程序會加上命名空間,這也就是爲何在script標籤中有個name參數。
<template> <div class="mod-module mod-parallel"> <div class="img-list type-full"> <div class="img-box"> <p class="img-item"> <a class="page-link" href="#/btn">按鈕</a> </p> </div> <div class="img-box"> <p class="img-item"> <a class="page-link" href="#/list">列表</a> </p> </div> <div class="img-box"> <p class="img-item"> <a class="page-link" href="#/nav">導航</a> </p> </div> </div> </div> </template> <style scoped> @import './css/index.css'; </style>
首頁的代碼也是很是簡單,和咱們平時寫html差很少,就是幾個跳轉連接跳到對應的子頁面,程序運行的時候,會將<template>
標籤裏面的內容都注入到App.vue頁面中的router-view
標籤中,從而實現無刷新的路由跳轉。
從下面的內容開始,咱們的知識將會深刻一些。咱們先不急着看其餘幾個子頁面,由於子頁面裏面只是引用對應的組件,因此咱們先從組件開始入手。
<template> <button class="qui-btn"> <span>{{msg}}</span> </button> </template> <script> export default { data:function(){ return { msg:'下載' } } } </script> <style scoped> @import './css/reset.import.css'; @import './css/qui-btn.import.css'; </style>
按鈕組件很簡單,就是一個正常的button標籤,script標籤中暴露這個組件的data屬性(data是Vue的屬性值,不是亂寫的~~)。當按鈕組件被初始化的時候,msg自定義屬性會被綁定到<span>標籤中的{{msg}}中,兩個花括號用來綁定屬性,這種寫法學過模版化前端代碼的人應該都比較熟悉。這裏須要注意一個地方,若是不是組件的話,正常data的寫法能夠直接寫一個對象,好比data:{ msg : ' 下載 ' },但因爲組件是會在多個地方引用的,JS中直接共享對象會形成引用傳遞,也就是說修改了msg後全部按鈕的msg都會跟着修改,因此這裏用function來每次返回一個對象實例。
這就是一個很是簡單的按鈕組件,結構、樣式+文案。
這時候問題來了,按鈕中的文案我但願能夠異化,不能每次都初始化一個叫作「下載」文案的按鈕吧,但願能夠以屬性的方式來使用,好比這樣子寫就能夠改變咱們的按鈕文案:
<qui-btn msg="肯定" class="small"></qui-btn>
沒問題,屬性的接口暴露只須要寫在prosp裏面就能夠了,以下所示修改下script標籤的內容:
<script> export default { props: { msg: { default: '下載' } } } </script>
把屬性寫在props裏面,就能夠暴露給其餘頁面調用了,在組件中,props是專門用來暴露組件的屬性接口的,這裏給了一個默認值‘下載’
,後面咱們要使用的話,只須要<btn msg="確認"></btn>
就能夠修改按鈕的默認文案了。
咱們在上一篇文章的開頭就講了Vue是數據驅動模式的,當我在btn結構寫上msg="確認"
的時候,對應script裏面的msg屬性就會自動修改了。
按鈕總少不了點擊事件吧,那在Vue中怎麼綁定事件呢,用methods屬性,看下代碼:
<template> <button class="qui-btn" v-on:click="btnClickEvent"> <span>{{msg}}</span> </button> </template> <script> export default { props: { msg: { default: '下載' } }, methods: { //綁定事件的關鍵代碼 btnClickEvent: function(){ alert(this.msg); } } } </script>
methods屬性中能夠寫任何的自定義函數,寫完以後綁定的方式也很簡單,在button上寫關鍵字v-on:click
,把對應的事件寫上就能夠了,以上代碼實現的就是點擊按鈕彈出按鈕中的文案,v-XXX
是Vue裏的一些關鍵字,叫作指令,咱們後面會慢慢學到更多的指令;v-on:click
能夠縮寫爲@click
,固然還有其餘的事件好比v-on:tab
等等;
如今咱們大體作了一個按鈕組件了,那麼怎麼調用它呢,去到咱們的pageQuiButton子頁面。
//pageQuiButton.vue <template> <div id="pageQuiButton"> <!--使用--> <qui-btn msg="肯定" class="small"></qui-btn> </div> </template> <script> import quiBtn from '../components/quiButton.vue' /*引用*/ export default { name: 'pageQuiButton', components: { 'qui-btn': quiBtn /*註冊自定義標籤*/ } } </script>
從script開始解析,首先引入咱們的組件賦值給變量quiBtn,使用時候直接將quiBtn做爲對象的一部分寫進components屬性,這是Vue用來存儲引用組件的關鍵字,同時對應咱們自定義的標籤 "qui-btn"
,完成這些操做以後,咱們就能夠在template中使用自定義的按鈕組件<qui-btn>
上面也說了用msg屬性來自定義按鈕的文案。完成以後,咱們就能夠在頁面中看到具體效果,點擊按鈕彈出對應的文案。
上述咱們將按鈕事件寫成默認的alert(this.msg)
,若是有些按鈕想要異化怎麼辦。以前說了msg屬性能夠支持自定義,那麼按鈕的點擊事件如何支持自定義呢?
//pageQuiButton.vue //監聽子組件的事件 <qui-btn v-on:btnClickEvent="doSth" msg="我能夠點擊" ></qui-btn>
上面的代碼在引用組件的時候,註冊了一個事件,這個btnClickEvent事件是以前咱們在按鈕組件中綁定到按鈕的click事件中的,而後咱們給這個事件一個自定義的方法doSth,同時,在script中聲明這個自定義的方法以下:
//pageQuiButton.vue //頁面中引用子組件並監聽子組件的事件 <script> import quiBtn from '../components/quiButton.vue' export default { name: 'pageQuiButton', components: { 'qui-btn': quiBtn }, methods: { doSth: function(){ alert('你點擊了組件的click:btnClickEvent'); } } } </script>
專業一點的說,這種作法叫作監聽,由引用方(暫且叫作父組件)監聽子組件的內置方法;同時在子組件中,須要觸發這個事件,如下是在子組件中的關鍵代碼:
//quiButton.vue //子組件中的代碼 <script> export default { props: { msg: { default: '下載' } }, methods: { btnClickEvent: function(){ alert("先彈出默認的文案"); this.$emit('btnClickEvent');//關鍵代碼父組件觸發自定義事件 } } } </script>
這裏的關鍵代碼就是$emit
,也叫觸發機制,父組件監聽,子組件觸發。若是以爲繞,如下描述可能會比較好理解:小B(子組件)有一個電話號碼(子組件註冊的事件),有一天小B把電話號碼告訴了小A(父組件),讓小A打電話給他,因而小A就撥打了小B的電話號碼(監聽),這時候整個溝通流程沒有結束,必需要小B接聽了電話(觸發),兩人之間纔算完成了打電話這件事情。
完成這步以後,引用方(父組件)就能夠給不一樣子組件調用不一樣的事件處理了:
<qui-btn v-on:btnClickEvent="doSth1" msg="肯定" ></qui-btn> <qui-btn v-on:btnClickEvent="doSth2" msg="取消" ></qui-btn> <script> /*這裏只是簡單展現*/ methods: { doSth1: function(){ alert('111'); }, doSth2: function(){ alert('222'); } } </script>
有時候單純的文案異化還不夠,好比一些按鈕是圖標+文字類型的,並且圖標還可能不同,那應該怎麼辦呢?
若是按鈕組件的結構除了開發時候預設的那些dom結構以外,容許咱們在調用的時候添加一些本身想要的結構,那是否是解決了呢,是的,Vue早就爲咱們考慮了這一點,他就是slot標籤。
下面給咱們的按鈕組件加上一段結構
//quiButton.vue <template> <button class="qui-btn" v-on:click="btnClickEvent"> <slot name="icon"></slot><!--重點在這裏--> <span>{{msg}}</span> </button> </template>
加入了關鍵字slot並賦予一個name值以後,咱們再看看引用如何使用
//pageQuiButton.vue <qui-btn msg="下載" class="with-icon"> <img slot="icon" class="ico" src="xxx.png" /> </qui-btn>
img上有個關鍵字slot="icon"
對應組件中的name="icon"
,渲染的時候,會將img整個替換掉組件中的對應name的<slot>
標籤,其實很好理解,slot的翻譯是插槽的意思,至關於把img這塊內容插到一個名叫icon的插槽裏面去。
學到這裏,咱們已經學會了用props給按鈕自定義文案,用on和emit給按鈕自定義點擊觸發,用slot給按鈕添加一些自定義結構。當你回頭去翻文檔的時候,你會發現props,事件,slot這三樣恰好就是學習組件通信中最最最關鍵的三個環節。將這三個環節以實際案例解析出來後,好像也沒有那麼難了吧~!
上述咱們已經討論瞭如何製做一個按鈕組件,以及如何使用咱們的按鈕組件。
接下來咱們經過製做一個導航組件,來了解Vue中對於for循環的巧妙使用。
咱們將完成這樣一個導航組件,點擊導航中的tab,能夠給當前tab加上一個active類,同時切換底部的黃色滑條,而且輸出當前tab的文案,同時支持自定義事件。
因爲在現實項目中,咱們導航的tab個數是不定的,因此製做組件的時候,咱們但願能夠暴露一個屬性來支持導航的tab個數,而tab的長相和應用實際上是同樣的,那麼這時候咱們能夠用一個for循環來輸出每個tab。先看看關鍵代碼:
//quiNav.vue <template> <div class="qui-nav nav-type-1"> <a v-for="(item, index) in items" ><!--關鍵代碼v-for--> <span class="nav-txt">{{item.text}}</span> </a> </div> </template> <script> export default { data:function(){ return { items:[ { text: '首頁', active : true }, { text: '列表', active : false }, { text: '關於', active : false }, { text: '招聘', active : false } ] } } } } </script>
該段代碼的關鍵地方在於a標籤上v-for
關鍵字(還記得咱們在前面說過的v-on綁定事件嗎,v-XXX關鍵字是Vue預留的)能夠把它理解爲js中的for in 循環,items是咱們在data裏面定義的對象(還記得爲何data要寫在function中返回嗎?)。v-for="(item,index) in items"
暴露了item和index兩個接口,這是Vue提供的,表明items中的每一項以及該項對應的下標,接着咱們就能夠在標籤中使用綁定{{item.text}}
了。
這段代碼理解了以後,咱們再延伸一個動態添加class的概念。咱們但願每一個tab都有默認的class類名(好比nav-item
類),在點擊每一個tab的時候,當前tab添加active類,其餘的tab刪除這個active類。在Vue怎麼實現呢?
//quiNav.vue <template> <div class="qui-nav nav-type-1"> <a v-for="(item, index) in items" :class="[commonClass,item.active ? activeClass : '']" > <span class="nav-txt">{{item.text}}</span> </a> </div> </template> <script> export default { data:function(){ return { commonClass:'nav-item', activeClass:'active', items:[ ...//數據 ] } } } </script>
在template中添加了一句關鍵代碼
:class="[commonClass,item.active ? activeClass : '']"
:class
給組件綁定一個class屬性(相似jQuery中的attr方法),這裏的寫法是縮寫,他的全拼應該是v-bind:(又一個v-XXX寫法)。注意到最前面有個冒號,:class=XXX
和class=XXX
的區別在於不帶冒號的是靜態的字符串綁定,帶冒號的是動態的變量綁定。咱們給class綁定了一個數組,這個數組帶有變量,先看commonClass,這個變量在data中定義了,而後數組的第二個元素是一個JS的三元運算符:item.active?activeClass:''
,當每一個item中的active值爲true時,綁定activeClass變量對應的類,若是爲false,則爲空。最後的結果是當item.active爲true時候,tab的class值爲'nav-item active'
,當爲false,就只有'nav-item'
。
上面的代碼能夠理解的話,那麼咱們切換tab的active類,就轉換爲修改每一個item裏面的active的值(再次體現數據驅動)。
那麼問題來了,怎麼去修改每一個item裏面的active值呢?沒錯,給每一個tab綁定一個點擊事件,當點擊事件觸發的時候,修改當前tab對應item的active值。因而代碼變成了以下:
<template> <div class="qui-nav nav-type-1"> <a v-for="(item, index) in items" :class="[commonClass,item.active ? activeClass : '']" v-on:click="navClickEvent(items,index)" > <span class="nav-txt">{{item.text}}</span> </a> </div> </template> <script> export default { data:function(){ return { commonClass:'nav-item', activeClass:'active', items:[ { text: '首頁', active : true }, ...... ] } }, methods:{ navClickEvent:function(items,index){ /*默認切換類的動做*/ items.forEach(function(el){ el.active = false; }); items[index].active = true; /*開放用戶自定義的接口*/ this.$emit('navClickEvent',items,index); } } } </script>
咱們利用for循環給每一個a標籤綁定了一個click事件,對應methods中定義的navClickEvent,接收兩個參數items和index(你也能夠傳人item和index,看我的代碼喜愛),而後當點擊的時候,把items中的每一個item.active置爲false,把當前的tab的active值置爲true,這樣就能夠動態切換active類了。最後再觸發一次自定義事件(參考按鈕製做自定義事件)。
以上就是咱們導航組件的內容了,回想下咱們作了啥?for循環輸出每一個tab,爲每一個tab綁定動態的class類名,同時在點擊事件中動態切換類(底部的小黃條實際上是利用active類作的CSS)
回顧下咱們這一篇章都學了什麼內容。
上述內容已經基本上涵蓋了組件的重要知識點,主要是父組件(頁面)和子組件之間的調用和通信(數據交互綁定),好好消耗一下咱們會發現,其實Vue的整體邏輯思想和jQuery是同樣的,畢竟最後都回歸到javascript,只是因爲代碼設計角度的不一樣,咱們可能看到和之前調用jQuery時候的寫法不一致,但其實都有對方的影子在裏面,相信理解了Vue的代碼思想以後,之後咱們學習React等其餘相似的框架的時候,也會比較駕輕就熟了。
下一篇文章《包學會之淺入淺出Vue.js:結業篇》中,咱們將會學習如何用多個組件來組成一個大的組件,也就是真正意義上的父子組件之間的關係。再忍耐一下,就能夠出山了,新領域的大門就在前面,讓咱們大步往前跨吧。
文末附上全部相關代碼和官方文檔地址~~~
相關閱讀
【每日課程推薦】機器學習實戰!快速入門在線廣告業務及CTR相應知識
此文已由做者受權騰訊雲+社區發佈,更多原文請點擊
搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!
海量技術實踐經驗,盡在雲加社區!