vue.js的项目实战
歡迎大家前往騰訊云+社區(qū),獲取更多騰訊海量技術(shù)實(shí)踐干貨哦~
本文由蔡述雄發(fā)表于云+社區(qū)專欄需求背景
組件庫是做UI和前端日常需求中經(jīng)常用到的,把一個(gè)按鈕,導(dǎo)航,列表之類的元素封裝起來,方便日常使用,調(diào)用方法只需直接寫上<qui-button></qui-button>或者<qui-nav></qui-nav>這樣的代碼就可以,是不是很方便呢,接下來我們將要完成以下頁面:
這是我們組件庫的首頁,包含三個(gè)子頁面,按鈕頁面、列表頁面、導(dǎo)航頁面;點(diǎn)擊進(jìn)去子頁面會(huì)由路由來配置。先看我們的目錄結(jié)構(gòu):
pages目錄存放我們的頁面,包括首頁和三個(gè)子頁面;components目錄存放我們的具體組件,包括按鈕組件,箭頭組件,列表組件和導(dǎo)航組件(組件和頁面其實(shí)是一樣的文件類型,只是由于功能不一樣,我們就叫不同的稱呼)
先看路由配置的代碼吧!
路由配置
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}] })有了上一篇的分析之后,這里應(yīng)該很容易看出來幾個(gè)路由地址
首頁:http://localhost:8080/#/
按鈕子頁:http://localhost:8080/#/btn
列表子頁:http://localhost:8080/#/list
導(dǎo)航子頁:http://localhost:8080/#/nav
具體每一頁的內(nèi)容分別對應(yīng)每一頁的.vue文件,不知大家是否還記得入口頁App.vue,這個(gè)文件承載著一些公用的元素,還有就是一個(gè)路由容器,我們的首頁index.vue到時(shí)候也是掛載在路由容器中的,看看App.vue的代碼
入口頁App.vue
<template><div id="app"><h1 class="page-title"><a href="#/">開發(fā)組件庫</a></h1><router-view></router-view></div> </template><script>export default {name: 'app' } </script><style scoped>@import './assets/css/App.css'; </style>簡單分析一下入口頁的代碼,h1標(biāo)簽是一個(gè)公用元素,也就是說到時(shí)候每個(gè)子頁面都會(huì)帶著這個(gè)h1,他的作用就是方便我們快速回到首頁,子頁面的內(nèi)容會(huì)注入到router-view中。這里值得關(guān)注的地方是style標(biāo)簽,我們可以在style標(biāo)簽里面直接寫樣式,也可以直接引入一個(gè)樣式文件,scoped關(guān)鍵字表示這個(gè)樣式是私有的,也就是說,即使兩個(gè)組件寫著一樣的#app{}樣式也不會(huì)沖突,程序會(huì)加上命名空間,這也就是為什么在script標(biāo)簽中有個(gè)name參數(shù)。
首頁index.vue
<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">導(dǎo)航</a></p></div></div></div> </template><style scoped>@import './css/index.css'; </style>首頁的代碼也是非常簡單,和我們平時(shí)寫html差不多,就是幾個(gè)跳轉(zhuǎn)鏈接跳到對應(yīng)的子頁面,程序運(yùn)行的時(shí)候,會(huì)將<template>標(biāo)簽里面的內(nèi)容都注入到App.vue頁面中的router-view標(biāo)簽中,從而實(shí)現(xiàn)無刷新的路由跳轉(zhuǎn)。
從下面的內(nèi)容開始,我們的知識(shí)將會(huì)深入一些。我們先不急著看其他幾個(gè)子頁面,因?yàn)樽禹撁胬锩嬷皇且脤?yīng)的組件,所以我們先從組件開始入手。
按鈕組件quiButton.vue
<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>按鈕組件很簡單,就是一個(gè)正常的button標(biāo)簽,script標(biāo)簽中暴露這個(gè)組件的data屬性(data是Vue的屬性值,不是亂寫的~~)。當(dāng)按鈕組件被初始化的時(shí)候,msg自定義屬性會(huì)被綁定到<span>標(biāo)簽中的{{msg}}中,兩個(gè)花括號用來綁定屬性,這種寫法學(xué)過模版化前端代碼的人應(yīng)該都比較熟悉。這里需要注意一個(gè)地方,如果不是組件的話,正常data的寫法可以直接寫一個(gè)對象,比如data:{ msg : ' 下載 ' },但由于組件是會(huì)在多個(gè)地方引用的,JS中直接共享對象會(huì)造成引用傳遞,也就是說修改了msg后所有按鈕的msg都會(huì)跟著修改,所以這里用function來每次返回一個(gè)對象實(shí)例。
這就是一個(gè)非常簡單的按鈕組件,結(jié)構(gòu)、樣式+文案。
這時(shí)候問題來了,按鈕中的文案我希望可以異化,不能每次都初始化一個(gè)叫做“下載”文案的按鈕吧,希望可以以屬性的方式來使用,比如這樣子寫就可以改變我們的按鈕文案:
<qui-btn msg="確定" class="small"></qui-btn>沒問題,屬性的接口暴露只需要寫在prosp里面就可以了,如下所示修改下script標(biāo)簽的內(nèi)容:
<script>export default {props: {msg: {default: '下載'}}} </script>把屬性寫在props里面,就可以暴露給其他頁面調(diào)用了,在組件中,props是專門用來暴露組件的屬性接口的,這里給了一個(gè)默認(rèn)值‘下載’,后面我們要使用的話,只需要<btn msg="確認(rèn)"></btn> 就可以修改按鈕的默認(rèn)文案了。
我們在上一篇文章的開頭就講了Vue是數(shù)據(jù)驅(qū)動(dòng)模式的,當(dāng)我在btn結(jié)構(gòu)寫上msg="確認(rèn)"的時(shí)候,對應(yīng)script里面的msg屬性就會(huì)自動(dòng)修改了。
按鈕事件
按鈕總少不了點(diǎn)擊事件吧,那在Vue中怎么綁定事件呢,用methods屬性,看下代碼:
<template><button class="qui-btn" v-on:click="btnClickEvent"><span>{{msg}}</span></button> </template><script>export default {props: {msg: {default: '下載'}},methods: { //綁定事件的關(guān)鍵代碼btnClickEvent: function(){alert(this.msg);}}} </script>methods屬性中可以寫任何的自定義函數(shù),寫完之后綁定的方式也很簡單,在button上寫關(guān)鍵字v-on:click,把對應(yīng)的事件寫上就可以了,以上代碼實(shí)現(xiàn)的就是點(diǎn)擊按鈕彈出按鈕中的文案,v-XXX是Vue里的一些關(guān)鍵字,叫做指令,我們后面會(huì)慢慢學(xué)到更多的指令;v-on:click可以縮寫為@click,當(dāng)然還有其他的事件比如v-on:tab等等;
使用按鈕組件pageQuiButton.vue
現(xiàn)在我們大致做了一個(gè)按鈕組件了,那么怎么調(diào)用它呢,去到我們的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 /*注冊自定義標(biāo)簽*/}} </script>從script開始解析,首先引入我們的組件賦值給變量quiBtn,使用時(shí)候直接將quiBtn作為對象的一部分寫進(jìn)components屬性,這是Vue用來存儲(chǔ)引用組件的關(guān)鍵字,同時(shí)對應(yīng)我們自定義的標(biāo)簽 "qui-btn",完成這些操作之后,我們就可以在template中使用自定義的按鈕組件<qui-btn>上面也說了用msg屬性來自定義按鈕的文案。完成之后,我們就可以在頁面中看到具體效果,點(diǎn)擊按鈕彈出對應(yīng)的文案。
上述我們將按鈕事件寫成默認(rèn)的alert(this.msg),如果有些按鈕想要異化怎么辦。之前說了msg屬性可以支持自定義,那么按鈕的點(diǎn)擊事件如何支持自定義呢?
//pageQuiButton.vue //監(jiān)聽子組件的事件 <qui-btn v-on:btnClickEvent="doSth" msg="我可以點(diǎn)擊" ></qui-btn>上面的代碼在引用組件的時(shí)候,注冊了一個(gè)事件,這個(gè)btnClickEvent事件是之前我們在按鈕組件中綁定到按鈕的click事件中的,然后我們給這個(gè)事件一個(gè)自定義的方法doSth,同時(shí),在script中聲明這個(gè)自定義的方法如下:
//pageQuiButton.vue //頁面中引用子組件并監(jiān)聽子組件的事件 <script>import quiBtn from '../components/quiButton.vue'export default {name: 'pageQuiButton',components: {'qui-btn': quiBtn},methods: {doSth: function(){alert('你點(diǎn)擊了組件的click:btnClickEvent');}}} </script>專業(yè)一點(diǎn)的說,這種做法叫做監(jiān)聽,由引用方(暫且叫做父組件)監(jiān)聽子組件的內(nèi)置方法;同時(shí)在子組件中,需要觸發(fā)這個(gè)事件,以下是在子組件中的關(guān)鍵代碼:
//quiButton.vue //子組件中的代碼 <script>export default {props: {msg: {default: '下載'}},methods: {btnClickEvent: function(){alert("先彈出默認(rèn)的文案");this.$emit('btnClickEvent');//關(guān)鍵代碼父組件觸發(fā)自定義事件}}} </script>這里的關(guān)鍵代碼就是$emit,也叫觸發(fā)機(jī)制,父組件監(jiān)聽,子組件觸發(fā)。如果覺得繞,以下描述可能會(huì)比較好理解:小B(子組件)有一個(gè)電話號碼(子組件注冊的事件),有一天小B把電話號碼告訴了小A(父組件),讓小A打電話給他,于是小A就撥打了小B的電話號碼(監(jiān)聽),這時(shí)候整個(gè)溝通流程沒有結(jié)束,必須要小B接聽了電話(觸發(fā)),兩人之間才算完成了打電話這件事情。
完成這步之后,引用方(父組件)就可以給不同子組件調(diào)用不同的事件處理了:
<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>給按鈕加圖標(biāo)
有時(shí)候單純的文案異化還不夠,比如一些按鈕是圖標(biāo)+文字類型的,而且圖標(biāo)還可能不一樣,那應(yīng)該怎么辦呢?
如果按鈕組件的結(jié)構(gòu)除了開發(fā)時(shí)候預(yù)設(shè)的那些dom結(jié)構(gòu)之外,允許我們在調(diào)用的時(shí)候添加一些自己想要的結(jié)構(gòu),那是不是解決了呢,是的,Vue早就為我們考慮了這一點(diǎn),他就是slot標(biāo)簽。
下面給我們的按鈕組件加上一段結(jié)構(gòu)
//quiButton.vue <template><button class="qui-btn" v-on:click="btnClickEvent"><slot name="icon"></slot><!--重點(diǎn)在這里--><span>{{msg}}</span></button> </template>加入了關(guān)鍵字slot并賦予一個(gè)name值之后,我們再看看引用如何使用
//pageQuiButton.vue <qui-btn msg="下載" class="with-icon"><img slot="icon" class="ico" src="xxx.png" /> </qui-btn>img上有個(gè)關(guān)鍵字slot="icon"對應(yīng)組件中的name="icon",渲染的時(shí)候,會(huì)將img整個(gè)替換掉組件中的對應(yīng)name的<slot>標(biāo)簽,其實(shí)很好理解,slot的翻譯是插槽的意思,相當(dāng)于把img這塊內(nèi)容插到一個(gè)名叫icon的插槽里面去。
中場休息一下
學(xué)到這里,我們已經(jīng)學(xué)會(huì)了用props給按鈕自定義文案,用on和emit給按鈕自定義點(diǎn)擊觸發(fā),用slot給按鈕添加一些自定義結(jié)構(gòu)。當(dāng)你回頭去翻文檔的時(shí)候,你會(huì)發(fā)現(xiàn)props,事件,slot這三樣剛好就是學(xué)習(xí)組件通訊中最最最關(guān)鍵的三個(gè)環(huán)節(jié)。將這三個(gè)環(huán)節(jié)以實(shí)際案例解析出來后,好像也沒有那么難了吧~!
上述我們已經(jīng)討論了如何制作一個(gè)按鈕組件,以及如何使用我們的按鈕組件。
接下來我們通過制作一個(gè)導(dǎo)航組件,來了解Vue中對于for循環(huán)的巧妙使用。
導(dǎo)航組件quiNav.vue
我們將完成這樣一個(gè)導(dǎo)航組件,點(diǎn)擊導(dǎo)航中的tab,可以給當(dāng)前tab加上一個(gè)active類,同時(shí)切換底部的黃色滑條,并且輸出當(dāng)前tab的文案,同時(shí)支持自定義事件。
由于在現(xiàn)實(shí)項(xiàng)目中,我們導(dǎo)航的tab個(gè)數(shù)是不定的,所以制作組件的時(shí)候,我們希望可以暴露一個(gè)屬性來支持導(dǎo)航的tab個(gè)數(shù),而tab的長相和應(yīng)用其實(shí)是一樣的,那么這時(shí)候我們可以用一個(gè)for循環(huán)來輸出每一個(gè)tab。先看看關(guān)鍵代碼:
//quiNav.vue <template><div class="qui-nav nav-type-1"><a v-for="(item, index) in items" ><!--關(guān)鍵代碼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: '關(guān)于',active : false},{text: '招聘',active : false}]}}}} </script>該段代碼的關(guān)鍵地方在于a標(biāo)簽上v-for關(guān)鍵字(還記得我們在前面說過的v-on綁定事件嗎,v-XXX關(guān)鍵字是Vue預(yù)留的)可以把它理解為js中的for in 循環(huán),items是我們在data里面定義的對象(還記得為什么data要寫在function中返回嗎?)。v-for="(item,index) in items"暴露了item和index兩個(gè)接口,這是Vue提供的,代表items中的每一項(xiàng)以及該項(xiàng)對應(yīng)的下標(biāo),接著我們就可以在標(biāo)簽中使用綁定{{item.text}}了。
這段代碼理解了之后,我們再延伸一個(gè)動(dòng)態(tài)添加class的概念。我們希望每個(gè)tab都有默認(rèn)的class類名(比如nav-item類),在點(diǎn)擊每個(gè)tab的時(shí)候,當(dāng)前tab添加active類,其他的tab刪除這個(gè)active類。在Vue怎么實(shí)現(xiàn)呢?
動(dòng)態(tài)類名
//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:[...//數(shù)據(jù)]}}} </script>在template中添加了一句關(guān)鍵代碼
:class="[commonClass,item.active ? activeClass : '']":class給組件綁定一個(gè)class屬性(類似jQuery中的attr方法),這里的寫法是縮寫,他的全拼應(yīng)該是v-bind:(又一個(gè)v-XXX寫法)。注意到最前面有個(gè)冒號,:class=XXX和class=XXX的區(qū)別在于不帶冒號的是靜態(tài)的字符串綁定,帶冒號的是動(dòng)態(tài)的變量綁定。我們給class綁定了一個(gè)數(shù)組,這個(gè)數(shù)組帶有變量,先看commonClass,這個(gè)變量在data中定義了,然后數(shù)組的第二個(gè)元素是一個(gè)JS的三元運(yùn)算符:item.active?activeClass:'',當(dāng)每個(gè)item中的active值為true時(shí),綁定activeClass變量對應(yīng)的類,如果為false,則為空。最后的結(jié)果是當(dāng)item.active為true時(shí)候,tab的class值為'nav-item active',當(dāng)為false,就只有'nav-item'。
上面的代碼可以理解的話,那么我們切換tab的active類,就轉(zhuǎn)換為修改每個(gè)item里面的active的值(再次體現(xiàn)數(shù)據(jù)驅(qū)動(dòng))。
那么問題來了,怎么去修改每個(gè)item里面的active值呢?沒錯(cuò),給每個(gè)tab綁定一個(gè)點(diǎn)擊事件,當(dāng)點(diǎn)擊事件觸發(fā)的時(shí)候,修改當(dāng)前tab對應(yīng)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){/*默認(rèn)切換類的動(dòng)作*/items.forEach(function(el){el.active = false;});items[index].active = true;/*開放用戶自定義的接口*/this.$emit('navClickEvent',items,index);}}} </script>我們利用for循環(huán)給每個(gè)a標(biāo)簽綁定了一個(gè)click事件,對應(yīng)methods中定義的navClickEvent,接收兩個(gè)參數(shù)items和index(你也可以傳人item和index,看個(gè)人代碼喜好),然后當(dāng)點(diǎn)擊的時(shí)候,把items中的每個(gè)item.active置為false,把當(dāng)前的tab的active值置為true,這樣就可以動(dòng)態(tài)切換active類了。最后再觸發(fā)一次自定義事件(參考按鈕制作自定義事件)。
以上就是我們導(dǎo)航組件的內(nèi)容了,回想下我們做了啥?for循環(huán)輸出每個(gè)tab,為每個(gè)tab綁定動(dòng)態(tài)的class類名,同時(shí)在點(diǎn)擊事件中動(dòng)態(tài)切換類(底部的小黃條其實(shí)是利用active類做的CSS)
小結(jié)
回顧下我們這一篇章都學(xué)了什么內(nèi)容。
上述內(nèi)容已經(jīng)基本上涵蓋了組件的重要知識(shí)點(diǎn),主要是父組件(頁面)和子組件之間的調(diào)用和通訊(數(shù)據(jù)交互綁定),好好消耗一下我們會(huì)發(fā)現(xiàn),其實(shí)Vue的總體邏輯思想和jQuery是一樣的,畢竟最后都回歸到j(luò)avascript,只是由于代碼設(shè)計(jì)角度的不同,我們可能看到和以前調(diào)用jQuery時(shí)候的寫法不一致,但其實(shí)都有對方的影子在里面,相信理解了Vue的代碼思想之后,以后我們學(xué)習(xí)React等其他類似的框架的時(shí)候,也會(huì)比較得心應(yīng)手了。
下一篇文章《包學(xué)會(huì)之淺入淺出Vue.js:結(jié)業(yè)篇》中,我們將會(huì)學(xué)習(xí)如何用多個(gè)組件來組成一個(gè)大的組件,也就是真正意義上的父子組件之間的關(guān)系。再忍耐一下,就可以出山了,新領(lǐng)域的大門就在前面,讓我們大步往前跨吧。
文末附上所有相關(guān)代碼和官方文檔地址~~~
http://cn.vuejs.org/v2/guide/
相關(guān)閱讀【每日課程推薦】機(jī)器學(xué)習(xí)實(shí)戰(zhàn)!快速入門在線廣告業(yè)務(wù)及CTR相應(yīng)知識(shí)
此文已由作者授權(quán)騰訊云+社區(qū)發(fā)布,更多原文請點(diǎn)擊
搜索關(guān)注公眾號「云加社區(qū)」,第一時(shí)間獲取技術(shù)干貨,關(guān)注后回復(fù)1024 送你一份技術(shù)課程大禮包!
海量技術(shù)實(shí)踐經(jīng)驗(yàn),盡在云加社區(qū)!
總結(jié)
以上是生活随笔為你收集整理的vue.js的项目实战的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: react 16.6 懒加载 Lazy
- 下一篇: easyui,layui和 vuejs