一、使用 kebab-casevue
Vue.component('my-component-name', { /* ... */ })
二、使用 PascalCasewebpack
Vue.component('MyComponentName', { /* ... */ })
當使用 PascalCase (首字母大寫命名) 定義一個組件時,你在引用這個自定義元素時兩種命名法均可以使用。也就是說 <my-component-name> 和 <MyComponentName> 都是可接受的。web
直接在 DOM (即非字符串的模板) 中使用時只有 kebab-case 是有效的
Vue.component('my-component-name', { // ... 選項 ... })
註冊以後能夠用在任何新建立的Vue跟實例(new Vue)的模版中。正則表達式
咱們每每有不少功能單一的基礎組件,而這些組件有常常會被各個功能組件頻繁的用到。這就會致使每一個功能組件都會有一長串基礎組件的長列表。數組
若是使用了 webpack (或在內部使用了 webpack 的 Vue CLI 3+),則能夠經過require.context
來全局註冊這些經常使用的基礎組件。瀏覽器
import Vue from 'vue' import upperFirst from 'lodash/upperFirst' import camelCase from 'lodash/camelCase' const requireComponent = require.context( // 其組件目錄的相對路徑 './components', // 是否查詢其子目錄 false, // 匹配基礎組件文件名的正則表達式 /Base[A-Z]\w+\.(vue|js)$/ ) requireComponent.keys().forEach(fileName => { // 獲取組件配置 const componentConfig = requireComponent(fileName) // 獲取組件的 PascalCase 命名 const componentName = upperFirst( camelCase( // 剝去文件名開頭的 `./` 和結尾的擴展名 fileName.replace(/^\.\/(.*)\.\w+$/, '$1') ) ) // 全局註冊組件 Vue.component( componentName, // 若是這個組件選項是經過 `export default` 導出的, // 那麼就會優先使用 `.default`, // 不然回退到使用模塊的根。 componentConfig.default || componentConfig ) })
全局註冊的行爲必須在根 Vue 實例 (經過 new Vue) 建立以前發生
先經過一個普通的對象來定義組件,而後在須要使用的地方經過屬性-值的方式來進行定義引入。app
var ComponentA = { /* ... */ } var ComponentB = { /* ... */ } new Vue({ el: '#app', components: { 'component-a': ComponentA, 'component-b': ComponentB } })
因爲是局部註冊,因此只在引入的模塊內有效,若是但願在子組件中使用,一樣須要定義引入。函數
var ComponentA = { /* ... */ } var ComponentB = { components: { 'component-a': ComponentA }, // ... } 或者經過 Babel 和 webpack 使用 ES2015 模塊 import ComponentA from './ComponentA.vue' export default { components: { ComponentA }, // ... }
在上面例子中經過ES2015的模塊方式進行了組件的局部註冊。咱們能夠將全部的組件放到同一個目錄下,每個都在一個單獨的文件中,而後在須要使用的地方經過模塊引入的方式進行局部註冊便可。post
一、HTML 中的特性名是大小寫不敏感的,因此瀏覽器會把全部大寫字符解釋爲小寫字符。因此在使用 DOM 中的模板時,camelCase (駝峯命名法) 的 prop 名須要使用其等價的 kebab-case (短橫線分隔命名) 命名ui
Vue.component('blog-post', { // 在 JavaScript 中是 camelCase 的 props: ['postTitle'], template: '<h3>{{ postTitle }}</h3>' }) <!-- 在 HTML 中是 kebab-case 的 --> <blog-post post-title="hello!"></blog-post>
使用字符串模版時則不存在上述限制
二、能夠經過直接賦值的方式來給prop傳遞一個靜態的值,也能夠經過v-bind的方式來動態傳入prop的值。
三、幾種數據類型的傳入方式
1.數字類型 <!-- 即使 `42` 是靜態的,咱們仍然須要 `v-bind` 來告訴 Vue --> <!-- 這是一個 JavaScript 表達式而不是一個字符串。--> <blog-post v-bind:likes="42"></blog-post> <!-- 用一個變量進行動態賦值。--> <blog-post v-bind:likes="post.likes"></blog-post> 2.布爾值類型 <!-- 包含該 prop 沒有值的狀況在內,都意味着 `true`。--> <blog-post is-published></blog-post> <!-- 即使 `false` 是靜態的,咱們仍然須要 `v-bind` 來告訴 Vue --> <!-- 這是一個 JavaScript 表達式而不是一個字符串。--> <blog-post v-bind:is-published="false"></blog-post> <!-- 用一個變量進行動態賦值。--> <blog-post v-bind:is-published="post.isPublished"></blog-post> 3.數組類型 <!-- 即使數組是靜態的,咱們仍然須要 `v-bind` 來告訴 Vue --> <!-- 這是一個 JavaScript 表達式而不是一個字符串。--> <blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post> <!-- 用一個變量進行動態賦值。--> <blog-post v-bind:comment-ids="post.commentIds"></blog-post> 4.對象類型 <!-- 即使對象是靜態的,咱們仍然須要 `v-bind` 來告訴 Vue --> <!-- 這是一個 JavaScript 表達式而不是一個字符串。--> <blog-post v-bind:author="{ name: 'Veronica', company: 'Veridian Dynamics' }" ></blog-post> <!-- 用一個變量進行動態賦值。--> <blog-post v-bind:author="post.author"></blog-post> 5.對象的所有屬性 post: { id: 1, title: 'My Journey with Vue' } <blog-post v-bind="post"></blog-post> 等價於 <blog-post v-bind:id="post.id" v-bind:title="post.title" ></blog-post>
經過prop傳遞數據是的父子組件之間造成了單向下行綁定,父級 prop 的更新會向下流動到子組件中,可是反過來則不行。這樣會防止從子組件意外改變父級組件的狀態,從而致使你的應用的數據流向難以理解。
每次父級組件發生更新時,子組件中全部的 prop 都將會刷新爲最新的值。這意味着你不該該在一個子組件內部改變 prop。
兩種在子組件中試圖改變一個prop的狀況:
一、這個 prop 用來傳遞一個初始值;這個子組件接下來但願將其做爲一個本地的 prop 數據來使用。在這種狀況下,最好定義一個本地的 data 屬性並將這個 prop 用做其初始值:
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }
二、這個 prop 以一種原始的值傳入且須要進行轉換。在這種狀況下,最好使用這個 prop 的值來定義一個計算屬性:
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
注意在 JavaScript 中對象和數組是經過引用傳入的,因此對於一個數組或對象類型的 prop 來講,在子組件中改變這個對象或數組自己將會影響到父組件的狀態。
咱們能夠爲Prop提供一個驗證來保證傳入的數據類型的有效性。爲 props 中的值提供一個帶有驗證需求的對象,而不是一個字符串數組。
Vue.component('my-component', { props: { // 基礎的類型檢查 (`null` 和 `undefined` 會經過任何類型驗證) propA: Number, // 多個可能的類型 propB: [String, Number], // 必填的字符串 propC: { type: String, required: true }, // 帶有默認值的數字 propD: { type: Number, default: 100 }, // 帶有默認值的對象 propE: { type: Object, // 對象或數組默認值必須從一個工廠函數獲取 default: function () { return { message: 'hello' } } }, // 自定義驗證函數 propF: { validator: function (value) { // 這個值必須匹配下列字符串中的一個 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } } })
注意那些 prop 會在一個組件實例建立以前進行驗證,因此實例的屬性 (如 data、computed 等) 在 default 或 validator 函數中是不可用的。
type
能夠是下列原生構造函數中的一個:
type
還能夠是一個自定義的構造函數,而且經過 instanceof
來進行檢查確認。
function Person (firstName, lastName) { this.firstName = firstName this.lastName = lastName } Vue.component('blog-post', { props: { author: Person } })
對於絕大多數特性來講,從外部提供給組件的值會替換掉組件內部設置好的值。class
和 style
特性會將兩邊的值合併起來。
若是你不但願組件的根元素繼承特性,你能夠在組件的選項中設置 inheritAttrs: false
。
注意inheritAttrs: false
選項不會影響style
和class
的綁定。
不一樣於組件和 prop,事件名不存在任何自動化的大小寫轉換。而是觸發的事件名須要徹底匹配監聽這個事件所用的名稱。不一樣於組件和 prop,事件名不會被用做一個 JavaScript 變量名或屬性名,因此就沒有理由使用 camelCase 或 PascalCase 了。而且 v-on 事件監聽器在 DOM 模板中會被自動轉換爲全小寫 (由於 HTML 是大小寫不敏感的)。因此,始終使用 kebab-case 的事件名
能夠經過model選項將v-model的value特性用於不一樣的目的。
Vue.component('base-checkbox', { model: { prop: 'checked', event: 'change' }, props: { checked: Boolean }, template: ` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" > ` }) <base-checkbox v-model="lovingVue"></base-checkbox>
這裏的 lovingVue
的值將會傳入這個名爲 checked
的 prop。同時當 <base-checkbox>
觸發一個 change
事件並附帶一個新的值的時候,這個 lovingVue
的屬性將會被更新。
可能有不少次想要在一個組件的根元素上直接監聽一個原生事件。這時,你可使用 v-on 的 .native 修飾符來進行綁定。可是若是是一個相似原生元素的組件時,可能組件內部已經被重構了,此時會.native失效。
爲了解決這個問題,Vue 提供了一個 $listeners 屬性,它是一個對象,裏面包含了做用在這個組件上的全部監聽器。有了這個 $listeners 屬性,你就能夠配合 v-on="$listeners" 將全部的事件監聽器指向這個組件的某個特定的子元素。
Vue.component('base-input', { inheritAttrs: false, props: ['label', 'value'], computed: { inputListeners: function () { var vm = this // `Object.assign` 將全部的對象合併爲一個新對象 return Object.assign({}, // 咱們從父級添加全部的監聽器 this.$listeners, // 而後咱們添加自定義監聽器, // 或覆寫一些監聽器的行爲 { // 這裏確保組件配合 `v-model` 的工做 input: function (event) { vm.$emit('input', event.target.value) } } ) } }, template: ` <label> {{ label }} <input v-bind="$attrs" v-bind:value="value" v-on="inputListeners" > </label> ` })
在前面咱們知道,prop是單行向下的數據流方式,子組件不能直接修改prop的值,咱們推薦經過update:myPropName
的模式觸發事件取而代之。
//父組件監聽事件 <text-document v-bind:title="doc.title" v-on:update:title="doc.title = $event" ></text-document> //子組件觸發事件 this.$emit('update:title', newTitle)
咱們爲這種模式提供一個縮寫,即 .sync
修飾符:
<text-document v-bind:title.sync="doc.title"></text-document>
注意帶有 .sync 修飾符的 v-bind 不能和表達式一塊兒使用 (例如 v-bind:title.sync=」doc.title + ‘!’」 是無效的)。取而代之的是,你只能提供你想要綁定的屬性名,相似 v-model。
當咱們用一個對象同時設置多個 prop 的時候,也能夠將這個 .sync 修飾符和 v-bind 配合使用:
<text-document v-bind.sync="doc"></text-document>
這樣會把 doc 對象中的每個屬性 (如 title) 都做爲一個獨立的 prop 傳進去,而後各自添加用於更新的 v-on 監聽器。
將 v-bind.sync 用在一個字面量的對象上,例如 v-bind.sync=」{ title: doc.title }」,是沒法正常工做的,由於在解析一個像這樣的複雜表達式的時候,有不少邊緣狀況須要考慮。
本次主要圍繞組件註冊和組件的Prop,以及組件的自定義事件相關內容進行了梳理。咱們知道,在Vue.js的開發過程當中,咱們會面臨各類各樣的組件相關的處理,組件也是咱們開發過程當中十分重要的部分,因此關於Vue.js組件相關的內容咱們分兩次來進行整理。