前言
前兩天寫過一篇關(guān)于我對(duì)小程序的看法和對(duì)未來前端發(fā)展趨勢(shì),接觸小程序開發(fā)也有兩周左右了腿堤,對(duì)于一個(gè)之前從來沒有正經(jīng)寫過js代碼的我來說砂代,并沒有遇到多大的麻煩到忽,相對(duì)寫原生代碼和服務(wù)端代碼要簡(jiǎn)單很多吴菠,也更容易上手者填,今天這篇就介紹下小程序的基礎(chǔ)和如何動(dòng)手寫個(gè)Todolist,TodoList也是iOS和android官方入門教程的保留曲目橄务,通過TodoList的編寫基本可以初步認(rèn)識(shí)下小程序幔托。
微信為小程序提供了IDE工具,目前的版本更新到了V1.0.2蜂挪,雖然有時(shí)候還存在一些bug不過已經(jīng)相對(duì)比原來好用很多了重挑,具體就不介紹如何使用IDE了,做過開發(fā)的同學(xué)一看就明白棠涮,一個(gè)很簡(jiǎn)單的IDE工具谬哀。
下載地址:微信小程序IDE
小程序工程結(jié)構(gòu)
這是一個(gè)小程序工程的目錄,從目錄結(jié)構(gòu)我們可以看出下面3個(gè)app開頭的文件是整個(gè)工程的一些配置文件:
- app.js: 用于定義小程序的js公共邏輯严肪。
- app.json: 是對(duì)當(dāng)前小程序的公共全局配置史煎,包括了小程序的所有頁(yè)面路徑、界面表現(xiàn)驳糯、網(wǎng)絡(luò)超時(shí)時(shí)間篇梭、底部 tab 等。
- app.wxss: wxss用于定義小程序的公共樣式酝枢,類似于H5中的CSS恬偷,連基本語法也是一樣的,用于定義頁(yè)面樣式帘睦。
上面還有一些文件夾袍患,assets和images是分別用于存放一些大文件和圖片的,例如音頻竣付、本地文件之類的都可以存在里面诡延,文件夾名稱是可以隨意更改的,不像android這類資源文件夾是有個(gè)規(guī)范的約定古胆,不能隨便修改名稱肆良。
pages文件夾中存放的就是各種頁(yè)面模塊,這里建了兩個(gè)頁(yè)面模塊逸绎,一個(gè)是index首頁(yè)惹恃,一個(gè)是logs操作日志頁(yè),小程序中要求下方tab最少甚至2個(gè)桶良,最多設(shè)置5個(gè)座舍,在使用的時(shí)候要注意下。
每個(gè)頁(yè)面模塊一般由3類文件組成:
- xxx.js: 在js文件中主要是用來定義頁(yè)面變量和函數(shù)方法的陨帆,小程序的定義語法和vue.js這類MVVM框架非常像曲秉,可以直接參考最后一節(jié)的代碼樣例采蚀,一看就明白了。
- xxx.wxml:wxml類似h5中的html文件承二,用于描述頁(yè)面的布局結(jié)構(gòu)榆鼠,需要注意的是wxml的標(biāo)簽名稱和html的還是有很大不同的,需要熟悉下亥鸠,這些標(biāo)簽和關(guān)鍵字妆够。
- xxx.wxss:wxss類似于css文件,其中描述了頁(yè)面中需要用到的樣式负蚊。
數(shù)據(jù)綁定
數(shù)據(jù)綁定是整個(gè)小程序框架的核心神妹,其實(shí)就是客戶端用的比較多的MVVM框架,可以做到邏輯層的數(shù)據(jù)和視圖層的數(shù)據(jù)關(guān)聯(lián)變更家妆,這樣就可以方便的實(shí)現(xiàn)通過事件觸發(fā)邏輯層的數(shù)據(jù)變化鸵荠,從而體現(xiàn)到視圖層展示給用戶看到的數(shù)據(jù)變化。以前寫過.net伤极、vue.js蛹找,用過android、ios MVVM框架的同學(xué)是不是都很熟悉這樣的data binding模式哨坪。
//在js文件中定義好了數(shù)據(jù)message
Page({
data: {
message: 'Hello MINA!'
}
})
在wxml頁(yè)面布局文件中定義好了
//在頁(yè)面布局文件中引用js中定義的message庸疾,就可以在頁(yè)面展示邏輯層定義的數(shù)據(jù)了,同時(shí)邏輯層的message如果修改了当编,同時(shí)框架也會(huì)觸發(fā)頁(yè)面局部刷新
<view> {{ message }} </view>
基本語法
一門計(jì)算機(jī)語言的基本語法無外乎届慈,變量定義、數(shù)據(jù)類型凌箕、基礎(chǔ)運(yùn)算符拧篮、基礎(chǔ)語句词渤、基礎(chǔ)類庫(kù)這些牵舱,下面我們就簡(jiǎn)單介紹下這些,讓有經(jīng)驗(yàn)的開發(fā)人員一看就明白缺虐。
變量定義
在js中定義的變量均為值引用芜壁,沒有聲明的變量直接賦值使用會(huì)被定義為全局變量,變量定義和javascript一致高氮。
var num = 1;
var str = "hello world!";
var eof;
上面定義變量的方式和javascript是一模一樣的慧妄,var變量會(huì)根據(jù)賦值的類型將變量定義為相應(yīng)的數(shù)據(jù)類型。
數(shù)據(jù)類型
小程序支持的數(shù)據(jù)類型有:
Number : 數(shù)值
String :字符串
Boolean:布爾值
Object:對(duì)象
Function:函數(shù)
Array : 數(shù)組
Date:日期
Regexp:正則
這些類型和JavaScript中的類型也是一致的剪芍,所以對(duì)于有一定js基礎(chǔ)的同學(xué)塞淹,學(xué)小程序是非常簡(jiǎn)單的,這些數(shù)據(jù)類型對(duì)應(yīng)的方法這里就不介紹了罪裹。
基礎(chǔ)運(yùn)算符
小程序的基礎(chǔ)運(yùn)算符和常見語言的基礎(chǔ)運(yùn)算符使用方法一模一樣饱普,也是一元運(yùn)算符运挫、二元運(yùn)算符、三元運(yùn)算符套耕、比較運(yùn)算符谁帕、等值運(yùn)算符、賦值運(yùn)算符冯袍、運(yùn)算符優(yōu)先級(jí)也和js匈挖、C、Java這類的語言一樣康愤,所以基礎(chǔ)運(yùn)算符這里就不介紹了儡循,看看下面的實(shí)例代碼就會(huì)用了。
基礎(chǔ)語句
小程序中的基礎(chǔ)語句也和常用語言一樣征冷,就是if/else贮折、switch、for资盅、while這些调榄,用法也是和其他主流語言一致,一看就明白了呵扛。
if (表達(dá)式) {
代碼塊;
} else {
代碼塊;
}
switch (表達(dá)式) {
case 變量:
語句;
case 數(shù)字:
語句;
break;
case 字符串:
語句;
default:
語句;
}
for (語句; 語句; 語句) {
代碼塊;
}
while (表達(dá)式){
代碼塊;
}
基礎(chǔ)類庫(kù)
每個(gè)語言的基礎(chǔ)類庫(kù)一般多少都有一些差別每庆,小程序的基礎(chǔ)類庫(kù)和JavaScript的基礎(chǔ)類庫(kù)基本一致,熟悉ES5或者ES6標(biāo)準(zhǔn)的同學(xué)了解下就會(huì)使用了今穿。
類庫(kù)名稱 | 屬性 | 方法 |
---|---|---|
console |
console.log 方法用于在 console 窗口輸出信息缤灵。它可以接受多個(gè)參數(shù),將它們的結(jié)果連接起來輸出蓝晒。 |
|
Math |
E LN10 LN2 LOG2E LOG10E PI SQRT1_2 SQRT2
|
abs acos asin atan atan2 ceil cos exp floor log max min pow random round sin sqrt tan
|
JSON |
stringify(object) : 將 object 對(duì)象轉(zhuǎn)換為 JSON 字符串腮出,并返回該字符串。 parse(string) : 將 JSON 字符串轉(zhuǎn)化成對(duì)象芝薇,并返回該對(duì)象胚嘲。 |
|
Number |
MAX_VALUE MIN_VALUE NEGATIVE_INFINITY POSITIVE_INFINITY
|
|
Date |
parse UTC now
|
|
Global |
NaN Infinity undefined
|
parseInt parseFloat isNaN isFinite decodeURI decodeURIComponent encodeURI encodeURIComponent
|
常用組件
視圖容器
視圖容器很好理解,做過原生開發(fā)的同學(xué)可以理解為類似android中的各種layout洛二,所有的視圖組件都要在一個(gè)容器中馋劈。
組件名 | 說明 |
---|---|
view | 視圖容器 |
scroll-view | 可滾動(dòng)視圖容器 |
swiper | 滑塊視圖容器 |
基礎(chǔ)內(nèi)容
基礎(chǔ)內(nèi)容組件沒啥好說的,就是常見的文本框晾嘶、進(jìn)度條妓雾,這些在原生開發(fā)中可能被官方定義為基礎(chǔ)視圖view組件,而微信官方將這些定義為了基礎(chǔ)內(nèi)容組件垒迂。
組件名 | 說明 |
---|---|
icon | 圖標(biāo) |
text | 文字 |
progress | 進(jìn)度條 |
表單
表單組件沿用了js中的叫法械姻,在原生開發(fā)中這些按鈕、輸入框這些都叫做基礎(chǔ)視圖組件机断,小程序中的常用視圖組件比android和ios中的少了很多楷拳,但是這些基本能夠滿足日常開發(fā)需求了材部。
標(biāo)簽名 | 說明 |
---|---|
button | 按鈕 |
form | 表單 |
input | 輸入框 |
checkbox | 多項(xiàng)選擇器 |
radio | 單項(xiàng)選擇器 |
picker | 列表選擇器 |
picker-view | 內(nèi)嵌列表選擇器 |
slider | 滾動(dòng)選擇器 |
switch | 開關(guān)選擇器 |
label | 標(biāo)簽 |
導(dǎo)航
小程序中的頁(yè)面路由都是通過框架進(jìn)行跳轉(zhuǎn),其中頁(yè)面都是通過runtime的頁(yè)面棧進(jìn)行管理唯竹,具體的頁(yè)面棧管理可以查看官方對(duì)頁(yè)面棧的說明乐导,這塊需要好好學(xué)習(xí)下,和原生開發(fā)中的棧管理還是存在一些區(qū)別的浸颓,導(dǎo)航組件跳轉(zhuǎn)方式的不同會(huì)導(dǎo)致頁(yè)面對(duì)棧的操作不同物臂,同時(shí)也會(huì)調(diào)用頁(yè)面不同的生命周期回調(diào)。
組件名 | 說明 |
---|---|
navigator | 應(yīng)用鏈接 |
多媒體
多媒體組件就比較好理解了产上,就是常見的音頻棵磷、視頻、圖片展示晋涣,小程序已經(jīng)將基礎(chǔ)功能封裝好了仪媒,使用起來非常方便。
組件名 | 說明 |
---|---|
audio | 音頻 |
image | 圖片 |
video | 視頻 |
地圖
小程序目前也提供了地圖組件谢鹊,小程序的地圖組件使用起來個(gè)人感覺比百度和高德的sdk要簡(jiǎn)單算吩。
組件名 | 說明 |
---|---|
map | 地圖 |
畫布
畫布組件個(gè)人一直沒有用過,這里就不多介紹了佃扼,感興趣的朋友可以自己查看官方文檔偎巢。
組件名 | 說明 |
---|---|
canvas | 畫布 |
常用API
大家都知道微信小程序最牛逼的地方在于有微信巨大的用戶流量和微信原生API的支持,下面就介紹下常用的微信原生API兼耀,通過這些原生API可以實(shí)現(xiàn)對(duì)原生操作系統(tǒng)一些API的操作和微信app自身功能的調(diào)用压昼。
目前小程序開放的API能力已經(jīng)非常豐富了,手機(jī)硬件基本都可以操作了瘤运,小程序現(xiàn)階段提供的API包括:
- 網(wǎng)絡(luò)請(qǐng)求窍霞、文件上傳、下載拯坟、WebSocket相關(guān)操作
- 本地圖片操作但金、錄音操作、音頻操作似谁、視頻操作
- 本地文件操作
- 本地緩存操作
- 地理位置操作傲绣、地圖操作
- 系統(tǒng)基本信息查詢
- 加速器掠哥、羅盤巩踏、電話、藍(lán)牙续搀、NFC塞琼、WiFi、iBeacon等操作
- 微信開放平臺(tái)登錄禁舷、授權(quán)彪杉、支付毅往、轉(zhuǎn)發(fā)、二維碼派近、卡券攀唯、APP跳轉(zhuǎn)、小程序跳轉(zhuǎn)等相關(guān)接口
微信為小程序具體開放的接口細(xì)節(jié)請(qǐng)參考官方文檔 https://developers.weixin.qq.com/miniprogram/dev/api/
TodoList
app.json
{
//定義所有頁(yè)面的相對(duì)路徑渴丸,框架會(huì)自動(dòng)去相應(yīng)目錄眾找到目錄同名的wxss侯嘀、wxml、wxs谱轨、js戒幔、json的相關(guān)文件
"pages":[
"pages/index/index",
"pages/logs/logs"
],
//定義小程序窗口的字體、背景色土童、titlebar內(nèi)容等信息
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "TODOS",
"navigationBarTextStyle":"black"
},
//定義下方tabbar
"tabBar": {
"color": "#999",
"selectedColor": "#222",
"backgroundColor": "#f8f9fb",
"borderStyle": "white",
//通過list接收tab數(shù)組诗茎,每個(gè)數(shù)組中需要定義頁(yè)面目錄相對(duì)路徑、文字献汗、未選中圖標(biāo)敢订、選中圖標(biāo)
"list": [
{
"pagePath": "pages/index/index",
"text": "todos",
"iconPath": "assets/todos.png",
"selectedIconPath": "assets/todos-active.png"
},
{
"pagePath": "pages/logs/logs",
"text": "logs",
"iconPath": "assets/logs.png",
"selectedIconPath": "assets/logs-active.png"
}
]
}
}
app.wxss
.container {
padding: 30rpx;
border-top: 1rpx solid #e5e5e5;
}
index.wxml
<view class="container">
<view class="header">
<image class="plus" src="../../assets/plus.png" bindtap='addTodoHandle'/>
<input class="new-todo" value="{{ input }}" placeholder="Anything here..." auto-focus bindinput="inputChangeHandle" bindconfirm="addTodoHandle"/>
</view>
<!-- 根據(jù)任務(wù)數(shù)判斷展示哪段代碼塊 -->
<block wx:if="{{ todos.length }}">
<view class="todos">
<!--列表展示所有任務(wù) -->
<view class="item{{ item.completed ? ' completed' : '' }}" wx:for="{{ todos }}" wx:key="{{ index }}" bindtap="toggleTodoHandle" data-index="{{ index }}">
<!-- 任務(wù)狀態(tài)根據(jù)item項(xiàng)是否completed: success, todo: circle,根據(jù)狀態(tài)定義icon type -->
<icon class="checkbox" type="{{ item.completed ? 'success' : 'circle' }}"/>
<text class="name">{{ item.name }}</text>
<icon class="remove" type="clear" size="16" catchtap="removeTodoHandle" data-index="{{ index }}"/>
</view>
</view>
<view class="footer">
<text class="btn" bindtap="toggleAllHandle">Toggle all</text>
<text wx:if="{{ leftCount }}">{{ leftCount }} {{ leftCount === 1 ? 'item' : 'items' }} left</text>
<text class="btn" wx:if="{{ todos.length > leftCount }}" bindtap="clearCompletedHandle">Clear completed</text>
</view>
</block>
<block wx:else>
<view class="empty">
<text class="title">Congratulations!</text>
<text class="content">There's no more work left.</text>
</view>
</block>
</view>
index.wxss
.header {
display: flex;
align-items: center;
border: 1rpx solid #e0e0e0;
border-radius: 10rpx;
box-shadow: 0 0 5rpx #e0e0e0;
margin-bottom: 30rpx;
padding: 20rpx;
}
.header .plus {
width: 41rpx;
height: 41rpx;
margin-right: 20rpx;
}
.header .new-todo {
flex: 1;
font-size: 28rpx;
}
.todos {
border: 1rpx solid #e0e0e0;
border-radius: 10rpx;
box-shadow: 0 0 5rpx #e0e0e0;
}
.todos .item {
display: flex;
align-items: center;
padding: 25rpx;
border-bottom: 1rpx solid #e0e0e0;
}
.todos .item:last-child {
border-bottom: 0;
}
.todos .item .checkbox {
margin-right: 20rpx;
}
.todos .item .name {
flex: 1;
font-size: 30rpx;
color: #444;
}
.todos .item.completed .name {
text-decoration: line-through;
color: #888;
}
/*
.todos .item .remove {
cursor: pointer;
}
*/
.footer {
display: flex;
justify-content: space-between;
margin: 30rpx 0;
padding: 0 10rpx;
font-size: 26rpx;
color: #777;
}
/*
.footer .btn {
cursor: pointer;
}
*/
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.empty .title {
font-size: 60rpx;
margin: 200rpx 50rpx 50rpx;
color: #444;
}
.empty .content {
color: #666;
text-align: center;
}
index.js
Page({
//定義頁(yè)面變量
data: {
input: '',
todos: [],
leftCount: 0,
allCompleted: false,
logs: []
},
//保存方法罢吃,將todo-list和todo-logs保存在小程序本地枢析,通過調(diào)用小程序開放api wx.setStorageSync()
save: function () {
wx.setStorageSync('todo_list', this.data.todos)
wx.setStorageSync('todo_logs', this.data.logs)
},
//加載本地緩存中的todo-list
load: function () {
var todos = wx.getStorageSync('todo_list')
if (todos) {
var leftCount = todos.filter(function (item) {
return !item.completed
}).length
this.setData({ todos: todos, leftCount: leftCount })
}
var logs = wx.getStorageSync('todo_logs')
if (logs) {
this.setData({ logs: logs })
}
},
onLoad: function () {
this.load()
},
inputChangeHandle: function (e) {
this.setData({ input: e.detail.value })
},
//增加任務(wù)
addTodoHandle: function (e) {
if (!this.data.input || !this.data.input.trim()) return
var todos = this.data.todos
//將任務(wù)數(shù)據(jù)push到map中
todos.push({ name: this.data.input, completed: false })
var logs = this.data.logs
logs.push({ timestamp: new Date(), action: 'Add', name: this.data.input })
this.setData({
input: '',
todos: todos,
leftCount: this.data.leftCount + 1,
logs: logs
})
this.save()
},
//完成任務(wù)
toggleTodoHandle: function (e) {
var index = e.currentTarget.dataset.index
var todos = this.data.todos
todos[index].completed = !todos[index].completed
var logs = this.data.logs
logs.push({
timestamp: new Date(),
action: todos[index].completed ? 'Finish' : 'Restart',
name: todos[index].name
})
this.setData({
todos: todos,
leftCount: this.data.leftCount + (todos[index].completed ? -1 : 1),
logs: logs
})
this.save()
},
//移除所有任務(wù)
removeTodoHandle: function (e) {
var index = e.currentTarget.dataset.index
var todos = this.data.todos
var remove = todos.splice(index, 1)[0]
var logs = this.data.logs
logs.push({ timestamp: new Date(), action: 'Remove', name: remove.name })
this.setData({
todos: todos,
leftCount: this.data.leftCount - (remove.completed ? 0 : 1),
logs: logs
})
this.save()
},
toggleAllHandle: function (e) {
this.data.allCompleted = !this.data.allCompleted
var todos = this.data.todos
for (var i = todos.length - 1; i >= 0; i--) {
todos[i].completed = this.data.allCompleted
}
var logs = this.data.logs
logs.push({
timestamp: new Date(),
action: this.data.allCompleted ? 'Finish' : 'Restart',
name: 'All todos'
})
this.setData({
todos: todos,
leftCount: this.data.allCompleted ? 0 : todos.length,
logs: logs
})
this.save()
},
clearCompletedHandle: function (e) {
var todos = this.data.todos
var remains = []
for (var i = 0; i < todos.length; i++) {
todos[i].completed || remains.push(todos[i])
}
var logs = this.data.logs
logs.push({
timestamp: new Date(),
action: 'Clear',
name: 'Completed todo'
})
this.setData({ todos: remains, logs: logs })
this.save()
}
})
源碼git地址歡迎star:https://github.com/feiweiwei/mktodo.git
總結(jié)
TodoList這個(gè)例子其實(shí)很簡(jiǎn)單,大家看著注釋就能知道什么意思了刃麸。這篇文章其實(shí)主要目的還是讓大家小程序入門醒叁,對(duì)于有一定其他語言開發(fā)經(jīng)驗(yàn)的同學(xué)應(yīng)該看這一篇基本就入門了,后續(xù)會(huì)再補(bǔ)充幾篇小程序開發(fā)進(jìn)階的文章泊业,會(huì)涉及到一些小程序網(wǎng)絡(luò)請(qǐng)求把沼、頁(yè)面生命周期、微信API的使用吁伺、runtime的一些內(nèi)容饮睬,敬請(qǐng)期待。