wepy學習記錄

原文地址:微信小程序組件化開發框架WePY官方文檔

一、wepy中的文件

wepy的文件主要以.wpy存在,其分類有三種:

1.1 入口文件

app.wpy,其繼承自wepy.app類,包含一個config屬性和其它全局屬性、方法、事件。其中config屬性對應原生的app.json文件,build編譯時會根據config屬性自動生成app.json文件。

一個app.wpy文件的典型結構如下:

<script>
import wepy from 'wepy';
export default class extends wepy.app {
	// 對應 app.json 文件
    config = {
        "pages":[
            "pages/index/index"
        ],
        "window":{
            "backgroundTextStyle": "light",
            "navigationBarBackgroundColor": "#fff",
            "navigationBarTitleText": "WeChat",
            "navigationBarTextStyle": "black"
        }
    };
	//自定義數據
    customData = {};
	//自定義方法
    customFunction () { }
	//生命週期回調
    onLaunch () {}
	//生命週期回調
    onShow () {}
	//全局數據
    globalData = {}
}
</script>

<style lang="less">
/** less **/
</style>

1.2 頁面文件

page.wpy,其繼承自wepy.page類,該類的主要屬性介紹如下(在Page頁面實例中,可以通過this.$parent來訪問App實例):

屬性 說明
config 頁面配置對象,對應於原生的page.json文件,類似於app.wpy中的config
components 頁面組件列表對象,聲明頁面所引入的組件列表
data 頁面渲染數據對象,存放可用於頁面模板綁定的渲染數據
methods wxml事件處理函數對象,存放響應wxml中所捕獲到的事件的函數,如bindtapbindchange
events WePY組件事件處理函數對象,存放響應組件之間通過$broadcast$emit$invoke所傳遞的事件的函數
其它 小程序頁面生命週期函數,如onLoadonReady等,以及其它自定義的方法與屬性

一個page.wpy文件的典型結構如下:

<script>
import wepy from 'wepy';
import Counter from '../components/counter';

export default class Page extends wepy.page {
    components = {counter1: Counter}; // 聲明頁面中所引用的組件,或聲明組件中所引用的子組件

    customData = {}  // 自定義數據

    customFunction () {}  //自定義方法

    onLoad () {}  // 在Page和Component共用的生命週期函數

    onShow () {}  // 只在Page中存在的頁面生命週期函數

    config = {};  // 只在Page實例中存在的配置數據,對應於原生的page.json文件

    data = {};  // 頁面所需數據均需在這裏聲明,可用於模板數據綁定

    mixins = [];  // 聲明頁面所引用的Mixin實例

    computed = {};  // 聲明計算屬性

    watch = {};  // 聲明數據watcher

    methods = {};  // 聲明頁面wxml中標籤的事件處理函數。注意,此處只用於聲明頁面wxml中標籤的bind、catch事件,自定義方法需以自定義方法的方式聲明

    events = {};  // 聲明組件之間的事件處理函數
}
</script>

<template lang="wxml">
    <view>
    </view>
    <counter1></counter1>
</template>

<style lang="less">
/** less **/
</style>

注意:WePY中的組件都是靜態組件,是以組件ID作爲唯一標識的,每一個ID都對應一個組件實例,當頁面引入兩個相同ID的組件時,這兩個組件共用同一個實例與數據,當其中一個組件數據變化時,另外一個也會一起變化。
如果需要避免這個問題,則需要分配多個組件ID和實例(推薦)。

1.3 組件文件

組件文件中所聲明的組件實例繼承自wepy.component類,除了不需要config配置以及頁面特有的一些生命週期函數之外,其屬性與頁面屬性大致相同(實際上wepy.Page類繼承自wepy.Component,所以page能用的接口component都能用,除了生命週期方法)。

一個組件文件的典型結構如下:

<template lang="wxml">
    <view>  </view>
</template>

<script>
import wepy from 'wepy';
export default class Com extends wepy.component {
    components = {};

    data = {};
    methods = {};

    events = {};
    // Other properties
}
</script>

<style lang="less">
/** less **/
</style>

二、組件渲染與通信

2.1 WEPY組件的循環渲染

wepy的template部分的語法是符合wxml標準的(一般情況下),可以通過wxml的循環渲染去渲染小程序原生組件。但是如果要循環渲染wepy的自定義組件,則需要特殊處理:

當需要循環渲染WePY組件時(類似於通過wx:for循環渲染原生的wxml標籤),必須使用WePY定義的輔助標籤<repeat>

示例如下:

/**
project
└── src
    ├── components
    |   └── child.wpy
    ├── pages
    |   ├── index.wpy    index 頁面配置、結構、樣式、邏輯
    |   └── log.wpy      log 頁面配置、結構、樣式、邏輯
    └──app.wpy           小程序配置項(全局樣式配置、聲明鉤子等)
**/

// index.wpy

<template>
    <!-- 注意,使用for屬性,而不是使用wx:for屬性 -->
    <repeat for="{{list}}" key="index" index="index" item="item">
        <!-- 插入<script>腳本部分所聲明的child組件,同時傳入item -->
        <child :item="item"></child>
    </repeat>
</template>

<script>
    import wepy from 'wepy';
    // 引入child組件文件
    import Child from '../components/child';

    export default class Index extends wepy.component {
        components = {
            // 聲明頁面中要使用到的Child組件的ID爲child
            child: Child
        }

        data = {
            list: [{id: 1, title: 'title1'}, {id: 2, title: 'title2'}]
        }
    }
</script>

2.2 computed和watcher

wepy框架也可以用類似vuecomputedwatcher屬性。具體內容可以在vue或者wepy的文檔中查看,這裏僅給出簡要介紹:

  • computed 計算屬性。computed計算屬性,是一個有返回值的函數,可直接被當作綁定數據來使用。因此類似於data屬性,代碼中可通過this.計算屬性名來引用,模板中也可通過{{ 計算屬性名 }}來綁定數據。

    需要注意的是,只要是組件中有任何數據發生了改變,那麼所有計算屬性就都會被重新計算。
  • watcher 監聽器。通過監聽器watcher能夠監聽到任何屬性的更新。監聽器在watch對象中聲明,類型爲函數,函數名與需要被監聽的data對象中的屬性同名,每當被監聽的屬性改變一次,監聽器函數就會被自動調用執行一次。

    監聽器適用於當屬性改變時需要進行某些額外處理的情形。

2.3 父子組件屬性通信

wepy中的父子組件之間,可以使用props進行傳值,傳值有兩種方式:靜態傳值、動態傳值。靜態傳值指父組件向子組件傳遞常量數據,且只能傳遞String字符串類型。

靜態傳值代碼示例如下:

<child title="mytitle"></child>

// child.wpy
props = {
    title: String
};

onLoad () {
    console.log(this.title); // mytitle
}

上面的代碼中,在父組件template模板部分的組件標籤中,使用子組件props對象中所聲明的屬性名作爲其屬性名來接收父組件傳遞的值。

動態傳值是指父組件向子組件傳遞動態數據內容,父子組件數據完全獨立互不干擾。但可以通過使用.sync修飾符來達到父組件數據綁定至子組件的效果,也可以通過設置子組件propstwoWay: true來達到子組件數據綁定至父組件的效果。那如果既使用.sync修飾符,同時子組件props中添加的twoWay: true時,就可以實現數據的雙向綁定了。

注意:下文示例中的twoWaytrue時,表示子組件向父組件單向動態傳值,而twoWayfalse(默認值,可不寫)時,則表示子組件不向父組件傳值。這是與Vue不一致的地方,而這裏之所以仍然使用twoWay,只是爲了儘可能保持與Vue在標識符命名上的一致性。

在父組件template模板部分所插入的子組件標籤中,使用:prop屬性(等價於Vue中的v-bind:prop屬性)來進行動態傳值。
動態傳值代碼示例如下:

// parent.wpy

<child :title="parentTitle" :syncTitle.sync="parentTitle" :twoWayTitle="parentTitle"></child>

data = {
    parentTitle: 'p-title'
};


// child.wpy

props = {
    // 靜態傳值
    title: String,

    // 父向子單向動態傳值
    syncTitle: {
        type: String,
        default: 'null'
    },

    twoWayTitle: {
        type: String,
        default: 'nothing',
        twoWay: true
    }
};

onLoad () {
    console.log(this.title); // p-title
    console.log(this.syncTitle); // p-title
    console.log(this.twoWayTitle); // p-title

    this.title = 'c-title';
    console.log(this.$parent.parentTitle); // p-title.
    this.twoWayTitle = 'two-way-title';
    this.$apply();
    console.log(this.$parent.parentTitle); // two-way-title.  --- twoWay爲true時,子組件props中的屬性值改變時,會同時改變父組件對應的值
    this.$parent.parentTitle = 'p-title-changed';
    this.$parent.$apply();
    console.log(this.title); // 'c-title';
    console.log(this.syncTitle); // 'p-title-changed' --- 有.sync修飾符的props屬性值,當在父組件中改變時,會同時改變子組件對應的值。
}

2.4 組件通信與交互

wepy.component基類提供$broadcast$emit$invoke三個方法用於組件之間的通信和交互。

  • $broadcast用於父組件向子組件(及子組件的子組件等)廣播事件,事件廣播的順序爲廣度優先搜索順序。如,頁面Page_Index發起一個$broadcast事件,那麼按先後順序依次接收到該事件的組件爲:ComA、ComB、ComC、ComD、ComE、ComF、ComG、ComH。如下圖:
    在這裏插入圖片描述
  • $emit用於子組件向父組件(及父組件的父組件等)發射事件,事件發起組件的所有祖先組件會依次接收到$emit事件。如,組件ComE發起一個$emit事件,那麼接收到事件的先後順序爲:組件ComA、頁面Page_Index。如下圖:
    在這裏插入圖片描述
  • $invoke用於組件對另一個組件中的方法的直接調用,語法類似this.$invoke('ComA', 'someMethod', 'someArgs')

組件broadcastemit的語法類似this.$emit('some-event', 1, 2, 3, 4),如果組件需要接收某事件,那麼需要在自己的events屬性添加相應的事件處理函數。

import wepy from 'wepy'

export default class Com extends wepy.component {
    components = {};

    data = {};

    methods = {};

    // events對象中所聲明的函數爲用於監聽組件之間的通信與交互事件的事件處理函數
    events = {
        'some-event': (p1, p2, p3, $event) => {
               console.log(`${this.$name} receive ${$event.name} from ${$event.source.$name}`);
        }
    };
    // Other properties
}

2.5 組件自定義事件處理函數

wepy的組件可以設置事件處理函數,語法類似@customEvent.user="myFn",其中@表示事件修飾符,customEvent表示事件名稱,.user表示事件後綴。

目前總共有三種事件後綴:

  • .default: 綁定小程序冒泡型事件,如bindtap.default後綴可省略不寫
  • .stop: 綁定小程序捕獲型事件,如catchtap
  • .user: 綁定用戶自定義組件事件,通過$emit觸發。注意,如果用了自定義事件,則events中對應的監聽函數不會再執行

代碼示例:

// index.wpy

<template>
	//當$emit發射childFn事件時,會傳遞給parentFn方法
    <child @childFn.user="parentFn"></child>
</template>

<script>
    import wepy from 'wepy'
    import Child from '../components/child'

    export default class Index extends wepy.page {
        components = {
            child: Child
        }

        methods = {
            parentFn (num, evt) {
                console.log('parent received emit event, number is: ' + num)
            }
        }
    }
</script>


// child.wpy

<template>
	//這裏綁定的是小程序的事件,點擊時會傳遞給tap方法
    <view @tap="tap">Click me</view>
</template>

<script>
    import wepy from 'wepy'

    export default class Child extends wepy.component {
        methods = {
            tap () {
                console.log('child is clicked')
                this.$emit('childFn', 100)
            }
        }
    }
</script>

2.6 slot 組件內容分發插槽

可以理解爲子組件的空間佔位標籤,父組件可以用其他組件替代這些佔位標籤。

//Panel組件中模板如下
<view class="panel">
    <slot name="title">默認標題</slot>
    <slot name="content">默認內容</slot>
</view>

//父組件使用Panel時用自定義的組件替代slot
<panel>
    <view slot="title">新的標題</view>
    <view slot="content">
        <text>新的內容</text>
    </view>
</panel>

三、interceptor 攔截器

wepy提供攔截器攔截原生API的請求,具體方法是配置API的configfailsuccesscomplete回調函數。參考示例:

import wepy from 'wepy';

export default class extends wepy.app {
    constructor () {
        // this is not allowed before super()
        super();
        // 攔截request請求
        this.intercept('request', {
            // 發出請求時的回調函數
            config (p) {
                // 對所有request請求中的OBJECT參數對象統一附加時間戳屬性
                p.timestamp = +new Date();
                console.log('config request: ', p);
                // 必須返回OBJECT參數對象,否則無法發送請求到服務端
                return p;
            },

            // 請求成功後的回調函數
            success (p) {
                // 可以在這裏對收到的響應數據對象進行加工處理
                console.log('request success: ', p);
                // 必須返回響應數據對象,否則後續無法對響應數據進行處理
                return p;
            },

            //請求失敗後的回調函數
            fail (p) {
                console.log('request fail: ', p);
                // 必須返回響應數據對象,否則後續無法對響應數據進行處理
                return p;
            },

            // 請求完成時的回調函數(請求成功或失敗都會被執行)
            complete (p) {
                console.log('request complete: ', p);
            }
        });
    }
}

四、數據綁定

wepy中不需要用setData方法來綁定數據,直接爲綁定數據賦值即可。

注意,在異步函數中更新數據的時候,必須手動調用$apply方法。

setTimeout(() => {
    this.title = 'this is title';
    this.$apply();
}, 3000);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章