小程式程式碼構成

  • JSON 配置
  • WXML 模版
  • WXSS 樣式
  • JS 邏輯互動

JSON 配置

在小程式中,JSON扮演的靜態配置的角色。

小程式配置 app.json

{
    "pages": ["pages/index/index", "pages/logs/logs"],
    "window": {
        "backgroundTextStyle": "light",
        "navigationBarBackgroundColor": "#fff",
        "navigationBarTitleText": "WeChat",
        "navigationBarTextStyle": "black"
    }
}
  • pages欄位 —— 用於描述當前小程式所有頁面路徑,這是為了讓微信客戶端知道當前你的小程式頁面定義在哪個目錄。
  • window欄位 —— 定義小程式所有頁面的頂部背景顏色,文字顏色定義等。

全域性配置

https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html

tabBar

如果小程式是一個多 tab 應用(客戶端視窗的底部或頂部有 tab 欄可以切換頁面),可以通過 tabBar 配置項指定 tab 欄的表現,以及 tab 切換時顯示的對應頁面。

其中 list 接受一個數組,只能配置最少 2 個、最多 5 個 tab。tab 按陣列的順序排序,每個項都是一個物件

{
    "pages": ["pages/index/index", "pages/logs/index"],
    "tabBar": {
        "list": [
            {
                "iconPath": "assets/fonts/首頁.png",
                "selectedIconPath": "assets/fonts/home.png",
                "pagePath": "pages/index/index",
                "text": "首頁"
            },
            {
                "pagePath": "pages/logs/logs",
                "text": "日誌"
            }
        ]
    }
}

tabBar 還有其他屬性

顏色僅支援十六進位制,定位僅支援top和bottom,其中top時不支援圖示。

{
    "tabBar": {
        "color": "#ff00ff",
        "selectedColor": "#0000ff",
        "backgroundColor": "#00ff00",
        "position":"bottom"  
    }
}

頁面配置

每一個小程式頁面也可以使用 .json 檔案來對本頁面的視窗表現進行配置。頁面中配置項在當前頁面會覆蓋 app.json 的 window 中相同的配置項

頁面配置中只能設定 app.json 中 window 對應的配置項,以決定本頁面的視窗表現,所以無需寫 window 這個屬性。

WXML 模版

WXML 充當的就是類似 HTML 的角色。

<view class="container">
    <view class="userinfo">
        <button wx:if="{{!hasUserInfo && canIUse}}">獲取頭像暱稱</button>
        <block wx:else>
            <image src="{{userInfo.avatarUrl}}" background-size="cover"></image>
            <text class="userinfo-nickname">{{userInfo.nickName}}</text>
        </block>
    </view>
    
    <view class="usermotto">
        <text class="user-motto">{{motto}}</text>
    </view>
</view>

block標籤的作用是直接解析裡面的內容,不解析自身block標籤。

WXSS 樣式

WXSS 具有 CSS 大部分的特性,小程式在 WXSS 也做了一些擴充和修改。

  • 新增了尺寸單位。在寫 CSS 樣式時,開發者需要考慮到手機裝置的螢幕會有不同的寬度和裝置畫素比,採用一些技巧來換算一些畫素單位。WXSS 在底層支援新的尺寸單位 rpx ,開發者可以免去換算的煩惱,只要交給小程式底層來換算即可,由於換算採用的浮點數運算,所以運算結果會和預期結果有一點點偏差。

  • 提供了全域性的樣式和區域性樣式。和前邊 app.json, page.json 的概念相同,你可以寫一個 app.wxss 作為全域性樣式,會作用於當前小程式的所有頁面,區域性頁面樣式 page.wxss 僅對當前頁面生效。

  • 此外 WXSS 僅支援部分 CSS 選擇器

https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html

==iPhone6 1rpx = 0.5px 1px = 2rpx==

JS 邏輯互動

<view>{{ msg }}</view>
<button bindtap="clickMe">點選我</button>
Page({
    data:{
        msg: '小程式'
    },
    clickMe() {
        this.setData({msg: 'Hello World'})
    }
})

更詳細的資料:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html

小程式宿主環境

  • 渲染層和邏輯層
  • 程式與頁面
  • 元件
  • API

渲染層和邏輯層

小程式的執行環境分成渲染層和邏輯層,其中 WXML 模板和 WXSS 樣式工作在渲染層,JS 指令碼工作在邏輯層。

小程式的渲染層和邏輯層分別由2個執行緒管理:渲染層的介面使用了WebView 進行渲染;邏輯層採用JsCore執行緒執行JS指令碼。一個小程式存在多個介面,所以渲染層存在多個WebView執行緒,這兩個執行緒的通訊會經由微信客戶端(Native)做中轉,邏輯層傳送網路請求也經由Native轉發。

程式與頁面

微信客戶端在開啟小程式之前,會把整個小程式的程式碼包下載到本地。

緊接著通過 app.json 的 pages 欄位就可以知道你當前小程式的所有頁面路徑。

整個小程式只有一個 App 例項,是全部頁面共享的,具體內容檢視註冊程式 App https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/app.html

元件

小程式提供了豐富的元件

https://developers.weixin.qq.com/miniprogram/dev/component/

<navigator
    url="/page/navigate/navigate?title=navigate"
    hover-class="navigator-hover"
>
    跳轉到新頁面
</navigator>

<navigator
    url="../../redirect/redirect/redirect?title=redirect"
    open-type="redirect"
    hover-class="other-navigator-hover"
>
    在當前頁開啟
</navigator>

<navigator
    url="/page/index/index"
    open-type="switchTab"
    hover-class="other-navigator-hover"
>
    切換 Tab
</navigator>

API

為了讓開發者可以很方便的調起微信提供的能力,例如獲取使用者資訊、微信支付等等,小程式提供了很多 API 給開發者去使用。

wx.scanCode({
    success: (res) => {
        console.log(res)
    }
})

https://developers.weixin.qq.com/miniprogram/dev/api/index.html

檢視層

  • WXML
  • WXS
  • 事件系統
  • 動畫
  • 元件

WXML

WXML(WeiXin Markup Language)是框架設計的一套標籤語言,結合基礎元件、事件系統,可以構建出頁面的結構。

資料繫結

<!--wxml-->
<view>{{message}}</view>
// page.js
Page({
    data: {
        message: 'Hello MINA!'
    }
})

https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/data.html

列表渲染

預設陣列的當前項的下標變數名預設為 index,陣列當前項的變數名預設為 item

<!--wxml-->
<view wx:for="{{array}}">{{item}}</view>
// page.js
Page({
    data: {
        array: [1, 2, 3, 4, 5]
    }
})

https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/list.html

如不提供 wx:key,會報一個 warning

<switch wx:for="{{objectArray}}" wx:key="unique" style="display: block;">
    {{item.id}}
</switch>
Page({
    data: {
        objectArray: [
            {id: 5, unique: 'unique_5'},
            {id: 4, unique: 'unique_4'},
            {id: 3, unique: 'unique_3'},
            {id: 2, unique: 'unique_2'},
            {id: 1, unique: 'unique_1'},
            {id: 0, unique: 'unique_0'}
        ]
    }
}) 

條件渲染

<!--wxml-->
<view wx:if="{{view == 'WEBVIEW'}}">WEBVIEW</view>
<view wx:elif="{{view == 'APP'}}">APP</view>
<view wx:else="{{view == 'MINA'}}">MINA</view>
// page.js
Page({
    data: {
        view: 'MINA'
    }
})

https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/conditional.html

wx:if vs hidden

wx:if 是惰性的,如果在初始渲染條件為 false,框架什麼也不做,在條件第一次變成真的時候才開始區域性渲染。

相比之下,hidden 就簡單的多,元件始終會被渲染,只是簡單的控制顯示與隱藏。

一般來說,wx:if 有更高的切換消耗而 hidden 有更高的初始渲染消耗。因此,如果需要頻繁切換的情景下,用 hidden 更好,如果在執行時條件不大可能改變則 wx:if 較好。

<view hidden="{{hidden}}">
    內容
</view>

模板

<!--wxml-->
<template name="staffName">
    <view>
        FirstName: {{firstName}}, LastName: {{lastName}}
    </view>
</template>

<template is="staffName" data="{{...staffA}}"></template>
<template is="staffName" data="{{...staffB}}"></template>
<template is="staffName" data="{{...staffC}}"></template>
// page.js
Page({
    data: {
        staffA: {firstName: 'Hulk', lastName: 'Hu'},
        staffB: {firstName: 'Shang', lastName: 'You'},
        staffC: {firstName: 'Gideon', lastName: 'Lin'}
    }
})

https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/template.html

引用

WXML 提供兩種檔案引用方式import和include。

https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/import.html

<!-- item.wxml -->
<template name="item">
    <text>{{text}}</text>
</template>

在 index.wxml 中引用了 item.wxml,就可以使用item模板:

<import src="item.wxml" />
<template is="item" data="{{text: 'forbar'}}" />

WXS

WXS(WeiXin Script)是小程式的一套指令碼語言,結合 WXML,可以構建出頁面的結構。

https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxs/

頁面渲染

<!--wxml-->
<wxs module="m1">
    var msg = "hello world"; module.exports.message = msg;
</wxs>

<view>{{m1.message}}</view>

頁面輸出:

hello world

事件系統

https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html

什麼是事件

  • 事件是檢視層到邏輯層的通訊方式。
  • 事件可以將使用者的行為反饋到邏輯層進行處理。
  • 事件可以繫結在元件上,當達到觸發事件,就會執行邏輯層中對應的事件處理函式。
  • 事件物件可以攜帶額外資訊,如 id, dataset, touches。

事件的使用方式

<view id="tapTest" data-hi="WeChat" bindtap="tapName">Click me!</view>
Page({
    tapName(event) {
        console.log(event)
    }
})

事件繫結和冒泡

事件繫結的寫法同組件的屬性,以 key、value 的形式。

  • key 以bind或catch開頭,然後跟上事件的型別,如bindtap、catchtouchstart。
  • value 是一個字串,需要在對應的 Page 中定義同名的函式。不然當觸發事件的時候會報錯。

bind事件繫結不會阻止冒泡事件向上冒泡,catch事件繫結可以阻止冒泡事件向上冒泡。

如在下邊這個例子中,點選 inner view 會先後呼叫handleTap3和handleTap2(因為tap事件會冒泡到 middle view,而 middle view 阻止了 tap 事件冒泡,不再向父節點傳遞),點選 middle view 會觸發handleTap2,點選 outer view 會觸發handleTap1。

<view id="outer" bindtap="handleTap1">
    outer view
    <view id="middle" catchtap="handleTap2">
        middle view
        <view id="inner" bindtap="handleTap3">
            inner view
        </view>
    </view>
</view>

事件的捕獲階段

需要在捕獲階段監聽事件時,可以採用capture-bind、capture-catch關鍵字,後者將中斷捕獲階段和取消冒泡階段。

在下面的程式碼中,點選 inner view 會先後呼叫handleTap2、handleTap4、handleTap3、handleTap1。

<view
    id="outer"
    bind:touchstart="handleTap1"
    capture-bind:touchstart="handleTap2"
>
    outer view
    <view
        id="inner"
        bind:touchstart="handleTap3"
        capture-bind:touchstart="handleTap4"
    >
        inner view
    </view>
</view>

動畫

方案1:寫css樣式

#box2{
    height: 100px;
    width: 100px;
    background: blue;
}

#box2:hover{
    transform: rotate(180deg) scale(.5, .5);
    background: red;
    transition: background 2s ease, transform 2s ease-in 1s;
}

方案2:animate.css 庫

// app.wxss 在主頁面引入,page頁面就都可以使用了
@import './assets/animate.wxss';
<view class='{{a}}'></view>
this.setData({
    a : ['box', 'animated', 'fadeOutRight']
})

方案3:寫js函式

https://developers.weixin.qq.com/miniprogram/dev/api/wx.createAnimation.html

<button bindtap="fn">動畫</button>

<view
    animation="{{animationData}}"
    style="background:red;height:100rpx;width:100rpx;position: absolute;top:1000rpx;"
></view>
data: {
    animationData: {}
},
fn() {
    const animation = wx.createAnimation({
        duration: 3000
    })
    
    this.animation = animation
    
    animation.scale(2, 2).rotate(45).step().width(400).top(10).step()
    
    this.setData({
        animationData: animation.export()
    })
}

step方法表示動畫完成。一組動畫中的所有動畫會同時開始,一組動畫完成後才會進行下一組動畫。

匯出動畫佇列。export 方法每次呼叫後會清掉之前的動畫操作。

元件

定義元件

滑鼠右鍵->新建元件,會生成一組檔案。

// components/toast/toast.js
Component({
    //元件的屬性列表
    properties: {
        str : String
    },
    
    //元件的生命週期函式
    lifetimes: {
        attached() {
            // 在元件例項進入頁面節點樹時執行
            console.log('attached:', this.properties.str);
        },
        detached() {
            // 在元件例項被從頁面節點樹移除時執行
            console.log('刪除')
        }
    },
    
    // 元件的初始資料
    data: {
    
    },
    
    // 元件的方法列表
    methods: {
        show(){
        }
    }
})
// components/toast/toast.wxml
<view>
    傳過來的屬性:{{str}}
</view>

使用元件

// pages/mine/mine.json
{
    "usingComponents": {
        "toast":"/components/toast/toast"
    }
}
// pages/mine/mine.wxml
<toast id="abc" str="hello"></toast>
// pages/mine/mine.js
Page({
    data: {
    },
    fn(){
        this.toast = this.selectComponent("#abc");
        this.toast.show();
    }
})

邏輯層

  • 生命週期
  • 路由
  • 模組化

生命週期

page頁的生命週期:https://developers.weixin.qq.com/miniprogram/dev/guide/framework/page-life-cycle.html

page頁生命週期鉤子函式的示例程式碼:https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html

component頁的生命週期:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/lifetimes.html

元件的生命週期,指的是元件自身的一些函式,這些函式在特殊的時間點或遇到一些特殊的框架事件時被自動觸發。

其中,最重要的生命週期是 created attached detached ,包含一個元件例項生命流程的最主要時間點。

  • 元件例項剛剛被建立好時, created 生命週期被觸發。此時,元件資料 this.data 就是在 Component 構造器中定義的資料 data 。 此時還不能呼叫 setData 。 通常情況下,這個生命週期只應該用於給元件 this 新增一些自定義屬性欄位。
  • 在元件完全初始化完畢、進入頁面節點樹後, attached 生命週期被觸發。此時, this.data 已被初始化為元件的當前值。這個生命週期很有用,絕大多數初始化工作可以在這個時機進行。
  • 在元件離開頁面節點樹後, detached 生命週期被觸發。退出一個頁面時,如果元件還在頁面節點樹中,則 detached 會被觸發。

==自小程式基礎庫版本 2.2.3 起,元件的的生命週期也可以在 lifetimes 欄位內進行宣告(這是推薦的方式,其優先順序最高)。==

Component({
    lifetimes: {
        attached() {
            // 在元件例項進入頁面節點樹時執行
        },
        detached() {
            // 在元件例項被從頁面節點樹移除時執行
        },
    },
    // 以下是舊式的定義方式,可以保持對 <2.2.3 版本基礎庫的相容
    attached() {
        // 在元件例項進入頁面節點樹時執行
    },
    detached() {
        // 在元件例項被從頁面節點樹移除時執行
    },
    // ...
})

頁面路由

在小程式中所有頁面的路由全部由框架進行管理。

頁面棧

https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/route.html

開啟新頁面

呼叫 API wx.navigateTo

保留當前頁面,跳轉到應用內的某個頁面。但是不能跳到 tabbar 頁面。

wx.navigateTo({
    url: 'test?id=1'
})

// test.js
Page({
    onLoad(option) {
        console.log(option.query)
    }
})

使用元件

<navigator open-type="navigateTo"/>

頁面重定向

呼叫 API wx.redirectTo

關閉當前頁面,跳轉到應用內的某個頁面。但是不允許跳轉到 tabbar 頁面。

wx.redirectTo({
    url: 'test?id=1'
})

使用元件

<navigator open-type="redirectTo"/>

頁面返回

呼叫 API wx.navigateBack

關閉當前頁面,返回上一頁面或多級頁面。

// 注意:呼叫 navigateTo 跳轉時,呼叫該方法的頁面會被加入堆疊,而 redirectTo 方法則不會。見下方示例程式碼

// 此處是A頁面
wx.navigateTo({
    url: 'B?id=1'
})

// 此處是B頁面
wx.navigateTo({
    url: 'C?id=1'
})

// 在C頁面內 navigateBack,將返回A頁面
wx.navigateBack({
    delta: 2
})

使用元件

<navigator open-type="navigateBack">

使用者按左上角返回按鈕

Tab 切換

呼叫 API wx.switchTab

跳轉到 tabBar 頁面,並關閉其他所有非 tabBar 頁面

{
    "tabBar": {
        "list": [
            {
                "pagePath": "index",
                "text": "首頁"
            },
            {
                "pagePath": "other",
                "text": "其他"
            }
        ]
    }
}
wx.switchTab({
    url: '/index'
})

使用元件

<navigator open-type="switchTab"/> 

使用者切換 Tab

重啟動

呼叫 API wx.reLaunch

關閉所有頁面,開啟到應用內的某個頁面

wx.reLaunch({
    url: 'test?id=1'
})

使用元件

<navigator open-type="reLaunch"/>

模組化

CommonJS規範

可以將一些公共的程式碼抽離成為一個單獨的 js 檔案,作為一個模組。模組通過 module.exports 能對外暴露介面。

// common.js
function sayHello(name) {
    console.log(`Hello ${name} !`)
}

module.exports.sayHello = sayHello
const common = require('common.js')
Page({
    helloMINA() {
        common.sayHello('MINA')
    }
})

檔案作用域

在 JavaScript 檔案中宣告的變數和函式只在該檔案中有效;不同的檔案中可以宣告相同名字的變數和函式,不會互相影響。

通過全域性函式 getApp 可以獲取全域性的應用例項,如果需要全域性的資料可以在 App 中設定,如:

// app.js
App({
    globalData: 1
})
// a.js
// Get the app instance.
const app = getApp()
// Get the global data and change it.
app.globalData++
// b.js
// 如果是從a跳轉到b,那麼這次輸出的是2
console.log(getApp().globalData)

能力

  • 網路
  • 儲存

網路

伺服器域名配置

每個微信小程式需要事先設定一個通訊域名,小程式只可以跟指定的域名與進行網路通訊。包括普通 HTTPS 請求(wx.request)、上傳檔案(wx.uploadFile)、下載檔案(wx.downloadFile) 和 WebSocket 通訊(wx.connectSocket)

==跳過域名校驗==

在微信開發者工具中,可以臨時開啟 開發環境不校驗請求域名、TLS版本及HTTPS證書 選項,跳過伺服器域名的校驗。此時,在微信開發者工具中及手機開啟除錯模式時,不會進行伺服器域名的校驗。

wx.request({
    url: 'test.php', // 僅為示例,並非真實的介面地址
    data: {
        x: '',
        y: ''
    },
    header: {
        'content-type': 'application/json' // 預設值
    },
    success(res) {
        console.log(res.data)
    }
})

https://developers.weixin.qq.com/miniprogram/dev/api/wx.request.html

儲存

每個微信小程式都可以有自己的本地快取

設定

==單個 key 允許儲存的最大資料長度為 1MB,所有資料儲存上限為 10MB。==

wx.setStorage({
    key: 'key',
    data: 'value'
})
try {
    wx.setStorageSync('key', 'value')
} catch (e) { }

獲取

wx.getStorage({
    key: 'key',
    success(res) {
        console.log(res.data)
    }
})
const value = wx.getStorageSync('key')

清理本地所有資料

wx.clearStorage({
    success(){}    
})
wx.clearStorageSync()

刪除指定的key

wx.removeStorage({
    key: 'key',
    success(res) {
        console.log(res.data)
    }
})
wx.removeStorageSync('key')

介面互動

顯示訊息提示框

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

顯示模態對話方塊

wx.showModal({
    title: '提示',
    content: '這是一個模態彈窗',
    success(res) {
        if (res.confirm) {
            console.log('使用者點選確定')
        } else if (res.cancel) {
            console.log('使用者點選取消')
        }
    }
})

loading 提示框

顯示 loading 提示框。需主動呼叫 wx.hideLoading 才能關閉提示框

wx.showLoading({
    title: '載入中',
})

setTimeout(function () {
    wx.hideLoading()
}, 2000)

顯示操作選單

wx.showActionSheet({
    itemList: ['A', 'B', 'C'],
    success(res) {
        console.log(res.tapIndex)
    },
    fail(res) {
        console.log(res.errMsg)
    }
})

結束

微信小程式當然還提供其他能力,具體應檢視官方文件