小程式在2017年1月上線之初,被社會極力吹捧,刻意去將其製造為一個“風口”,透支其價值。但是在之後一個月裡,石破天驚迅速歸為沉寂。媒體又開始過度消費小程式,大談其雞肋之處。
個人認為小程式的一個分水嶺是在12月28日。微信升級到6.6.1版本,將小程式入口移植主介面,下拉主介面即可選擇進入,並且支援新類目“小遊戲”。小程式逐漸升溫,在整個微信生態中扮演越來越重要的角色。時至今日,小程式的風潮如日中天,優秀的小程式很容易得到融資。這究竟是是另一輪泡沫,還是小程式本身真正已經進入了成熟期?我個人更相信後者。
我個人做過兩款小程式,對小程式有一定認識,但理解還是比較淺的。今天就僅從如何快速搭建一個小程式談起,揭開小程式的一絲神祕面紗。
概述
- 小程式是前後端分離的。
- 前端使用的是微信自定義的一套規範
wxml
+wxss
+json
+js
,我認為本質還是html
+css
+js
。 - 後臺可以選用任何你熟悉的語言:
Java
,Python
,PHP
,Ruby
等等,在這篇文章裡我選用Python
的Flask
框架+Gunicorn
+Nginx
來快速搭建。 - 資料庫我選擇
MySQL
,nosql資料庫我選擇Redis
。當然,你的小程式可以很輕量級,甚至不需要使用到資料庫。小程式一大思想“用完即走”。 - 後臺需要跑在一臺自己的伺服器上,同時你也需要一個已備案的
https
域名來進行對映。
附註
現在市面上也有一些第三方快速生成小程式的工具,和以前那些快速生成網站的是同一門生意。我個人並不推薦去使用那些,因為那些小程式幾乎千篇一律,無法結合你自己的創意,無法定製你需要提供的服務,而且必定存在一些收費。
當然,這些平臺既然存在,那麼必定是市場需求,假如確實適合你,能為你帶來一些效益,不妨一試。
今天,我們是以學習者的角度去構建小程式。
準備工作
- 一臺雲伺服器,可以上各大雲提供商平臺租用,我使用的是學生低配,¥10/月。
- 我在伺服器上使用的作業系統為
ubuntu
- 購買一個域名,並通過備案。域名價格在1-10000000不等,我使用的是某
com
域名,¥50/年。 - 在微信公眾平臺註冊一個賬號並下載小程式開發工具。詳細說明
- 從
http
到https
。現在很多SSL證照可以免費申請,下面會詳細說下如何配置。
目標
我們的目標是實現一個簡單的小程式,能夠實現前後端對接。
從http到https
- 首先擁有一個已備案域名,並已經解析到你的伺服器上了。
- 如果你在阿里/騰訊雲租用了伺服器,可以申請免費的SSL證照。找到相應入口並申請就可以了。稽核一般很快,我的在一小時以內。
- 稽核通過後下載頒發的證照,先儲存在本地。之後通過ftp傳到伺服器的相應路徑。
- 在伺服器上安裝
Nginx
。 - 首先測試你的Nginx服務是否能正常執行,配置完開啟自己的域名能顯示nginx的歡迎頁時即為成功配置。
- 然後將你的證照通過ftp上傳到伺服器的任意路徑下(建議和
Nginx
在同一路徑下) - 開啟
Nginx
的配置檔案,如圖配置(證照路徑填寫自己的) - 重啟服務,瀏覽器通過https訪問,能正常顯示頁面即為配置成功。
前端
現在,開啟你的小程式開發工具,並使用你的APPID新建一個專案。(我這裡沒有多餘的APPID,所以先使用測試環境)
可以先勾選“建立普通快速啟動模板”來生成一個官方測試demo,如下圖:
讓我們來觀察一下目錄結構。app.js
,app.json
,app.wxss
分別對應全域性的方法,全域性配置引數和全域性樣式。而在具體包下的index.js
,index.wxml
,index.wxss
則對應相應的元素。
現在讓我們來寫一點簡單的頁面的程式碼。
<
!-- index.wxml -->
<
view class="main-card">
<
view class="main-card-item" id="toast" wx:if="{{news_flag
}
}">
<
view class="card-item-hd" >
<
image class="card-item-icon" src="/images/index/toast.png">
<
/image>
<
text class="card-item-name">
大事兒<
/text>
<
/view>
<
view class='toast'>
Hello,歡迎觀看此教程,希望對你有幫助。 <
/view>
<
/view>
<
/view>
複製程式碼
/* index.wxss */page {
background-color: #f8f8f8;
}.main-card {
padding-bottom: 100rpx;
}.main-card-item{
display: flex;
flex-direction: column;
background: #fff;
border-top: 1rpx solid #F6F6EF;
border-bottom: 1rpx solid #F6F6EF;
margin-bottom: 20rpx;
background-repeat: no-repeat;
background-size: 100% auto;
background-position: bottom center;
overflow: hidden;
margin-left: 12rpx;
margin-right: 12rpx;
border-radius: 15rpx;
}.card-item-hd{
display: flex;
align-items: center;
height: 75rpx;
border-bottom: 1rpx solid #e5e5e5;
margin-left: 30rpx;
}.card-item-icon{
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}.card-item-name{
letter-spacing: 1px;
font-size: 25rpx;
}.toast{
letter-spacing:3rpx;
line-height: 50rpx;
font-size: 28rpx;
margin-left: 20rpx;
margin-top: 20rpx;
margin-bottom: 40rpx;
}複製程式碼
此時,一個簡單的頁面已經生成了,讓我們來看看效果。
很簡單,但是可以看出來「大事兒」裡的內容是寫死的,此時我們需要後端來提供資料。
###伺服器環境
在編寫後端之前,我們先把伺服器的環境部署一下。
安裝:
- 安裝了Python環境
apt-get install python-dev
- 安裝Flask
pip install flask
- 安裝UWSGI
pip install uwsgi
- 安裝了Nginx
apt-get install nginx
- 安裝了Gunicorn
pip install gunicorn
準備
首先在你的/var/www/
目錄下建立一個測試目錄,比如/var/www# mkdir test
然後使用chmod
更改此目錄的許可權chmod 777 /var/www/test
這裡講一下chmod
的規則,因為這裡是測試用例,所以為了方便,直接使用777。
Nginx
Ubuntu下的Nginx的目錄結構大致如下:
- 所有的配置檔案都在
/etc/nginx
下,每個虛擬主機已經安排在了/etc/nginx/sites-available
目錄下 - 啟動程式檔案在
/usr/sbin/nginx
- 日誌檔案放在了
/var/log/nginx
中,分別是access.log
和error.log
- 在
/etc/init.d/
下建立了啟動指令碼nginx - 預設的虛擬主機的目錄設定在了
/usr/share/nginx/www
啟動服務:/etc/init.d/nginx start
,重啟服務:/etc/init.d/nginx restart
現在,我們需要進入到Nginx的配置中,改動配置檔案。vim /etc/nginx/site-avalidable/default
更改配置檔案後重啟服務/etc/init.d/nginx restart
,或者service nginx restart
Gunicorn
Gunicorn 綠色獨角獸 是一個Python WSGI UNIX的HTTP伺服器。這是一個pre-fork worker的模型,從Ruby的獨角獸(Unicorn )專案移植。該Gunicorn伺服器大致與各種Web框架相容,只需非常簡單的執行,輕量級的資源消耗,以及相當迅速。
此時需要在“準備”步驟中建立的測試目錄下放入我們的測試執行專案,我選擇的FTP工具是:xftp。我傳入了一個簡單的用來測試的Python檔案wsgi.py
,使用命令/var/www/myflask# vim wsgi.py
預覽。
此時在測試目錄下鍵入命令gunicorn -w 4 -b 127.0.0.1:8000 wsgi:app
執行。
此時,訪問伺服器,可以看到“Hello World”已經可以正常顯示了。
關於Flask
後端我們採用Python
的Flask
框架+Gunicorn
+Nginx
來快速搭建。首先需要一些Python
的基礎知識,相信大家在菜鳥學Python學了這麼久,這完全不是問題。現在,讓我們瞭解一下Flask
如何使用。
一位使用多種語言開發複雜程式並且擁有十多年經驗的軟體工程師,曾經用 PHP, Ruby, Smalltalk 甚至 C++ 寫過 web 應用,他認為,在所有這些中,Python/Flask 組合是最為自由的一種。
在使用了Flask
之後,我也不得不承認,它確實很便捷快速。當然也會有一定的缺點,這是後話。
迴歸正題。
獲得物件
from flask import Flaskapp = Flask(__name__)@app.route('/')def hello_world(): return 'Hello World'if __name__ == '__main__': app.run() 複製程式碼
這是一個最簡單的Demo。
執行流程為:從flask模組獲取物件app,通過路由,執行方法,返回內容。
此時在瀏覽器訪問(預設埠5000):127.0.0.1:5000/ ,可以看到國際慣例Helloworld的介面。
路由
-
唯一URL:
@app.route('/hello')@app.route('/hello/')#這兩種需要區分#@app.route('/hello/')#在使用這種尾部帶斜線的url時,假如使用者沒有輸入尾部/,也將訪問到正確的頁面#@app.route('/hello')#在使用這種尾部不帶斜線的url時,假如使用者在尾部輸入了/,將返回404複製程式碼
這個規則似乎有點拗口,但其實也不能理解。優點是:
- 使得使用者在遺忘尾斜線時,允許關聯的 URL 接任工作,與 Apache 和其它的伺服器的行為並無二異
- 保證了 URL 的唯一,有助於避免搜尋引擎索引同一個頁面兩次。
如果實在記不清,最好的方法是破罐子破摔:統一不帶尾部“/”
-
構造URL中的動態部分
@app.route('/var/<
name>
')def var(name): return 'hello'+' '+name複製程式碼
這點就不贅述了,可以看一下演示效果:
模板渲染
大部分時候,在使用者訪問了一個URL的時候,我們都需要給他/她返回一個介面,我們當然不會用Python本身去渲染HTML,為此,Flask 配備了Jinja2 模板引擎。
看完以下程式碼示例,相信你就能理解。
首先,我們建立“templates”資料夾用於儲存模板。
Flask 會在 templates 資料夾裡尋找模板。所以,如果你的應用是個模組,這個資料夾應該與模組同級;如果它是一個包,那麼這個資料夾作為包的子目錄:
#情況 1: 模組:/application.py/templates /hello.html#情況 2: 包:/application /__init__.py /templates /hello.html複製程式碼
- 不含引數示例在程式執行:
@app.route('/redi/')def redi(): return render_template('hello.html')複製程式碼
- 再看另一個例子,加入動態引數:
@app.route('/redi2/<
name>
')def redi2(name): return render_template('hello2.html',name=name)複製程式碼
GET和POST
請求方式不止這個兩種,但是最常用的是這兩種,如果對這兩種不熟悉,可以先去查一下HTTP方法的資料,這裡只演示在flask中的用法。
@app.route('/met',methods=['GET','POST'])def met(): if request.method=='GET': return '這是get方法' if request.method=='POST': return '這是post方法'複製程式碼
開啟Postman這款軟體(Web神器),模擬傳送HTTP請求。
請求物件
下面我來模擬一個簡單的登入操作。
首先是控制器:
@app.route('/login',methods=['POST','GET'])def login(): error=None if request.method=='POST': print (request.form['username']+' '+request.form['password']) if func.login_func.valid_login(request.form['username'], request.form['password']): return func.login_func.login_success(request.form['username']) else: error='Invalid username/password' return render_template('login_error.html',error=error)複製程式碼
可以看到執行流程:
- 獲得請求
- 判斷請求型別
- 獲得登陸資料
- valid_login()方法驗證登陸4.1 若登陸成功,執行login_success()方法4.2 若登入失敗,新增失敗資訊,返回失敗模板
下面是上述用到的兩個方法:
def valid_login(username,password): if username=='admin' and password=='admin': return True複製程式碼
def login_success(username): return render_template('login_success.html',username=username)複製程式碼
下面使用Postman來模擬請求,看看能不能返回設想的結果。
這是一些簡單的Flask
操作,好了,我們現在對web有了一定的瞭解了。現在開始編寫我們的程式碼。
###後端萬事俱備,只欠東風。首先我們來寫兩個路由,一個用於更新通知,一個用於獲取通知。
@app.route("/updateToast",methods=['POST'])@allow_cross_domaindef update_toast(): data=db_util.update_toast(request.form['toastUpdateInfo']) return jsonify(data)@app.route("/getToast",methods=['GET'])@allow_cross_domaindef get_toast(): data = db_util.get_toast_info() return jsonify(data)複製程式碼
然後寫一個工具類,用於直接運算元據庫(這種設計並不規範,只是為了快速演示)
#db_util.pydef get_toast_info(): db = pymysql.Connect( host='xxx', port=3306, user='xxx', passwd='xxx', db='xxx', charset='utf8' ) cursor = db.cursor() sql = "select content from guohe_lite_toast order by id desc limit 1 " try: cursor.execute(sql) result = cursor.fetchone() return response_info.success('小程式通知查詢成功', result) except: return response_info.error('2', '小程式通知查詢失敗', result) # 關閉資料庫連線 finally: db.close()def update_toast(toast_update_info): db = pymysql.Connect( host='xxx', port=3306, user='xxxx', passwd='xxx', db='xxxx', charset='utf8' ) cursor = db.cursor() sql = "insert into guohe_lite_toast(content,update_time) values(%s,%s) " try: dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") cursor.execute(sql,(toast_update_info,dt)) db.commit() return response_info.success('通知更新成功', toast_update_info) except: db.rollback() return response_info.error("2",'更新失敗', toast_update_info) finally: db.close()複製程式碼
現在,讓我們使用postman來測試一下介面。
首先更新一下通知:
然後看能不能成功獲取:
這一切都生效了,資料介面已經準備就緒。
資料渲染
那麼,現在如何在小程式端獲取資料並顯示呢?我們去簡要讀下小程式的官方文件。
請注意,小程式是純非同步方式來傳送請求的。
依葫蘆畫瓢,我們來模仿一下:
#index.jswx.request({
url: 'https://example.com/getToast', method: 'GET', header: {
'content-type': 'application/x-www-form-urlencoded' // 預設值
}, success: function (res) {
var message = res.data.info[0] console.log(message) that.setData({
toast: message
})
}
})複製程式碼
我們將獲取的資料已經儲存在”toast”這個變數中了,再去讀文件,看看小程式是如何進行資料繫結的。然後我們將之前寫死的文字換成”{{toast
}
}”,這時再重新整理,可以看到,資料已經顯示了。
<
!-- index.wxml -->
<
view class="main-card">
<
view class="main-card-item" id="toast" wx:if="{{news_flag
}
}">
<
view class="card-item-hd" >
<
image class="card-item-icon" src="/images/index/toast.png">
<
/image>
<
text class="card-item-name">
大事兒<
/text>
<
/view>
<
view class='toast'>
{{toast
}
} <
/view>
<
/view>
<
/view>
複製程式碼
此時,一套完整的流程已經結束,雖然實現了一個微小的功能,但麻雀雖小,五臟俱全。接下來,就是去進一步學習,去如何改造以及豐富我們的專案了。
比如稍微努力一下:
來源:https://juejin.im/post/5ae7b96f6fb9a07aa6318df1?utm_medium=fe&utm_source=weixinqun