網(wǎng)上已經(jīng)有很多前端開發(fā)者分享了小程序的入坑心得, 本篇文章不刻意重復(fù)造輪子, 面向有過Vue.js
開發(fā)經(jīng)驗正準備接觸微信小程序開發(fā)的讀者.
從框架入手
早些版本的原生小程序基礎(chǔ)庫存在組件化開發(fā)困難
和npm模塊無法使用
等問題, 騰訊團隊開源了wepy解決了老版原生小程序開發(fā)的短板. 后來支付寶,今日頭條等app相繼推出自己的小程序, 于是mpvue跨小程序端框架出現(xiàn); 再到后來出現(xiàn)了多端開發(fā)框架uni-app, taro等.
使用框架開發(fā)小程序的開發(fā)者可以按自己的需求和習(xí)慣選擇框架:
- Vue系開發(fā)者, 不需要跨端:
wepy
- Vue系需跨端的開發(fā)者:
uni-app
- React系需跨端的開發(fā)者:
taro
如果你是在糾結(jié)選擇哪個框架, 筆者推薦uni-app
, 有幾點理由:
主要注意的是, 無論使用哪個框架(特別是多端開發(fā)框架), 踩坑次數(shù)都不會少; 另外小程序基礎(chǔ)庫也在不斷完善, 目前也支持了組件化開發(fā)和npm
模塊, 喜歡清爽簡潔
方式開發(fā)的不妨用原生小程序進行開發(fā).
從原生小程序入手
所謂原生小程序開發(fā), 就是不使用上述對小程序基礎(chǔ)庫和語法二次封裝和構(gòu)建的框架進行小程序開發(fā)的方式.
為了寫清楚小程序的語法和能力及其生態(tài)規(guī)范, 說實話官方文檔已經(jīng)做得不錯了, 但同時學(xué)習(xí)成本也不低. 閱讀此文檔時, 推薦開發(fā)者先快速通讀
一兩遍除服務(wù)端相關(guān)模塊(服務(wù)端
,云開發(fā)
)外的部分,并閱讀以下筆者針對vue開發(fā)者給一些"捷徑", 快速熟悉小程序開發(fā).然后再到編碼時邊寫變查
, 逐步熟悉.
項目搭建
新建項目
你需要脫離vue-cli
等腳手架工具, 下載微信小程序的官方IDE,然后新建一個小程序項目哟楷,其中AppID
是在微信公眾平臺創(chuàng)建小程序應(yīng)用時獲取姻乓,如果沒有客店點擊使用測試號
箕般,足夠開發(fā)階段使用(正式appid才可以正式發(fā)布小程序)却舀。
新建成功后就有有了一個可運行的demo
崭添,這里介紹一下最簡單的小程序需要哪些目錄和文件:
pages
- 存放頁面的目錄伶氢,每個頁面自成一個文件夾俊嗽,需包含同名的js
,wxss
,json
,wxml
文件, 下一小節(jié)詳細介紹
utils
- 非必需目錄翔忽,存放工具方法,公共方法等
app.js
- 入口文件,類似vue.js應(yīng)用中實例化Vue對象的js(一般命名為app.js
或者main.js
)
app.wxss
- 公共樣式表(wxss你可以看做css語法子集)睹簇,這里所寫樣式都是全局的牙捉,你無需在任何文件引入
project.config.json
- IDE的配置目錄,無需手動配置
sitemap.json
- 微信搜索索引配置材失,配置你的某些頁面是否可以在當前小程序內(nèi)被搜索痕鳍,有興趣了解的同學(xué)可參考文檔
當然,實際項目中目錄會稍微復(fù)雜,以筆者某個小程序項目為例:
有些大家已經(jīng)熟悉的或者已介紹文件不介紹:
components
- 存放小程序自定義組件目錄
resource
- 存放圖片笼呆、字體等靜態(tài)文件的目錄熊响,習(xí)慣上還常命名static
config.js
- 常量配置文件,當然你可以放在其他你喜歡存放的目錄
miniprogram_npm
- npm依賴被微信開發(fā)者工具二次構(gòu)建生成的目錄诗赌,不需手動創(chuàng)建汗茄,后面會有npm依賴安裝相關(guān)的介紹
UI框架
有了基本目錄結(jié)構(gòu),你可能需要一個UI框架來處理反饋(modal铭若、toast洪碳、動畫)等交互,而不是重新埋頭寫叼屠,這里筆者推薦幾個小程序UI框架瞳腌,具體的安裝(推薦npm安裝)移步各自文檔:
-
vant-weapp
vant
的小程序版本,強烈推薦 -
iview-weapp
iview
的小程序版本镜雨,已經(jīng)較久不更新 - weui 官方出品的微信風(fēng)格UI嫂侍,但組件也相對少
構(gòu)建
編輯器
在發(fā)布和調(diào)試時,你必須使用微信開發(fā)者
工具進行冷离。但在編碼時吵冒,你仍然可以使用自己喜好的編輯器,并安裝小程序的相關(guān)輔助插件
來協(xié)助開發(fā)小程序, 微信會實時監(jiān)聽
項目文件的修改并編譯到模擬器西剥。
在小程序使用npm依賴
npm依賴
的安裝和引入都和node環(huán)境下無差別痹栖,但你需在安裝好依賴或者有依賴更新
后在微信開發(fā)者工具
進行npm構(gòu)建
:
此時你會發(fā)現(xiàn)多了一個
miniprogram_npm
目錄瞭空,此時我們就可以直接像node環(huán)境一樣直接引入npm依賴.
當然揪阿,一些像
操作DOM
等小程序不支持的對象
的依賴是無法運行的,需注意區(qū)分
ES6
你可以使用ES6咆畏,考慮到兼容性時南捂,無需借助babel
來轉(zhuǎn)換為ES5,在右上角的詳情中勾選:
環(huán)境變量
在node環(huán)境旧找,可以在打包時注入一個全局變量用來區(qū)分環(huán)境溺健,以便處理在生產(chǎn)環(huán)境、開發(fā)環(huán)境下的不同邏輯:
#生產(chǎn)環(huán)境變量設(shè)置钮蛛,如:
set NODE_ENV=production
#開發(fā)環(huán)境變量設(shè)置鞭缭,如:
set NODE_ENV=development
但是小程序里,官方一直沒有做這方面的支持魏颓!所以你需在開發(fā)或者發(fā)布的時候手動
改一下用來充當環(huán)境區(qū)分的變量:
//示例岭辣,config.js
const ENV = 'product'; //環(huán)境變量,手動修改
module.exports = {
ENV,
DOMAIN: ENV === 'product'? 'a.com': 'dev.a.com',
TOKEN_KEY: ENV === 'product'? 'token' : 'token_dev',
//more...
}
編寫頁面
頁面的構(gòu)造甸饱,頁面的構(gòu)造需使用Page方法沦童。詳細的屬性和生命周期你可以在文檔中查看仑濒。
為了頁面生效,你還必須在根目錄下的
app.json
中的pages
字段配上所有頁面的路徑, 并且pages
數(shù)組第一個為小程序進入的首個頁面:{ "pages": [ "pages/index/index", //進入小程序時加載的第一個頁面 "pages/logs/logs" ], //more... }
新建頁面
頁面組成文件
假設(shè)新建的頁面叫page1
,則會生成四個文件:
page1.js
- Page方法的調(diào)用偷遗,處理頁面邏輯
page1.json
- 當前頁面的配置文件墩瞳,比如設(shè)置頁面背景色、導(dǎo)航欄標題等
page1.wxml
- 頁面視圖氏豌,類似html語法矗烛,但你使用的標簽需是小程序自有組件或者自定義組件名(下小節(jié)介紹);另外箩溃,view
標簽的地位相當于html中的div
瞭吃;并且,指令和事件綁定的語法和vue不同(如:v-if
對應(yīng)wx:if
)涣旨,具體介紹查看這里歪架,模板
那一塊看的不太明白可以先放下,因為你很可能可以使用組件的方式替代它霹陡。
page1.wxss
- 你幾乎可以把它當成一個css文件來看和蚪,只是不需要手動引入就會在當前頁面生效。具體與css的差異可查看這里
注意
: 小程序直接修改Page里的data并不會更新視圖烹棉,你需通過Page.prototype.setData(文檔搜索Page.prototype.setData
查看)方法來設(shè)置value才可更新視圖
組件
在較新的小程序版本已經(jīng)支持自定義組件攒霹,但開發(fā)體驗筆者認為不如vue的單文件組件。這里做一些簡單的對比介紹浆洗。
組件化
與頁面的構(gòu)成類似,組件的四個文件作用同頁面的一樣, 不同的主要有兩點:
- 在
js
文件里, 定義組件使用Component構(gòu)造器. - 在
json
文件的component
字段設(shè)為true
,如:
{
"component": true
}
組件的調(diào)用
在頁面
或者其他組件
目錄下的json
文件, 將所有引用的組件的標簽名(key,自己取名)和文件路徑(value)傳入usingComponents
{
"usingComponents": {
"component-tag-name1": "/components/component-tag-name1/component-tag-name1",
"component-tag-name1": "/components/component-tag-name1/component-tag-name1"
//more component...
}
//more config...
}
并在wxml
文件寫入組件標簽及其傳入的參數(shù)催束,事件等:
<component-tag-name1></component-tag-name1>
<component-tag-name2 option="{{yourOptionData}}" bindSomeEvent="eventCallback" />
下面介紹幾個傳入Component
構(gòu)造器的幾個重要屬性.
properties
相當于Vue
中的props
屬性, 約定父組件或者頁面?zhèn)魅氲膮?shù)格式:
Component({
properties: {
data1: {
type: Number, //參數(shù)的類型
value: 0 //默認值
},
data2: {
type: Number,
optionalTypes: [String, Object], // 可以指定多個屬性的類型, 如果type也定義了的話或被計入optionalTypes, 這里的參數(shù)可以是 Number 、 String 伏社、 Boolean 三種類型中的一種
observer: function(newVal, oldVal) { //參數(shù)值變化時的回調(diào)函數(shù), 與vue中`watch`的作用類似
}
}
}
})
behaviors
behavior
相當于vue
中的mixins
, 首先你得先構(gòu)造一個Behavior:
module.exports = Behavior({
behaviors: [], //behavior里可以引用其他behavior
properties: { //要共享的參數(shù)
myBehaviorProperty: {
type: String
}
},
data: { //要共享的data
myBehaviorData: {}
},
attached: function(){}, //要共享的生命周期處理邏輯
methods: { //要共享的方法
myBehaviorMethod: function(){}
}
})
然后在組件中引入:
// my-component.js
var myBehavior = require('my-behavior')
Component({
behaviors: [myBehavior],
properties: {
myProperty: {
type: String
}
},
//more...
})
observers
作用與vue
中的watch
屬性類似, 可以監(jiān)聽一個或多個數(shù)據(jù)變化并執(zhí)行回調(diào), 但語法上不同,詳情請移步文檔,這里不再贅述
生命周期
在小程序里, 組件的生命周期與頁面的生命周期并不相同(這是因為,在vue
里,無論頁面和組件都是vue
實例, 都可以看成組件
, 而在小程序是不一樣的實例), 并且App注冊小程序時也有自己的生命周期.
小程序組件的生命周期與Vue中的生命周期時間節(jié)點基本一致, 只是命名上的差異;并且生命周期方法可以作為和data
同級的屬性, 也可以放在lifetimes
屬性里,但后者的優(yōu)先級更高:
Component({
lifetimes: {
attached: function() {
// 在組件實例進入頁面節(jié)點樹時執(zhí)行
},
detached: function() {
// 在組件實例被從頁面節(jié)點樹移除時執(zhí)行
},
},
// 以下是舊式的定義方式抠刺,可以保持對 <2.2.3 版本基礎(chǔ)庫的兼容
attached: function() {
// 在組件實例進入頁面節(jié)點樹時執(zhí)行
},
detached: function() {
// 在組件實例被從頁面節(jié)點樹移除時執(zhí)行
},
// ...
})
另外組件還有一組生命周期可使用, 即組件所在頁面的生命周期(但并不是頁面所有生命周期方法, 也沒這個必要), 只有show
,hide
,resize
; 所在頁面生命周期回調(diào)寫在pageLifetimes
屬性:
Component({
pageLifetimes: {
show: function() {
// 頁面被展示
},
hide: function() {
// 頁面被隱藏
},
resize: function(size) {
// 頁面尺寸變化
}
}
})
組件通信和事件
與vue的通信方式一樣,事件是組件和頁面
或者組件和組件
主要和提倡的通信方式, 并且在小程序里, 也倡導(dǎo)單向數(shù)據(jù)流
規(guī)范.
在頁面或者組件的wxml
綁定事件:
<!-- 當自定義組件觸發(fā)“myevent”事件時,調(diào)用“onMyEvent”方法 -->
<component-tag-name bindmyevent="onMyEvent" />
<!-- 或者可以寫成 -->
<component-tag-name bind:myevent="onMyEvent" />
在組件里觸發(fā)事件:
Component({
properties: {},
methods: {
onTap: function(){
var myEventDetail = {} // detail對象摘昌,會作為參數(shù)傳遞到接收事件的方法里
var myEventOption = {} // 觸發(fā)事件的選項, 主要控制事件是否可冒泡或者可有捕獲截斷,詳情可參考文檔
this.triggerEvent('myevent', myEventDetail, myEventOption) //觸發(fā)事件`myevent`
}
}
})
路由
組件介紹完畢, 小程序的路由機制不依賴其他插件, 由小程序自己控制. 并且開放出來的能力只限于作頁面跳轉(zhuǎn)
并可攜帶參數(shù), 像路由變化回調(diào)
等能力并不開放, 跳轉(zhuǎn)攜帶參數(shù)時可在目標頁面的onLoad
生命周期方法的參數(shù)中接收:
//more...
//假設(shè)當前頁面路由為'/pages/demo/demo?a=1&b=2'
onLoad: function (options) { //options用于接收上個頁面?zhèn)鬟f過來的參數(shù)
console.log(options.a, options.b); // =>1, 2
})
//more...
路由跳轉(zhuǎn)的幾個方法你可以直接看文檔,這里不詳細介紹,只需注意理解的是頁面棧
的概念(類似于瀏覽器環(huán)境中的history
對象) ,和跳轉(zhuǎn)到tabbar
頁面需用wx.switchTab
方法.
全局變量/全局方法
在vue
的開發(fā)過程中, 定義一個全局方法/屬性時, 我們通常做法是掛到Vue
對象的原型上:
Vue.prototype.data1 = 'xxx';
Vue.prototype.method1 = ()=>{
};
//在Vue實例里可以方便的訪問到:
//more...
mounted(){
console.log(this.data1);
this.method1()
}
//more...
當然,一些經(jīng)常變動和訪問的狀態(tài)我們會交給Vuex
處理.
小程序里,全局變量/方法可以在App
方法中定義:
App({
onLaunch (options) {
// Do something initial when launch.
},
globalData: { //全局變量放在globalData
data1: '1',
data2: '2'
},
//全局方法直接定義在根級別
method1(){
}
})
調(diào)用:
const app = getApp();
Page({
data: {
},
onLoad() {
app.method1();
console.log(app.data1, app.data2); //=>1, 2
}
//more...
})
另外, 你還可以將它們直接掛到global
對象中, 但是一般不推薦
這么做.
global.data1 = '';
調(diào)試
微信開發(fā)者工具是用開源的chrome內(nèi)核二次開發(fā), 在微信開發(fā)者工具
中調(diào)試小程序, 跟在chrome
調(diào)試web幾乎一樣; 可惜的是你并不能下載一些比如'vue.js Devtools'這樣的擴展來協(xié)助調(diào)試, 所以很多時候你只能通過斷點
和打印log
來調(diào)試. 雖然工具提供了app級別的組件結(jié)構(gòu)和數(shù)據(jù)視圖, 但在筆者看來作用甚微(你可以體驗一下):
測試
與vue
的Vue Test Utils類似, 小程序提供miniprogram-simulate用于調(diào)試組件, 配合jest
之類的測試框架可以實現(xiàn)組件的測試.
更多細節(jié)開發(fā)者可以慢慢摸索, 感謝閱讀!