今天是2018年8月25日(寫于10:00 a.m. 剛摸索完微信公眾號(hào)開發(fā)的權(quán)限)振惰,如今的小程序開發(fā)如火如荼,而筆者接觸過小程序開發(fā)后,認(rèn)為小程序后端更適合用充血模型來寫(PHP凿叠、node.js、Python等)嚼吞,而微信團(tuán)隊(duì)也為每個(gè)小程序開發(fā)者配備一個(gè)免費(fèi)的專門部署該小程序后端項(xiàng)目的云服務(wù)器(僅支持PHP盒件、node.js這兩種單線程語(yǔ)言,筆者懷疑這種云服務(wù)器只分配一個(gè)端口給開發(fā)者)舱禽。所以炒刁,為了更方便地開發(fā)小程序,筆者決定學(xué)習(xí)node.js誊稚,并仿照 反義詞消消樂 小程序來開發(fā)一款面向0基礎(chǔ)日語(yǔ)愛好者的產(chǎn)品——日語(yǔ)50音消消樂
文章內(nèi)容預(yù)告
本文會(huì)按著日期把筆者的學(xué)習(xí)進(jìn)度以及開發(fā)進(jìn)度寫進(jìn)來翔始,包括一些學(xué)習(xí)筆記以及開發(fā)細(xì)節(jié)
思維導(dǎo)圖
一、node.js學(xué)習(xí)(8.25)
-
同步與異步體現(xiàn)JavaScript也頗具后端開發(fā)價(jià)值(第四章)
同步的體現(xiàn):回調(diào)函數(shù)里伯、除了delay功能外的以分號(hào)分隔的每一句話
異步的體現(xiàn):delay事件
-
Controller控制層的實(shí)現(xiàn)(第五章绽昏、第十五章)
node.js可以通過下面的示例代碼實(shí)現(xiàn)類似JavaWeb的Controller,實(shí)現(xiàn)業(yè)務(wù)的分發(fā)俏脊。其中全谤,wx.request.get: https://bxee5pf9.qcloud.la//data前面那個(gè)是小程序后端自動(dòng)分配的https域名,后面會(huì)提到
即可得到 json 格式的數(shù)據(jù)爷贫。做到這一步认然,就距離完成后端的搭建就只差node.js和mysql的交互了!漫萄!
// 引入包
var http = require('http'),
url = require('url');
//定義測(cè)試對(duì)象
var obj = {
name : "Officer",
surname : "Dibble"
}
//相互轉(zhuǎn)化
var json = JSON.stringify(obj);
var parsedJson = JSON.parse(json);
// 創(chuàng)建服務(wù)器
http.createServer(function (req, res) {
var pathname = url.parse(req.url).pathname;
if (pathname === '/') { //默認(rèn)首頁(yè)(普通nodejs學(xué)習(xí))
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('Home Page\n');
} else if (pathname === '/about') { //測(cè)試跳轉(zhuǎn)頁(yè)面(普通nodejs學(xué)習(xí))
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('About Us\n');
} else if (pathname === '/data') { //小程序測(cè)試數(shù)據(jù)API(返回json數(shù)據(jù))
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end(json);
} else {
res.writeHead(404, { //報(bào)錯(cuò)設(shè)置
'Content-Type': 'text/plain'
});
res.end('Page not found\n');
}
//小程序后端服務(wù)器默認(rèn)開放5757端口
}).listen(5757, "127.0.0.1");
-
數(shù)據(jù)庫(kù)表的設(shè)計(jì)(存放筆記數(shù)據(jù))
每一個(gè)小程序賬號(hào)都可以免費(fèi)開通一個(gè)無法進(jìn)入終端的騰訊云服務(wù)器卷员,詳見官方教程。開通后再重新建立一個(gè) node.js快速啟動(dòng)模板 小程序項(xiàng)目腾务,即可自動(dòng)生成含有后端代碼示例的項(xiàng)目毕骡,注釋掉其中的核心代碼,換上上一小節(jié)的代碼就可以使用了。(PS:首次上傳代碼需要初始化服務(wù)器的配置未巫,要耐心等上一點(diǎn)時(shí)間窿撬,完成后有驚喜)
CREATE TABLE `notes` (
`id` int(11) NOT NULL COMMENT '主鍵',
`title` varchar(15) NOT NULL COMMENT '筆記標(biāo)題',
`content` text NOT NULL COMMENT '內(nèi)容',
`author` varchar(10) NOT NULL COMMENT '署名',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
`password` varchar(15) DEFAULT NULL COMMENT '密碼',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改時(shí)間',
`star` int(4) NOT NULL DEFAULT '0' COMMENT '星星數(shù)'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `notes`
ADD PRIMARY KEY (`id`);
ALTER TABLE `notes`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵';COMMIT;
沒想到配給小程序的服務(wù)器居然自帶mysql(含GUI),而筆者接觸最多的也是mysql叙凡,所以就不折騰MongoDB了
-
node.js與mysql的交互
到這里劈伴,筆者認(rèn)為有必要順便學(xué)一下node.js的封裝方式,這樣可以實(shí)現(xiàn)解耦握爷,MVC分層的項(xiàng)目最容易修改邏輯了跛璧。目標(biāo):1.跨文件調(diào)用方法 2.跨文件返回?cái)?shù)據(jù)。
(1) 跨文件調(diào)用方法
(module.exports >) [object].function
/* ========= 這里是 server.js ======== */
var service=require('./MVC/service');
service.initModelList();
(2) 跨文件返回?cái)?shù)據(jù)
module.exports = [object]
/* ========= 這里是 ./MVC/service ======== */
//初始化
var initModelList = function(){
//運(yùn)行時(shí)的特效新啼,可用于檢驗(yàn)邏輯
console.log("[Service function] initModelList");
//循環(huán)補(bǔ)全數(shù)組
for (var i = 0; i < 23; i++) {
//假裝在初始化
}
}
//返回方法
module.exports = {
checkPassword
}
------------------------ 8.26 ------------------------
學(xué)習(xí)新技術(shù)最好的方式是看官方文檔或看書追城,其次是搜博客。
node.js 與 mysql 交互流程如下:
- 創(chuàng)建連接
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
port: 3306,
user: 'root',
database: 'japan',
password: 'root',
charset: 'utf8'
});
var startConnection = function(){
//運(yùn)行時(shí)的特效燥撞,可用于檢驗(yàn)邏輯
console.log("[DAO function] startConnection");
connection.connect();
}
var closeConnection = function(){
//運(yùn)行時(shí)的特效漓柑,可用于檢驗(yàn)邏輯
console.log("[DAO function] closeConnection");
connection.end();
}
- 設(shè)計(jì)SQL語(yǔ)句并執(zhí)行
var creadNote = function(note){
//運(yùn)行時(shí)的特效,可用于檢驗(yàn)邏輯
console.log("[DAO function] creadNote");
connection.query(
'INSERT INTO notes ( title, content, author, password ) VALUES ( ?, ?, ?, ? )',
[note.title, note.content, note.author, note.password],
function (error, results, fields) {
// error will be an Error if one occurred during the query
// results will contain the results of the query
// fields will contain information about the returned results fields (if any)
if (error) throw error;
return results;
}
);
}
- 幾個(gè)瓶頸
(1)JS的深拷貝和淺拷貝
(2)node.js 返回時(shí)叨吮,要將對(duì)象轉(zhuǎn)化為json數(shù)據(jù)辆布,否則會(huì)報(bào)錯(cuò),注意茶鉴,若沒有報(bào)錯(cuò)而是返回空值锋玲,則是其他問題(返回的對(duì)象為空值)
(3)node.js 中的 mysql 做查詢時(shí),要不停地 callback 直到返回給用戶涵叮,否則將返回空值
var getList = function(callback){
//運(yùn)行時(shí)的特效惭蹂,可用于檢驗(yàn)邏輯
console.log("[DAO function] getList");
connection.query(
'SELECT * FROM `notes`',
function (error, results, fields) {
// error will be an Error if one occurred during the query
// results will contain the results of the query
// fields will contain information about the returned results fields (if any)
if (error) throw error;
result=JSON.stringify(results);
callback(result);
}
);
}
好了,有了這些基礎(chǔ)知識(shí)割粮,后端就可以搭建完了
- 日文50音的獲取
-
從百度百科獲取所有文字?jǐn)?shù)據(jù)
解讀百度語(yǔ)音api
解讀后發(fā)現(xiàn)百度語(yǔ)音合成不支持日文盾碗。而且這種自動(dòng)合成的服務(wù)(科大訊飛依然),聲音也一般般舀瓢⊥⒀牛或許這種情況下更適合用真人錄音吧。為了便于整理京髓,還是把音頻文件爬下來吧
import requests
import csv,codecs
from pyquery import PyQuery as pq
rooturl='http://o-oo.net.cn/50yinmp3/'
def do():
# 新建文件存儲(chǔ)數(shù)據(jù)
with codecs.open('japan50.csv', 'w','utf_8_sig') as csvf:
fieldnames=['No','Hiragana', 'url']
writer=csv.DictWriter(csvf,fieldnames=fieldnames)
writer.writeheader()
csvf.close()
# 訪問小楠日語(yǔ)50音頁(yè)面
response=requests.get(rooturl)
# 預(yù)處理航缀,除去無關(guān)url
doc=pq(response.content).remove_namespaces()
table=doc('.table-striped')
items=table.find('td').items()
i=1
h='あ'
with codecs.open('japan50.csv', 'a+','utf_8_sig') as csvf:
fieldnames=['No','Hiragana', 'url']
writer=csv.DictWriter(csvf,fieldnames=fieldnames)
for item in items:
if(i%2!=0):
h=item.text()
else:
message={
'No':i/2,
'Hiragana':h,
'url':item('audio').attr('src')
}
print(message)
writer.writerow(message)
i=i+1
csvf.close()
do()
- 按一定規(guī)則上傳到小程序CDN
再次使用快速編程手槍——Python3進(jìn)行數(shù)據(jù)處理
import urllib.request as req
import xlrd
h='./mp3/h/'
k='./mp3/k/'
def do():
workbook1=xlrd.open_workbook(r'japan50.xlsx')
sheets1 = workbook1.sheet_names()
print("wordSHEET= ",sheets1[2])
wordSHEET = workbook1.sheet_by_name(sheets1[2])
print(wordSHEET.nrows-1)
for a in range(1,wordSHEET.nrows):
ROW=wordSHEET.row_values(a)
#Hiragana
req.urlretrieve(ROW[2],h+ROW[0]+'.mp3')
#Katakana
req.urlretrieve(ROW[2],k+ROW[1]+'.mp3')
do()
------------------------ 8.27 ------------------------
今天的時(shí)間都用來實(shí)現(xiàn)頁(yè)面了,到晚上提交審核了才有時(shí)間寫博
二堰怨、游戲頁(yè)面實(shí)現(xiàn)
這里算是整個(gè)項(xiàng)目中最考驗(yàn)智慧的地方了芥玉,這個(gè)時(shí)候不應(yīng)該魯莽地死磕,而應(yīng)梳理好合適的思路再行動(dòng)备图,避免做無用功灿巧。
-
和傳統(tǒng)消消樂的對(duì)比
普通的消消樂:棋盤格子多赶袄、消除子種類少且不限制每種消除子同時(shí)出現(xiàn)的個(gè)數(shù)。
50音消消樂:棋盤格子少抠藕、消除子種類多且限制每對(duì)消除子必須同時(shí)出現(xiàn)饿肺。 -
棋盤刷新業(yè)務(wù)歸納
(1)平假名和片假名以羅馬音配對(duì),沒一對(duì)必須同時(shí)出現(xiàn)在棋盤上
(2)消除子隨機(jī)出現(xiàn)在盤上幢痘,確保游戲的公正無規(guī)律性 - 棋盤刷新思路與算法
- 思路:
(1)雙進(jìn)程同步刷新消除子唬格,實(shí)現(xiàn)消除子的成對(duì)性
(2)每個(gè)格子生成隨機(jī)數(shù)家破,完成后再按隨機(jī)數(shù)對(duì)格子排序颜说,實(shí)現(xiàn)刷新的無規(guī)律性
(3)順序遍歷所有棋盤格,以有序的方法實(shí)現(xiàn)無序汰聋,提升算法效率(起碼不用隨機(jī)生成坐標(biāo)后再判斷該格子是否可落子) - 算法:(啰啰嗦嗦暴力算法寫了近100行)
/**
* 備注:
* 1. 棋盤大小 4×4
* 2. 100000000是為了盡量減小隨機(jī)數(shù) rand[] 出現(xiàn)相同的情況 (否則將報(bào)錯(cuò))
*/
refresh:function(){
//判斷取大取忻欧唷(以排序后的左端為選取文字的標(biāo)準(zhǔn)還是以右端)Z[0, 1]
var judge = new Array(8);
for (var i = 0; i < judge.length; i++) {
judge[i] = Math.round(Math.random());
}
//對(duì)應(yīng)每個(gè)格子的隨機(jī)數(shù) Z[0, 99]
var rand = new Array(16);
for (var i = 0; i < rand.length; i++) {
rand[i] = Math.floor(Math.random()*100000000);
}
//rand[]數(shù)組每個(gè)格子對(duì)應(yīng)的排序序號(hào)(升序序號(hào))
var sort = new Array(16);
var n = 1;
for (var i = 0; i < sort.length; i++) {
n = 1;
for (var j = 0; j < rand.length; j++) {
if (rand[i] > rand[j]) {
n++;
}else{
continue;
}
}
sort[i] = n;
}
//合并 sort 和 rand
var connect = new Array(16);
for (var i = 0; i < connect.length; i++) {
connect[i]={
rand:rand[i],
sort:sort[i]
};
}
//存放排序后的 rand
var sortedRandom = new Array(16);
for (var j = 1; j < 17; j++) {
//按照 sort 對(duì) rand 排序
for (var i = 0; i < sortedRandom.length; i++) {
if (sort[i]==j) {
sortedRandom[j-1]=rand[i];
}else{
continue;
}
}
}
//刷新業(yè)務(wù)
var that=this;
var L = that.data.hiragana.length;
for (var i = 0; i < connect.length; i++) { //i遍歷所有格子
var x = Math.floor(i/4);
var y = i - x*4;
var value = connect[i].sort;//當(dāng)前格子的隨機(jī)排序值
var no = 'chess['+x+']['+y+'].no';
var word = 'chess['+x+']['+y+'].word';
var url = 'chess['+x+']['+y+'].url';
if (value < 9) { //分開 sort[1, 8] sort[9, 16] (輸入類型 大/小) 輸入=小
//隨機(jī)大小標(biāo)準(zhǔn)
if (judge[value-1]==0) {//無需折半獲取判斷值 (輸出結(jié)果 取大還是取小 輸入是否有效)
that.setData({
[no]:value,
[word]:that.data.hiragana[Math.floor(connect[i].rand*L/100000000)].word,
[url]:that.data.hiragana[Math.floor(connect[i].rand*L/100000000)].voice
});
}else{ //以大為準(zhǔn)烹困,則此處跳過 (大之處理)
that.setData({
[no]:value,
[word]:that.data.katakana[Math.floor(sortedRandom[16-value]*L/100000000)].word,
[url]:that.data.katakana[Math.floor(sortedRandom[16-value]*L/100000000)].voice
});
}
}else{ // 輸入=大
if (judge[16-value]==0) {//需鏡返獲取判斷值 (輸出結(jié)果 取大還是取小 輸入是否有效)(小之處理)value]*L/100000000)].word);
that.setData({
[no]:value,
[word]:that.data.katakana[Math.floor(sortedRandom[16-value]*L/100000000)].word,
[url]:that.data.katakana[Math.floor(sortedRandom[16-value]*L/100000000)].voice
});
}else{ //以大為準(zhǔn)玄妈,則此處執(zhí)行
that.setData({
[no]:value,
[word]:that.data.hiragana[Math.floor(connect[i].rand*L/100000000)].word,
[url]:that.data.hiragana[Math.floor(connect[i].rand*L/100000000)].voice
});
}
}
}
},
三、筆記頁(yè)面實(shí)現(xiàn)
既然后端搭建好了髓梅,這里難度不大拟蜻,主要工作包括:1.布局實(shí)現(xiàn) 2.數(shù)據(jù)渲染
邊框干貨
四、要點(diǎn)總結(jié)
寫了一天前端枯饿,先從前端開始說吧酝锅,總結(jié)一下本次項(xiàng)目中一些發(fā)揮了重要作用的東西
-
前端方面:
(1)要注意this.setData({ })的各種用法,尤其是數(shù)組對(duì)象的拼接奢方,因?yàn)橹安冗^坑搔扁,積累下來的經(jīng)驗(yàn)使我這次前端開發(fā)整體順利。
var that=this;
......
var no = 'chess['+x+']['+y+'].no';
var word = 'chess['+x+']['+y+'].word';
var url = 'chess['+x+']['+y+'].url';
......
that.setData({
[no]:value,
[word]:that.data.hiragana[Math.floor(connect[i].rand*L/100000000)].word,
[url]:that.data.hiragana[Math.floor(connect[i].rand*L/100000000)].voice
});
(2)wx:for實(shí)現(xiàn)循環(huán)渲染蟋字,wx:if實(shí)現(xiàn)條件渲染稿蹲,這兩個(gè)是實(shí)現(xiàn)動(dòng)態(tài)渲染以及游戲邏輯的重要工具
(3)WXSS的積累,例如padding鹊奖、margin等css樣式苛聘,規(guī)律是:上 右 下 左 (或:上下 左右),還有 flex 很實(shí)用布局的樣式設(shè)置也很實(shí)用
如果一個(gè)產(chǎn)品代表著一個(gè)武俠的話忠聚,上面三個(gè)點(diǎn)則方別代表著:內(nèi)功(數(shù)據(jù)的流轉(zhuǎn))焰盗、武功(數(shù)據(jù)的加載)、武器(用戶交互界面)
-
后端方面:
(1)在分層思想的基礎(chǔ)上開發(fā)很重要咒林,可以使思路清晰熬拒,代碼也容易找到與修改
(2)對(duì)后端的作用有較深后,使用不同語(yǔ)言進(jìn)行后端開發(fā)其實(shí)是差不多的垫竞,無非就是 業(yè)務(wù)的分發(fā)(Controller澎粟、DTO)+業(yè)務(wù)的實(shí)現(xiàn)(Service蛀序、DAO) 罷了
五、展望未來
更新方向:
- 完善游戲邏輯(添加游戲說明)
- 實(shí)現(xiàn)添加與修改筆記功能(為了滿足用戶一個(gè)人使用的需求)
- 實(shí)現(xiàn)筆記列表的下拉加載活烙、實(shí)現(xiàn)筆記的按作者查詢(為了方便用戶以作筆記的形式與日語(yǔ)學(xué)習(xí)者交流徐裸,分享干貨,共同積累)
- 實(shí)現(xiàn)筆記點(diǎn)贊功能(增強(qiáng)本產(chǎn)品的平臺(tái)性啸盏,提升價(jià)值與趣味)
感想:
1.如果貧血模型springboot(Java)是自動(dòng)步槍的話重贺,那么充血模型node.js就是自動(dòng)手槍,像小程序這種小項(xiàng)目回懦,用手槍就夠了气笙。兩者同樣是為了命中目標(biāo)而被設(shè)計(jì)出來的工具罷了,理解了射擊的原理怯晕,學(xué)習(xí)槍支的使用還是蠻簡(jiǎn)單的潜圃。
2.前端實(shí)現(xiàn)的效率還是低,沒能按計(jì)劃完成所有頁(yè)面舟茶,前端也是一個(gè)需要經(jīng)驗(yàn)積累的領(lǐng)域谭期,有經(jīng)驗(yàn)則方便(數(shù)據(jù)傳遞與渲染),無經(jīng)驗(yàn)則難行(WXSS樣式)吧凉。
3.第一次以寫博客日記的形式記錄做項(xiàng)目的過程隧出,感覺效率不錯(cuò),一方面督促自己提高效率阀捅,另一方便鍛煉自己的表達(dá)能力胀瞪,滿滿的健康感。
4.不過可惜但只好接受的是也搓,理想的功能并未完全實(shí)現(xiàn)赏廓,僅僅達(dá)到基本能用的程度(自我評(píng)價(jià)65分吧),要開學(xué)了傍妒,以后有時(shí)間再完善之幔摸。(如果后面覺得項(xiàng)目達(dá)到值得傳到開源社區(qū)的級(jí)別,我再把鏈接發(fā)上來)
本人總結(jié)的學(xué)習(xí)新技能步驟:
1.hello world嘗試颤练,明白基本理論(明白了理論就具備了解讀優(yōu)秀作品的能力)
2.解讀優(yōu)秀作品(例如借鑒先導(dǎo)們的作品既忆,結(jié)合自身的實(shí)際需求進(jìn)行修改,投入到實(shí)際的使用中嗦玖,在使用中消化知識(shí)患雇、感悟知識(shí))
3.自己從0開始獨(dú)立開發(fā)一次,通過實(shí)踐成為別人的先導(dǎo)宇挫,鞏固這項(xiàng)新技能苛吱。
學(xué)習(xí)知識(shí) = 進(jìn)食 + 咀嚼 + 消化 + 吸收
進(jìn)食 = 閱讀/學(xué)習(xí),掌握基本理論器瘪、掌握賞析能力
咀嚼 = 做筆記整理/解讀優(yōu)秀作品(順便再次閱讀/學(xué)習(xí))
消化 = 反復(fù)思考+理解
吸收 = 在實(shí)際生活學(xué)習(xí)或項(xiàng)目中嘗試使用該知識(shí)翠储、感受該知識(shí)
——《如何高效學(xué)習(xí)》
------------------------ 后續(xù) 8.28 ------------------------
小程序計(jì)時(shí)器
小程序自定義彈窗
小程序<input>數(shù)據(jù)的獲取
小程序<text>省略超長(zhǎng)內(nèi)容
小程序動(dòng)態(tài)改變頁(yè)面標(biāo)題
小程序字體樣式小結(jié)
小程序布局小結(jié)
------------------------ 后續(xù) 8.29 ------------------------
管窺帶來的帶寬減少效應(yīng)越來越明顯了绘雁,今天之內(nèi)一定要把 “三天計(jì)劃” 想達(dá)到的程度實(shí)現(xiàn)!
2018.8.29 11:35 a.m. 基本實(shí)現(xiàn) “三天計(jì)劃” 想達(dá)到的程度援所,達(dá)到自評(píng)90分的程度并提交審核庐舟。
2018.8.31 14:22 p.m. 修改三次,通過審核(只剩下游戲頁(yè)面)