vue.js的項目實戰

来源:https://www.cnblogs.com/qcloud1001/archive/2018/11/15/9964905.html
-Advertisement-
Play Games

歡迎大家前往 "騰訊雲+社區" ,獲取更多騰訊海量技術實踐乾貨哦~ 本文由 "蔡述雄" 發表於 "雲+社區專欄" 需求背景 組件庫是做UI和前端日常需求中經常用到的,把一個按鈕,導航,列表之類的元素封裝起來,方便日常使用,調用方法只需直接寫上 或者 這樣的代碼就可以,是不是很方便呢,接下來我們將要完 ...


歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~

本文由蔡述雄發表於雲+社區專欄

需求背景

組件庫是做UI和前端日常需求中經常用到的,把一個按鈕,導航,列表之類的元素封裝起來,方便日常使用,調用方法只需直接寫上或者這樣的代碼就可以,是不是很方便呢,接下來我們將要完成以下頁面:

img

這是我們組件庫的首頁,包含三個子頁面,按鈕頁面、列表頁面、導航頁面;點擊進去子頁面會由路由來配置。先看我們的目錄結構:

img

pages目錄存放我們的頁面,包括首頁和三個子頁面;components目錄存放我們的具體組件,包括按鈕組件,箭頭組件,列表組件和導航組件(組件和頁面其實是一樣的文件類型,只是由於功能不一樣,我們就叫不同的稱呼)

先看路由配置的代碼吧!

路由配置

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
    }
  ]
})

有了上一篇的分析之後,這裡應該很容易看出來幾個路由地址

首頁:http://localhost:8080/#/

按鈕子頁:http://localhost:8080/#/btn

列表子頁:http://localhost:8080/#/list

導航子頁:http://localhost:8080/#/nav

具體每一頁的內容分別對應每一頁的.vue文件,不知大家是否還記得入口頁App.vue,這個文件承載著一些公用的元素,還有就是一個路由容器,我們的首頁index.vue到時候也是掛載在路由容器中的,看看App.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參數。

首頁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">導航</a>
        </p>
      </div>
    </div>
  </div>
</template>

<style scoped>
 @import './css/index.css';
</style>

首頁的代碼也是非常簡單,和我們平時寫html差不多,就是幾個跳轉鏈接跳到對應的子頁面,程式運行的時候,會將<template>標簽裡面的內容都註入到App.vue頁面中的router-view標簽中,從而實現無刷新的路由跳轉。

從下麵的內容開始,我們的知識將會深入一些。我們先不急著看其他幾個子頁面,因為子頁面裡面只是引用對應的組件,所以我們先從組件開始入手。

按鈕組件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>

按鈕組件很簡單,就是一個正常的button標簽,script標簽中暴露這個組件的data屬性(data是Vue的屬性值,不是亂寫的~~)。當按鈕組件被初始化的時候,msg自定義屬性會被綁定到標簽中的{{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.vue

現在我們大致做了一個按鈕組件了,那麼怎麼調用它呢,去到我們的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屬性來自定義按鈕的文案。完成之後,我們就可以在頁面中看到具體效果,點擊按鈕彈出對應的文案。

img

上述我們將按鈕事件寫成預設的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>

給按鈕加圖標

有時候單純的文案異化還不夠,比如一些按鈕是圖標+文字類型的,而且圖標還可能不一樣,那應該怎麼辦呢?

img

如果按鈕組件的結構除了開發時候預設的那些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這三樣剛好就是學習組件通訊中最最最關鍵的三個環節。將這三個環節以實際案例解析出來後,好像也沒有那麼難了吧~!

上述我們已經討論瞭如何製作一個按鈕組件,以及如何使用我們的按鈕組件。

img

接下來我們通過製作一個導航組件,來瞭解Vue中對於for迴圈的巧妙使用。

導航組件quiNav.vue

img

我們將完成這樣一個導航組件,點擊導航中的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=XXXclass=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)

小結

回顧下我們這一篇章都學了什麼內容。

  1. 頁面路由的配置
  2. 按鈕組件自定義屬性props
  3. 按鈕組件自定義事件 $on $emit
  4. 按鈕組件自定義子塊slot
  5. for迴圈實現導航組件
  6. 動態類名

上述內容已經基本上涵蓋了組件的重要知識點,主要是父組件(頁面)和子組件之間的調用和通訊(數據交互綁定),好好消耗一下我們會發現,其實Vue的總體邏輯思想和jQuery是一樣的,畢竟最後都回歸到javascript,只是由於代碼設計角度的不同,我們可能看到和以前調用jQuery時候的寫法不一致,但其實都有對方的影子在裡面,相信理解了Vue的代碼思想之後,以後我們學習React等其他類似的框架的時候,也會比較得心應手了。

下一篇文章《包學會之淺入淺出Vue.js:結業篇》中,我們將會學習如何用多個組件來組成一個大的組件,也就是真正意義上的父子組件之間的關係。再忍耐一下,就可以出山了,新領域的大門就在前面,讓我們大步往前跨吧。

文末附上所有相關代碼和官方文檔地址~~~

http://cn.vuejs.org/v2/guide/

相關閱讀
【每日課程推薦】機器學習實戰!快速入門線上廣告業務及CTR相應知識

此文已由作者授權騰訊雲+社區發佈,更多原文請點擊

搜索關註公眾號「雲加社區」,第一時間獲取技術乾貨,關註後回覆1024 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社區


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 要說頁面佈局的話,那就必須說說margin,padding,和background。這三個屬性其實都是前面講過的,這裡還是再次講解以下,為什麼呢?因為是這樣的,光靠前面的css樣式來設置,你很可能會遇到以下的問題,而且還不知道怎麼解決,所以,這裡再拿出來講解下 margin margin幹嘛的不再... ...
  • 傳統的select在沒有設置固定寬度的情況,會因為自身的 option 選項的里,寬度最寬的option作為select本身的寬度 例如 可見效果為: select的寬度因為“寬度最寬的option作為select本身的寬度”導致select變寬 但是這跟我們想要的select寬度跟隨option內 ...
  • 使用watch 替代 mounted。 通過watch屬性來響應數據的變化,當數據改變時執行非同步操作。 ...
  • 1、 var old = $("#old").val(); if (old != null && old != 'undefined' && old != '') { var Gbox=$("input:checked[id^='"+old+"']"); //獲取id以 old的值開頭並且被選中的c ...
  • 1、創建list或者數組 var list = []; list中添加元素:list.push("hello"); 2、創建map var map = {} map中添加元素:map.name = "流川楓"; 或者: var userid = "10023"; map[userid] = "luo ...
  • (1-2)line-height的各類屬性值 首先來個疑問!沒有問題印象不深嘛 一、line-height支持哪些屬性值呢? 五隻手指頭就能數過來了咯。 比如normal, <number>, <length>,<percent>,<inherit>。啊,驚奇發現,剛剛好5個, 接下來是長長的大圖, ...
  • 利用圖片播放 ...
  • 經典名著。 前面幾章,從生活各處現象入手分析抽象與分層,收穫良多。其實軟體也像生活中很多平常事物一樣,有著它的本質,只是需要我們不斷學習,不斷抽象,不斷改進。 中間幾章概略描述了UML2.0的表示法,可作入門,也可作參考 內容簡介 · · · · · · 《面向對象分析與設計(第3版)》是《Jolt ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...