做(zuò)自(zì)由與創造的先行(xíng)者

組件基礎

Vue.js中文手冊

組件允許我們将 UI 劃分為(wèi)獨立的、可(kě)重用的部分,并且可(kě)以對每個部分進行(xíng)單獨的思考。在實際應用中,組件常常被組織成層層嵌套的樹(shù)狀結構:

這(zhè)和(hé)我們嵌套 HTML 元素的方式類似,Vue 實現了(le)自(zì)己的組件模型,使我們可(kě)以在每個組件內(nèi)封裝自(zì)定義內(nèi)容與邏輯。Vue 同樣也能(néng)很(hěn)好地(dì)配合原生 Web Component。如(rú)果你想知道(dào) Vue 組件與原生 Web Components 之間(jiān)的關系,可(kě)以閱讀此章(zhāng)節。

定義一(yī)個組件 ​

當使用構建步驟時(shí),我們一(yī)般會将 Vue 組件定義在一(yī)個單獨的 .vue 文件中,這(zhè)被叫做(zuò)單文件組件 (簡稱 SFC):

vue

<script>

export default {

data() {

return {

count: 0

}

}

}

</script>

<template>

<button @click="count++">You clicked me {{ count }} times.</button>

</template>

當不使用構建步驟時(shí),一(yī)個 Vue 組件以一(yī)個包含 Vue 特定選項的 JavaScript 對象來定義:

js

export default {

data() {

return {

count: 0

}

},

template: `

<button @click="count++">

You clicked me {{ count }} times.

</button>`

}

這(zhè)裏的模闆是一(yī)個內(nèi)聯的 JavaScript 字符串,Vue 将會在運行(xíng)時(shí)編譯它。你也可(kě)以使用 ID 選擇器來指向一(yī)個元素 (通(tōng)常是原生的 <template> 元素),Vue 将會使用其內(nèi)容作(zuò)為(wèi)模闆來源。

上(shàng)面的例子中定義了(le)一(yī)個組件,并在一(yī)個 .js 文件裏默認導出了(le)它自(zì)己,但(dàn)你也可(kě)以通(tōng)過具名導出在一(yī)個文件中導出多個組件。

使用組件 ​

TIP

我們會在接下(xià)來的指引中使用 SFC 語法,無論你是否使用構建步驟,組件相關的概念都(dōu)是相同的。示例一(yī)節中展示了(le)兩種場景中的組件使用情況。

要(yào)使用一(yī)個子組件,我們需要(yào)在父組件中導入它。假設我們把計數(shù)器組件放在了(le)一(yī)個叫做(zuò) ButtonCounter.vue 的文件中,這(zhè)個組件将會以默認導出的形式被暴露給外(wài)部。

vue

<script>

import ButtonCounter from './ButtonCounter.vue'

export default {

components: {

ButtonCounter

}

}

</script>

<template>

<h1>Here is a child component!</h1>

<ButtonCounter />

</template>

若要(yào)将導入的組件暴露給模闆,我們需要(yào)在 components 選項上(shàng)注冊它。這(zhè)個組件将會以其注冊時(shí)的名字作(zuò)為(wèi)模闆中的标簽名。

當然,你也可(kě)以全局地(dì)注冊一(yī)個組件,使得它在當前應用中的任何組件上(shàng)都(dōu)可(kě)以使用,而不需要(yào)額外(wài)再導入。關于組件的全局注冊和(hé)局部注冊兩種方式的利弊,我們放在了(le)組件注冊這(zhè)一(yī)章(zhāng)節中專門讨論。

組件可(kě)以被重用任意多次:

template

<h1>Here is a child component!</h1>

<ButtonCounter />

<ButtonCounter />

<ButtonCounter />

你會注意到,每當點擊這(zhè)些按鈕時(shí),每一(yī)個組件都(dōu)維護着自(zì)己的狀态,是不同的 count。這(zhè)是因為(wèi)每當你使用一(yī)個組件,就創建了(le)一(yī)個新的實例。

在單文件組件中,推薦為(wèi)子組件使用 PascalCase 的标簽名,以此來和(hé)原生的 HTML 元素作(zuò)區(qū)分。雖然原生 HTML 标簽名是不區(qū)分大小寫的,但(dàn) Vue 單文件組件是可(kě)以在編譯中區(qū)分大小寫的。我們也可(kě)以使用 /> 來關閉一(yī)個标簽。

如(rú)果你是直接在 DOM 中書(shū)寫模闆 (例如(rú)原生 <template> 元素的內(nèi)容),模闆的編譯需要(yào)遵從(cóng)浏覽器中 HTML 的解析行(xíng)為(wèi)。在這(zhè)種情況下(xià),你應該需要(yào)使用 kebab-case 形式并顯式地(dì)關閉這(zhè)些組件的标簽。

template

<!-- 如(rú)果是在 DOM 中書(shū)寫該模闆 -->

<button-counter></button-counter>

<button-counter></button-counter>

<button-counter></button-counter>

請(qǐng)看(kàn) DOM 模闆解析注意事項了(le)解更多細節。

傳遞 props ​

如(rú)果我們正在構建一(yī)個博客,我們可(kě)能(néng)需要(yào)一(yī)個表示博客文章(zhāng)的組件。我們希望所有的博客文章(zhāng)分享相同的視(shì)覺布局,但(dàn)有不同的內(nèi)容。要(yào)實現這(zhè)樣的效果自(zì)然必須向組件中傳遞數(shù)據,例如(rú)每篇文章(zhāng)标題和(hé)內(nèi)容,這(zhè)就會使用到 props。

Props 是一(yī)種特别的 attributes,你可(kě)以在組件上(shàng)聲明(míng)注冊。要(yào)傳遞給博客文章(zhāng)組件一(yī)個标題,我們必須在組件的 props 列表上(shàng)聲明(míng)它。這(zhè)裏要(yào)用到 props 選項:

vue

<!-- BlogPost.vue -->

<script>

export default {

props: ['title']

}

</script>

<template>

<h4>{{ title }}</h4>

</template>

當一(yī)個值被傳遞給 prop 時(shí),它将成為(wèi)該組件實例上(shàng)的一(yī)個屬性。該屬性的值可(kě)以像其他(tā)組件屬性一(yī)樣,在模闆和(hé)組件的 this 上(shàng)下(xià)文中訪問(wèn)。

一(yī)個組件可(kě)以有任意多的 props,默認情況下(xià),所有 prop 都(dōu)接受任意類型的值。

當一(yī)個 prop 被注冊後,可(kě)以像這(zhè)樣以自(zì)定義 attribute 的形式傳遞數(shù)據給它:

template

<BlogPost title="My journey with Vue" />

<BlogPost title="Blogging with Vue" />

<BlogPost title="Why Vue is so fun" />

在實際應用中,我們可(kě)能(néng)在父組件中會有如(rú)下(xià)的一(yī)個博客文章(zhāng)數(shù)組:

js

export default {

// ...

data() {

return {

posts: [

{ id: 1, title: 'My journey with Vue' },

{ id: 2, title: 'Blogging with Vue' },

{ id: 3, title: 'Why Vue is so fun' }

]

}

}

}

這(zhè)種情況下(xià),我們可(kě)以使用 v-for 來渲染它們:

template

<BlogPost

v-for="post in posts"

:key="post.id"

:title="post.title"

/>

留意我們是如(rú)何使用 v-bind 來傳遞動态 prop 值的。當事先不知道(dào)要(yào)渲染的确切內(nèi)容時(shí),這(zhè)一(yī)點特别有用。

以上(shàng)就是目前你需要(yào)了(le)解的關于 props 的全部了(le)。如(rú)果你看(kàn)完本章(zhāng)節後還想知道(dào)更多細節,我們推薦你深入閱讀關于 props 的完整指引。

監聽事件 ​

讓我們繼續關注我們的 <BlogPost> 組件。我們會發現有時(shí)候它需要(yào)與父組件進行(xíng)交互。例如(rú),要(yào)在此處實現 A11y 的需求,将博客文章(zhāng)的文字能(néng)夠放大,而頁面的其餘部分仍使用默認字号。

在父組件中,我們可(kě)以添加一(yī)個 postFontSize 數(shù)據屬性來實現這(zhè)個效果:

js

data() {

return {

posts: [

/* ... */

],

postFontSize: 1

}

}

在模闆中用它來控制所有博客文章(zhāng)的字體大小:

template

<div :style="{ fontSize: postFontSize + 'em' }">

<BlogPost

v-for="post in posts"

:key="post.id"

:title="post.title"

/>

</div>

然後,給 <BlogPost> 組件添加一(yī)個按鈕:

vue

<!-- BlogPost.vue, 省略了(le) <script> -->

<template>

<div class="blog-post">

<h4>{{ title }}</h4>

<button>Enlarge text</button>

</div>

</template>

這(zhè)個按鈕目前還沒有做(zuò)任何事情,我們想要(yào)點擊這(zhè)個按鈕來告訴父組件它應該放大所有博客文章(zhāng)的文字。要(yào)解決這(zhè)個問(wèn)題,組件實例提供了(le)一(yī)個自(zì)定義事件系統。父組件可(kě)以通(tōng)過 v-on 或 @ 來選擇性地(dì)監聽子組件上(shàng)抛的事件,就像監聽原生 DOM 事件那(nà)樣:

template

<BlogPost

...

@enlarge-text="postFontSize += 0.1"

/>

子組件可(kě)以通(tōng)過調用內(nèi)置的 $emit 方法,通(tōng)過傳入事件名稱來抛出一(yī)個事件:

<!-- BlogPost.vue, 省略了(le) <script> -->

<template>

<div class="blog-post">

<h4>{{ title }}</h4>

<button @click="$emit('enlarge-text')">Enlarge text</button>

</div>

</template>

因為(wèi)有了(le) @enlarge-text="postFontSize += 0.1" 的監聽,父組件會接收這(zhè)一(yī)事件,從(cóng)而更新 postFontSize 的值。

我們可(kě)以通(tōng)過 emits 選項來聲明(míng)需要(yào)抛出的事件:

vue

<!-- BlogPost.vue -->

<script>

export default {

props: ['title'],

emits: ['enlarge-text']

}

</script>

這(zhè)聲明(míng)了(le)一(yī)個組件可(kě)能(néng)觸發的所有事件,還可(kě)以對事件的參數(shù)進行(xíng)驗證。同時(shí),這(zhè)還可(kě)以讓 Vue 避免将它們作(zuò)為(wèi)原生事件監聽器隐式地(dì)應用于子組件的根元素。

以上(shàng)就是目前你需要(yào)了(le)解的關于組件自(zì)定義事件的所有知識了(le)。如(rú)果你看(kàn)完本章(zhāng)節後還想知道(dào)更多細節,請(qǐng)深入閱讀組件事件章(zhāng)節。

通(tōng)過插槽來分配內(nèi)容 ​

一(yī)些情況下(xià)我們會希望能(néng)和(hé) HTML 元素一(yī)樣向組件中傳遞內(nèi)容:

template

<AlertBox>

Something bad happened.

</AlertBox>

我們期望能(néng)渲染成這(zhè)樣:

This is an Error for Demo Purposes

Something bad happened.

這(zhè)可(kě)以通(tōng)過 Vue 的自(zì)定義 <slot> 元素來實現:

vue

<template>

<div class="alert-box">

<strong>This is an Error for Demo Purposes</strong>

<slot />

</div>

</template>

<style scoped>

.alert-box {

/* ... */

}

</style>

如(rú)上(shàng)所示,我們使用 <slot> 作(zuò)為(wèi)一(yī)個占位符,父組件傳遞進來的內(nèi)容就會渲染在這(zhè)裏。

以上(shàng)就是目前你需要(yào)了(le)解的關于插槽的所有知識了(le)。如(rú)果你看(kàn)完本章(zhāng)節後還想知道(dào)更多細節,請(qǐng)深入閱讀組件插槽章(zhāng)節。

動态組件 ​

有些場景會需要(yào)在兩個組件間(jiān)來回切換,比如(rú) Tab 界面:

上(shàng)面的例子是通(tōng)過 Vue 的 <component> 元素和(hé)特殊的 is attribute 實現的:

template

<!-- currentTab 改變時(shí)組件也改變 -->

<component :is="currentTab"></component>

在上(shàng)面的例子中,被傳給 :is 的值可(kě)以是以下(xià)幾種:

被注冊的組件名

導入的組件對象

你也可(kě)以使用 is attribute 來創建一(yī)般的 HTML 元素。

當使用 <component :is="..."> 來在多個組件間(jiān)作(zuò)切換時(shí),被切換掉的組件會被卸載。我們可(kě)以通(tōng)過 <KeepAlive> 組件強制被切換掉的組件仍然保持“存活”的狀态。

DOM 模闆解析注意事項 ​

如(rú)果你想在 DOM 中直接書(shū)寫 Vue 模闆,Vue 則必須從(cóng) DOM 中獲取模闆字符串。由于浏覽器的原生 HTML 解析行(xíng)為(wèi)限制,有一(yī)些需要(yào)注意的事項。

TIP

請(qǐng)注意下(xià)面讨論隻适用于直接在 DOM 中編寫模闆的情況。如(rú)果你使用來自(zì)以下(xià)來源的字符串模闆,就不需要(yào)顧慮這(zhè)些限制了(le):

單文件組件

內(nèi)聯模闆字符串 (例如(rú) template: '...')

<script type="text/x-template">

大小寫區(qū)分 ​

HTML 标簽和(hé)屬性名稱是不分大小寫的,所以浏覽器會把任何大寫的字符解釋為(wèi)小寫。這(zhè)意味着當你使用 DOM 內(nèi)的模闆時(shí),無論是 PascalCase 形式的組件名稱、camelCase 形式的 prop 名稱還是 v-on 的事件名稱,都(dōu)需要(yào)轉換為(wèi)相應等價的 kebab-case (短橫線連字符) 形式:

js

// JavaScript 中的 camelCase

const BlogPost = {

props: ['postTitle'],

emits: ['updatePost'],

template: `

<h3>{{ postTitle }}</h3>

`

}

template

<!-- HTML 中的 kebab-case -->

<blog-post post-title="hello!" @update-post="onUpdatePost"></blog-post>

閉合标簽 ​

我們在上(shàng)面的例子中已經使用過了(le)閉合标簽 (self-closing tag):

template

<MyComponent />

這(zhè)是因為(wèi) Vue 的模闆解析器支持任意标簽使用 /> 作(zuò)為(wèi)标簽關閉的标志。

然而在 DOM 模闆中,我們必須顯式地(dì)寫出關閉标簽:

template

<my-component></my-component>

這(zhè)是由于 HTML 隻允許一(yī)小部分特殊的元素省略其關閉标簽,最常見(jiàn)的就是 <input> 和(hé) <img>。對于其他(tā)的元素來說,如(rú)果你省略了(le)關閉标簽,原生的 HTML 解析器會認為(wèi)開(kāi)啓的标簽永遠沒有結束,用下(xià)面這(zhè)個代碼片段舉例來說:

template

<my-component /> <!-- 我們想要(yào)在這(zhè)裏關閉标簽... -->

<span>hello</span>

将被解析為(wèi):

template

<my-component>

<span>hello</span>

</my-component> <!-- 但(dàn)浏覽器會在這(zhè)裏關閉标簽 -->

元素位置限制 ​

某些 HTML 元素對于放在其中的元素類型有限制,例如(rú) <ul>,<ol>,<table> 和(hé) <select>,相應的,某些元素僅在放置于特定元素中時(shí)才會顯示,例如(rú) <li>,<tr> 和(hé) <option>。

這(zhè)将導緻在使用帶有此類限制元素的組件時(shí)出現問(wèn)題。例如(rú):

template

<table>

<blog-post-row></blog-post-row>

</table>

自(zì)定義的組件 <blog-post-row> 将作(zuò)為(wèi)無效的內(nèi)容被忽略,因而在最終呈現的輸出中造成錯誤。我們可(kě)以使用特殊的 is attribute 作(zuò)為(wèi)一(yī)種解決方案:

template

<table>

<tr is="vue:blog-post-row"></tr>

</table>

TIP

當使用在原生 HTML 元素上(shàng)時(shí),is 的值必須加上(shàng)前綴 vue: 才可(kě)以被解析為(wèi)一(yī)個 Vue 組件。這(zhè)一(yī)點是必要(yào)的,為(wèi)了(le)避免和(hé)原生的自(zì)定義內(nèi)置元素相混淆。

以上(shàng)就是你需要(yào)了(le)解的關于 DOM 模闆解析的所有注意事項,同時(shí)也是 Vue 基礎部分的所有內(nèi)容。祝賀你!雖然還有很(hěn)多需要(yào)學習的,但(dàn)你可(kě)以先暫停一(yī)下(xià),去用 Vue 做(zuò)一(yī)些有趣的東西(xī),或者研究一(yī)些示例。

完成了(le)本頁的閱讀後,回顧一(yī)下(xià)你剛才所學到的知識,如(rú)果還想知道(dào)更多細節,我們推薦你繼續閱讀關于組件的完整指引。

網站建設開(kāi)發|APP設計開(kāi)發|小程序建設開(kāi)發
下(xià)一(yī)篇:組件注冊
上(shàng)一(yī)篇:模闆引用