小程序基於wepy框架開發仿微信界面DEMO

先看一個視頻,這個視頻並非去演示如何使用微信,而是演示基於wepy開發的微信小程序demo。javascript

視頻地址: https://v.qq.com/x/page/x0352lsswtq.htmlhtml

demo中包含的功能有:java

  • 仿微信界面git

  • 聯繫人列表github

  • 私聊與自動回覆npm

  • 聊天記錄本地存儲與清除小程序

項目代碼地址:https://github.com/wepyjs/wepy-wechat-demo後端

下面就講講是如何一步一步實現這個仿微信demo的。微信小程序

1、需求分析

首先要肯定好自已在DEMO中想要實現的功能,微信有四個tab:微信聊天,通信錄,發現,我。右上角的搜索,添加好友功能,以及發現裏的朋友圈和各項菜單功能,這裏主要想實現的就是聊天,還有通信錄好友功能。由於考慮到小程序真機體驗時只容許請求安全域名,因此數據不打算使用後端接口返回,而是採用MOCK數據模擬後端接口返回。聊天記錄儲存於小程序提供的Storage中。這樣就能完整的模擬聊天功能,並且下載下來的DEMO能夠直接在真機上體驗。api

同時評估一些技術細節:

涉及的原生API

  • 登陸相關API wx.login。

  • 獲取用戶信息API wx.getUserInfo。

  • Storage相關 wx.getStorage,wx.setStorage,wx.clearStorage。

技術方案

  • 樣式部分使用sass,wepy現階段支持lesssass,本demo使用sass

  • 代碼部分使用新特性async/await

  • 數據接口使用MOCK數據模擬接口返回。

2、頁面組件劃分

按微信界面展現大體劃分爲兩個頁面,首頁index,聊天頁chat,以及若干組件,以下圖:
圖片描述

首頁index中包含一個tab組件和四個tab分別所對應的組件messagecontactdiscoveryme。並且各自還包含一些子組件,如contact組件中包含alpha字母列表組件,discoveryme組件中分別包含一些list菜單列表組件。其中list組件達到了很好的複用效果。

聊天頁chat中包含一個聊天面板組件chatboard和輸入框組件input

根據劃分的組件,大體能夠獲得開發的目錄結構:

src
    components
        alpha.wpy --- 聯繫人
        chatboard.wpy --- "聊天面板" 組件
        contact.wpy --- "聯繫人" 組件
        discovery.wpy --- "發現" 組件
        input.wpy --- 聊天頁輸入框組件
        list.wpy --- 菜單列表組件
        me.wpy --- "我" 組件
        message.wpy --- message 組件
        tab.wpy --- tab 組件
    pages
        chat.wpy --- 聊天頁
        index.wpy --- 首頁
    app.wpy --- 小程序入口

3、切圖與重構

直接用手機截屏而後放到Photoshop中處理。小程序作不一樣機型的適配很方便,提供了一個rpx的單位,官方說明以下:

rpx(responsive pixel): 能夠根據屏幕寬度進行自適應。規定屏幕寬爲750rpx。如在 iPhone6 上,屏幕寬度爲375px,共有750個物理像素,則750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

個人手機截圖尺寸是 720px 1280px, 爲了方便計算,直接將截圖按比例調整爲 750px 1333px。那麼此時的單位換算就是1px = 1rpx,也就是說一個圖片在Photoshop中是 80px * 80px,那麼就直接寫width: 80rpx;height: 80rpx;

整理出各圖標大小以及各元素之間的寬高間距等,方便在sass中使用。以下圖:

![Alt text](./me.png)

按照第二步劃分的頁面組件,對組件進行基本的填充。而後頁面內容就十分簡單了。

src/pages/index.wpy:

<style type="sass">
    .body, .tab_item {
        height: 100%;
    }
</style>
<template>
    <view class="body">
        <view class="tab_item tab_message">
            <component id="message"></component>
        </view>
        <view class="tab_item tab_contact">
            <component id="contact"></component>
        </view>
        <view class="tab_item tab_discovery">
            <component id="discovery"></component>
        </view>
        <view class="tab_item tab_me">
            <component id="me"></component>
        </view>
        <component id="tab"></component>
    </view>
</template>

src/pages/chat.wpy:

<style type="sass">
    .body {
        height: 100%;
        background-color: #ededed;
    }
</style>
<template>
    <view class="body">
        <component id="chartboard"></component>
        <component id="input"></component>
    </view>
</template>

接着完成基本的重構工做:

![Alt text](./wepy_change.png)

4、MOCK數據設計

經過需求分析獲得只須要兩份基礎數據:

  • 聯繫人數據

  • 初始聊天記錄數據

其對應的數據表結構以下:

ID 姓名 頭像
誰發的 發給誰 消息類型 消息內容 發送時間

所以咱們可使用js構建這兩份數據表做爲原始數據,
目錄結構設計大體以下:

src
    mocks  --- mock數據目錄
        users  --- 用戶頭像目錄
            xxxx.png  --- xxxx頭像
        contact.js  --- 聯繫人mock數據
        history.js  --- 聊天記錄mock數據

src/mock/contact.js 模擬聯繫人數據返回,代碼以下:

// 全部聯繫人數據
let users = [
    {id: 'jimgreen', name: 'Jim Green'},
    {id: 'hanmeimei', name: '韓梅梅'}
];
users = users.sort((a, b) => a.id.charCodeAt(0) - b.id.charCodeAt(0));

let table = users.map((v) => {
    return {
        name: v.name,
        id: v.id,
        icon: `/mocks/users/${v.id}.png`
    };
});
export default table

src/mock/history.js模擬初始聊天記錄數據返回,代碼以下 :

export default [
    {'to': 'jimgrenn', 'from': 'me', 'type': 'text', 'msg': 'My name is Jim Green, nice to meet you.', 'time': 1480338091374},
    {'to': 'me', 'from': 'jimgreen', 'type': 'text', 'msg': 'Nice to meet you too', 'time': 1480338091375},
];

5、接口API設計

由於使用MOCK數據的關係,咱們能夠同步吐出接口數據,但這裏但願能更接近於AJAX訪問的異步效果,因此全部接口均返回setTimeout處理的Promise對象。

整理出所需功能的全部數據請求以下:

  • 拉取聊天列表頁的聊天列表(用戶頭像,用戶名稱,最後一條聊天信息)

  • 拉取聊天頁面的聊天記錄 (用戶頭像,本身頭像,聊天記錄)

  • 發送聊天信息

  • 拉取tab下的我的頭像以及用戶暱稱等信息

由於涉及到的數據接口並很少,因此單獨放在src/common/api模塊下。
代碼結構大體以下:

import m_contacts from '../mocks/contact';
import m_history from '../mocks/history';

export default {
    
    // 拉取用戶信息
    getUserInfo () {},
    
    // 拉取與某個用戶的聊天曆史記錄
    getHistory (id) {},
    
    // 拉取首頁聊天列表
    getMessageList () {},

    // 發送聊天信息
    sendMsg (to, msg, type = 'text') {}
}

6、邏輯代碼開發

邏輯代碼部分主要包括三部分:

  • 調用數據接口,返回數據,渲染視圖。

  • 組件內事件交互。

  • 組件之間相互通訊。

message組件中須要拉取聊天列表信息而且渲染,代碼以下:

<template>
    <view class="message">
        <block wx:for="{{list}}" wx:for-index="index" wx:for-item="item">
            <view class="item" bindtap="select" data-wepy-params="{{item.id}}">
                <view class="header">
                    <image class="img" src="{{item.icon}}"></image>
                </view>
                <view class="content">
                    <view class="name">{{item.name}}</view>
                    <view class="lastmsg">{{item.lastmsg}}</view>
                </view>
            </view>
        </block>
    </view>
</template>
<script>
    import wepy from 'wepy';
    import api from '../common/api';

    export default class Message extends wepy.component {

        data = {
            list: []
        };
      
        methods = {
            select (evt, id) {
                wx.navigateTo({url: 'chat?id=' + id});
            }
        };
        async loadMessage () {
            this.list = await api.getMessageList();
            this.$apply();
        }
    }
</script>

message組件中只有一個數據源list,經過自定義方法loadMessage調用api模塊獲取聊天列表信息進行渲染,由於是在自定義的異步方法中進行數據綁定,因此須要執行this.$apply()讓視圖渲染。
同時,組件響應頁面的tap事件select,選中聊天以後跳轉至chat頁面。
chat頁面進行聊天以後,返回到index頁面時,須要message頁面再次調用接口數據,從新渲染聊天列表頁,這就須要在index頁面的onShow方法中去讓message組件從新調用loadMessage方法。這裏能夠選用 wepy 提供的$boradcast方法或者$invoke方法,代碼以下:

// src/pages/index.wpy
onShow() {
    this.$invoke('message', 'loadMessage');
}

這樣就完成了message組件的全部功能,進入頁面渲染列表,點擊消息進入聊天頁面。

圖片描述

index頁面中加入狀態currentTab來標記當前選中tab。並提供切換tab事件。

src/pages/index:

<template>
    <view class="body">
        <view class="tab_item tab_message" hidden="{{currentTab != 0}}">
            <component id="message"></component>
        </view>
        <view class="tab_item tab_contact" hidden="{{currentTab != 1}}">
            <component id="contact"></component>
        </view>
        <view class="tab_item tab_discovery" hidden="{{currentTab != 2}}">
            <component id="discovery"></component>
        </view>
        <view class="tab_item tab_me" hidden="{{currentTab != 3}}">
            <component id="me"></component>
        </view>
        <component id="tab"></component>
    </view>
</template>

<script>
    //....
    changeTab (idx) {
        this.currentTab = +idx;
        this.$apply();
    }
</script>

而後在tab組件中的每一個tab中添加bindtap="change" data-wepy-params="{{index}}"事件。

<script>
    import wepy from 'wepy';

    export default class Tab extends wepy.component {
        data = {
            active: 0,
        };
        methods = {
            change (evt, idx) {
                this.active = +idx;
                this.$parent.changeTab(idx);
            }
        };
    }
</script>

在tab組件中,直接經過$parent去調用父組件的changeTab方法,來達到實現tab切換效果:

至此已完成大體雛形,更多代碼還請參考提供源代碼。

結束語:wepy讓用戶能以組件化思惟開發小程序,加上一些新特性的引入讓開發與維護變得更簡單,但同時缺點又在於引入框架以及額外的polyfill,npm增長項目代碼體積(壓縮後170kb),在僅限1M代碼體積的小程序中,代碼容量時時刻刻又顯得有些捉肘見襟了。但願小程序能早日能放寬限制。

相關文章
相關標籤/搜索