我記得當時我拿起CakePHP,我很喜歡,開始使用它是多麼容易。這些文檔不只結構合理,詳盡無遺,並且用戶友好。多年之後,這正是我在Vue.js中感覺到的。然而,與Cake相比,Vue文檔仍然缺乏一件事:一個真實的項目教程。css
不管框架的文檔記錄如何,這對每一個人來講都是不夠的。閱讀概念並不老是有助於瞭解大局或瞭解如何使用它們來實際製做某些東西。若是您像我同樣,經過製做一些可用的組件來更好地學習,並在編碼時參考文檔,幫助記憶文檔和熟練使用Vue。html
在本教程中,咱們將構建星級評分系統組件。咱們將學習幾種Vue.js中的概念,咱們會在項目中使用到他們,並將討論爲何咱們使用它們。vue
這篇文章深刻介紹瞭如何以及爲什麼。它旨在幫助您掌握Vue.js的一些核心概念,並教您如何爲將來的項目作出設計決策。若是您想了解整個思考過程,請繼續閱讀。不然,您能夠查看CodeSandbox上的最終代碼。webpack
Vue.js,固然你會以本身做爲一個簡單的腳本運行而以爲已經不錯了,可是當你想使用單文件組件時狀況有點不一樣。固然,你不必定必須構建以組件的這種方式。您能夠完美地使用定義全局組件Vue.component
。問題是,這須要權衡,例如必須使用字符串模板,沒有範圍的CSS支持,也沒有構建步驟(所以,沒有預處理器)。然而,咱們但願更深刻地學習如何構建一個能夠在實際項目中使用的實際組件。出於這些緣由,咱們將採用由Webpack提供支持的實際設置。git
爲了簡化操做並縮短配置時間,咱們將使用vue-cli和webpack-simple Vue.js模板。github
首先,您須要在全局安裝vue-cli。啓動終端並鍵入如下內容:web
npm install -g vue-cli
複製代碼
您須要繼續輸入:chrome
vue init webpack-simple path/to/my-project
複製代碼
你會在這個過程當中被問到幾個問題。選擇除「使用sass」之外的全部內容,都是爲默認值。而後,vue-cli將初始化項目並建立package.json文件。完成後,您能夠導航到項目的目錄,安裝依賴項並運行項目:vue-cli
cd path/to/my-project
npm install
npm run dev
複製代碼
不出意外!Webpack將開始在端口上提供項目8080(若是可用,8080端口沒有被其餘程序佔用)並在瀏覽器中觸發它。若是一切順利,你應該看到像這樣的歡迎頁面。npm
幾乎!要正確調試Vue.js組件,您須要正確的工具。繼續安裝Vue.js devtools瀏覽器擴展程序(Firefox / Chrome / Safari)。
最好的功能之一是Vue.js單文件組件(SFC)。它們容許您在一個文件中定義組件的結構,樣式和行爲,而沒有混合HTML,CSS和JavaScript的常見缺點。
SFC以.vue擴展名結尾,具備如下結構:
<template>
<!-- Your HTML goes here -->
</template>
<script>
/* Your JS goes here */
</script>
<style>
/* Your CSS goes here */
</style>
複製代碼
讓咱們來建立咱們的第一個組件:建立一個Rating.vue
文件/src/components
,而後複製/粘貼上面的代碼片斷。而後,打開/src/main.js
並調整現有代碼:
import Vue from 'vue'
import Rating from './components/Rating'
new Vue({
el: '#app',
template: '<Rating/>',
components: { Rating }
})
複製代碼
最後,爲您的Rating.vue
添加一些HTML:
<template>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
</template>
複製代碼
如今查看瀏覽器中的頁面,您應該看到列表!Vue.js將您的<Rating>
組件附加到#app
元素中index.html
。若是檢查HTML,則應該看不到該#app
元素的符號:Vue.js將其替換爲Rating
組件。
旁註:您是否注意到您甚至不須要從新加載頁面?那是由於Webpack的vue-loader帶有熱重載功能。與實時從新加載或瀏覽器同步相反,熱從新加載不會在每次更改文件時刷新頁面。相反,它會監視組件更改並僅刷新它們,保持狀態不變。
如今咱們已經花了一些時間來設置,但如今是咱們實際編寫有意義的代碼的時候了。
咱們將使用vue-awesome,一個用Font Awesome圖標構建的Vue.js的SVG圖標組件。這容許咱們只加載咱們須要的圖標。繼續使用npm(或Yarn)安裝它:
npm install vue-awesome
複製代碼
而後編輯您的組件,以下所示:
<template>
<div>
<ul>
<li><icon name="star"/></li>
<li><icon name="star"/></li>
<li><icon name="star"/></li>
<li><icon name="star-o"/></li>
<li><icon name="star-o"/></li>
</ul>
<span>3 of 5</span>
</div>
</template>
<script>
import 'vue-awesome/icons/star'
import 'vue-awesome/icons/star-o'
import Icon from 'vue-awesome/components/Icon'
export default {
components: { Icon }
}
</script>
複製代碼
好吧好吧,讓咱們放慢腳步並解釋全部這些內容😅
Vue.js使用原生ES6模塊來處理依賴項和導出組件。<script>
塊中的前兩行表示單獨導入圖標,所以您最終不會在最終bundle(資源)中找到您不須要的圖標。第三個Icon組件是從vue-awesome
中導出的,所以您能夠在您的組件中使用它。
Icon也是一個Vue.js的單文件組件(SFC),就像咱們正在構建的組件那樣。若是你打開文件,你會發現它具備與咱們徹底相同的結構。
該export default
導出對象文字做爲組件的視圖模型。咱們在當前的components
中註冊了Icon
組件,所以咱們能夠在咱們的本地使用它。
最後,咱們在<template>
使用HTML它並傳遞了一個name屬性來定義咱們想要的圖標。組件能夠經過將它們轉換爲kebab-case(例如:MyComponent
成爲`)來用做自定義HTML標記。咱們不須要在組件內嵌套任何東西,所以咱們使用了自閉合標記。
旁註:您是否注意到在HTML周圍添加了一個<div>
包裝?那是由於咱們還在根級別添加了一個計數器<span>
,而Vue.js中的組件模板只接受一個根元素。若是你不遵循這個規則,你會收到編譯錯誤。
若是你已經使用過CSS一段時間了,你知道其中一個主要問題是必須處理它的全局性。嵌套一直被認爲是解決這個問題的方法。如今咱們知道它能夠很快致使特殊性問題,使得樣式難以覆蓋,亦或沒法重用,以及是擴展的噩夢。
像BEM這樣的方法被髮明來經過命名空間類來規避這個問題並保持低特異性。在一段時間內,它是編寫乾淨且可擴展的CSS的理想方式。而後,像Vue.js或React這樣的框架和庫出現了,並帶來了範圍樣式。
React具備樣式化的組件,Vue.js具備組件範圍的CSS。它容許您編寫特定於組件的CSS,而無需提供什麼特定神奇的技巧。您使用「普通」類名編寫常規CSS,而且Vue.js經過將數據屬性分配給HTML元素並將其附加到已編譯樣式來肯定處理範圍。
讓咱們在組件上添加一些簡單的類:
<template>
<div class="rating">
<ul class="list">
<li class="star active"><icon name="star"/></li>
<li class="star active"><icon name="star"/></li>
<li class="star active"><icon name="star"/></li>
<li class="star"><icon name="star-o"/></li>
<li class="star"><icon name="star-o"/></li>
</ul>
<span>3 of 5</span>
</div>
</template>
複製代碼
並添加上樣式:
<style scoped>
.rating {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
font-size: 14px;
color: #a7a8a8;
}
.list {
margin: 0 0 5px 0;
padding: 0;
list-style-type: none;
}
.list:hover .star {
color: #f3d23e;
}
.star {
display: inline-block;
cursor: pointer;
margin: 0 1px;
}
.star:hover ~ .star:not(.active) {
color: inherit;
}
.active {
color: #f3d23e;
}
</style>
複製代碼
你看到了scoped屬性了嗎?這就是告訴Vue.js,如何肯定樣式範圍的緣由,所以它們不會泄漏到任何其餘地方。若是您正確地在index.html
複製/粘貼HTML代碼,您會注意到您的樣式不生效:那是由於它們的做用域是組件!🎉
Vue.js能夠輕鬆地從普通的CSS切換到您最喜歡的預處理器模式。您只須要正確的使用Webpack loader
,並在<style>
塊上設置簡單的屬性。還記得咱們在生成項目的時候,問咱們是否使用sass
嗎,若是選擇"是"使用sass。同時vue-cli已經爲咱們安裝並配置了sass-loader。如今,咱們須要作的就是在<style>
添加lang="scss"
標記。
咱們如今可使用Sass進行編寫組件級別風格的,經過import
引入Sass,就像使用import
引入變量,顏色定義等。若是你喜歡的縮進語法(或「sass」符號),只需lang
屬性切換scss
爲sass
。
如今咱們的組件看起來很好,是時候讓它工做了。目前,咱們有一個硬編碼模板。讓咱們設置一些初始模擬狀態並調整模板以便它反映出來:
<script>
...
export default {
components: { Icon },
data() {
return {
stars: 3,
maxStars: 5
}
}
}
</script>
複製代碼
模版修改:
<template>
<div class="rating">
<ul class="list">
<li v-for="star in maxStars" :class="{ 'active': star <= stars }" class="star">
<icon :name="star <= stars ? 'star' : 'star-o'"/>
</li>
</ul>
<span>3 of 5</span>
</div>
</template>
複製代碼
咱們在這裏作的是使用Vue data
來設置組件狀態。您定義的每一個屬性都會跟着data中的屬性進行綁定:若是它發生變化,它將反映在視圖中。
咱們正在製做一個可重用的組件,所以data須要是一個工廠函數而不是一個對象字面量。經過這種方式,咱們得到了一個新對象,而不是對將在多個組件之間共享的現有對象的引用。
咱們的data工廠返回兩個屬性:stars表示當前「激活」星數量,maxStars表示總的星星數量。從這些,咱們調整了咱們的模板,以便它反映實際組件的狀態。Vue.js附帶了一系列指令,可讓您在模板中添加表示邏輯,而無需將其與純JavaScript混合使用。該v-for指令遍歷任何可迭代對象(數組,對象文字,映射等)。它也能夠將數字做爲重複x次的範圍。這就是咱們所作的v-for="star in maxStars",因此咱們<li>
對組件中的每一個星都有一個。
您可能已經注意到某些屬性以冒號爲前綴:這是該v-bind指令的簡寫,它將屬性動態綁定到表達式。咱們原本能夠用它的不省略形式寫出來:v-bind:class
。
當星號處於激活狀態時,咱們須要
active
類。在咱們的例子中,這意味着每一個<li>
索引都應該小於star
才能附加active
類。咱們在:class
指令中使用了一個表達式,只在當前star
小於stars
的時候追加active
。咱們使用相同的條件,此次使用三元運算符來定義與Icon組件一塊兒使用的圖標:star
或star-o
。 既然咱們的star列表與實際數據綁定,那麼咱們就應該爲計數器作一樣的事了。最簡單的方法是使用「鬍子語法」進行文本插值:
<span>{{ stars }} of {{ maxStars }}</span>
複製代碼
很直白,不是嗎?如今在咱們的例子中,這能夠解決問題,但若是咱們須要更復雜的JavaScript表達式,最好在計算屬性中對其進行抽象。
export default {
...
computed: {
counter() {
return `${this.stars} of ${this.maxStars}`
}
}
}
複製代碼
這裏有點矯枉過正。咱們可使用模板內表達式能夠保持可讀性。然而,當您必須處理更復雜的邏輯時,請記住計算屬性。
咱們須要作的另外一件事是提供一種方法來隱藏計數器,若是咱們不想要它。最簡單的方法是使用v-if
帶有布爾值的指令。
<span v-if="hasCounter">{{ stars }} of {{ maxStars }}</span>
複製代碼
data中添加hasCounter屬性
export default {
...
data() {
return {
stars: 3,
maxStars: 5,
hasCounter: true
}
}
}
複製代碼
咱們差很少完成了,但咱們仍然須要實現組件中最有趣的部分:反應性。咱們將使用v-on
處理事件的Vue.js指令,以及methods
一個能夠附加全部方法的Vue.js屬性。
<template>
...
<li @click="rate(star)" ...>
...
</template>
複製代碼
methods中
export default {
...
methods: {
rate(star) {
// do stuff
}
}
}
複製代碼
咱們在上面添加了一個@click
屬性<li>
,這是一個簡寫v-on:click
。該指令包含咱們在methods組件屬性中定義的rate方法的調用。
「等一下......這看起來很是熟悉HTML的onclick屬性。在HTML中使用內聯JavaScript不是一種過期的錯誤作法嗎?「
確實如此,但即便語法看起來很像onclick
,比較二者也是一個錯誤。當您構建Vue.js組件時,您不該將其視爲分離的HTML / CSS / JS,而應將其視爲使用多種語言的一個組件。當項目在瀏覽器中編譯後,全部HTML和指令都編譯爲純JavaScript。若是在瀏覽器中檢查呈現的HTML,則不會看到任何指令的跡象,也不會看到任何onclick屬性。Vue.js編譯了您的組件並建立了正確的綁定。這也是您能夠直接從模板訪問組件上下文的緣由:由於指令綁定到視圖模型。與具備單獨HTML的傳統項目相反,模板是組件的組成部分。
回到咱們的rate
方法。咱們須要在click時候修改stars爲當前li
的索引,因此咱們從@click
指令傳遞索引,咱們能夠執行如下操做:
export default {
...
methods: {
rate(star) {
this.stars = star
}
}
}
複製代碼
在瀏覽器中查看頁面並嘗試點擊星標:它有效!
若是您在瀏覽器devtools中打開Vue面板並選擇該組件,您將在單擊星標時看到數據發生變化。這代表您的stars屬性是被綁定的:當您對其進行修改時,它會將其更改分派給視圖。這個概念稱爲數據綁定,若是你曾經使用過像Backbone.js或Knockout這樣的框架,你應該熟悉它。不一樣之處在於Vue.js與React同樣,僅在一個方向上執行:這稱爲單向數據綁定。但這個話題又能夠用另外一篇文章來討論了😊
到這個點上,咱們能夠稱之爲完成,但咱們能夠作更多的工做來改善用戶體驗。
如今,咱們實際上不能給出零等級,由於點擊一個星標將stars
設置爲其索引,而li
的索引永遠大於0。更好的作法是從新點擊同一顆星並讓它切換當前狀態而不是保持活躍狀態。
export default {
...
methods: {
rate(star) {
this.stars = this.stars === star ? star - 1 : star
}
}
}
複製代碼
如今,若是點擊的星的索引等於當前值stars,咱們減小它的值。不然,咱們爲其賦值star。
若是咱們想完全,咱們還應該添加一層保護,以確保stars永遠不會分配一個沒有意義的值。咱們須要確保stars永遠不會少於0,永遠不會超過maxStars,而且這是一個合適的數字。
export default {
...
methods: {
rate(star) {
if (typeof star === 'number' && star <= this.maxStars && star >= 0) {
this.stars = this.stars === star ? star - 1 : star
}
}
}
}
複製代碼
如今,組件的數據在data屬性中是硬編碼的。若是咱們但願咱們的組件實際可用,咱們須要可以從其實例傳遞自定義數據。在Vue.js中,咱們用props能夠作到。
export default {
props: ['grade', 'maxStars', 'hasCounter'],
data() {
return {
stars: this.grade
}
},
...
}
複製代碼
並在main.js
:
new Vue({
el: '#app',
template: '<Rating :grade="3" :maxStars="5" :hasCounter="true"/>',
components: { Rating }
})
複製代碼
這裏有三件事須要注意:
首先,咱們使用v-bind
簡寫來從組件實例傳遞props:這就是Vue.js所謂的動態語法。當您想要傳遞字符串值時,您不須要它,使用文字語法(不帶v-bind的正常屬性)將起做用。但在咱們的例子中,因爲咱們傳遞數字和布爾值,因此咱們這樣作很重要。
props
和data
特性是在編譯時合併了,因此props中的屬性會被視圖模型中的標籤綁定(若是定義了)。但出於一樣的緣由,咱們不能使用相同的名稱props和data屬性,不然data中的屬性會被props中的屬性覆蓋。
最後,咱們定義了一個grade props,並經過它做爲一個data的stars屬性。之因此咱們這樣作,而不是直接使用grade props,是由於當咱們改變等級時,star會發生修改。在Vue.js中,props是從父母組件傳遞給孩子組件,而不是相反,因此你不會意外地改變父母的狀態。這將違反單向數據流原則並使事情更難調試。這就是爲何你應該不嘗試修改子組件內的屬性。相反,定義一個data使用prop的初始值做爲本身的本地屬性,也是一個很好的選擇。
咱們將使用Vue.js中的一個很不錯的功能: prop validation
Vue.js容許您在將數據傳遞給組件以前對其進行控制。您能夠執行如下四項主要操做:檢查類型,要求定義prop,設置默認值以及執行自定義驗證。
export default {
props: {
grade: {
type: Number,
required: true
},
maxStars: {
type: Number,
default: 5
},
hasCounter: {
type: Boolean,
default: true
}
},
...
}
複製代碼
咱們使用類型檢查來確保將正確類型的數據傳遞給組件。若是咱們忘記使用動態語法傳遞非字符串值,這將特別有用。咱們還確保grade
是符合組件的給定要求進行傳遞的。對於其餘道具,咱們定義了默認值,所以即便沒有傳遞自定義數據,組件也能正常工做。
如今咱們能夠經過執行如下操做來實例化組件:
<Rating :grade="3"/>
複製代碼
就是這樣!您建立了第一個Vue.js組件,並探討了不少概念,包括生成與樣板謨VUE-CLI,單文件組件,在組件數據傳遞,scope style,指令,事件處理,計算屬性,Vue methods,單方式數據流,props和prop validation。而這僅僅是Vue.js所提供的淺顯的功能!
這是一個很是密集的教程,因此若是你不理解一切,不要擔憂。再次閱讀,在各部分之間暫停,探索並擺弄CodeSandbox上的代碼。若是您對本教程有任何疑問或評論,請不要猶豫,在微博上@我!
若是您有幸已經讀到這裏,相信必定對您有所幫助。
本文爲翻譯文,並添加一些本身的討論,不少地方不流暢,請見諒。
本文的源碼rating-vue-component,若是對您有用,但願您給個star