既然 Vue 是基于組件去構(gòu)造視圖的,那么最重要的事情當(dāng)然是要掌握組件的知識(shí)點(diǎn)了。
Vue 為了極大簡(jiǎn)化組件的開發(fā),把組件的書寫形式精煉為配置的形式,一個(gè)組件使用一個(gè) js 對(duì)象來描述,然后利用該 js 描述對(duì)象可以生成多個(gè)該組件實(shí)例。
打開 vue/types/index.d.ts 文件,可以看到四個(gè)與組件相關(guān)的類型:
- Component
- ComponentOptions
- FunctionalComponentOptions
- AsyncComponent
此處主要關(guān)注 AsyncComponent 、 ComponentOptions 和 FunctionalComponentOptions ,分別對(duì)應(yīng)異步組件、普通組件、函數(shù)組件。
ComponentOptions
在 vue/types/options.d.ts 中可以看到 ComponentOptions 的聲明,重點(diǎn)關(guān)注如下屬性聲明:
-
data
組件中使用到的數(shù)據(jù)聲明與初始化。可以是一個(gè)對(duì)象,也可以是一個(gè)方法,不過強(qiáng)烈推薦使用方法,原因?qū)⒃诤罄m(xù)文章講解。
-
props
組件對(duì)外暴露的數(shù)據(jù)接口,外部可以通過該接口向組件傳遞數(shù)據(jù)。
-
methods
組件實(shí)例上的方法。
-
template
組件模板,默認(rèn)使用 HTML 配合一些簡(jiǎn)單的 Vue 語法來書寫,也可以借助于編譯時(shí)插件去支持其它的一些模板語法(比如 pug )。
-
created
生命周期方法,組件實(shí)例化時(shí),在各項(xiàng)數(shù)據(jù)準(zhǔn)備就緒之后,解析
template之前執(zhí)行。
以上是構(gòu)造普通組件的最基礎(chǔ)屬性,那么定義一個(gè)簡(jiǎn)單普通組件就可以這樣寫:
import Vue, { ComponentOptions } from 'vue';
const MyComponent: ComponentOptions<Vue> = {
props: {
name: String,
age: number
},
data() {
return {
counter: 0
};
},
created() {
this.setTimer();
},
methods: {
setTimer() {
setInterval(() => this.counter++, 1000);
}
},
template: '<div class="my-component">{{ name }} {{ age > 18 ? 'old' : 'young' }} {{ counter }}</div>'
};
Vue.component('MyComponent', MyComponent);
在上述組件定義中,有若干知識(shí)點(diǎn):
-
data方法、methods配置中的方法、created方法中,注入的this都指向當(dāng)前組件實(shí)例vm。遵照 Vue 官方文檔的慣例,我們使用
vm( ViewModel 縮寫) 表示 Vue 組件實(shí)例。 template中的{{ expression }}是 Vue 給模板添加的語法,用于在當(dāng)前位置輸出字符串,其中表達(dá)式轉(zhuǎn)換成字符串的方式與'' + expression行為一致,因此,如果表達(dá)式值是undefined,將會(huì)在相應(yīng)位置輸出undefined字符串。目前,
template只能且必須返回一個(gè)元素根節(jié)點(diǎn)(有例外,但是正常用法是碰不到的)。template中訪問外部傳入的數(shù)據(jù)(props)、data中聲明的數(shù)據(jù)時(shí),不需要寫this,直接訪問就行,因?yàn)檫@些數(shù)據(jù)都會(huì)被直接掛載到vm實(shí)例上去。
上述組件配置通過 Vue.component 方法注冊(cè)到 Vue 的全局空間中去,其中,第一個(gè)參數(shù)指定該組件在全局空間中的名字為 MyComponent ,在需要使用該組件的地方通過名字調(diào)用就行了:
import Vue, { ComponentOptions } from 'vue';
const App: ComponentOptions<Vue> = {
el: 'app',
data() {
return {
age: 27
};
},
template: '<MyComponent name="yibuyisheng" :age="age"></MyComponent>'
};
Vue.component('MyComponent', MyComponent);
對(duì)于 props 屬性,直接通過標(biāo)簽屬性(比如 name="yibuyisheng")的形式傳遞給組件。
在執(zhí)行上述示例的時(shí)候,可以發(fā)現(xiàn),{{ counter }} 表達(dá)式部分的輸出會(huì)一秒加一。實(shí)際上, data 里面事先聲明的數(shù)據(jù)都是響應(yīng)式的,改變 data 中聲明的數(shù)據(jù),模板中相應(yīng)部分就會(huì)發(fā)生變化。
FunctionalComponentOptions
函數(shù)式組件的主要特點(diǎn)在于沒有本地狀態(tài) data ,不會(huì)生成組件實(shí)例,組件需要的一切信息都從上下文參數(shù)中獲取。
函數(shù)式組件配置項(xiàng)非常少:
-
name
主要用于組件遞歸調(diào)用,以及開發(fā)的時(shí)候能方便找到針對(duì)該組件打印的日志。由于在渲染的時(shí)候并不會(huì)生成組件實(shí)例,因此在 Vue devtools 的組件樹中是看不到函數(shù)式組件的。
-
props
同普通組件的 props 。
-
inject
配置從上層組件注入的數(shù)據(jù),后續(xù)文章會(huì)詳細(xì)介紹。
-
functional
標(biāo)記該組件是否為函數(shù)式組件。
-
render
普通組件和函數(shù)式組件都會(huì)有
render函數(shù),用于渲染界面。雖然上面在講解普通組件的時(shí)候沒有提到render函數(shù),但是實(shí)際上template會(huì)被 Vue 的模板編譯器轉(zhuǎn)換成render函數(shù)。函數(shù)式組件的
render函數(shù)與普通組件的有很大區(qū)別:- 函數(shù)式組件不會(huì)生成實(shí)例,所以在
render中沒有this。 - 函數(shù)式組件的
render存在第二個(gè)參數(shù)context,從context中可以獲取到props等信息,具體參考官方文檔。
- 函數(shù)式組件不會(huì)生成實(shí)例,所以在
編寫一個(gè)簡(jiǎn)單的函數(shù)式組件:
import Vue, { FunctionalComponentOptions } from 'vue';
const MyFunctionalComponent: FunctionalComponentOptions<Record<string, any>, string[]> = {
props: ['name'],
functional: true,
render(createElement, context) {
return createElement('div', context.data, ['name: ' + context.props.name]);
}
};
Vue.component<string>('MyFunctionalComponent', MyFunctionalComponent);
說明:
createElement方法用于創(chuàng)建一個(gè) VNode 節(jié)點(diǎn),詳細(xì)內(nèi)容將會(huì)在后續(xù)文章介紹,目前只需要知道createElement('div', ...)會(huì)在 DOM 樹中生成一個(gè)div元素節(jié)點(diǎn)。- 通過
context.props訪問函數(shù)式組件外部傳入的props數(shù)據(jù)。
AsyncComponent
在開發(fā)一些大型( SPA )應(yīng)用的時(shí)候,可能會(huì)存在大量前端組件代碼,對(duì)首屏進(jìn)入可交互狀態(tài)造成一定的延遲。
實(shí)際上,渲染頁面1的時(shí)候,最好不要去加載頁面2的組件,直到用戶點(diǎn)擊進(jìn)入頁面2的時(shí)候,才去加載頁面2相關(guān)的組件。
為了實(shí)現(xiàn)這個(gè)功能, Vue 組件支持異步寫法:
import Vue, { AsyncComponent } from 'vue';
const MyAsyncComponent: AsyncComponent = function (resolve) {
setTimeout(function () {
resolve({
template: '<div>async component.</div>'
});
}, 1000);
};
Vue.component('MyAsyncComponent', MyAsyncComponent);
在一秒后,組件加載完成,相應(yīng)界面得到渲染。
預(yù)告
下一篇文章將會(huì)深入介紹 Vue 內(nèi)部的響應(yīng)式原理。