編寫簡(jiǎn)單的 Vue 組件

既然 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 、 ComponentOptionsFunctionalComponentOptions ,分別對(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 等信息,具體參考官方文檔。

編寫一個(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)式原理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容,還有我對(duì)于 Vue 1.0 印象不深的內(nèi)容。關(guān)于...
    云之外閱讀 5,174評(píng)論 0 29
  • Vue 實(shí)例 屬性和方法 每個(gè) Vue 實(shí)例都會(huì)代理其 data 對(duì)象里所有的屬性:var data = { a:...
    云之外閱讀 2,368評(píng)論 0 6
  • 1.安裝 可以簡(jiǎn)單地在頁面引入Vue.js作為獨(dú)立版本,Vue即被注冊(cè)為全局變量,可以在頁面使用了。 如果希望搭建...
    Awey閱讀 11,295評(píng)論 4 129
  • 下載安裝搭建環(huán)境 可以選npm安裝,或者簡(jiǎn)單下載一個(gè)開發(fā)版的vue.js文件 瀏覽器打開加載有vue的文檔時(shí),控制...
    冥冥2017閱讀 6,196評(píng)論 0 42
  • “致虛極,守靜篤。萬物并作,吾以觀復(fù)。夫物蕓蕓,各復(fù)歸其根。歸根曰靜,靜曰復(fù)命。復(fù)命曰常,知常曰明。不知常...
    錢江潮369閱讀 306評(píng)論 0 1

友情鏈接更多精彩內(nèi)容