入坑微信小程序(項(xiàng)目搭建)

超榮幸能夠參與我司【更美小程序】的搭建伍宦,在此分享些心得希望能夠幫助到像我一樣的前端界萌新票堵。因【更美小程序】源碼需保密攘宙,我僅向大家分享基礎(chǔ)建設(shè)級(jí)別的非業(yè)務(wù)代碼坦弟。點(diǎn)我~

一個(gè)最基本的小程序項(xiàng)目需具備:app.js(入口文件)蜗细、app.json(全局配置)裆操、app.wxss(通用樣式)、pages/(頁面)炉媒。pages/ 下的每一頁面擁有獨(dú)自的 .js踪区、.json、.wxss吊骤。形如:

想了解更多請參考 微信小程序代碼構(gòu)成缎岗。對(duì)于中大型項(xiàng)目需明確劃分功能模塊,我司小程序文件目錄如下:

  • assets:靜態(tài)資源

    <image /> 及 tabBar 支持引用本地靜態(tài)資源白粉,而 wxss 中 background-image 不支持传泊,但支持引用 base64 及網(wǎng)絡(luò)資源鼠渺。

  • components:公用組件

  • templates:公用模板

    組件模板 的應(yīng)用場景易混淆。父節(jié)點(diǎn)可向組件也可向模板傳入 data 控制其視圖眷细。然組件的優(yōu)勢在于其 數(shù)據(jù)監(jiān)聽拦盹、事件監(jiān)聽生命周期 等機(jī)制溪椎,自行科普 component 構(gòu)造器 你便明了普舆。

    但構(gòu)造組件成本較高,json校读、wxml沼侣、wxss、js 需齊備:

    反之模板較輕便歉秫,構(gòu)造 wxml 接收 page data 即可:

    <template name="mError">
        <view class="mError">
            <image src="/assets/images/holder_error.png"></image>
            <text>網(wǎng)絡(luò)錯(cuò)誤</text>
        </view>
    </template>
    <template is="mError" />
    

    將模塊封裝為組件或是模板需開發(fā)者分析其特性并結(jié)合業(yè)務(wù)場景定奪(純粹的視圖控制請選擇模板)蛾洛。

  • settings:配置文件

    module.exports = {
        version: '1.0.0',
        server: 'https://backend.igengmei.com',
        release: 1
    }
    

    開發(fā)階段的網(wǎng)絡(luò)環(huán)境往往與生產(chǎn)階段不同,settings.js 配置了生產(chǎn)環(huán)境端考,需自行創(chuàng)建 settings_local.js(不入庫)配置開發(fā)環(huán)境雅潭。

    var settings = require('settings');
    var settings_local = null;
    try {settings_local = require('settings_local');} catch (err) {}
    module.exports = settings_local || settings
    

    上述腳本會(huì)優(yōu)先 export settings_local.js 內(nèi)配置。也可將 server 配置為本地服務(wù)却特,然小程序合法域名不支持 localhost...我們可在開發(fā)階段“不校驗(yàn)安全域名扶供、TLS 版本以及 HTTPS 證書”。(在微信開發(fā)者工具中設(shè)置)

  • utils:公用腳本

    utils 類腳本非全局注冊需在 page 內(nèi) import 方可調(diào)用裂明。app.js 內(nèi)注冊的全局函數(shù)無需 import椿浓,可通過 app.method(params) 直接調(diào)用:

    // utils 類腳本
    import Common from '../../utils/common'
    
    const app = getApp();
    Page({
        data: {},
        ...Common,  
        onLoad: function () {
            this.exampleRequest();
            // 全局注冊類腳本
            app.showToast(this, {
                message: '呆戀小喵一枚',
                duration: 3000,
                type: 'common'
            });
        },
        exampleRequest: function () {
            // 全局注冊類腳本  
            app.request({
                url: 'url',
                method: 'GET'
            });
        }
    });  
    

    全局注冊使用率高的模塊,可減少 page 內(nèi)的 import闽晦,例如 app.request(params)扳碍、app.showToast(params) 等:

    import { getBaseInfo } from 'utils/baseInfo'
    import Request from 'utils/request'
    import Toast from 'utils/toast'
    
    App({
        GLOBAL: {
            baseInfo: getBaseInfo()
        },
        request: function (params) {
            Request(params);
        },
        showToast: function (page, opts) {
            Toast.show(page, opts);
        }
    });
    

    也可在 GLOBAL 內(nèi)注冊一些全局 data,在 page 內(nèi)通過 app.GLOBAL 獲取仙蛉。


踩坑札記

關(guān)于 tabBar

app.json 內(nèi)可配置 tabBar 的 pagePath笋敞、text、iconPath荠瘪、selectedIconPath夯巷,但圖標(biāo)尺寸、文字大小哀墓、元素間距不可自定義趁餐。icon 尺寸建議為 81px * 81px,若 icon 切圖恰好撐滿畫布篮绰,圖標(biāo)與文字便相互緊貼不美觀后雷。故 icon 切圖底邊距需有所保留:

關(guān)于 toast

小程序自帶 wx.showToast 必須傳入 icon:

wx.showToast({
    title: '成功',
    icon: 'success',
    duration: 2000
});

但我想使用樸素的 toast:

自行封裝 toast 捎帶默認(rèn)類型及自定義類型是個(gè)不錯(cuò)的選擇:

switch (opts.type) {
    case 'common':
        page.setData({
            'render.toast.show': true,
            'render.toast.message': opts.message
        });
        let t = setTimeout(() => {
            page.setData({
                'render.toast.show': false,
                'render.toast.message': ''
            });
            opts.callback();
        }, opts.duration);
        break;
    case 'loading':
        wx.showToast({
            title: opts.message,
            duration: opts.duration,
            icon: 'loading'
        });
        break;
    case 'success':
        wx.showToast({
            title: opts.message,
            duration: opts.duration,
            icon: 'success'
        });
        break;
}

關(guān)于 <rich-text />

<rich-text /> 渲染時(shí)不會(huì)將 nodes 解析為常規(guī)標(biāo)簽,你只能拿到這樣一大坨:

無法直接獲取其中的 dom,且不可在 .wxss 中定義其樣式故必須添加內(nèi)聯(lián) style臀突。

且 <rich-text /> 無法對(duì) nodes 自動(dòng)糾錯(cuò):例如部分瀏覽器可解析 <u>一段錯(cuò)誤代碼</u>勉抓, <rich-text /> 則直接過濾錯(cuò)誤代碼不進(jìn)行渲染。

關(guān)于 onPullDownRefresh

enablePullDownRefresh 僅可開啟 pulldown 的交互及監(jiān)聽候学,并非想象中的 window.location.reload琳状。我們需要定義自己的 reload:

reload: function (page, callback) {
    page.setData({
        reqError: false
    });
    callback && callback();
    page.onLoad();
    page.onReady();
}
onPullDownRefresh: function () {
    const _page = this;
    Loadmore.clear(_page);
    app.reload(_page, function () {
        _page.setData({
            'render.orders': [],
            'render.loading': true,
            'render.empty.show': false
        });
    });
    wx.stopPullDownRefresh();
}

小程序無 window 概念,不可調(diào)用 window.location.reload盒齿。其實(shí) reload 無非 重置 data、重新調(diào)用 onLoadonReady(原諒我這膚淺的理解困食,但你可在 callback 中做任何意義上的重置)边翁。

在 onPullDownRefresh 回調(diào)執(zhí)行時(shí) wx.stopPullDownRefresh() 防止用戶瘋狂 pulldown 導(dǎo)致卡澀。

關(guān)于 wx.getSystemInfo

調(diào)用 wx.getSystemInfo 可獲取設(shè)備信息硕盹,fail 回調(diào)限制了獲取失敗時(shí)的嘗試次數(shù):

function getMobileInfo(i) {
    wx.getSystemInfo({
        success: (res) => {
            BaseInfo.mobile = res.brand + res.model;
            BaseInfo.system = res.platform + res.system;
            BaseInfo.wechat = res.version;
            BaseInfo.winWidth = res.windowWidth / (res.windowWidth / 750);
            BaseInfo.winHeight = res.windowHeight / (res.windowWidth / 750);
        },
        fail: () => {
            (i < 3) && getMobileInfo(i + 1);
        }
    });
}
getMobileInfo(0);

請注意 windowWidth符匾、windowHeight 度量單位為 px,而我司項(xiàng)目規(guī)定使用 rpx瘩例。為實(shí)現(xiàn)單位統(tǒng)一啊胶,需對(duì) windowWidth 及 windowHeight 做單位轉(zhuǎn)換:

BaseInfo.winWidth = res.windowWidth / (res.windowWidth / 750);
BaseInfo.winHeight = res.windowHeight / (res.windowWidth / 750);

1rpx = (設(shè)備寬度 / 750) px

關(guān)于 wx.getLocation

首次 執(zhí)行 wx.getLocation 小程序?qū)⒆詣?dòng)調(diào)啟如下 dialog:

請注意是 首次!無論用戶選擇“確定”或是“取消”垛贤,再次進(jìn)入“更美測試”均不會(huì)被詢問是否開啟定位(調(diào)用 100 次 wx.getLocation 也無濟(jì)于事)焰坪。除非用戶手動(dòng)清理微信緩存、更新微信聘惦、切換賬號(hào)...

各種緩存:

存在上述問題的 API 絕不止 wx.getLocation 例如 wx.login某饰,遺憾的是,小程序并未開放清理緩存的接口善绎。但可通過 wx.openSetting 再次請求用戶開啟授權(quán):

關(guān)于 wx.reportAnalytics

小程序數(shù)據(jù)分析可通過填寫配置上報(bào)黔漂、API 上報(bào):

對(duì)于填寫配置上報(bào),需提交觸發(fā)動(dòng)作禀酱、觸發(fā)頁面炬守、觸發(fā)元素、埋點(diǎn)數(shù)據(jù)等剂跟。但埋點(diǎn)數(shù)據(jù)需從 page data 中獲取减途,看看官方文檔是怎么曰的:

事件數(shù)據(jù)來源于對(duì)頁面 page 實(shí)例 data 對(duì)應(yīng)字段值的收集。

OMG...需要在 page data 內(nèi)維護(hù)埋點(diǎn)狀態(tài)浩聋,當(dāng)埋點(diǎn)量較大時(shí)上報(bào)數(shù)據(jù)的復(fù)雜度可想而知观蜗。我曾傻傻的認(rèn)為 data 字段值等同 dataset 值:

<text
    wx:for="{{ areas }}"
    data-id="{{ item.id }}"
    data-name="{{ item.name }}"
    data-idx="{{ index }}"
    bindtap="tapItem">{{ item.name }}</text>

未曾想竟為 page 實(shí)例中的 data 值:

Page({
    data: {},
    onLoad: function () {},
    onReady: function () {}
});

如此看來 API 上報(bào)更簡單,為觸發(fā)元素 dataset 埋點(diǎn)數(shù)據(jù)并調(diào)用 wx.reportAnalytics 傳入?yún)?shù):

<text
    wx:for="{{ orders }}"
    data-id="{{ item.id }}"
    data-name="{{ item.name }}"
    data-type="order"
    bindtap="triggerSelected">{{ item.name }}</text>
triggerSelected (e) {
    var dataset = e.target.dataset;
    var id = dataset.id;
    var name = dataset.name;
    var type = dataset.type;
    wx.reportAnalytics('click_fliter_item', {
        item_type: type,
        item_id: id,
        item_name: name
    });
}

關(guān)于 rpx

rpx 在不同設(shè)備被小程序換算為 px 時(shí)能產(chǎn)生各種 bug衣洁,當(dāng)設(shè)備寬度除不盡 750 時(shí)結(jié)果值精確至哪一位呢(額...bug 產(chǎn)生原因本人猜的)墓捻,看看換算表:

舉個(gè)例子:

<view class="fliter-bar" style="top: {{ top }}rpx;"></view>
<view class="fliter-wrap" style="top: {{ top + 84 }}rpx;"></view>

問題一:當(dāng) top = 0 時(shí),0rpx 被換算為 0.5px 也是厲害~

解決方案:

<view class="fliter-bar" style="top: {{ top ? (top + 'rpx') : 0 }};"></view>

問題二:當(dāng) fliter-bar 高度為 84rpx,理論上緊貼的 fliter-bar 與 fliter-wrap 在部分設(shè)備上也不緊貼...

關(guān)于 setData

假如你想在 this.setData 的 key 中傳入變量砖第,下述寫法報(bào)錯(cuò):

triggerSelected (e) {
    var dataset = e.target.dataset;
    var id = dataset.id;
    var name = dataset.name;
    var type = dataset.type;
    this.setData({
        selected[type]: {
            id: id,
            name: name
        }
    });
}

且 this.setData 不支持模板字符串形式的 key撤卢,下述寫法也報(bào)錯(cuò):

triggerSelected (e) {
    var dataset = e.target.dataset;
    var id = dataset.id;
    var name = dataset.name;
    var type = dataset.type;
    this.setData({
        `selected.${type}`: {
            id: id,
            name: name
        }
    });
}

可將 selected 存入變量,直接操作 selected 變量后再 this.setData:

triggerSelected (e) {
    var dataset = e.target.dataset;
    var id = dataset.id;
    var name = dataset.name;
    var type = dataset.type;
    var selected = this.data.selected;
    selected[type] = {
        id: id,
        name: name
    };
    this.setData({
        selected: selected
    });
}

檢測 page data 內(nèi) selected 值與預(yù)期的一致梧兼,但當(dāng) selected 與視圖渲染相關(guān)時(shí)放吩,意想不到的情況發(fā)生了...假定我通過 selected 的某一屬性值控制元素 class:

<text
    class="{{ selected.order.id == item.id ? 'active' : '' }}"
    wx:for="{{ orders }}"
    data-id="{{ item.id }}"
    data-name="{{ item.name }}"
    data-type="order"
    bindtap="triggerSelected">{{ item.name }}</text>

當(dāng)元素被點(diǎn)擊時(shí)其 class 被賦值 active 使之呈現(xiàn)綠色:

而后我點(diǎn)擊了另一與之前被點(diǎn)擊元素 type 不同的元素,理論上不應(yīng)影響第一次被點(diǎn)擊元素的狀態(tài)(selected.type2 變化不影響 selected.type1)羽杰,然而:

active 仍在綠色卻不見了渡紫,這 bug 也是醉了,我不得不寫點(diǎn)爛代碼了(通過 switch case 一一處理):

triggerSelected (e) {
    var dataset = e.target.dataset;
    var id = dataset.id;
    var name = dataset.name;
    var type = dataset.type;
    var selected = this.data.selected;
    switch (type) {
        case 'area':
            this.setData({
                'selected.area': {
                    id: id,
                    name: name
                }
            });
            break;
        case 'tag':
            this.setData({
                'selected.tag': {
                    id: id,
                    name: name
                }
            });
            break;
        case 'order':
            this.setData({
                'selected.order': {
                    id: id,
                    name: name
                }
            });
            break;
    }
}

未完待續(xù)考赛,謝謝關(guān)注~


作者:呆戀小喵

相關(guān)文章:初嘗微信小程序(浪漫調(diào)酒師)

我的后花園:https://sunmengyuan.github.io/garden/

我的 github:https://github.com/sunmengyuan

原文鏈接:https://sunmengyuan.github.io/garden/2018/01/04/xcx-gm.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惕澎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子颜骤,更是在濱河造成了極大的恐慌唧喉,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忍抽,死亡現(xiàn)場離奇詭異八孝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)鸠项,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門干跛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人祟绊,你說我怎么就攤上這事驯鳖。” “怎么了久免?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵浅辙,是天一觀的道長。 經(jīng)常有香客問我阎姥,道長记舆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任呼巴,我火速辦了婚禮泽腮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘衣赶。我一直安慰自己诊赊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布府瞄。 她就那樣靜靜地躺著碧磅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鲸郊,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天丰榴,我揣著相機(jī)與錄音,去河邊找鬼秆撮。 笑死四濒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的职辨。 我是一名探鬼主播盗蟆,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼舒裤!你這毒婦竟也來了姆涩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤惭每,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后亏栈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體台腥,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年绒北,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了黎侈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡闷游,死狀恐怖峻汉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情脐往,我是刑警寧澤休吠,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站业簿,受9級(jí)特大地震影響瘤礁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜梅尤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一柜思、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巷燥,春花似錦赡盘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春霉咨,著一層夾襖步出監(jiān)牢的瞬間蛙紫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國打工途戒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坑傅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓喷斋,卻偏偏與公主長得像唁毒,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子星爪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353