form最后一節。
組件基礎
- 組件的復用: data必須是函數
- 組織
- 通過Prop向子組件傳遞data
- 單個根元素
- 通過event向父組件發送消息: 使用事件拋出一個value, 在組件上用v-model
- 動態組件
- 解析DOM模版時的⚠️.
深入組件
- 組件注冊
- Prop
- 自定義事件: this.$emit('my-event')用kebab-case做事件名稱
- 插槽
- 異步組件
- 處理邊界情況
例子:
組件是可復用的 Vue 實例,且帶有一個名字,如Vue.component("名字", {data..., template...})。
因為組件是可復用的 Vue 實例,所以它們與 new Vue
接收相同的選項,例如 data
、computed
、watch
、methods
以及生命周期鈎子等。僅有的例外是像 el
這樣根實例特有的選項。
組件可以復用
無限次反復用比如上例。多次使用<button-counter>, 每用一個,就創建了一個實例。
data是函數
和新建Vue中的data:{...}不一樣,
data必須是函數 data: function(){...}, 這是為了每個實例可以維護一份被返回對象的獨立的拷貝。
即data不是共用的數據集合,它是函數,是方法,通過它每個實例都會得到各自的結果。
組件的組織
為了能在模板中使用,這些組件必須先注冊以便 Vue 能夠識別。這里有兩種組件的注冊類型:全局注冊和局部注冊
Vue.component
全局注冊:- 全局注冊的組件可以用在其被注冊之后的任何 (通過
new Vue
) 新創建的 Vue 根實例,也包括其組件樹中的所有子組件的模板中。
(深入看: 組件注冊)
通過 Prop 向子組件傳遞數據
Prop 是你可以在組件上注冊的一些自定義特性。
當一個值傳遞給一個 prop 特性的時候,它就變成了那個組件實例的一個屬性。
可以使用 v-bind
來動態傳遞 prop。這在你一開始不清楚要渲染的具體內容,比如從一個 API 獲取博文列表的時候,是非常有用的。
單個根元素
每個組件只能有一個根元素,一般使用<div>包裹其他元素。
否則會報錯 every component must have a single root element
⚠️:
JavaScript 的模板字符串來讓多行的模板更易讀。
它們在 IE 下並沒有被支持,所以如果你需要在不編譯的情況下支持 IE,請使用折行轉義字符取而代之。
什么是模版字符串:
- 模板字符串使用反引號 (` `) 來代替普通字符串中的用雙引號和單引號。
- 模板字符串可以包含特定語法(
${expression}
)的占位符。
在多行string每行結尾加上反斜杠 \
var htmlSTring = "<div>\
This is a string.\
</div>";
通過事件向父級組件發送消息
案例: https://codepen.io/chentianwei411/pen/EeXxrM/?editors=1010
分析:
在我們開發 <blog-post>
組件時,它的一些功能可能要求和父級組件進行溝通。
例如我們可能會引入一個可訪問性的功能來放大博文的字號,同時讓頁面的其它部分保持默認的字號。
注意:⚠️:
1.自定義組件可以使用v-for,但必須配合使用v-bind:key
2. 任何數據都不會被自動傳遞到組件里,因為組件有自己獨立的作用域。為了把迭代數據傳遞到組件里,我們要用 props
:
<my-component |
- 明確組件數據的來源能夠使組件在其他場合重復使用。
3. vm.$emit( eventName, […args] ) (點擊看api)關於事件的實例方法。
-
觸發當前實例上的事件。附加參數都會傳給監聽器回調。
- 監聽器指v-on,用於監聽當前實例上的自定義事件,事件可以由vm.$emit觸發,
參數的上傳:
第一種:
在監聽v-on:enlarge-text用$event獲得上傳的參數:v-on:enlarge-text="postFontSize += $event"
第二種:
如果這個事件處理函數是一個方法,那么這個值將會作為第一個參數傳入這個方法:
1. v-on:enlarge-text='onEnLargeText'
2.在nev Vue中添加這個方法:
methods: {
onEnlargeText: function(enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
在組件上用v-model
點擊:查看(詳細分析了數據的流入和流出。及相關。)
還需要⚠️:
- v-model本身就是一個利用叫value的prop和叫input的自定義事件。所以完全可以自己寫,因為單選,復選等類型的輸入控鍵,會使用value特性用於其他目的。
- 2.2—+新增了組件的model選項,可以自定義v-model的prop和event 其實就是語法糖。
自己寫的案例分析:https://codepen.io/chentianwei411/pen/bxozoj?editors=1010
在DOM上增加了一個組件實例<custom-input>,
- 這個組件把外部數據傳給 prop特性value,
- 有一個監聽v-on:input事件。
關於$event.target.value
$event是觸發的事件,這里是在輸入框輸入的動作。
$event.target是這個動作作用在哪個元素上。target特性返回被事件激活的元素。這里是輸入框input元素。
value指的是input中的value特性,即輸入的內容。
el的作用
定位html中的元素,這個元素將作為Vue實例的掛載目標。在實例掛載后,元素可以用vm.$el訪問。
template: Vue實例的模版
模版會替換被掛載的html元素,被掛載的元素的內容會被忽略,除非使用inline-template。
<custom-input v-bind:value="searchText"
這是把初始化中的data屬性中的數據對象和自定義的prop特性value綁定在一切。
<custom-input v-on:input='searchText = $event'
用於監聽input事件,並對參數進行回調處理。
template中的 <input v-bind:value='value'>
把Props: ['value']特性的值賦予了<input>的value屬性。
template中的<input v-on:input=''"$emit('input', $event.target.value)">
- 當監聽到input這個原生HTML事件時, 執行$emit這個實例方法。
- $emit會觸發當前Vue實例上的自定義事件input(自定義,自定義,自定義),並傳遞參數。
- Vue實例上的監聽器v-on會監聽到這個觸發事件input,並執行預期的行為。這里是執行一條inline statement。
動態組件
根據‘組件名稱’來動態的調用組件。類似動態調用方法。把動態和異步組件讀完。
<component v-bind:is='組件名稱'></component>
用法:
渲染‘元組件’為動態組件。 根據v-bind:is的值,來決定渲染哪個組件。
這里是根據vm的計算屬性中currentTabComponent函數來得到組件的名稱
內置的組件:
component
Props:
- is - string | ComponentDefinition | ComponentConstructor
- inline-template -boolean
在動態組件上使用keep-alive緩存組件
keep-alive
目的:提高效能:保留組件狀態,或避免重新渲染。
Props:
- include -字符串或正則表達式。 匹配的組件會被cache
- exclude -字符串或正則表達式。任何匹配的組件都不會被cache
用法:
- <keep-alive>包裹動態組件</keep-alive>, cache不活動的組件,不會銷魂它們。
- <keep-alive>是一個抽象組件,不會渲染DOM元素,不會出現在父組件鏈中。
鈎子函數:(這兩個鈎子,放到聲明組件的代碼中)
- activated: 在keep-alive組件激活時用
- deactivated: 在keep-alive組件停用時調用。
Vue.component('tab-home', {
template: '<div>Home component</div>',
activated() {
console.log('activatsse')
},
deactivated() {
console.log('deactivated')
}
})
異步組件
以工廠函數的方式定義組件。
Vue.component('async', function(resolve, reject) { ... })
這個工廠函數會異步解析你的組件定義。
在這個組件被渲染的時候,會觸發該工廠函數,並把結構緩存起來供未來重新渲染。
工廠函數?
當一個函數返回一個對象時,並且函數沒有使用new關鍵字,這個函數稱為factory function。
不是類,也不是構造函數。
個人理解:工廠函數好像就是類,傳遞不同的參數,返回不同的對象,但對象們的結構都一樣,只是值不一樣。
將異步組件和 webpack 的 code-splitting 功能一起配合使用,未學習這個功能!!
Promise是什么?(點擊看api)
new Promise( function(resolve, reject) {...} );
一個代表了異步操作最終完成或者失敗的對象。大多數人都在使用由其他函數創建並返回的promise.
promise本質上是一個綁定了回調的對象.
- 通過.then形式添加回調函數。
- 通過多次調用.then,可以添加多個回調函數。
- 鏈式調用,涼鞋執行多個異步操作。
誕生的原因猜測: 傳統的多重回調的寫法是各地獄,因此改用promise的方式。
回調地獄:崩潰的寫發和讀發:
doSomething(function(result) { doSomethingElse(result, function(newResult) { doThirdThing(newResult, function(finalResult) { console.log('Got the final result: ' + finalResult); }, failureCallback); }, failureCallback); }, failureCallback);
現代的方法:
doSomething().then(function(result) { return doSomethingElse(result); }) .then(function(newResult) { return doThirdThing(newResult); }) .then(function(finalResult) { console.log('Got the final result: ' + finalResult); }) .catch(failureCallback);
或者使用箭頭函數:
doSomething() .then(result => doSomethingElse(result)) .then(newResult => doThirdThing(newResult)) .then(finalResult => { console.log(`Got the final result: ${finalResult}`); }) .catch(failureCallback);
解析 DOM 模板時的注意事項
部分HTML標簽限制子元素的標簽類型,
如<ul>內部只能是<li>,如果在里面用自定義組件則會渲染報錯。
可以使用is特性,進行變通:
<table>
<tr is="blog-post-row"></tr>
</table>
但是⚠️,如果從以下來源使用模板的話,這條限制是不存在的:
- 字符串 (例如:
template: '...'
)✅ - 單文件組件 (
.vue
) <script type="text/x-template"> 一種定義模版的方式。
Class用在組件上
- 可以直接在組件上寫:
<my-component class="baz boo"></my-component>
- 也可以用在v-bind:class='{active: isActive}'上面, 當isActive是true時,類加上active.
<p class="foo bar active">Hi</p> |
深入Prop
camelCase vs kebab-case
當你使用 DOM 中的模板時,camelCase (駝峰命名法) 的 prop 名需要使用其等價的 kebab-case (短橫線分隔命名) 命名:
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>
當定義組件的時候,即在JavaScript中用:props: ['postTitle']
Prop類型:
- 使用字符串數組的形式:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
- 使用對象形式
- 使用對象形式:可以進行Prop的驗證。
- type, 可以是任意類型null, 多個類型的任意一種(用數組)
- required: true/false 是否必須填
- default,默認可以是值,也可以是function(){ return ...}
- 自定義驗證函數validator。
- 用到這個組件的時候,傳入的數據必須符合prop指定的類型,並通過驗證,否則browser的控制台會提示❌)
props: {
title: null, #type可以任意類型,用null
likes: [Number, String, Date] #可以設置多個類型。
isPublished: Boolean,
commentIds: Array,
author: [Object, Function, Symbol]
}
傳遞靜態或者動態Prop類型:
需要用 v-bind, 即使數據是靜態的,仍然需要 v-bind來告訴Vue:
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>
或者用一個變量進行動態賦值:
<blog-post v-bind:comment-ids="post.commentIds"></blog-post>
⚠️(即使加了雙括號v-bind="xxxxx"這還是Javascript表達式)
可以傳遞任何類型的值給一個prop。每個類型略有使用區別:
- 傳入一個數字:
- 傳入一個boolean:
- 傳入一個數組:
- 傳入一個對象:
- 傳入一個對象的所有屬性。綁定對象名即可 v-bind:prop-name
父子組件中的prop的數據流動是單向的:
但⚠️: JS中對象和數組是通過引用傳入的。所以對這兩種類型的prop, 在子組件中改變這個對象或數組,改變的是對象和數組本身,因此會影響到父組件的狀態。
非Prop的特性
一個非prop特性是:傳向一個組件的特性,但該組件沒有對應prop定義的特性。
這個非prop特性會添加到這個組件的根元素上。
因為組件的作者不能遇見組件會被什么場景使用,為了讓組件更加的靈活,所以組件可以接受任意的特性。
禁用特性繼承
Vue.component('my-component', {
inheritAttrs: false,
// ...
})
自定義事件
推薦你始終使用 kebab-case 的事件名。
自定義組件v-model: 見上面用v-model案例,或者api。其實就是語法糖。
將原生事件綁定到組件
在v-on上使用修飾符.native,它用於監聽根元素上的原生事件。
base-input v-on:focus.native="onFocus"></base-input>
案例:
JS對象知識點)知識點:
A Javascript object is a collection of named values
Object是mutable: 對象的地址是引用的,不是by value。
JS variable是by value的。
Object.assign(target, ..sources) 返回一個新的Object,他的值包括target和sources,但不重復同時value會被覆蓋。
vm.$listeners,
一個對象,包含父作用域(不含.native)的事件監聽器。
可以通過v-on='$listeners'把它傳入內部組件,即父組件上的事件監聽,也可以用在內部組件中了。
vm.$attrs(沒有案例,不是很懂。)
類型: { |key: string|: string}
詳細: 包含了父作用域中不作為prop被識別獲取的特性綁定。可以用v-bind='$attrs'傳入內部組件。
inheritAttrs選項和$attrs是一對兒。同時出現在2.4版本
選項/其他 #inheritAttrs
類型 boolean
詳細:當寫包裹一個目標元素(或另一個組件)的組件時,目標元素(或子組件)不能得到父作用域的非prop特性綁定。這些特性會‘回退’並且作為普通的HTML特性應用在目標元素上(或子組件的根元素上)。
去掉這個默認行為,可以使用inheritAttrs: false。不繼承屬性。
想要繼承特性綁定,可以使用v-bind='$attrs',把這些特性綁定到目標元素(非根元素)上。
inheritAttrs選項和$attrs是一對兒。同時出現在2.4版本
.sync修飾符 沒有看。
<solt>插槽
通過slot可以分發內容。
Vue自定義的標簽。見:插槽。
理解: 組件實例內的html會被保留。
<solt> 有一個特別的name特性,用於和其他solt區別
- 可以在父組件的<template>元素上使用slot的name特性
- 可以直接用在一個普通的元素上
插槽的默認內容
可以為solt提供默認內容。如在template中有一個插槽

- 如果組件實例內沒有html內容就會使用插槽默認內容。
- 如果父組件為這個插槽提供了內容,默認的內容就會被替換掉。
編譯作用域 (不是很理解,沒有案例)
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
可以在組件實例中的插槽位置加上{{xxx}},來插入數據。
但不能訪問url, 這屬於<navigation-link>
的作用域。
- 父組件模板的所有東西都會在父級作用域內編譯;
- 子組件模板的所有東西都會在子級作用域內編譯。
作用域slot
處理邊界情況(特殊情況)
訪問元素/組件
有些特定情況,需要觸及另一個組件實例內部,或手動操作DOM元素。
訪問子組件實例/子元素
除了prop,event, 有時需要在JS中直接訪問一個子組件。
方法:
通過使用ref特性,為子組件/元素賦予一個id引用。
例如,這個子組件:
<base-input ref="username"></base-input>
然后,用this.$refs.username來訪問組件實例。
注意⚠️: ref的使用是非響應式的,並且只會在組件渲染完成后生效。所以盡量避免使用它。
可以用於從父組件聚焦一個輸入框:
<input ref="input">
然后添加一個方法:
methods: {
// 用來從父級組件聚焦輸入框
focus: function () {
this.$refs.input.focus()
}
}